Files
cfw-autumn/server/tests/utils/setup/clearOrg.ts
2026-05-16 02:05:13 +01:00

129 lines
3.7 KiB
TypeScript

import {
AppEnv,
migrationItemRuns,
migrations,
} from "@autumn/shared";
import { and, eq, inArray } from "drizzle-orm";
import { initDrizzle } from "@/db/initDrizzle.js";
import type { DrizzleCli } from "@/db/initDrizzle.js";
import { AutumnInt } from "@/external/autumn/autumnCli.js";
import { CusService } from "@/internal/customers/CusService.js";
import { hashApiKey } from "@/internal/dev/api-keys/apiKeyUtils.js";
import { FeatureService } from "@/internal/features/FeatureService.js";
import { OrgService } from "@/internal/orgs/OrgService.js";
import { ProductService } from "@/internal/products/ProductService.js";
import { rewardRepo } from "@/internal/rewards/repos/index.js";
import { CacheManager } from "@/utils/cacheUtils/CacheManager.js";
import { CacheType } from "@/utils/cacheUtils/CacheType.js";
/**
* DB-only org cleanup — no HTTP calls, no connection lifecycle.
* Safe to call from setup scripts that already hold a db handle.
*/
export const clearOrgDbOnly = async ({
db,
orgId,
env,
}: {
db: DrizzleCli;
orgId: string;
env: AppEnv;
}) => {
await CusService.deleteByOrgId({ db, orgId, env });
await ProductService.deleteByOrgId({ db, orgId, env });
await rewardRepo.deleteByOrgId({ db, orgId, env });
await FeatureService.deleteByOrgId({ db, orgId, env });
};
export const clearOrg = async ({
orgSlug,
env,
skipStripeReset,
}: {
orgSlug: string;
env: AppEnv;
skipStripeReset?: boolean;
}) => {
if (env !== AppEnv.Sandbox) {
console.error("Cannot clear non-sandbox orgs");
process.exit(1);
}
const autumn = new AutumnInt();
const { db, client } = initDrizzle();
const org = await OrgService.getBySlug({ db, slug: orgSlug });
await Promise.all([
CacheManager.invalidate({
action: CacheType.SecretKey,
value: hashApiKey(process.env.UNIT_TEST_AUTUMN_SECRET_KEY!),
}),
CacheManager.invalidate({
action: CacheType.PublicKey,
value: process.env.UNIT_TEST_AUTUMN_PUBLIC_KEY!,
}),
]);
// await CacheManager.disconnect();
if (!org) {
throw new Error(`Org ${orgSlug} not found`);
}
// Allow unit-test-org, ci_cd, and platform test orgs (test-*|org_...)
const isAllowed =
org.slug === "unit-test-org" ||
org.slug === "ci_cd" ||
org.slug.startsWith("test-");
if (!isAllowed) {
console.error("Cannot clear non-unit-test-orgs");
process.exit(1);
}
const orgId = org.id;
if (!skipStripeReset) {
// Reset default account using the new internal endpoint
// This will delete and recreate the Stripe account, which automatically removes all Stripe resources
console.log(" 🔄 Resetting default account...");
try {
const data = await autumn.organization.resetDefaultAccount();
console.log(
" ✅ Reset default account, new account:",
data?.new_account_id,
);
} catch (error: any) {
console.error(" ❌ Failed to reset default account:", error.message);
// Continue anyway as this is not critical
}
}
await clearOrgDbOnly({ db, orgId, env });
console.log(" ✅ Deleted customers, products, rewards, and features");
// migration_item_runs has no FK to migrations/org; clear by joining first.
// migrations cascades to migration_runs, so deleting it is enough.
const orgMigrations = await db
.select({ internalId: migrations.internal_id })
.from(migrations)
.where(and(eq(migrations.org_id, orgId), eq(migrations.env, env)));
if (orgMigrations.length > 0) {
await db.delete(migrationItemRuns).where(
inArray(
migrationItemRuns.migration_internal_id,
orgMigrations.map((m) => m.internalId),
),
);
}
await db
.delete(migrations)
.where(and(eq(migrations.org_id, orgId), eq(migrations.env, env)));
console.log(" ✅ Deleted migrations + migration item runs");
console.log(`✅ Cleared org ${orgSlug} (${env})`);
await client.end();
return org;
};