Skip to content

Commit a5e04f2

Browse files
Ekaterina BulatovaEkaterina Bulatova
authored andcommitted
test(webapp): cover runs live fields mapping and hasNewRuns
1 parent 748ec65 commit a5e04f2

2 files changed

Lines changed: 182 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { describe, expect, it } from "vitest";
2+
import { mapRunToLiveFields } from "~/presenters/v3/mapRunToLiveFields.server";
3+
4+
describe("mapRunToLiveFields", () => {
5+
it("maps an executing run with lockedAt fallback and non-final flags", () => {
6+
const updatedAt = new Date("2026-05-07T10:00:00.000Z");
7+
const lockedAt = new Date("2026-05-07T09:59:50.000Z");
8+
9+
const result = mapRunToLiveFields({
10+
friendlyId: "run_123",
11+
status: "EXECUTING",
12+
updatedAt,
13+
startedAt: null,
14+
lockedAt,
15+
completedAt: null,
16+
usageDurationMs: BigInt(2500),
17+
costInCents: 10,
18+
baseCostInCents: 5,
19+
});
20+
21+
expect(result).toEqual({
22+
friendlyId: "run_123",
23+
status: "EXECUTING",
24+
updatedAt: updatedAt.toISOString(),
25+
startedAt: lockedAt.toISOString(),
26+
finishedAt: undefined,
27+
hasFinished: false,
28+
isCancellable: true,
29+
isPending: false,
30+
usageDurationMs: 2500,
31+
costInCents: 10,
32+
baseCostInCents: 5,
33+
});
34+
});
35+
36+
it("maps a final run and prefers completedAt for finishedAt", () => {
37+
const updatedAt = new Date("2026-05-07T10:00:00.000Z");
38+
const startedAt = new Date("2026-05-07T09:59:00.000Z");
39+
const completedAt = new Date("2026-05-07T09:59:30.000Z");
40+
41+
const result = mapRunToLiveFields({
42+
friendlyId: "run_456",
43+
status: "COMPLETED_SUCCESSFULLY",
44+
updatedAt,
45+
startedAt,
46+
lockedAt: null,
47+
completedAt,
48+
usageDurationMs: 1200,
49+
costInCents: 20,
50+
baseCostInCents: 7,
51+
});
52+
53+
expect(result.finishedAt).toBe(completedAt.toISOString());
54+
expect(result.startedAt).toBe(startedAt.toISOString());
55+
expect(result.hasFinished).toBe(true);
56+
expect(result.isCancellable).toBe(false);
57+
});
58+
59+
it("falls back to updatedAt when a final run has no completedAt", () => {
60+
const updatedAt = new Date("2026-05-07T10:00:00.000Z");
61+
62+
const result = mapRunToLiveFields({
63+
friendlyId: "run_789",
64+
status: "CRASHED",
65+
updatedAt,
66+
startedAt: null,
67+
lockedAt: null,
68+
completedAt: null,
69+
usageDurationMs: 0,
70+
costInCents: 0,
71+
baseCostInCents: 0,
72+
});
73+
74+
expect(result.finishedAt).toBe(updatedAt.toISOString());
75+
expect(result.hasFinished).toBe(true);
76+
expect(result.isPending).toBe(false);
77+
expect(result.isCancellable).toBe(false);
78+
});
79+
});

apps/webapp/test/runsRepository.part2.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,4 +789,107 @@ describe("RunsRepository (part 2/2)", () => {
789789
expect(secondPage.pagination.previousCursor).toBeTruthy();
790790
}
791791
);
792+
793+
containerTest(
794+
"should detect new runs with hasNewRuns",
795+
async ({ clickhouseContainer, redisOptions, postgresContainer, prisma }) => {
796+
const { clickhouse } = await setupClickhouseReplication({
797+
prisma,
798+
databaseUrl: postgresContainer.getConnectionUri(),
799+
clickhouseUrl: clickhouseContainer.getConnectionUrl(),
800+
redisOptions,
801+
});
802+
803+
const organization = await prisma.organization.create({
804+
data: {
805+
title: "test",
806+
slug: "test",
807+
},
808+
});
809+
810+
const project = await prisma.project.create({
811+
data: {
812+
name: "test",
813+
slug: "test",
814+
organizationId: organization.id,
815+
externalRef: "test",
816+
},
817+
});
818+
819+
const runtimeEnvironment = await prisma.runtimeEnvironment.create({
820+
data: {
821+
slug: "test",
822+
type: "DEVELOPMENT",
823+
projectId: project.id,
824+
organizationId: organization.id,
825+
apiKey: "test",
826+
pkApiKey: "test",
827+
shortcode: "test",
828+
},
829+
});
830+
831+
const taskRun = await prisma.taskRun.create({
832+
data: {
833+
friendlyId: "run_has_new",
834+
taskIdentifier: "my-task",
835+
payload: JSON.stringify({ foo: "bar" }),
836+
traceId: "1234",
837+
spanId: "1234",
838+
queue: "test",
839+
runtimeEnvironmentId: runtimeEnvironment.id,
840+
projectId: project.id,
841+
organizationId: organization.id,
842+
environmentType: "DEVELOPMENT",
843+
engine: "V2",
844+
},
845+
});
846+
847+
await setTimeout(1000);
848+
849+
const runsRepository = new RunsRepository({
850+
prisma,
851+
clickhouse,
852+
});
853+
854+
const baseOptions = {
855+
projectId: project.id,
856+
environmentId: runtimeEnvironment.id,
857+
organizationId: organization.id,
858+
};
859+
860+
const createdAtMs = taskRun.createdAt.getTime();
861+
862+
await expect(
863+
runsRepository.hasNewRuns({
864+
...baseOptions,
865+
from: createdAtMs - 1,
866+
})
867+
).resolves.toBe(true);
868+
869+
await expect(
870+
runsRepository.hasNewRuns({
871+
...baseOptions,
872+
from: createdAtMs + 60_000,
873+
})
874+
).resolves.toBe(false);
875+
876+
const fromBeforeRun = createdAtMs - 1;
877+
878+
await expect(
879+
runsRepository.hasNewRuns({
880+
...baseOptions,
881+
from: fromBeforeRun,
882+
tasks: ["my-task"],
883+
})
884+
).resolves.toBe(true);
885+
886+
await expect(
887+
runsRepository.hasNewRuns({
888+
...baseOptions,
889+
from: fromBeforeRun,
890+
tasks: ["other-task"],
891+
})
892+
).resolves.toBe(false);
893+
}
894+
);
792895
});

0 commit comments

Comments
 (0)