package service import ( "archive/zip" "bytes" "encoding/json" "strings" "testing" "gpt-plus/internal/db" "github.com/glebarez/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) func setupExportTestDB(t *testing.T) *gorm.DB { t.Helper() d, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { t.Fatalf("open db: %v", err) } d.AutoMigrate(&db.Account{}) return d } func TestExportSinglePlusAccount(t *testing.T) { d := setupExportTestDB(t) d.Create(&db.Account{ Email: "plus@test.com", Plan: "plus", Status: "active", AccessToken: "at-123", RefreshToken: "rt-456", IDToken: "id-789", AccountID: "acc-001", }) data, filename, err := ExportAccounts(d, []uint{1}, "") if err != nil { t.Fatalf("export: %v", err) } if !strings.HasSuffix(filename, ".auth.json") { t.Fatalf("filename = %q, want *.auth.json", filename) } var auth authFile if err := json.Unmarshal(data, &auth); err != nil { t.Fatalf("unmarshal: %v", err) } if auth.AccessToken != "at-123" { t.Fatalf("access_token = %q", auth.AccessToken) } if auth.Tokens.AccountID != "acc-001" { t.Fatalf("tokens.account_id = %q", auth.Tokens.AccountID) } } func TestExportTeamWithSubAccounts(t *testing.T) { d := setupExportTestDB(t) owner := db.Account{ Email: "owner@test.com", Plan: "team_owner", Status: "active", AccessToken: "owner-at", RefreshToken: "owner-rt", AccountID: "team-acc", TeamWorkspaceID: "ws-123", WorkspaceToken: "ws-tok", } d.Create(&owner) d.Create(&db.Account{ Email: "member@test.com", Plan: "team_member", Status: "active", ParentID: &owner.ID, AccessToken: "mem-at", RefreshToken: "mem-rt", }) data, filename, err := ExportAccounts(d, []uint{owner.ID}, "test") if err != nil { t.Fatalf("export: %v", err) } if !strings.HasSuffix(filename, ".zip") { t.Fatalf("filename = %q, want *.zip", filename) } r, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { t.Fatalf("open zip: %v", err) } if len(r.File) != 2 { t.Fatalf("zip has %d files, want 2", len(r.File)) } names := make(map[string]bool) for _, f := range r.File { names[f.Name] = true } if !names["owner@test.com.auth.json"] { t.Fatal("missing owner auth file") } if !names["member@test.com.auth.json"] { t.Fatal("missing member auth file") } } func TestExportTeamOwnerUsesWorkspaceToken(t *testing.T) { d := setupExportTestDB(t) d.Create(&db.Account{ Email: "team@test.com", Plan: "team_owner", Status: "active", AccessToken: "normal-at", WorkspaceToken: "ws-at", TeamWorkspaceID: "team-ws-id", AccountID: "personal-acc", }) data, _, err := ExportAccounts(d, []uint{1}, "") if err != nil { t.Fatalf("export: %v", err) } var auth authFile json.Unmarshal(data, &auth) if auth.AccessToken != "ws-at" { t.Fatalf("should use workspace token, got %q", auth.AccessToken) } if auth.Tokens.AccountID != "team-ws-id" { t.Fatalf("should use team workspace ID, got %q", auth.Tokens.AccountID) } } func TestExportMultiplePlusAccounts(t *testing.T) { d := setupExportTestDB(t) d.Create(&db.Account{Email: "a@test.com", Plan: "plus", AccessToken: "at-a"}) d.Create(&db.Account{Email: "b@test.com", Plan: "plus", AccessToken: "at-b"}) data, filename, err := ExportAccounts(d, []uint{1, 2}, "batch") if err != nil { t.Fatalf("export: %v", err) } if !strings.HasSuffix(filename, ".zip") { t.Fatalf("filename = %q, want *.zip for multiple", filename) } if !strings.Contains(filename, "batch") { t.Fatalf("filename should contain note, got %q", filename) } r, _ := zip.NewReader(bytes.NewReader(data), int64(len(data))) if len(r.File) != 2 { t.Fatalf("zip has %d files, want 2", len(r.File)) } } func TestExportNotFound(t *testing.T) { d := setupExportTestDB(t) _, _, err := ExportAccounts(d, []uint{999}, "") if err == nil { t.Fatal("expected error for nonexistent account") } } func TestBuildAuthFileFallbacks(t *testing.T) { acct := &db.Account{ Email: "basic@test.com", AccessToken: "at-1", RefreshToken: "rt-1", IDToken: "id-1", AccountID: "acc-1", } auth := buildAuthFile(acct) if auth.AccessToken != "at-1" { t.Fatalf("access_token = %q", auth.AccessToken) } if auth.Tokens.AccountID != "acc-1" { t.Fatalf("tokens.account_id = %q", auth.Tokens.AccountID) } if auth.LastRefresh == "" { t.Fatal("last_refresh should be set") } }