// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial // SPDX-FileCopyrightText: 2025-2026 Christian Galo package test import ( "os" "path/filepath" "strings" "testing" ) // staleEnvRefusal is the stack scripts' refusal of a test/.env that lacks // MC_DB_MIGRATE_DSN (design copy 28), for the .env at envFile. func staleEnvRefusal(envFile string) string { return "error: MC_DB_MIGRATE_DSN is not set in " + envFile + ".\n" + "\n" + "This .env predates the stack's runtime database user. Regenerate it:\n" + "\n" + " cd test && docker compose down && rm .env && ./bootstrap-stack.sh && docker compose up -d\n" + "\n" + "Volumes survive, and postgres-seed adds the runtime user to the existing cluster.\n" } // exportedMigrateDSN is the entry of a shell that sourced another worktree's // test/.env. const exportedMigrateDSN = "MC_DB_MIGRATE_DSN=postgres://x@h/db" // TestScriptsRefuseAnEnvWithoutTheMigrationDSN runs each script that reads // test/.env beside a .env that holds every other key the script reads and // lacks MC_DB_MIGRATE_DSN, with the shims of TestScriptsRefuseUnknownArguments. // Each must print the regeneration command on stderr, exit 1, print nothing on // stdout, and reach no database or container: a guard that runs after the // script's first psql, go or docker call shows as a shim that ran. The // bootstrap script asks git for the repository's top first, so its one // permitted shim is git. Each also runs in a shell that exports // MC_DB_MIGRATE_DSN: the file is what the script reads, so the exported value // must not stand in for the key the file lacks. func TestScriptsRefuseAnEnvWithoutTheMigrationDSN(t *testing.T) { for _, c := range []struct { script string env string ran string }{ {"reset-test-db.sh", "POSTGRES_ADMIN_DSN=postgres://u:p@localhost:5432/postgres\n" + "TEST_DATABASE_URL=postgres://u:p@localhost:5432/member_console_test\n" + "E2E_DATABASE_URL=postgres://u:p@localhost:5432/member_console_e2e\n", ""}, {"reset-app-db.sh", "POSTGRES_ADMIN_DSN=postgres://u:p@localhost:5432/postgres\n" + "MC_DB_DSN=postgres://r:p@localhost:5432/member_console\n", ""}, {"seed-demo.sh", "MC_DB_DSN=postgres://r:p@localhost:5432/member_console\n", ""}, } { t.Run(c.script, func(t *testing.T) { sb := newScriptBox(t, c.script) envFile := filepath.Join(sb.dir, ".env") if err := os.WriteFile(envFile, []byte(c.env), 0o644); err != nil { t.Fatal(err) } checkStaleEnvRefused(t, sb, envFile, c.ran) sb.env = []string{exportedMigrateDSN} checkStaleEnvRefused(t, sb, envFile, c.ran) }) } t.Run("bootstrap-stack.sh", func(t *testing.T) { sb := newFarmNameBox(t, "", "") envFile := filepath.Join(sb.dir, "repo", "test", ".env") // The farm-name fixture holds every key bootstrap reads once it has // found the .env; drop the one under test. env, err := os.ReadFile(envFile) if err != nil { t.Fatal(err) } var kept []string for _, line := range strings.Split(string(env), "\n") { if !strings.HasPrefix(line, "MC_DB_MIGRATE_DSN=") { kept = append(kept, line) } } if err := os.WriteFile(envFile, []byte(strings.Join(kept, "\n")), 0o644); err != nil { t.Fatal(err) } checkStaleEnvRefused(t, sb, envFile, "git") sb.env = []string{exportedMigrateDSN} checkStaleEnvRefused(t, sb, envFile, "git") }) } func checkStaleEnvRefused(t *testing.T, sb scriptBox, envFile, ran string) { t.Helper() r := sb.run(t) if r.code != 1 { t.Errorf("exit status %d, want 1 (stderr %q)", r.code, r.stderr) } if want := staleEnvRefusal(envFile); r.stderr != want { t.Errorf("stderr %q, want %q", r.stderr, want) } if r.stdout != "" { t.Errorf("stdout %q, want nothing", r.stdout) } if r.ran != ran { t.Errorf("the script ran %q, want %q", r.ran, ran) } }