package admin import ( "bytes" "context" "encoding/json" "errors" "net/http" "net/http/httptest" "strings" "testing" "ds2api/internal/auth" "ds2api/internal/config" "ds2api/internal/deepseek" ) type testingDSMock struct { loginCalls int createSessionCalls int getPowCalls int callCompletionCalls int deleteAllSessionsCalls int deleteAllSessionsError error deleteAllSessionsErrorOnce bool } func (m *testingDSMock) Login(_ context.Context, _ config.Account) (string, error) { m.loginCalls++ return "new-token", nil } func (m *testingDSMock) CreateSession(_ context.Context, _ *auth.RequestAuth, _ int) (string, error) { m.createSessionCalls++ return "session-id", nil } func (m *testingDSMock) GetPow(_ context.Context, _ *auth.RequestAuth, _ int) (string, error) { m.getPowCalls++ return "", errors.New("should not call GetPow in this test") } func (m *testingDSMock) CallCompletion(_ context.Context, _ *auth.RequestAuth, _ map[string]any, _ string, _ int) (*http.Response, error) { m.callCompletionCalls++ return nil, errors.New("should not call CallCompletion in this test") } func (m *testingDSMock) DeleteAllSessionsForToken(_ context.Context, _ string) error { m.deleteAllSessionsCalls++ if m.deleteAllSessionsError != nil { err := m.deleteAllSessionsError if m.deleteAllSessionsErrorOnce { m.deleteAllSessionsError = nil } return err } return nil } func (m *testingDSMock) GetSessionCountForToken(_ context.Context, _ string) (*deepseek.SessionStats, error) { return &deepseek.SessionStats{Success: true}, nil } func TestTestAccount_BatchModeOnlyCreatesSession(t *testing.T) { t.Setenv("DS2API_CONFIG_JSON", `{"accounts":[{"email":"batch@example.com","password":"pwd","token":""}]}`) store := config.LoadStore() ds := &testingDSMock{} h := &Handler{Store: store, DS: ds} acc, ok := store.FindAccount("batch@example.com") if !ok { t.Fatal("expected test account") } result := h.testAccount(context.Background(), acc, "deepseek-chat", "") if ok, _ := result["success"].(bool); !ok { t.Fatalf("expected success=true, got %#v", result) } msg, _ := result["message"].(string) if !strings.Contains(msg, "Token 刷新成功") { t.Fatalf("expected session-only success message, got %q", msg) } if ds.loginCalls != 1 || ds.createSessionCalls != 1 { t.Fatalf("unexpected Login/CreateSession calls: login=%d createSession=%d", ds.loginCalls, ds.createSessionCalls) } if ds.getPowCalls != 0 || ds.callCompletionCalls != 0 { t.Fatalf("expected no completion flow calls, got getPow=%d callCompletion=%d", ds.getPowCalls, ds.callCompletionCalls) } updated, ok := store.FindAccount("batch@example.com") if !ok { t.Fatal("expected updated account") } if updated.Token != "new-token" { t.Fatalf("expected refreshed token to be persisted, got %q", updated.Token) } testStatus, ok := store.AccountTestStatus("batch@example.com") if !ok || testStatus != "ok" { t.Fatalf("expected runtime test status ok, got %q (ok=%v)", testStatus, ok) } } func TestDeleteAllSessions_RetryWithReloginOnDeleteFailure(t *testing.T) { t.Setenv("DS2API_CONFIG_JSON", `{"accounts":[{"email":"batch@example.com","password":"pwd","token":"expired-token"}]}`) store := config.LoadStore() ds := &testingDSMock{deleteAllSessionsError: errors.New("token expired"), deleteAllSessionsErrorOnce: true} h := &Handler{Store: store, DS: ds} req := httptest.NewRequest(http.MethodPost, "/delete-all", bytes.NewBufferString(`{"identifier":"batch@example.com"}`)) rec := httptest.NewRecorder() h.deleteAllSessions(rec, req) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d", rec.Code) } var resp map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { t.Fatalf("unmarshal response: %v", err) } if ok, _ := resp["success"].(bool); !ok { t.Fatalf("expected success response, got %#v", resp) } if ds.loginCalls != 2 { t.Fatalf("expected initial login plus relogin, got %d", ds.loginCalls) } if ds.deleteAllSessionsCalls != 2 { t.Fatalf("expected delete called twice, got %d", ds.deleteAllSessionsCalls) } updated, ok := store.FindAccount("batch@example.com") if !ok { t.Fatal("expected account") } if updated.Token != "new-token" { t.Fatalf("expected refreshed token persisted, got %q", updated.Token) } }