diff --git a/internal/account/main_test.go b/internal/account/main_test.go new file mode 100644 index 0000000..6d915d8 --- /dev/null +++ b/internal/account/main_test.go @@ -0,0 +1,66 @@ +package account_test + +import ( + "context" + "crypto/rand" + "database/sql" + "encoding/hex" + "fmt" + "net/url" + "os" + "testing" + + hub "git.ipao.vip/rogee/creator-hub/internal/environment" +) + +// Account tests truncate their fixtures. They must never run in the configured +// database schema; always initialize and destroy a separate schema first. +func TestMain(m *testing.M) { os.Exit(runAccountTests(m)) } + +func runAccountTests(m *testing.M) (result int) { + databaseURL := os.Getenv("CREATORHUB_POSTGRES_TEST_URL") + if databaseURL == "" { + return m.Run() + } + fail := func(err error) int { + fmt.Fprintln(os.Stderr, "initialize isolated account test schema:", err) + return 1 + } + parsed, err := url.Parse(databaseURL) + if err != nil { + return fail(err) + } + bytes := make([]byte, 8) + if _, err := rand.Read(bytes); err != nil { + return fail(err) + } + schema := "account_test_" + hex.EncodeToString(bytes) + db, err := sql.Open("pgx", databaseURL) + if err != nil { + return fail(err) + } + defer db.Close() + if _, err := db.Exec(`CREATE SCHEMA "` + schema + `"`); err != nil { + return fail(err) + } + defer func() { + if _, err := db.Exec(`DROP SCHEMA "` + schema + `" CASCADE`); err != nil { + fmt.Fprintln(os.Stderr, "cleanup isolated account test schema:", err) + result = 1 + } + }() + query := parsed.Query() + query.Set("search_path", schema) + parsed.RawQuery = query.Encode() + store, err := hub.Open(context.Background(), parsed.String()) + if err != nil { + return fail(err) + } + if err := store.Close(); err != nil { + return fail(err) + } + if err := os.Setenv("CREATORHUB_POSTGRES_TEST_URL", parsed.String()); err != nil { + return fail(err) + } + return m.Run() +} diff --git a/internal/account/store_test.go b/internal/account/store_test.go index a3ca02c..7bf70d5 100644 --- a/internal/account/store_test.go +++ b/internal/account/store_test.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + "strings" "testing" "github.com/jackc/pgx/v5" @@ -126,6 +127,20 @@ func applyHubMigrationsForPhaseATest(t *testing.T, store *Store) { } } +func resetAccountTestFixtures(t *testing.T, store *Store) { + t.Helper() + var schema string + if err := store.db.QueryRow(`SELECT current_schema()`).Scan(&schema); err != nil { + t.Fatal(err) + } + if !strings.HasPrefix(schema, "account_test_") { + t.Fatalf("refusing to clear non-test schema %q", schema) + } + if _, err := store.db.Exec(`TRUNCATE audit_event, network_exit, social_account, browser_env, gateway RESTART IDENTITY CASCADE`); err != nil { + t.Fatal(err) + } +} + func TestCreateAccountWithoutCookiesSkipsCredentialStore(t *testing.T) { databaseURL := os.Getenv("CREATORHUB_POSTGRES_TEST_URL") if databaseURL == "" { @@ -138,11 +153,7 @@ func TestCreateAccountWithoutCookiesSkipsCredentialStore(t *testing.T) { } t.Cleanup(func() { _ = store.Close() }) applyHubMigrationsForPhaseATest(t, store) - if _, err := store.db.ExecContext(ctx, ` - TRUNCATE audit_event, network_exit, social_account, browser_env, - gateway RESTART IDENTITY CASCADE`); err != nil { - t.Fatal(err) - } + resetAccountTestFixtures(t, store) credentials := &testCredentialBridge{values: map[string]string{}} account := Account{ID: "account-no-cookies", Name: "扫码账号", Platform: "douyin", PlatformAccountKey: "qr-login", Tags: []string{}, Cookies: "", @@ -168,11 +179,7 @@ func TestAccountCredentialCommitResult(t *testing.T) { t.Fatal(err) } applyHubMigrationsForPhaseATest(t, store) - if _, err := store.db.ExecContext(ctx, ` - TRUNCATE audit_event, network_exit, social_account, browser_env, - gateway RESTART IDENTITY CASCADE`); err != nil { - t.Fatal(err) - } + resetAccountTestFixtures(t, store) credentials := &testCredentialBridge{values: map[string]string{}} account := func(id, platformKey string) Account { return Account{ID: id, Name: id, Platform: "douyin", PlatformAccountKey: platformKey, Tags: []string{}, Cookies: "sessionid=" + id, @@ -240,11 +247,7 @@ func TestAccountStoreLifecycleAgainstPostgres(t *testing.T) { t.Fatal(err) } t.Cleanup(func() { _ = store.Close() }) - if _, err := store.db.ExecContext(ctx, ` - TRUNCATE audit_event, network_exit, social_account, browser_env, - gateway RESTART IDENTITY CASCADE`); err != nil { - t.Fatal(err) - } + resetAccountTestFixtures(t, store) credentials := &testCredentialBridge{values: map[string]string{}} mustCreate := func(id, platformKey string) { t.Helper()