194 lines
7.6 KiB
TypeScript
194 lines
7.6 KiB
TypeScript
// Per-env run-ops-id mint cutover integration proof.
|
|
//
|
|
// NEVER mocks the DB: the mint decision runs through the pure core `computeRunIdMintKind`
|
|
// wired to a REAL `makeFlag(prisma)` that reads the REAL `Organization.featureFlags` /
|
|
// `FeatureFlag` rows in a testcontainers Postgres. Only the two boundary knobs
|
|
// are injected — `masterEnabled` and the `splitEnabled` boot-boolean — never a
|
|
// mocked DB. The run-ops-id/cuid format + residency are then proven through the SAME isomorphic
|
|
// helpers the real trigger path uses (`generateRunOpsId` / `RunId.toFriendlyId` /
|
|
// `RunId.fromFriendlyId` / `ownerEngine`).
|
|
import type { PrismaClient } from "@trigger.dev/database";
|
|
import { generateRunOpsId, ownerEngine, RunId } from "@trigger.dev/core/v3/isomorphic";
|
|
import { postgresTest } from "@internal/testcontainers";
|
|
import { describe, expect, vi } from "vitest";
|
|
import {
|
|
computeRunIdMintKind,
|
|
type RunIdMintKind,
|
|
} from "~/v3/runOpsMigration/runOpsMintKind.server";
|
|
import { FEATURE_FLAG } from "~/v3/featureFlags";
|
|
import { makeFlag } from "~/v3/featureFlags.server";
|
|
import {
|
|
createTestOrgProjectWithMember,
|
|
createRuntimeEnvironment,
|
|
uniqueId,
|
|
} from "./fixtures/environmentVariablesFixtures";
|
|
|
|
vi.setConfig({ testTimeout: 60_000 });
|
|
|
|
// The real trigger-path mint helper, copied verbatim from triggerTask.server.ts so the
|
|
// test exercises the exact id format a cut-over env produces.
|
|
function mintRunOpsFriendlyId(): string {
|
|
return RunId.toFriendlyId(generateRunOpsId());
|
|
}
|
|
|
|
// Mirrors the real trigger path: resolve the kind, then mint either a run-ops friendlyId or
|
|
// the default cuid one (RunId.generate()).
|
|
function mintRunFriendlyId(kind: RunIdMintKind): string {
|
|
return kind === "runOpsId" ? mintRunOpsFriendlyId() : RunId.generate().friendlyId;
|
|
}
|
|
|
|
async function seedOrgEnv(prisma: PrismaClient, mintFlag?: RunIdMintKind) {
|
|
const { organization, project } = await createTestOrgProjectWithMember(prisma);
|
|
const environment = await createRuntimeEnvironment(prisma, {
|
|
projectId: project.id,
|
|
organizationId: organization.id,
|
|
type: "PRODUCTION",
|
|
slug: uniqueId("prod"),
|
|
});
|
|
if (mintFlag) {
|
|
await prisma.organization.update({
|
|
where: { id: organization.id },
|
|
data: { featureFlags: { [FEATURE_FLAG.runOpsMintKind]: mintFlag } },
|
|
});
|
|
}
|
|
return { organization, environment };
|
|
}
|
|
|
|
// Build the env-bound `flag` dependency around a REAL makeFlag(prisma) reading the real
|
|
// Organization.featureFlags override store. Pure-core gets the real DB-backed flag; only
|
|
// masterEnabled + splitEnabled are injected boundary config.
|
|
function realFlag(prisma: PrismaClient) {
|
|
const flagFn = makeFlag(prisma);
|
|
return async (orgId: string, orgFeatureFlags: unknown | undefined): Promise<RunIdMintKind> => {
|
|
const overrides =
|
|
orgFeatureFlags !== undefined
|
|
? orgFeatureFlags
|
|
: (
|
|
await prisma.organization.findFirst({
|
|
where: { id: orgId },
|
|
select: { featureFlags: true },
|
|
})
|
|
)?.featureFlags;
|
|
return flagFn({
|
|
key: FEATURE_FLAG.runOpsMintKind,
|
|
defaultValue: "cuid",
|
|
overrides: (overrides as Record<string, unknown>) ?? {},
|
|
});
|
|
};
|
|
}
|
|
|
|
describe("per-env run-ops-id mint cutover", () => {
|
|
postgresTest(
|
|
"canary org mints run-ops/NEW; non-canary org mints cuid/LEGACY",
|
|
async ({ prisma }) => {
|
|
const a = await seedOrgEnv(prisma, "runOpsId"); // canary
|
|
const b = await seedOrgEnv(prisma); // not cut over
|
|
|
|
const flag = realFlag(prisma);
|
|
const deps = { masterEnabled: true, splitEnabled: async () => true, flag };
|
|
|
|
const kindA = await computeRunIdMintKind(
|
|
{ organizationId: a.organization.id, id: a.environment.id },
|
|
deps
|
|
);
|
|
const kindB = await computeRunIdMintKind(
|
|
{ organizationId: b.organization.id, id: b.environment.id },
|
|
deps
|
|
);
|
|
|
|
expect(kindA).toBe("runOpsId");
|
|
expect(kindB).toBe("cuid");
|
|
|
|
const friendlyA = mintRunFriendlyId(kindA);
|
|
const friendlyB = mintRunFriendlyId(kindB);
|
|
|
|
expect(RunId.fromFriendlyId(friendlyA).length).toBe(26);
|
|
expect(ownerEngine(RunId.fromFriendlyId(friendlyA))).toBe("NEW");
|
|
|
|
expect(RunId.fromFriendlyId(friendlyB).length).toBe(25);
|
|
expect(ownerEngine(RunId.fromFriendlyId(friendlyB))).toBe("LEGACY");
|
|
}
|
|
);
|
|
|
|
postgresTest(
|
|
"split OFF mints cuid even for a 'runOpsId'-flagged org (split gate dominates)",
|
|
async ({ prisma }) => {
|
|
const a = await seedOrgEnv(prisma, "runOpsId");
|
|
const flag = vi.fn(realFlag(prisma));
|
|
|
|
const kind = await computeRunIdMintKind(
|
|
{ organizationId: a.organization.id, id: a.environment.id },
|
|
{ masterEnabled: true, splitEnabled: async () => false, flag }
|
|
);
|
|
|
|
expect(kind).toBe("cuid");
|
|
expect(flag).not.toHaveBeenCalled(); // gated off before any DB read
|
|
}
|
|
);
|
|
|
|
postgresTest(
|
|
"drain-new-forward (D8): flipping back to cuid stops new run-ops mints without reverting existing",
|
|
async ({ prisma }) => {
|
|
const a = await seedOrgEnv(prisma, "runOpsId");
|
|
const flag = realFlag(prisma);
|
|
const deps = { masterEnabled: true, splitEnabled: async () => true, flag };
|
|
|
|
// First run is born run-ops/NEW while cut over.
|
|
const firstKind = await computeRunIdMintKind(
|
|
{ organizationId: a.organization.id, id: a.environment.id },
|
|
deps
|
|
);
|
|
const firstFriendly = mintRunFriendlyId(firstKind);
|
|
expect(firstKind).toBe("runOpsId");
|
|
expect(ownerEngine(RunId.fromFriendlyId(firstFriendly))).toBe("NEW");
|
|
|
|
// Roll the org back to cuid (drain-new-forward — set the flag to "cuid").
|
|
await prisma.organization.update({
|
|
where: { id: a.organization.id },
|
|
data: { featureFlags: { [FEATURE_FLAG.runOpsMintKind]: "cuid" } },
|
|
});
|
|
|
|
// The NEXT run mints cuid again (the env-bound resolver's TTL cache is not used here,
|
|
// so the flip is observed immediately — production waits one cache TTL).
|
|
const nextKind = await computeRunIdMintKind(
|
|
{ organizationId: a.organization.id, id: a.environment.id },
|
|
deps
|
|
);
|
|
const nextFriendly = mintRunFriendlyId(nextKind);
|
|
expect(nextKind).toBe("cuid");
|
|
expect(ownerEngine(RunId.fromFriendlyId(nextFriendly))).toBe("LEGACY");
|
|
|
|
// The already-minted run-ops run is untouched — drain-new-forward never reverts it.
|
|
expect(RunId.fromFriendlyId(firstFriendly).length).toBe(26);
|
|
expect(ownerEngine(RunId.fromFriendlyId(firstFriendly))).toBe("NEW");
|
|
}
|
|
);
|
|
|
|
postgresTest(
|
|
"parent and child re-resolve independently from their own org flag",
|
|
async ({ prisma }) => {
|
|
// Parent lives in a cut-over org; child is triggered into a NON-cut-over org.
|
|
const parentOrg = await seedOrgEnv(prisma, "runOpsId");
|
|
const childOrg = await seedOrgEnv(prisma); // not cut over
|
|
const flag = realFlag(prisma);
|
|
const deps = { masterEnabled: true, splitEnabled: async () => true, flag };
|
|
|
|
const parentKind = await computeRunIdMintKind(
|
|
{ organizationId: parentOrg.organization.id, id: parentOrg.environment.id },
|
|
deps
|
|
);
|
|
const childKind = await computeRunIdMintKind(
|
|
{ organizationId: childOrg.organization.id, id: childOrg.environment.id },
|
|
deps
|
|
);
|
|
|
|
// Observed behavior: the mint decision is resolved per the run's OWN org/env flag —
|
|
// it does NOT inherit the parent's residency. A child in a non-cut-over org mints cuid
|
|
// even when its parent was born run-ops. If children must inherit, that inheritance
|
|
// belongs to the child-trigger path, not this resolver.
|
|
expect(parentKind).toBe("runOpsId");
|
|
expect(childKind).toBe("cuid");
|
|
}
|
|
);
|
|
});
|