Files
2026-07-13 13:32:57 +08:00

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");
}
);
});