test: add TS checks and fix issues

This commit is contained in:
Pierre Martin 2024-03-10 10:49:36 +01:00
parent fd3783cc8d
commit 043051cb1c
14 changed files with 38 additions and 11 deletions

View File

@ -7,5 +7,5 @@ test:
tdd: tdd:
bun test --watch bun test --watch
lint: check: test
bun lint bun lint

View File

@ -1,3 +1,4 @@
import type DomainProjection from "./DomainProjection";
import ApplicationUpdates from "./projections/ApplicationUpdates"; import ApplicationUpdates from "./projections/ApplicationUpdates";
export default class AppProjections { export default class AppProjections {

View File

@ -1,4 +1,4 @@
interface DomainEvent<T> { export default interface DomainEvent<T> {
readonly type: string; readonly type: string;
readonly createdAt: Date; readonly createdAt: Date;
readonly payload: T; readonly payload: T;

View File

@ -1,3 +1,4 @@
interface DomainProjection { import type DomainEvent from "./DomainEvent";
export default interface DomainProjection {
handle(event: DomainEvent<any>): void; handle(event: DomainEvent<any>): void;
} }

View File

@ -1,3 +1,6 @@
import DomainEvent from "./DomainEvent";
import DomainProjection from "./DomainProjection";
export default interface EventStore { export default interface EventStore {
append(event: DomainEvent<any>): void; append(event: DomainEvent<any>): void;
subscribe(projection: DomainProjection): void; subscribe(projection: DomainProjection): void;

View File

@ -1,3 +1,5 @@
import DomainEvent from "../DomainEvent";
type ApplicationUpdateStartedPayload = { type ApplicationUpdateStartedPayload = {
id: string; id: string;
newVersion: string; newVersion: string;

View File

@ -1,3 +1,6 @@
import DomainEvent from "../DomainEvent";
import DomainProjection from "../DomainProjection";
export type UpdateDefinition = { export type UpdateDefinition = {
id: string; id: string;
newVersion: string; newVersion: string;

View File

@ -1,5 +1,7 @@
import AppProjections from "../AppProjections"; import AppProjections from "../AppProjections";
import AppQueries from "../AppQueries"; import AppQueries from "../AppQueries";
import DomainEvent from "../DomainEvent";
import DomainProjection from "../DomainProjection";
import EventStore from "../EventStore"; import EventStore from "../EventStore";
export class TestApp { export class TestApp {

View File

@ -1,6 +1,8 @@
import { appendFile } from "node:fs/promises"; import { appendFile } from "node:fs/promises";
import EventStore from "../domain/EventStore"; import EventStore from "../domain/EventStore";
import { ApplicationUpdateStarted } from "../domain/events/ApplicationUpdateStarted"; import { ApplicationUpdateStarted } from "../domain/events/ApplicationUpdateStarted";
import DomainEvent from "../domain/DomainEvent";
import DomainProjection from "../domain/DomainProjection";
export default class FileEventStore implements EventStore { export default class FileEventStore implements EventStore {
private handlers: DomainProjection[] = []; private handlers: DomainProjection[] = [];

1
joe.ts
View File

@ -15,6 +15,7 @@ const queries = new AppQueries(projections);
projections.getAll().forEach((projection) => { projections.getAll().forEach((projection) => {
eventStore.subscribe(projection); eventStore.subscribe(projection);
}); });
// @ts-ignore Bun handles top-level await
await eventStore.replay(); await eventStore.replay();
// External services // External services

View File

@ -1,4 +1,7 @@
{ {
"scripts": {
"lint": "tsc --noEmit --skipLibCheck"
},
"devDependencies": { "devDependencies": {
"bun-types": "^1.0.25", "bun-types": "^1.0.25",
"msw": "latest" "msw": "latest"

View File

@ -71,6 +71,7 @@ describe("Caprover", () => {
describe("Initialization", () => { describe("Initialization", () => {
it("should throw an error when no domain is provided", () => { it("should throw an error when no domain is provided", () => {
// @ts-ignore toThrowError exists¯\_(ツ)_/¯
expect(() => new Caprover(new TestApp().eventStore, "")).toThrowError( expect(() => new Caprover(new TestApp().eventStore, "")).toThrowError(
"Missing domain or password" "Missing domain or password"
); );
@ -78,6 +79,7 @@ describe("Caprover", () => {
it("should throw an error when no password is provided", () => { it("should throw an error when no password is provided", () => {
expect( expect(
() => new Caprover(new TestApp().eventStore, CAPROVER_TEST_DOMAIN) () => new Caprover(new TestApp().eventStore, CAPROVER_TEST_DOMAIN)
// @ts-ignore toThrowError exists¯\_(ツ)_/¯
).toThrowError("Missing domain or password"); ).toThrowError("Missing domain or password");
}); });
}); });
@ -125,6 +127,7 @@ describe("Caprover", () => {
it("should throw an error when the application does not exist and not emit any event", async () => { it("should throw an error when the application does not exist and not emit any event", async () => {
await expect( await expect(
caprover.updateApplication("unknown", "adminer:4.2.0") caprover.updateApplication("unknown", "adminer:4.2.0")
// @ts-ignore toThrowError exists¯\_(ツ)_/¯
).rejects.toThrowError(/Failed to update application unknown/); ).rejects.toThrowError(/Failed to update application unknown/);
const events = app.eventStore.getAllEvents(); const events = app.eventStore.getAllEvents();

View File

@ -133,14 +133,15 @@ class Caprover {
await this.authenticate(); await this.authenticate();
console.debug("-> Caprover Fetching", options?.method, path); console.debug("-> Caprover Fetching", options?.method, path);
const headers = new Headers(options?.headers);
headers.set("Content-Type", "application/json");
headers.set("x-namespace", "captain");
headers.set("x-captain-auth", this.authToken);
return fetch(`${this.apiUrl}${path}`, { return fetch(`${this.apiUrl}${path}`, {
...options, ...options,
headers: { headers: headers,
"Content-Type": "application/json",
...(options?.headers || {}),
"x-namespace": "captain",
"x-captain-auth": this.authToken,
},
}).then((res) => { }).then((res) => {
console.debug( console.debug(
"\t<- Caprover Response", "\t<- Caprover Response",

View File

@ -1,5 +1,10 @@
import {
http,
HttpResponse,
HttpResponseResolver,
PathParams
} from "msw";
import { setupServer } from "msw/node"; import { setupServer } from "msw/node";
import { http, HttpResponse, HttpResponseResolver, PathParams } from "msw";
import appsFixtures from "./apps.fixtures.json"; import appsFixtures from "./apps.fixtures.json";
export const CAPROVER_TEST_DOMAIN = "caprover.test"; export const CAPROVER_TEST_DOMAIN = "caprover.test";
@ -14,7 +19,7 @@ const withAuth = (resolver: HttpResponseResolver): HttpResponseResolver => {
headers.get("x-namespace") !== "captain" || headers.get("x-namespace") !== "captain" ||
headers.get("x-captain-auth") !== TEST_TOKEN headers.get("x-captain-auth") !== TEST_TOKEN
) { ) {
return new HttpResponse("", { status: 401 }); return HttpResponse.json({}, { status: 401 });
} }
return resolver(input); return resolver(input);
}; };