createTestOrg now calls the production afterOrgCreated hook directly so the test org goes through the same bootstrap as real orgs (Stripe Connect, Svix, OrgService.update). Both org branches gate createStripeAccount on the existing test_stripe_connect to stay idempotent across re-runs. Pins TEST_ORG_PUBLISHABLE_KEY (a real KSUID-generated am_pk_test_* value) and persists UNIT_TEST_AUTUMN_PUBLIC_KEY plus the auto-generated UNIT_TEST_AUTUMN_SECRET_KEY to server/.env.local + server/.env so test clients pick them up. Deletes ensureDefaultStripeAccount.ts (replaced by the gated afterOrgCreated call).
188 lines
4.8 KiB
TypeScript
188 lines
4.8 KiB
TypeScript
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import chalk from "chalk";
|
|
import { config } from "dotenv";
|
|
import { TEST_ORG_PUBLISHABLE_KEY } from "./createTestOrg.js";
|
|
|
|
// Get the directory of this script file
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
/**
|
|
* Find server/.env file robustly - works whether running from root or scripts dir
|
|
*/
|
|
function findEnvPath(): string {
|
|
// Try from setupTestUtils directory (scripts/setupTestUtils/updateEnvFile.ts -> ../../server/.env)
|
|
const fromScriptDir = resolve(__dirname, "../../server/.env");
|
|
if (existsSync(fromScriptDir)) {
|
|
return fromScriptDir;
|
|
}
|
|
|
|
// Try from current working directory
|
|
const fromCwd = resolve(process.cwd(), "server/.env");
|
|
if (existsSync(fromCwd)) {
|
|
return fromCwd;
|
|
}
|
|
|
|
// If neither exists, return the path from script dir (will fail later with clear error)
|
|
return fromScriptDir;
|
|
}
|
|
|
|
export const envPath = findEnvPath();
|
|
|
|
// Load existing env vars
|
|
config({ path: envPath });
|
|
|
|
/**
|
|
* Updates server/.env with new test configuration
|
|
*/
|
|
export function updateEnvFile({
|
|
testOrgSlug,
|
|
testOrgId,
|
|
autumnSecretKey,
|
|
stripeTestKey,
|
|
upstashUrl,
|
|
upstashToken,
|
|
tunnelUrl,
|
|
}: {
|
|
testOrgSlug: string;
|
|
testOrgId: string;
|
|
autumnSecretKey: string | null;
|
|
stripeTestKey: string;
|
|
upstashUrl: string;
|
|
upstashToken: string;
|
|
tunnelUrl: string;
|
|
}) {
|
|
console.log(
|
|
chalk.magentaBright(
|
|
"\n================ Updating Environment Variables ================\n",
|
|
),
|
|
);
|
|
|
|
// Read existing .env file
|
|
let envContent = "";
|
|
try {
|
|
envContent = readFileSync(envPath, "utf-8");
|
|
} catch {
|
|
console.log(
|
|
chalk.red(
|
|
`❌ Could not read server/.env file at ${envPath}. Make sure it exists.`,
|
|
),
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Parse existing env vars
|
|
const envVars = new Map<string, string>();
|
|
const lines = envContent.split("\n");
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
// Skip empty lines and comments
|
|
if (!trimmed || trimmed.startsWith("#")) {
|
|
continue;
|
|
}
|
|
|
|
const eqIndex = trimmed.indexOf("=");
|
|
if (eqIndex > 0) {
|
|
const key = trimmed.substring(0, eqIndex);
|
|
const value = trimmed.substring(eqIndex + 1);
|
|
envVars.set(key, value);
|
|
}
|
|
}
|
|
|
|
// Update with new test variables
|
|
envVars.set("TESTS_ORG", testOrgSlug);
|
|
envVars.set("TESTS_ORG_ID", testOrgId);
|
|
envVars.set("UNIT_TEST_AUTUMN_PUBLIC_KEY", TEST_ORG_PUBLISHABLE_KEY);
|
|
|
|
// Only update the secret key if a new one was generated
|
|
if (autumnSecretKey) {
|
|
envVars.set("UNIT_TEST_AUTUMN_SECRET_KEY", autumnSecretKey);
|
|
}
|
|
|
|
envVars.set("STRIPE_TEST_KEY", stripeTestKey);
|
|
envVars.set("UPSTASH_REDIS_REST_URL", upstashUrl);
|
|
envVars.set("UPSTASH_REDIS_REST_TOKEN", upstashToken);
|
|
envVars.set("STRIPE_WEBHOOK_URL", tunnelUrl);
|
|
|
|
// Build new env content, preserving structure
|
|
const sections: string[][] = [];
|
|
let currentSection: string[] = [];
|
|
let inTestSection = false;
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
|
|
// Check if this is a section header
|
|
if (trimmed.startsWith("#")) {
|
|
if (currentSection.length > 0) {
|
|
sections.push(currentSection);
|
|
currentSection = [];
|
|
}
|
|
currentSection.push(line);
|
|
inTestSection = trimmed.toLowerCase().includes("test");
|
|
continue;
|
|
}
|
|
|
|
// Skip test-related vars from existing content - we'll add them fresh
|
|
if (
|
|
trimmed.startsWith("TESTS_ORG") ||
|
|
trimmed.startsWith("UNIT_TEST_AUTUMN_PUBLIC_KEY") ||
|
|
trimmed.startsWith("UNIT_TEST_AUTUMN_SECRET_KEY") ||
|
|
trimmed.startsWith("STRIPE_TEST_KEY") ||
|
|
trimmed.startsWith("UPSTASH_REDIS_REST") ||
|
|
(trimmed.startsWith("STRIPE_WEBHOOK_URL") && inTestSection)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
currentSection.push(line);
|
|
}
|
|
|
|
if (currentSection.length > 0) {
|
|
sections.push(currentSection);
|
|
}
|
|
|
|
// Add test configuration section
|
|
const testSection = [
|
|
"",
|
|
"# Test Configuration",
|
|
`TESTS_ORG=${testOrgSlug}`,
|
|
`TESTS_ORG_ID=${testOrgId}`,
|
|
`UNIT_TEST_AUTUMN_PUBLIC_KEY=${TEST_ORG_PUBLISHABLE_KEY}`,
|
|
];
|
|
|
|
// Only add secret key if it was generated/updated
|
|
if (autumnSecretKey) {
|
|
testSection.push(`UNIT_TEST_AUTUMN_SECRET_KEY=${autumnSecretKey}`);
|
|
} else if (envVars.has("UNIT_TEST_AUTUMN_SECRET_KEY")) {
|
|
testSection.push(
|
|
`UNIT_TEST_AUTUMN_SECRET_KEY=${envVars.get("UNIT_TEST_AUTUMN_SECRET_KEY")}`,
|
|
);
|
|
}
|
|
|
|
testSection.push(
|
|
`STRIPE_TEST_KEY=${stripeTestKey}`,
|
|
"",
|
|
"# Upstash (for caching)",
|
|
`UPSTASH_REDIS_REST_URL=${upstashUrl}`,
|
|
`UPSTASH_REDIS_REST_TOKEN=${upstashToken}`,
|
|
"",
|
|
"# Tunnel URL (for Stripe webhooks)",
|
|
`STRIPE_WEBHOOK_URL=${tunnelUrl}`,
|
|
"",
|
|
);
|
|
|
|
sections.push(testSection);
|
|
|
|
// Write back to file
|
|
const newContent = sections.map((s) => s.join("\n")).join("\n");
|
|
writeFileSync(envPath, newContent);
|
|
|
|
console.log(
|
|
chalk.greenBright(`✅ Environment variables updated in ${envPath}`),
|
|
);
|
|
}
|