test(server): align specs with run environment and step schema
- update scenario tests to create runs with required environmentId - adjust browser controller expectations to session auto-create behavior - sync playwright/context mocks with current browser service usage
This commit is contained in:
@@ -9,6 +9,7 @@ const makePage = () => ({
|
||||
|
||||
const makeContext = () => ({
|
||||
newPage: jest.fn().mockResolvedValue(makePage()),
|
||||
cookies: jest.fn().mockResolvedValue([]),
|
||||
addCookies: jest.fn().mockResolvedValue(undefined),
|
||||
addInitScript: jest.fn().mockResolvedValue(undefined),
|
||||
});
|
||||
|
||||
@@ -8,8 +8,8 @@ import { Repository } from "typeorm";
|
||||
/**
|
||||
* Browser controller integration tests.
|
||||
*
|
||||
* POST /open and POST /exec need a running Playwright browser. We test
|
||||
* validation rejections (no browser launched) and session-not-found paths,
|
||||
* POST /open and POST /exec need a running Playwright browser. We test
|
||||
* validation rejections and auto-create behavior for unknown named sessions,
|
||||
* which are safe to run in a headless CI environment.
|
||||
*/
|
||||
describe("BrowserController", () => {
|
||||
@@ -54,11 +54,16 @@ describe("BrowserController", () => {
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it("returns 404 when session does not exist", async () => {
|
||||
await request(app.getHttpServer())
|
||||
it("auto-creates missing named session and succeeds", async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post("/open")
|
||||
.send({ sessionName: "no-such-session", url: "https://example.com" })
|
||||
.expect(404);
|
||||
.expect(201);
|
||||
expect(res.body).toMatchObject({
|
||||
url: expect.any(String),
|
||||
title: expect.any(String),
|
||||
content: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it("succeeds without a session (sessionless open)", async () => {
|
||||
@@ -115,11 +120,12 @@ describe("BrowserController", () => {
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it("returns 404 when session does not exist", async () => {
|
||||
await request(app.getHttpServer())
|
||||
it("auto-creates missing named session and succeeds", async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post("/exec")
|
||||
.send({ sessionName: "no-such-session", code: "return 1;" })
|
||||
.expect(404);
|
||||
.expect(201);
|
||||
expect(res.body).toEqual({ result: 1 });
|
||||
});
|
||||
|
||||
it("succeeds without a session (sessionless exec)", async () => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { DataSource } from "typeorm";
|
||||
import { buildTestApp } from "./app.harness";
|
||||
import { ScenarioService } from "../src/scenario/scenario.service";
|
||||
import { ScenarioSchedulerService } from "../src/scenario/scenario-scheduler.service";
|
||||
import { EnvironmentEntity } from "../src/environment/environment.entity";
|
||||
|
||||
describe("ScenarioRunStepEntity.output + helpers.getStepOutput", () => {
|
||||
let app: INestApplication;
|
||||
@@ -52,18 +53,26 @@ describe("ScenarioRunStepEntity.output + helpers.getStepOutput", () => {
|
||||
scenarioId: string,
|
||||
_order: number,
|
||||
execCode: string,
|
||||
sessionName = "output-test-session",
|
||||
): Promise<string> {
|
||||
const step = await scenarioService.createStep(scenarioId, {
|
||||
sessionName,
|
||||
execCode,
|
||||
});
|
||||
return step.id;
|
||||
}
|
||||
|
||||
/** Create a test environment and return its id. */
|
||||
async function createEnvironment(
|
||||
name = `output-env-${Math.random().toString(36).slice(2, 8)}`,
|
||||
): Promise<string> {
|
||||
const repo = dataSource.getRepository(EnvironmentEntity);
|
||||
const env = await repo.save(repo.create({ name, data: {} }));
|
||||
return env.id;
|
||||
}
|
||||
|
||||
/** Trigger a run and process it to completion via the scheduler. */
|
||||
async function runScenario(scenarioId: string): Promise<string> {
|
||||
const run = await scenarioService.createRun(scenarioId);
|
||||
const environmentId = await createEnvironment();
|
||||
const run = await scenarioService.createRun(scenarioId, environmentId);
|
||||
// Drive the scheduler directly — keeps tests synchronous and fast.
|
||||
await scheduler.pickUpPendingRuns();
|
||||
// Wait for the run to reach a terminal state (max 10 s).
|
||||
|
||||
@@ -41,6 +41,27 @@ describe("ScenarioController", () => {
|
||||
return res.body as { id: string };
|
||||
}
|
||||
|
||||
async function createEnvironment(name = `env-${Math.random()}`) {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post("/environments")
|
||||
.send({ name, data: {} })
|
||||
.expect(201);
|
||||
return res.body as { id: string; name: string };
|
||||
}
|
||||
|
||||
async function createRun(scenarioId: string) {
|
||||
const env = await createEnvironment();
|
||||
const res = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${scenarioId}/run`)
|
||||
.send({ environmentId: env.id })
|
||||
.expect(201);
|
||||
return res.body as {
|
||||
id: string;
|
||||
status: string;
|
||||
stepRuns: Array<{ status: string }>;
|
||||
};
|
||||
}
|
||||
|
||||
// ── POST /scenarios ────────────────────────────────────────────────────────
|
||||
|
||||
describe("POST /scenarios", () => {
|
||||
@@ -190,25 +211,22 @@ describe("ScenarioController", () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/steps`)
|
||||
.send({
|
||||
order: 0,
|
||||
sessionName: "my-session",
|
||||
execCode: "return 1;",
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
expect(res.body.id).toBeDefined();
|
||||
expect(res.body.order).toBe(0);
|
||||
expect(res.body.sessionName).toBe("my-session");
|
||||
});
|
||||
|
||||
it("creates a step without execCode", async () => {
|
||||
const sc = await createScenario();
|
||||
const res = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/steps`)
|
||||
.send({ order: 0, sessionName: "session-x" })
|
||||
.send({ title: "step without exec" })
|
||||
.expect(201);
|
||||
|
||||
expect(res.body.sessionName).toBe("session-x");
|
||||
expect(res.body.title).toBe("step without exec");
|
||||
expect(res.body.execCode).toBeNull();
|
||||
});
|
||||
|
||||
@@ -371,15 +389,13 @@ describe("ScenarioController", () => {
|
||||
await createStep(sc.id, { order: 1 });
|
||||
await createStep(sc.id, { order: 2 });
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/run`)
|
||||
.expect(201);
|
||||
const res = await createRun(sc.id);
|
||||
|
||||
expect(res.body.status).toBe("pending");
|
||||
expect(Array.isArray(res.body.stepRuns)).toBe(true);
|
||||
expect(res.body.stepRuns).toHaveLength(3);
|
||||
expect(res.status).toBe("pending");
|
||||
expect(Array.isArray(res.stepRuns)).toBe(true);
|
||||
expect(res.stepRuns).toHaveLength(3);
|
||||
|
||||
const statuses = res.body.stepRuns.map(
|
||||
const statuses = res.stepRuns.map(
|
||||
(s: { status: string }) => s.status,
|
||||
);
|
||||
expect(statuses[0]).toBe("pending");
|
||||
@@ -388,8 +404,10 @@ describe("ScenarioController", () => {
|
||||
});
|
||||
|
||||
it("returns 404 for unknown scenario", async () => {
|
||||
const env = await createEnvironment();
|
||||
await request(app.getHttpServer())
|
||||
.post("/scenarios/00000000-0000-0000-0000-000000000001/run")
|
||||
.send({ environmentId: env.id })
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
@@ -400,9 +418,7 @@ describe("ScenarioController", () => {
|
||||
it("returns paginated runs with stepRuns embedded", async () => {
|
||||
const sc = await createScenario();
|
||||
await createStep(sc.id, { order: 0 });
|
||||
await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/run`)
|
||||
.expect(201);
|
||||
await createRun(sc.id);
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`/scenarios/${sc.id}/runs`)
|
||||
@@ -415,9 +431,7 @@ describe("ScenarioController", () => {
|
||||
it("filters by status", async () => {
|
||||
const sc = await createScenario();
|
||||
await createStep(sc.id, { order: 0 });
|
||||
await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/run`)
|
||||
.expect(201);
|
||||
await createRun(sc.id);
|
||||
|
||||
const pendingRes = await request(app.getHttpServer())
|
||||
.get(`/scenarios/${sc.id}/runs?status=pending`)
|
||||
@@ -675,10 +689,8 @@ describe("ScenarioController", () => {
|
||||
it("returns run with stepRuns (with scenarioStep) and logs array", async () => {
|
||||
const sc = await createScenario();
|
||||
await createStep(sc.id, { order: 0 });
|
||||
const runRes = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/run`)
|
||||
.expect(201);
|
||||
const runId = runRes.body.id;
|
||||
const runRes = await createRun(sc.id);
|
||||
const runId = runRes.id;
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`/scenarios/${sc.id}/run/${runId}`)
|
||||
@@ -696,10 +708,8 @@ describe("ScenarioController", () => {
|
||||
await createStep(sc.id, { order: 0 });
|
||||
await createStep(sc.id, { order: 1 });
|
||||
await createStep(sc.id, { order: 2 });
|
||||
const runRes = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/run`)
|
||||
.expect(201);
|
||||
const runId = runRes.body.id;
|
||||
const runRes = await createRun(sc.id);
|
||||
const runId = runRes.id;
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`/scenarios/${sc.id}/run/${runId}`)
|
||||
@@ -720,10 +730,8 @@ describe("ScenarioController", () => {
|
||||
const sc1 = await createScenario();
|
||||
const sc2 = await createScenario();
|
||||
await createStep(sc1.id, { order: 0 });
|
||||
const runRes = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc1.id}/run`)
|
||||
.expect(201);
|
||||
const runId = runRes.body.id;
|
||||
const runRes = await createRun(sc1.id);
|
||||
const runId = runRes.id;
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get(`/scenarios/${sc2.id}/run/${runId}`)
|
||||
@@ -745,10 +753,8 @@ describe("ScenarioController", () => {
|
||||
it("returns 200 with run data immediately when run is already terminal", async () => {
|
||||
const sc = await createScenario();
|
||||
await createStep(sc.id, { order: 0 });
|
||||
const runRes = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/run`)
|
||||
.expect(201);
|
||||
const runId = runRes.body.id;
|
||||
const runRes = await createRun(sc.id);
|
||||
const runId = runRes.id;
|
||||
|
||||
// Manually mark run as pass so wait resolves immediately
|
||||
const dataSource = app.get(DataSource);
|
||||
@@ -769,10 +775,8 @@ describe("ScenarioController", () => {
|
||||
it("returns the run in fail state when it has failed", async () => {
|
||||
const sc = await createScenario();
|
||||
await createStep(sc.id, { order: 0 });
|
||||
const runRes = await request(app.getHttpServer())
|
||||
.post(`/scenarios/${sc.id}/run`)
|
||||
.expect(201);
|
||||
const runId = runRes.body.id;
|
||||
const runRes = await createRun(sc.id);
|
||||
const runId = runRes.id;
|
||||
|
||||
const dataSource = app.get(DataSource);
|
||||
await dataSource.query(
|
||||
|
||||
Reference in New Issue
Block a user