227 lines
8.9 KiB
TypeScript
227 lines
8.9 KiB
TypeScript
import { describe, expect, test, vi } from "vitest";
|
|
|
|
import { DEMO_PRO_TENANT_SLUG, parseSeedCommand } from "./cli";
|
|
|
|
const stagingEnv = {
|
|
SEED_DEMO_PRO_ALLOWED: "true",
|
|
SEED_DEMO_PRO_EXPECTED_DATABASE: "hivegrid_staging",
|
|
SEED_DEMO_PRO_EXPECTED_DATABASE_HOST: "postgres-staging:5432",
|
|
SEED_DEMO_PRO_EXPECTED_STORAGE_BUCKET: "hivegrid-staging-media",
|
|
SEED_DEMO_PRO_EXPECTED_STORAGE_ENDPOINT: "https://staging-storage.example.test",
|
|
SEED_DEMO_PRO_EXPECTED_TENANT_ID: "tenant_staging_demo_pro",
|
|
SEED_DEMO_PRO_OUTBOUND_SANDBOX: "true",
|
|
SEED_DEMO_PRO_WORKERS_DRAINED: "true",
|
|
SEED_DEMO_USER_PASSWORD: "R7!qL2#vN9@xP4$kT8&m",
|
|
SENTRY_ENVIRONMENT: "staging",
|
|
};
|
|
|
|
describe("demo-pro seed CLI", () => {
|
|
test("defaults to an environment-free dry run", () => {
|
|
expect(parseSeedCommand([], {})).toEqual({
|
|
mode: "dry-run",
|
|
tenantSlug: DEMO_PRO_TENANT_SLUG,
|
|
});
|
|
expect(parseSeedCommand(["--dry-run"], {})).toEqual({
|
|
mode: "dry-run",
|
|
tenantSlug: DEMO_PRO_TENANT_SLUG,
|
|
});
|
|
});
|
|
|
|
test("accepts only the demo-pro-crm tenant", () => {
|
|
expect(parseSeedCommand(["--tenant=demo-pro-crm"], {})).toEqual({
|
|
mode: "dry-run",
|
|
tenantSlug: DEMO_PRO_TENANT_SLUG,
|
|
});
|
|
expect(() => parseSeedCommand(["--tenant=another-tenant"], {})).toThrow("target tenant must be demo-pro-crm");
|
|
});
|
|
|
|
test("rejects unknown, duplicate and conflicting arguments", () => {
|
|
expect(() => parseSeedCommand(["apply"], {})).toThrow("unknown argument");
|
|
expect(() => parseSeedCommand(["--apply", "--cleanup"], stagingEnv)).toThrow("exactly one mode");
|
|
expect(() => parseSeedCommand(["--tenant=demo-pro-crm", "--tenant=demo-pro-crm"], {})).toThrow("--tenant may only be provided once");
|
|
expect(() => parseSeedCommand(["--apply", "--confirm=demo-pro-crm", "--confirm=demo-pro-crm"], stagingEnv)).toThrow("--confirm may only be provided once");
|
|
});
|
|
|
|
test("requires an exact tenant confirmation for apply", () => {
|
|
expect(() => parseSeedCommand(["--apply"], stagingEnv)).toThrow("--confirm=demo-pro-crm");
|
|
expect(() => parseSeedCommand(["--apply", "--confirm=another-tenant"], stagingEnv)).toThrow("--confirm=demo-pro-crm");
|
|
});
|
|
|
|
test("requires explicit authorization for apply and cleanup", () => {
|
|
expect(() =>
|
|
parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], {
|
|
SENTRY_ENVIRONMENT: "staging",
|
|
})
|
|
).toThrow("explicit staging seed authorization");
|
|
|
|
expect(() =>
|
|
parseSeedCommand(["--cleanup", "--confirm=demo-pro-crm"], {
|
|
SENTRY_ENVIRONMENT: "staging",
|
|
})
|
|
).toThrow("explicit staging seed authorization");
|
|
});
|
|
|
|
test("requires drained workers and a staging-only outbound sink", () => {
|
|
expect(() =>
|
|
parseSeedCommand(["--cleanup", "--confirm=demo-pro-crm"], {
|
|
...stagingEnv,
|
|
SEED_DEMO_PRO_WORKERS_DRAINED: "false",
|
|
})
|
|
).toThrow("drained workers");
|
|
expect(() =>
|
|
parseSeedCommand(["--cleanup", "--confirm=demo-pro-crm"], {
|
|
...stagingEnv,
|
|
SEED_DEMO_PRO_OUTBOUND_SANDBOX: "false",
|
|
})
|
|
).toThrow("outbound staging sandbox");
|
|
});
|
|
|
|
test("allows apply only on staging and resolves the configured staging password", () => {
|
|
expect(parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], stagingEnv)).toEqual({
|
|
mode: "apply",
|
|
tenantSlug: DEMO_PRO_TENANT_SLUG,
|
|
userPassword: "R7!qL2#vN9@xP4$kT8&m",
|
|
expectedTenantId: "tenant_staging_demo_pro",
|
|
expectedDatabaseName: "hivegrid_staging",
|
|
expectedDatabaseHost: "postgres-staging:5432",
|
|
expectedStorageBucket: "hivegrid-staging-media",
|
|
expectedStorageEndpoint: "https://staging-storage.example.test",
|
|
});
|
|
});
|
|
|
|
test("requires the password to be supplied as a staging secret", () => {
|
|
expect(() =>
|
|
parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], {
|
|
SEED_DEMO_PRO_ALLOWED: "true",
|
|
SEED_DEMO_PRO_EXPECTED_DATABASE: "hivegrid_staging",
|
|
SEED_DEMO_PRO_EXPECTED_DATABASE_HOST: "postgres-staging:5432",
|
|
SEED_DEMO_PRO_EXPECTED_STORAGE_BUCKET: "hivegrid-staging-media",
|
|
SEED_DEMO_PRO_EXPECTED_STORAGE_ENDPOINT: "https://staging-storage.example.test",
|
|
SEED_DEMO_PRO_EXPECTED_TENANT_ID: "tenant_staging_demo_pro",
|
|
SEED_DEMO_PRO_OUTBOUND_SANDBOX: "true",
|
|
SEED_DEMO_PRO_WORKERS_DRAINED: "true",
|
|
SENTRY_ENVIRONMENT: "staging",
|
|
})
|
|
).toThrow("demo-user password secret");
|
|
});
|
|
|
|
test("requires mutation to be bound to the exact staging tenant and database", () => {
|
|
expect(() =>
|
|
parseSeedCommand(["--cleanup", "--confirm=demo-pro-crm"], {
|
|
SEED_DEMO_PRO_ALLOWED: "true",
|
|
SEED_DEMO_PRO_OUTBOUND_SANDBOX: "true",
|
|
SEED_DEMO_PRO_WORKERS_DRAINED: "true",
|
|
SENTRY_ENVIRONMENT: "staging",
|
|
})
|
|
).toThrow("tenant, database, and storage binding");
|
|
});
|
|
|
|
test.each(["passwort", "password", "HiveGrid-Demo-Password", "short-but-varied!9A"])("rejects a weak shared staging password (%s)", (password) => {
|
|
expect(() =>
|
|
parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], {
|
|
...stagingEnv,
|
|
SEED_DEMO_USER_PASSWORD: password,
|
|
})
|
|
).toThrow("strong random secret");
|
|
});
|
|
|
|
test("allows the explicitly requested shared password only behind the dedicated staging guard", () => {
|
|
expect(
|
|
parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], {
|
|
...stagingEnv,
|
|
SEED_DEMO_PRO_ALLOW_SHARED_STAGING_PASSWORD: "true",
|
|
SEED_DEMO_USER_PASSWORD: "password",
|
|
}),
|
|
).toMatchObject({ mode: "apply", userPassword: "password" });
|
|
expect(() =>
|
|
parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], {
|
|
...stagingEnv,
|
|
SEED_DEMO_PRO_ALLOW_SHARED_STAGING_PASSWORD: "true",
|
|
SEED_DEMO_USER_PASSWORD: "passwort",
|
|
}),
|
|
).toThrow("strong random secret");
|
|
});
|
|
|
|
test("keeps a configured password opaque and does not log it", () => {
|
|
const consoleSpies = [
|
|
vi.spyOn(console, "log").mockImplementation(() => undefined),
|
|
vi.spyOn(console, "info").mockImplementation(() => undefined),
|
|
vi.spyOn(console, "warn").mockImplementation(() => undefined),
|
|
vi.spyOn(console, "error").mockImplementation(() => undefined),
|
|
];
|
|
const password = " R7!qL2#vN9@xP4$kT8&m ";
|
|
|
|
const command = parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], {
|
|
...stagingEnv,
|
|
SEED_DEMO_USER_PASSWORD: password,
|
|
});
|
|
|
|
expect(command).toEqual({
|
|
mode: "apply",
|
|
tenantSlug: DEMO_PRO_TENANT_SLUG,
|
|
userPassword: password,
|
|
expectedTenantId: "tenant_staging_demo_pro",
|
|
expectedDatabaseName: "hivegrid_staging",
|
|
expectedDatabaseHost: "postgres-staging:5432",
|
|
expectedStorageBucket: "hivegrid-staging-media",
|
|
expectedStorageEndpoint: "https://staging-storage.example.test",
|
|
});
|
|
for (const spy of consoleSpies) expect(spy).not.toHaveBeenCalled();
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
test("does not echo values from rejected arguments", () => {
|
|
const accidentalSecret = "Never expose this value";
|
|
|
|
expect(() => parseSeedCommand([`--password=${accidentalSecret}`], {})).toThrowError(
|
|
expect.not.objectContaining({ message: expect.stringContaining(accidentalSecret) })
|
|
);
|
|
});
|
|
|
|
test.each([undefined, "", "production", "development", "test", "STAGING", "staging-preview"])(
|
|
"rejects verify outside the exact staging environment (%s)",
|
|
(environment) => {
|
|
expect(() =>
|
|
parseSeedCommand(["--verify"], {
|
|
SENTRY_ENVIRONMENT: environment,
|
|
})
|
|
).toThrow("restricted to the staging environment");
|
|
}
|
|
);
|
|
|
|
test("allows read-only verification on staging without mutation authorization", () => {
|
|
expect(parseSeedCommand(["--verify"], { SENTRY_ENVIRONMENT: "staging" })).toEqual({
|
|
mode: "verify",
|
|
tenantSlug: DEMO_PRO_TENANT_SLUG,
|
|
});
|
|
});
|
|
|
|
test("allows cleanup only with every destructive-operation guard", () => {
|
|
expect(parseSeedCommand(["--cleanup", "--confirm=demo-pro-crm"], stagingEnv)).toEqual({
|
|
mode: "cleanup",
|
|
tenantSlug: DEMO_PRO_TENANT_SLUG,
|
|
expectedTenantId: "tenant_staging_demo_pro",
|
|
expectedDatabaseName: "hivegrid_staging",
|
|
expectedDatabaseHost: "postgres-staging:5432",
|
|
expectedStorageBucket: "hivegrid-staging-media",
|
|
expectedStorageEndpoint: "https://staging-storage.example.test",
|
|
});
|
|
});
|
|
|
|
test("does not treat NODE_ENV=production as production when the deployment is explicitly staging", () => {
|
|
expect(
|
|
parseSeedCommand(["--apply", "--confirm=demo-pro-crm"], {
|
|
...stagingEnv,
|
|
NODE_ENV: "production",
|
|
})
|
|
).toMatchObject({ mode: "apply", tenantSlug: DEMO_PRO_TENANT_SLUG });
|
|
});
|
|
|
|
test("does not accept confirmation for non-mutating modes", () => {
|
|
expect(() => parseSeedCommand(["--confirm=demo-pro-crm"], {})).toThrow("--confirm is only valid for apply and cleanup");
|
|
expect(() => parseSeedCommand(["--verify", "--confirm=demo-pro-crm"], { SENTRY_ENVIRONMENT: "staging" })).toThrow(
|
|
"--confirm is only valid for apply and cleanup"
|
|
);
|
|
});
|
|
});
|