feat(scenario): run logs, lint/format tooling, CONTRIBUTING
- add ScenarioRunLogEntity to persist step script output to DB - stepLogger dual-writes to NestJS logger and DB (fire-and-forget) - add GET /scenarios/:id/run/:runId returning run, stepRuns and logs - add POST /scenarios/:id/run/:runId/wait (polls until terminal state) - 9 new integration tests for the two endpoints (136 total) - add eslint with typescript-eslint and eslint-config-prettier - add npm scripts: format, lint, lint:fix - resolve all lint errors across src and test (no any types) - add CONTRIBUTING.md covering dev workflow
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import request from 'supertest';
|
||||
import { buildTestApp } from './app.harness';
|
||||
import { INestApplication } from "@nestjs/common";
|
||||
import request from "supertest";
|
||||
import { buildTestApp } from "./app.harness";
|
||||
|
||||
describe('EnvironmentController', () => {
|
||||
describe("EnvironmentController", () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -15,160 +15,187 @@ describe('EnvironmentController', () => {
|
||||
|
||||
// ── POST /environments ─────────────────────────────────────────────────────
|
||||
|
||||
describe('POST /environments', () => {
|
||||
it('creates an environment and returns 201', async () => {
|
||||
describe("POST /environments", () => {
|
||||
it("creates an environment and returns 201", async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-a', urls: { id_url: 'https://id.example.com' } })
|
||||
.post("/environments")
|
||||
.send({ name: "env-a", urls: { id_url: "https://id.example.com" } })
|
||||
.expect(201);
|
||||
|
||||
expect(res.body.id).toBeDefined();
|
||||
expect(res.body.name).toBe('env-a');
|
||||
expect(res.body.urls.id_url).toBe('https://id.example.com');
|
||||
expect(res.body.name).toBe("env-a");
|
||||
expect(res.body.urls.id_url).toBe("https://id.example.com");
|
||||
});
|
||||
|
||||
it('returns 400 when name is missing', async () => {
|
||||
it("returns 400 when name is missing", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ urls: { id_url: 'https://id.example.com' } })
|
||||
.post("/environments")
|
||||
.send({ urls: { id_url: "https://id.example.com" } })
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('returns 400 when urls is missing', async () => {
|
||||
it("returns 400 when urls is missing", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-no-urls' })
|
||||
.post("/environments")
|
||||
.send({ name: "env-no-urls" })
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('returns 400 when urls is not an object', async () => {
|
||||
it("returns 400 when urls is not an object", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-bad-urls', urls: 'not-an-object' })
|
||||
.post("/environments")
|
||||
.send({ name: "env-bad-urls", urls: "not-an-object" })
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('returns 409 when name already exists', async () => {
|
||||
it("returns 409 when name already exists", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-duplicate', urls: {} })
|
||||
.post("/environments")
|
||||
.send({ name: "env-duplicate", urls: {} })
|
||||
.expect(201);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-duplicate', urls: {} })
|
||||
.post("/environments")
|
||||
.send({ name: "env-duplicate", urls: {} })
|
||||
.expect(409);
|
||||
});
|
||||
});
|
||||
|
||||
// ── GET /environments ──────────────────────────────────────────────────────
|
||||
|
||||
describe('GET /environments', () => {
|
||||
it('returns 200 with a paginated result', async () => {
|
||||
const res = await request(app.getHttpServer()).get('/environments').expect(200);
|
||||
describe("GET /environments", () => {
|
||||
it("returns 200 with a paginated result", async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get("/environments")
|
||||
.expect(200);
|
||||
expect(Array.isArray(res.body.data)).toBe(true);
|
||||
expect(typeof res.body.total).toBe('number');
|
||||
expect(res.body).toHaveProperty('page');
|
||||
expect(res.body).toHaveProperty('limit');
|
||||
expect(typeof res.body.total).toBe("number");
|
||||
expect(res.body).toHaveProperty("page");
|
||||
expect(res.body).toHaveProperty("limit");
|
||||
});
|
||||
|
||||
it('respects page and limit params', async () => {
|
||||
it("respects page and limit params", async () => {
|
||||
// seed two extra environments
|
||||
await request(app.getHttpServer()).post('/environments').send({ name: 'env-page-1', urls: {} }).expect(201);
|
||||
await request(app.getHttpServer()).post('/environments').send({ name: 'env-page-2', urls: {} }).expect(201);
|
||||
await request(app.getHttpServer())
|
||||
.post("/environments")
|
||||
.send({ name: "env-page-1", urls: {} })
|
||||
.expect(201);
|
||||
await request(app.getHttpServer())
|
||||
.post("/environments")
|
||||
.send({ name: "env-page-2", urls: {} })
|
||||
.expect(201);
|
||||
|
||||
const res = await request(app.getHttpServer()).get('/environments?page=1&limit=1').expect(200);
|
||||
const res = await request(app.getHttpServer())
|
||||
.get("/environments?page=1&limit=1")
|
||||
.expect(200);
|
||||
expect(res.body.data).toHaveLength(1);
|
||||
expect(res.body.page).toBe(1);
|
||||
expect(res.body.limit).toBe(1);
|
||||
});
|
||||
|
||||
it('returns empty data array for out-of-range page', async () => {
|
||||
const res = await request(app.getHttpServer()).get('/environments?page=9999&limit=20').expect(200);
|
||||
it("returns empty data array for out-of-range page", async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get("/environments?page=9999&limit=20")
|
||||
.expect(200);
|
||||
expect(res.body.data).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('returns 400 for invalid page param', async () => {
|
||||
await request(app.getHttpServer()).get('/environments?page=0').expect(400);
|
||||
it("returns 400 for invalid page param", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.get("/environments?page=0")
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('orders by name ASC', async () => {
|
||||
await request(app.getHttpServer()).post('/environments').send({ name: 'zzz-env', urls: {} });
|
||||
await request(app.getHttpServer()).post('/environments').send({ name: 'aaa-env', urls: {} });
|
||||
it("orders by name ASC", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post("/environments")
|
||||
.send({ name: "zzz-env", urls: {} });
|
||||
await request(app.getHttpServer())
|
||||
.post("/environments")
|
||||
.send({ name: "aaa-env", urls: {} });
|
||||
|
||||
const res = await request(app.getHttpServer()).get('/environments?orderBy=name&orderDir=ASC').expect(200);
|
||||
const names: string[] = res.body.data.map((e: any) => e.name);
|
||||
const res = await request(app.getHttpServer())
|
||||
.get("/environments?orderBy=name&orderDir=ASC")
|
||||
.expect(200);
|
||||
const names: string[] = res.body.data.map((e: { name: string }) => e.name);
|
||||
expect(names).toEqual([...names].sort());
|
||||
});
|
||||
|
||||
it('orders by name DESC', async () => {
|
||||
const res = await request(app.getHttpServer()).get('/environments?orderBy=name&orderDir=DESC').expect(200);
|
||||
const names: string[] = res.body.data.map((e: any) => e.name);
|
||||
it("orders by name DESC", async () => {
|
||||
const res = await request(app.getHttpServer())
|
||||
.get("/environments?orderBy=name&orderDir=DESC")
|
||||
.expect(200);
|
||||
const names: string[] = res.body.data.map((e: { name: string }) => e.name);
|
||||
expect(names).toEqual([...names].sort().reverse());
|
||||
});
|
||||
|
||||
it('returns 400 for invalid orderDir', async () => {
|
||||
await request(app.getHttpServer()).get('/environments?orderDir=SIDEWAYS').expect(400);
|
||||
it("returns 400 for invalid orderDir", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.get("/environments?orderDir=SIDEWAYS")
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
// ── GET /environments/:id ──────────────────────────────────────────────────
|
||||
|
||||
describe('GET /environments/:id', () => {
|
||||
it('returns the created environment', async () => {
|
||||
describe("GET /environments/:id", () => {
|
||||
it("returns the created environment", async () => {
|
||||
const created = await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-get-one', urls: { cabinet_url: 'https://cabinet.example.com' } })
|
||||
.post("/environments")
|
||||
.send({
|
||||
name: "env-get-one",
|
||||
urls: { cabinet_url: "https://cabinet.example.com" },
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`/environments/${created.body.id}`)
|
||||
.expect(200);
|
||||
|
||||
expect(res.body.name).toBe('env-get-one');
|
||||
expect(res.body.name).toBe("env-get-one");
|
||||
});
|
||||
|
||||
it('returns 404 for unknown id', async () => {
|
||||
await request(app.getHttpServer()).get('/environments/99999').expect(404);
|
||||
it("returns 404 for unknown id", async () => {
|
||||
await request(app.getHttpServer()).get("/environments/99999").expect(404);
|
||||
});
|
||||
|
||||
it('returns 400 for non-numeric id', async () => {
|
||||
await request(app.getHttpServer()).get('/environments/abc').expect(400);
|
||||
it("returns 400 for non-numeric id", async () => {
|
||||
await request(app.getHttpServer()).get("/environments/abc").expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
// ── PATCH /environments/:id ────────────────────────────────────────────────
|
||||
|
||||
describe('PATCH /environments/:id', () => {
|
||||
it('updates name and returns 200', async () => {
|
||||
describe("PATCH /environments/:id", () => {
|
||||
it("updates name and returns 200", async () => {
|
||||
const created = await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-patch-me', urls: {} })
|
||||
.post("/environments")
|
||||
.send({ name: "env-patch-me", urls: {} })
|
||||
.expect(201);
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.patch(`/environments/${created.body.id}`)
|
||||
.send({ name: 'env-patched' })
|
||||
.send({ name: "env-patched" })
|
||||
.expect(200);
|
||||
|
||||
expect(res.body.name).toBe('env-patched');
|
||||
expect(res.body.name).toBe("env-patched");
|
||||
});
|
||||
|
||||
it('returns 404 for unknown id', async () => {
|
||||
it("returns 404 for unknown id", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.patch('/environments/99999')
|
||||
.send({ name: 'x' })
|
||||
.patch("/environments/99999")
|
||||
.send({ name: "x" })
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
// ── DELETE /environments/:id ───────────────────────────────────────────────
|
||||
|
||||
describe('DELETE /environments/:id', () => {
|
||||
it('deletes and returns 204', async () => {
|
||||
describe("DELETE /environments/:id", () => {
|
||||
it("deletes and returns 204", async () => {
|
||||
const created = await request(app.getHttpServer())
|
||||
.post('/environments')
|
||||
.send({ name: 'env-delete-me', urls: {} })
|
||||
.post("/environments")
|
||||
.send({ name: "env-delete-me", urls: {} })
|
||||
.expect(201);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
@@ -180,8 +207,10 @@ describe('EnvironmentController', () => {
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('returns 404 for unknown id', async () => {
|
||||
await request(app.getHttpServer()).delete('/environments/99999').expect(404);
|
||||
it("returns 404 for unknown id", async () => {
|
||||
await request(app.getHttpServer())
|
||||
.delete("/environments/99999")
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user