169 lines
4.3 KiB
Go
169 lines
4.3 KiB
Go
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")
|
|
}
|
|
}
|