package cultivateadmin import ( "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "io" "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" ) func TestClientSignsScopeAndConvertsSnakeCase(t *testing.T) { const secret = "test-secret" server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { t.Fatal(err) } if r.URL.RequestURI() != "/internal/admin/cultivate/tenants/default/dashboard?a=1&b=2" { t.Fatalf("request URI = %q", r.URL.RequestURI()) } if r.Header.Get("X-Tenant-ID") != "1" || r.Header.Get("X-Operator-ID") != "14" { t.Fatalf("unexpected scope headers") } hash := sha256.Sum256(body) canonical := strings.Join([]string{ r.Method, r.URL.RequestURI(), hex.EncodeToString(hash[:]), r.Header.Get("X-Timestamp"), r.Header.Get("X-Nonce"), "1", "14", "imManager", "req-test", }, "\n") mac := hmac.New(sha256.New, []byte(secret)) _, _ = mac.Write([]byte(canonical)) if !hmac.Equal([]byte(r.Header.Get("X-Signature")), []byte(hex.EncodeToString(mac.Sum(nil)))) { t.Fatal("signature mismatch") } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]any{ "code": 0, "data": map[string]any{"plant_count": 7, "active_7d": 3}, }) })) defer server.Close() client := NewClient(Config{BaseURL: server.URL, ServiceName: "im-manager", Secret: secret, Timeout: time.Second}) client.http = server.Client() var out struct { PlantCount int64 `json:"plantCount"` Active7d int64 `json:"active7d"` } values := make(url.Values) values["b"] = []string{"2"} values["a"] = []string{"1"} err := client.Get(context.Background(), Scope{TenantID: 1, TenantCode: "default", OperatorID: 14, RoleKey: "imManager", RequestID: "req-test"}, "/dashboard", values, &out) if err != nil { t.Fatal(err) } if out.PlantCount != 7 || out.Active7d != 3 { t.Fatalf("unexpected response: %+v", out) } } func TestClientReturnsBusinessMessage(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusConflict) _, _ = w.Write([]byte(`{"code":40901,"reason":"STATE_CONFLICT","message":"当前状态不允许操作"}`)) })) defer server.Close() client := NewClient(Config{BaseURL: server.URL, ServiceName: "im-manager", Secret: "secret", Timeout: time.Second}) client.http = server.Client() err := client.Post(context.Background(), Scope{TenantID: 1, TenantCode: "default", OperatorID: 14, RequestID: "req-test"}, "/content-reviews/feed_post/1/actions", map[string]any{"action": "approve"}, nil) if err == nil || err.Error() != "当前状态不允许操作" { t.Fatalf("unexpected error: %v", err) } } func TestClientGeneratesRequestIDWhenContextHasNone(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestID := r.Header.Get("X-Request-ID") if !strings.HasPrefix(requestID, "manager-") || len(requestID) <= len("manager-") { t.Fatalf("unexpected request ID: %q", requestID) } _, _ = w.Write([]byte(`{"code":0,"data":null}`)) })) defer server.Close() client := NewClient(Config{BaseURL: server.URL, ServiceName: "im-manager", Secret: "secret", Timeout: time.Second}) client.http = server.Client() err := client.Get(context.Background(), Scope{TenantID: 1, TenantCode: "default", OperatorID: 14}, "/dashboard", nil, nil) if err != nil { t.Fatal(err) } }