feat: add htsy CLI tool for Haitun private domain API

This commit is contained in:
dcsunny
2026-07-01 10:11:06 +08:00
commit 9e9ae38858
8 changed files with 1142 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
package main
import (
"os"
"path/filepath"
"testing"
)
func TestNormalizeBaseURL(t *testing.T) {
got, err := normalizeBaseURL("https://mpbot.cpshelp.cn/")
if err != nil {
t.Fatal(err)
}
if got != "https://mpbot.cpshelp.cn" {
t.Fatalf("got %q", got)
}
if _, err := normalizeBaseURL("mpbot.cpshelp.cn"); err == nil {
t.Fatal("expected invalid base url error")
}
}
func TestEndpointURL(t *testing.T) {
got, err := endpointURL("https://mpbot.cpshelp.cn/", "api/account/user/info")
if err != nil {
t.Fatal(err)
}
want := "https://mpbot.cpshelp.cn/api/account/user/info"
if got != want {
t.Fatalf("got %q want %q", got, want)
}
}
func TestReadJSONDataLiteralAndFile(t *testing.T) {
if _, err := readJSONData(`{"ok":true}`); err != nil {
t.Fatal(err)
}
if _, err := readJSONData(`{"ok":`); err == nil {
t.Fatal("expected invalid json error")
}
dir := t.TempDir()
path := filepath.Join(dir, "body.json")
if err := os.WriteFile(path, []byte(`{"from":"file"}`), 0o644); err != nil {
t.Fatal(err)
}
if _, err := readJSONData("@" + path); err != nil {
t.Fatal(err)
}
}
func TestConfigRoundTrip(t *testing.T) {
path := filepath.Join(t.TempDir(), ".htsy", "config.json")
cfg := Config{
BaseURL: "https://mpbot.cpshelp.cn",
Token: "token-value",
Username: "user",
LoginKind: "user",
}
if err := saveConfig(path, cfg); err != nil {
t.Fatal(err)
}
loaded, err := loadConfig(path)
if err != nil {
t.Fatal(err)
}
if loaded.Token != cfg.Token || loaded.Username != cfg.Username || loaded.BaseURL != cfg.BaseURL {
t.Fatalf("loaded config mismatch: %+v", loaded)
}
}
func TestTokenFromResponse(t *testing.T) {
token, err := tokenFromResponse([]byte(`{"token":"top-level"}`))
if err != nil {
t.Fatal(err)
}
if token != "top-level" {
t.Fatalf("got %q", token)
}
token, err = tokenFromResponse([]byte(`{"code":0,"data":{"token":"nested"}}`))
if err != nil {
t.Fatal(err)
}
if token != "nested" {
t.Fatalf("got %q", token)
}
}
func TestSplitCallArgs(t *testing.T) {
tests := []struct {
name string
args []string
wantPath string
wantFlags []string
wantErr bool
}{
{
name: "path first",
args: []string{"/api/account/user/info", "-d", "{}"},
wantPath: "/api/account/user/info",
wantFlags: []string{"-d", "{}"},
},
{
name: "flags first",
args: []string{"-d", "{}", "--raw", "/api/account/user/info"},
wantPath: "/api/account/user/info",
wantFlags: []string{"-d", "{}", "--raw"},
},
{
name: "equals flag",
args: []string{"--data={}", "/api/account/user/info"},
wantPath: "/api/account/user/info",
wantFlags: []string{"--data={}"},
},
{
name: "missing endpoint",
args: []string{"-d", "{}"},
wantErr: true,
},
{
name: "missing flag value",
args: []string{"/api/account/user/info", "-d"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotPath, gotFlags, err := splitCallArgs(tt.args)
if tt.wantErr {
if err == nil {
t.Fatal("expected error")
}
return
}
if err != nil {
t.Fatal(err)
}
if gotPath != tt.wantPath {
t.Fatalf("path got %q want %q", gotPath, tt.wantPath)
}
if len(gotFlags) != len(tt.wantFlags) {
t.Fatalf("flags got %#v want %#v", gotFlags, tt.wantFlags)
}
for i := range gotFlags {
if gotFlags[i] != tt.wantFlags[i] {
t.Fatalf("flags got %#v want %#v", gotFlags, tt.wantFlags)
}
}
})
}
}