diff --git a/packages/doorman-api/package.json b/packages/doorman-api/package.json index 4905d6b..377c643 100644 --- a/packages/doorman-api/package.json +++ b/packages/doorman-api/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "scripts": { - "integ-test": "bun run build && export $(grep -v '^#' .env.example | grep -v '=$' | xargs) && concurrently --success first --kill-others \"bun run start-twilio\" \"bun test\"", + "integ-test": "bun run build && export $(grep -v '^#' .env.example | grep -v '=$' | xargs) && concurrently --success first --kill-others \"bun run start-twilio\" \"bun test --timeout 30000\"", "start-twilio": "twilio-run --load-local-env --live --port 8080", "watch-build": "bun run --watch src/index.ts", "start": "concurrently \"bun run watch-build\" \"bun run start-twilio\"", diff --git a/packages/doorman-api/tst/info.test.ts b/packages/doorman-api/tst/info.test.ts deleted file mode 100644 index ab2c698..0000000 --- a/packages/doorman-api/tst/info.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, test, expect, beforeAll } from "bun:test"; - -const baseUrl = "http://localhost:8080"; -const doorName = "test"; -const buzzerNumber = "6133163433"; - -const waitForService = async (url: string, timeout = 30000) => { - const start = Date.now(); - - while (Date.now() - start < timeout) { - try { - await fetch(url); - return true; - } catch (err) { - await new Promise(resolve => setTimeout(resolve, 1000)); - } - } - throw new Error(`Service at ${url} did not start within ${timeout}ms`); -}; - -beforeAll(async () => { - await waitForService(baseUrl); -}); - -describe("info route works", () => { - test("info works from UI", async () => { - const resp = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as any; - - expect(resp.id as string).toBe(doorName); - expect(resp.buzzer).toBe(buzzerNumber); - }); - - test("info works from client", async () => { - const resp = await fetch(baseUrl + `/api/door/info?buzzer=${buzzerNumber}`).then(res => res.json()) as any; - - // TODO: why is this different? - expect(resp.door as string).toBe(doorName); - expect(resp.buzzer).toBe(buzzerNumber); - }); -}); diff --git a/packages/doorman-api/tst/integ.test.ts b/packages/doorman-api/tst/integ.test.ts new file mode 100644 index 0000000..fb063b6 --- /dev/null +++ b/packages/doorman-api/tst/integ.test.ts @@ -0,0 +1,74 @@ +import { describe, test, expect, beforeAll } from "bun:test"; +import { waitForService, baseUrl, doorName, buzzerNumber, key } from "./testCommon"; +import { DoorStatus } from "../src/types/DoorStatus"; +import { StatusResponse } from "../src/functions/api/door/status"; +import { sleep } from "bun"; + +beforeAll(async () => { + await waitForService(baseUrl); +}); + +describe("info path works", () => { + test("info works from UI", async () => { + const resp = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as any; + + expect(resp.id as string).toBe(doorName); + expect(resp.buzzer).toBe(buzzerNumber); + }); + + test("info works from client", async () => { + const resp = await fetch(baseUrl + `/api/door/info?buzzer=${buzzerNumber}`).then(res => res.json()) as any; + + // TODO: why is this different? + expect(resp.door as string).toBe(doorName); + expect(resp.buzzer).toBe(buzzerNumber); + }); +}); + +describe("unlock path works", () => { + test("auth updates info and status relocks auth", async () => { + // run status first, to make sure we are closed + const statusReset = await fetch(baseUrl + `/api/door/status?door=${doorName}`).then(res => res.json()) as StatusResponse; + + const resp = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as any; + + expect(resp.id).toBe(doorName); + expect(resp.status).toBe(DoorStatus.CLOSED); + + // unlock door with wrong code should be 401 + const badAuthResp = await fetch(baseUrl + `/api/door/auth?door=${doorName}&key=thisisthewrongkey`); + expect(badAuthResp.status).toBe(401); + + // unlock door with correct code should be 200 + const authResp = await fetch(baseUrl + `/api/door/auth?door=${doorName}&key=${key}`); + expect(authResp.status).toBe(200); + + // door should be unlocked on info route + const infoOpen = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as any; + expect(infoOpen.status).toBe(DoorStatus.OPEN); + + // calling status should unlock and close the door + const statusOpen = await fetch(baseUrl + `/api/door/status?door=${doorName}`).then(res => res.json()) as StatusResponse; + expect(statusOpen.status).toBe(DoorStatus.OPEN); + + const infoClosed = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as any; + expect(infoClosed.status).toBe(DoorStatus.CLOSED); + }); + + test("auth works for timeout", async () => { + // run status first, to make sure we are closed + const statusReset = await fetch(baseUrl + `/api/door/status?door=${doorName}`).then(res => res.json()) as StatusResponse; + + // run auth with timeout specified + const authResp = await fetch(baseUrl + `/api/door/auth?door=${doorName}&key=${key}&timeout=1`); + + // sleep 1s + await sleep(1_000); + + // we should be closed, because we passed the timeout + const infoClosed = await fetch(baseUrl + `/api/door/info?door=${doorName}`).then(res => res.json()) as any; + expect(infoClosed.status).toBe(DoorStatus.CLOSED); + }); +}); + +// TODO: add tests for edit / onboard. Not sure how I can automated receive info needed for discord DMs \ No newline at end of file diff --git a/packages/doorman-api/tst/testCommon.ts b/packages/doorman-api/tst/testCommon.ts new file mode 100644 index 0000000..71a3bed --- /dev/null +++ b/packages/doorman-api/tst/testCommon.ts @@ -0,0 +1,18 @@ +export const baseUrl = "http://localhost:8080"; +export const doorName = "test"; +export const buzzerNumber = "6133163433"; +export const key = "1234"; + +export const waitForService = async (url: string, timeout = 30000) => { + const start = Date.now(); + + while (Date.now() - start < timeout) { + try { + await fetch(url); + return true; + } catch (err) { + await new Promise(resolve => setTimeout(resolve, 1000)); + } + } + throw new Error(`Service at ${url} did not start within ${timeout}ms`); +}; \ No newline at end of file