package integration_test import ( "encoding/json" "net/http" "testing" "time" ) func TestAnalyticsOpsB(t *testing.T) { r, _ := setupAPI(t) sid := "s_test_" + time.Now().Format("150405") now := time.Now().UTC().Format(time.RFC3339Nano) env, key := doJSON(t, r, http.MethodPost, "/api/v1/analytics/events", map[string]any{ "items": []map[string]any{ {"name": "session_start", "session_id": sid, "client_ts": now, "props": map[string]any{"cold": true}}, {"name": "page_view", "session_id": sid, "page_path": "/portrait", "client_ts": now}, {"name": "page_view", "session_id": sid, "page_path": "/ask", "client_ts": now}, {"name": "page_leave", "session_id": sid, "page_path": "/portrait", "client_ts": now, "props": map[string]any{"dwell_ms": 1500}}, {"name": "ui_click", "session_id": sid, "client_ts": now, "props": map[string]any{"element_id": "home_cta", "page_path": "/"}}, {"name": "portrait_completed", "session_id": sid, "client_ts": now}, {"name": "session_end", "session_id": sid, "client_ts": now, "props": map[string]any{"exit_page": "/ask", "duration_ms": 8000}}, }, }, "") var accepted struct { Accepted int `json:"accepted"` } if err := json.Unmarshal(env.Data, &accepted); err != nil || accepted.Accepted < 6 { t.Fatalf("accepted=%v err=%v body=%s", accepted, err, string(env.Data)) } _ = key loginEnv, code := doAdminJSON(t, r, http.MethodPost, "/api/v1/admin/auth/login", map[string]string{ "username": "admin", "password": "change-me", }, "") if code != http.StatusOK || loginEnv.Code != 0 { t.Fatalf("admin login http=%d code=%d", code, loginEnv.Code) } var login struct { Token string `json:"token"` } _ = json.Unmarshal(loginEnv.Data, &login) if login.Token == "" { t.Fatal("missing admin token") } from := time.Now().UTC().Add(-24 * time.Hour).Format("2006-01-02") to := time.Now().UTC().Format("2006-01-02") q := "?from=" + from + "&to=" + to ov, code := doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/analytics/overview"+q, nil, login.Token) if code != http.StatusOK || ov.Code != 0 { t.Fatalf("overview http=%d code=%d msg=%s", code, ov.Code, ov.Message) } var overview map[string]any _ = json.Unmarshal(ov.Data, &overview) if sessions, _ := overview["sessions"].(float64); sessions < 1 { t.Fatalf("expected sessions>=1 got %v", overview) } pg, code := doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/analytics/pages"+q, nil, login.Token) if code != http.StatusOK || pg.Code != 0 { t.Fatalf("pages http=%d code=%d", code, pg.Code) } ex, code := doAdminJSON(t, r, http.MethodGet, "/api/v1/admin/analytics/exits"+q, nil, login.Token) if code != http.StatusOK || ex.Code != 0 { t.Fatalf("exits http=%d code=%d", code, ex.Code) } var exits struct { Items []struct { ExitPage string `json:"exit_page"` Count int `json:"count"` } `json:"items"` } _ = json.Unmarshal(ex.Data, &exits) found := false for _, it := range exits.Items { if it.ExitPage == "/ask" && it.Count >= 1 { found = true } } if !found { t.Fatalf("expected exit /ask in %v", exits.Items) } }