package task import ( "testing" "gpt-plus/internal/db" "gpt-plus/pkg/chatgpt" ) func TestFinalMembershipStateSatisfiedByTaskType(t *testing.T) { state := &finalMembershipState{ Personal: &chatgpt.AccountInfo{ AccountID: "personal-1", Structure: "personal", PlanType: "plus", HasActiveSubscription: true, SubscriptionID: "sub_123", }, Workspace: &chatgpt.AccountInfo{ AccountID: "workspace-1", Structure: "workspace", PlanType: "team", }, } if !state.satisfied(TaskTypePlus) { t.Fatal("expected plus task to be satisfied") } if !state.satisfied(TaskTypeTeam) { t.Fatal("expected team task to be satisfied") } if !state.satisfied(TaskTypeBoth) { t.Fatal("expected both task to be satisfied") } if got := state.resultPlanForTask(TaskTypePlus); got != TaskTypePlus { t.Fatalf("plus resultPlanForTask = %q, want %q", got, TaskTypePlus) } if got := state.resultPlanForTask(TaskTypeTeam); got != TaskTypeTeam { t.Fatalf("team resultPlanForTask = %q, want %q", got, TaskTypeTeam) } if got := state.resultPlanForTask(TaskTypeBoth); got != TaskTypeBoth { t.Fatalf("both resultPlanForTask = %q, want %q", got, TaskTypeBoth) } } func TestFinalMembershipStateFallbackToActualPlan(t *testing.T) { state := &finalMembershipState{ Personal: &chatgpt.AccountInfo{ AccountID: "personal-1", Structure: "personal", PlanType: "free", }, } if state.satisfied(TaskTypePlus) { t.Fatal("free personal account should not satisfy plus task") } if got := state.resultPlanForTask(TaskTypePlus); got != "free" { t.Fatalf("resultPlanForTask = %q, want free", got) } } func TestAccountStatusForResult(t *testing.T) { cases := []struct { name string result *chatgpt.AccountResult want string }{ {name: "nil", result: nil, want: "active"}, {name: "free", result: &chatgpt.AccountResult{PlanType: "free"}, want: "free"}, {name: "plus", result: &chatgpt.AccountResult{PlanType: "plus"}, want: "plus"}, {name: "team", result: &chatgpt.AccountResult{PlanType: "team"}, want: "team"}, {name: "unknown", result: &chatgpt.AccountResult{PlanType: "mystery"}, want: "active"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { if got := accountStatusForResult(tc.result); got != tc.want { t.Fatalf("accountStatusForResult() = %q, want %q", got, tc.want) } }) } } func TestSaveAccountToDBUsesRealMembershipStatus(t *testing.T) { d := setupTaskTestDB(t) runner := &TaskRunner{taskID: "task-1", gormDB: d} runner.saveAccountToDB(&chatgpt.AccountResult{ Email: "free@test.com", Password: "pw", PlanType: "free", }, "plus", nil, "task-1") var acct db.Account if err := d.First(&acct, "email = ?", "free@test.com").Error; err != nil { t.Fatalf("load account: %v", err) } if acct.Plan != "plus" { t.Fatalf("plan = %q, want plus", acct.Plan) } if acct.Status != "free" { t.Fatalf("status = %q, want free", acct.Status) } } func TestLogResultStoresUsefulMessage(t *testing.T) { d := setupTaskTestDB(t) taskRow := &db.Task{ID: "task-2", Type: "plus", Status: StatusRunning, TotalCount: 1} if err := d.Create(taskRow).Error; err != nil { t.Fatalf("create task: %v", err) } runner := &TaskRunner{taskID: "task-2", gormDB: d} runner.logResult(taskRow, 1, "a@test.com", LogStatusFailed, "free", "final membership mismatch", 3) var logRow db.TaskLog if err := d.First(&logRow, "task_id = ?", "task-2").Error; err != nil { t.Fatalf("load task log: %v", err) } if logRow.Message != "final membership mismatch" { t.Fatalf("message = %q, want final membership mismatch", logRow.Message) } }