package integration_test import ( "encoding/json" "fmt" "net/http" "net/url" "testing" "time" ) func TestUserInsight(t *testing.T) { r, _ := setupAPIPool(t) tok := adminLogin(t, r, "admin", "change-me") _, code := doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/users/"+fakeUUID()+"/insight", nil, "") if code != http.StatusUnauthorized { t.Fatalf("expected 401 without token, got %d", code) } testBearer = "" phone := fmt.Sprintf("1%010d", time.Now().UnixNano()%10_000_000_000) nick := "insight_" + phone[7:] env, key := doJSON(t, r, http.MethodPost, "/api/v1/auth/register", map[string]any{ "phone": phone, "password": "secret12", "nickname": nick, }, "") sess := decodeData[map[string]any](t, env.Data) tokUser, _ := sess["token"].(string) if tokUser == "" { t.Fatal("missing token") } testBearer = tokUser t.Cleanup(func() { testBearer = "" }) env, key = doJSON(t, r, http.MethodPost, "/api/v1/profiles", map[string]any{ "relation": "self", "birth_date": "1991-04-08", "display_name": "洞察测", }, key) profile := decodeData[map[string]any](t, env.Data) profileID, _ := profile["id"].(string) _, key = doJSON(t, r, http.MethodPost, "/api/v1/reports/portrait", map[string]any{ "profile_id": profileID, }, key) env, code = doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/users?q="+url.QueryEscape(nick), nil, tok) if code != 200 { t.Fatalf("list users http=%d", code) } var list struct { Items []struct { ID string `json:"id"` } `json:"items"` } _ = json.Unmarshal(env.Data, &list) if len(list.Items) == 0 { t.Fatal("expected users") } userID := list.Items[0].ID start := time.Now() env, code = doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/users/"+userID+"/insight", nil, tok) if code != 200 || env.Code != 0 { t.Fatalf("insight http=%d code=%d msg=%s", code, env.Code, env.Message) } if time.Since(start) > 500*time.Millisecond { t.Fatalf("insight too slow: %v", time.Since(start)) } var insight struct { UserID string `json:"user_id"` ProfilesCount int `json:"profiles_count"` ReportsByType []struct { Type string `json:"type"` Count int `json:"count"` } `json:"reports_by_type"` Tags []struct { Code string `json:"code"` Label string `json:"label"` } `json:"tags"` Behavior struct { Events []any `json:"events"` AskThreadCount int `json:"ask_thread_count"` } `json:"behavior"` } if err := json.Unmarshal(env.Data, &insight); err != nil { t.Fatalf("decode insight: %v %s", err, env.Data) } if insight.UserID != userID { t.Fatalf("user_id mismatch %s vs %s", insight.UserID, userID) } if insight.ProfilesCount < 1 { t.Fatalf("expected profiles_count>=1 got %d", insight.ProfilesCount) } foundPortrait := false for _, c := range insight.ReportsByType { if c.Type == "portrait" && c.Count >= 1 { foundPortrait = true } } if !foundPortrait { t.Fatalf("expected portrait in reports_by_type: %#v", insight.ReportsByType) } foundTag := false for _, tag := range insight.Tags { if tag.Code == "portrait" && tag.Label != "" { foundTag = true } } if !foundTag { t.Fatalf("expected portrait tag: %#v", insight.Tags) } if insight.Behavior.Events == nil { t.Fatal("behavior.events must be non-nil array") } _, code = doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/users/"+fakeUUID()+"/insight", nil, tok) if code != http.StatusNotFound { t.Fatalf("expected 404 missing user, got %d", code) } _ = key } func fakeUUID() string { return "00000000-0000-4000-8000-000000000099" }