110 lines
4.0 KiB
TypeScript
110 lines
4.0 KiB
TypeScript
import { describe, expect, vi } from "vitest";
|
|
import { randomBytes } from "crypto";
|
|
import type { TaskRun } from "@trigger.dev/database";
|
|
import { postgresTest } from "@internal/testcontainers";
|
|
import { seedTestEnvironment } from "./helpers/seedTestEnvironment";
|
|
import { seedTestRun } from "./helpers/seedTestRun";
|
|
|
|
// The service runs against the testcontainer prisma passed to its constructor.
|
|
// These empty stubs just satisfy the module-level db.server imports so the
|
|
// module tree loads; the guard under test uses the injected `this._prisma`.
|
|
vi.mock("~/db.server", () => ({
|
|
prisma: {},
|
|
$replica: {},
|
|
runOpsNewPrisma: {},
|
|
runOpsLegacyPrisma: {},
|
|
runOpsNewReplica: {},
|
|
runOpsLegacyReplica: {},
|
|
}));
|
|
|
|
import { ReplayTaskRunService } from "~/v3/services/replayTaskRun.server";
|
|
|
|
vi.setConfig({ testTimeout: 60_000 });
|
|
|
|
describe("ReplayTaskRunService environment scoping", () => {
|
|
postgresTest(
|
|
"refuses to replay a run into an environment in another tenant's project",
|
|
async ({ prisma }) => {
|
|
// Tenant A owns the run; the override targets tenant B's environment.
|
|
// Distinct orgs => distinct projects.
|
|
const tenantA = await seedTestEnvironment(prisma);
|
|
const tenantB = await seedTestEnvironment(prisma);
|
|
|
|
const { run } = await seedTestRun(prisma, {
|
|
environmentId: tenantA.environment.id,
|
|
projectId: tenantA.project.id,
|
|
});
|
|
|
|
const service = new ReplayTaskRunService(prisma);
|
|
|
|
await expect(service.call(run, { environmentId: tenantB.environment.id })).rejects.toThrow(
|
|
"Cannot replay a run into an environment outside its project"
|
|
);
|
|
|
|
// No run was created in tenant B's project.
|
|
const runsInVictimProject = await prisma.taskRun.count({
|
|
where: { projectId: tenantB.project.id },
|
|
});
|
|
expect(runsInVictimProject).toBe(0);
|
|
}
|
|
);
|
|
|
|
postgresTest(
|
|
"refuses to replay when the override environment id does not exist",
|
|
async ({ prisma }) => {
|
|
const tenantA = await seedTestEnvironment(prisma);
|
|
const { run } = await seedTestRun(prisma, {
|
|
environmentId: tenantA.environment.id,
|
|
projectId: tenantA.project.id,
|
|
});
|
|
|
|
const service = new ReplayTaskRunService(prisma);
|
|
|
|
await expect(service.call(run, { environmentId: "env_does_not_exist" })).rejects.toThrow(
|
|
"Cannot replay a run into an environment outside its project"
|
|
);
|
|
}
|
|
);
|
|
|
|
postgresTest(
|
|
"allows a same-project override even when the source run carries no projectId",
|
|
async ({ prisma }) => {
|
|
// The buffered-run fallback passes a synthetic TaskRun with a
|
|
// runtimeEnvironmentId but no projectId. A same-project override must
|
|
// still be allowed, since the source project comes from the run's
|
|
// environment, not from the (absent) projectId.
|
|
const tenant = await seedTestEnvironment(prisma);
|
|
const suffix = randomBytes(4).toString("hex");
|
|
const stagingEnvironment = await prisma.runtimeEnvironment.create({
|
|
data: {
|
|
slug: "staging",
|
|
type: "STAGING",
|
|
apiKey: `tr_stg_${suffix}`,
|
|
pkApiKey: `pk_stg_${suffix}`,
|
|
shortcode: `stg${suffix.slice(0, 1)}`,
|
|
projectId: tenant.project.id,
|
|
organizationId: tenant.organization.id,
|
|
},
|
|
});
|
|
|
|
// Mirror the synthetic dashboard replay run: source env present, no projectId.
|
|
const syntheticRun = {
|
|
id: "run_buffered_synthetic",
|
|
friendlyId: "run_buffered_synthetic",
|
|
runtimeEnvironmentId: tenant.environment.id,
|
|
} as unknown as TaskRun;
|
|
|
|
const service = new ReplayTaskRunService(prisma);
|
|
|
|
// The guard must pass; execution then proceeds past it (into stubbed
|
|
// db.server), so any resulting error must NOT be the rejection.
|
|
const error = await service
|
|
.call(syntheticRun, { environmentId: stagingEnvironment.id })
|
|
.catch((e) => e as Error);
|
|
expect(error?.message).not.toBe(
|
|
"Cannot replay a run into an environment outside its project"
|
|
);
|
|
}
|
|
);
|
|
});
|