Skip to content

Commit 1acde7d

Browse files
committed
test(webapp): regression tests for resuming manually paused environments
1 parent c7f6ed5 commit 1acde7d

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { postgresTest } from "@internal/testcontainers";
2+
import { EnvironmentPauseSource, type PrismaClient } from "@trigger.dev/database";
3+
import { describe, expect, vi } from "vitest";
4+
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
5+
import { authIncludeBase, toAuthenticated } from "~/models/runtimeEnvironment.server";
6+
import { PauseEnvironmentService } from "~/v3/services/pauseEnvironment.server";
7+
import {
8+
createRuntimeEnvironment,
9+
createTestOrgProjectWithMember,
10+
uniqueId,
11+
} from "./fixtures/environmentVariablesFixtures";
12+
13+
vi.setConfig({ testTimeout: 60_000 });
14+
15+
async function authEnv(prisma: PrismaClient, environmentId: string): Promise<AuthenticatedEnvironment> {
16+
const row = await prisma.runtimeEnvironment.findFirstOrThrow({
17+
where: { id: environmentId },
18+
include: authIncludeBase,
19+
});
20+
return toAuthenticated(row);
21+
}
22+
23+
async function seedProductionEnv(prisma: PrismaClient) {
24+
const { organization, project } = await createTestOrgProjectWithMember(prisma);
25+
const environment = await createRuntimeEnvironment(prisma, {
26+
projectId: project.id,
27+
organizationId: organization.id,
28+
type: "PRODUCTION",
29+
slug: uniqueId("prod"),
30+
});
31+
return { organization, project, environment };
32+
}
33+
34+
describe("PauseEnvironmentService", () => {
35+
postgresTest(
36+
"resumes a manually paused env (pauseSource stays null through pause and resume)",
37+
async ({ prisma }) => {
38+
const { environment } = await seedProductionEnv(prisma);
39+
const service = new PauseEnvironmentService(prisma);
40+
const env = await authEnv(prisma, environment.id);
41+
42+
const paused = await service.call(env, "paused");
43+
expect(paused).toEqual({ success: true, state: "paused" });
44+
45+
const afterPause = await prisma.runtimeEnvironment.findFirstOrThrow({
46+
where: { id: environment.id },
47+
});
48+
// Manual pause never sets pauseSource; leaving it null is what tripped the
49+
// pre-fix resume guard (Prisma NOT on a nullable field excludes NULL rows).
50+
expect(afterPause.paused).toBe(true);
51+
expect(afterPause.pauseSource).toBeNull();
52+
53+
const resumed = await service.call(env, "resumed");
54+
expect(resumed).toEqual({ success: true, state: "resumed" });
55+
56+
const afterResume = await prisma.runtimeEnvironment.findFirstOrThrow({
57+
where: { id: environment.id },
58+
});
59+
expect(afterResume.paused).toBe(false);
60+
expect(afterResume.pauseSource).toBeNull();
61+
}
62+
);
63+
64+
postgresTest("rejects resume of a billing-limit paused env and leaves it paused", async ({
65+
prisma,
66+
}) => {
67+
const { environment } = await seedProductionEnv(prisma);
68+
await prisma.runtimeEnvironment.update({
69+
where: { id: environment.id },
70+
data: { paused: true, pauseSource: EnvironmentPauseSource.BILLING_LIMIT },
71+
});
72+
73+
const service = new PauseEnvironmentService(prisma);
74+
const env = await authEnv(prisma, environment.id);
75+
76+
const result = await service.call(env, "resumed");
77+
expect(result.success).toBe(false);
78+
if (result.success) return;
79+
expect(result.error).toContain("billing limit");
80+
81+
const after = await prisma.runtimeEnvironment.findFirstOrThrow({
82+
where: { id: environment.id },
83+
});
84+
expect(after.paused).toBe(true);
85+
expect(after.pauseSource).toBe(EnvironmentPauseSource.BILLING_LIMIT);
86+
});
87+
88+
postgresTest(
89+
"manual pause while billing-limit paused is a no-op that preserves pauseSource",
90+
async ({ prisma }) => {
91+
const { environment } = await seedProductionEnv(prisma);
92+
await prisma.runtimeEnvironment.update({
93+
where: { id: environment.id },
94+
data: { paused: true, pauseSource: EnvironmentPauseSource.BILLING_LIMIT },
95+
});
96+
97+
const service = new PauseEnvironmentService(prisma);
98+
const env = await authEnv(prisma, environment.id);
99+
100+
const result = await service.call(env, "paused");
101+
// Idempotent success without overwriting pauseSource, so billing-limit
102+
// converge can still find and unpause this env on resolve.
103+
expect(result).toEqual({ success: true, state: "paused" });
104+
105+
const after = await prisma.runtimeEnvironment.findFirstOrThrow({
106+
where: { id: environment.id },
107+
});
108+
expect(after.paused).toBe(true);
109+
expect(after.pauseSource).toBe(EnvironmentPauseSource.BILLING_LIMIT);
110+
}
111+
);
112+
});

0 commit comments

Comments
 (0)