From b9ee04fcecb96b24e00e3a9638367dbb1518c5fa Mon Sep 17 00:00:00 2001 From: whitewofe <39736756+whitewofe@users.noreply.github.com> Date: Sun, 21 Jun 2026 20:47:36 +0800 Subject: [PATCH] test: harden ky create test and cover delete/list routes - create.test.ts: await ky.post, assert {ok:true}, verify persisted thing_id/name/description so the create+list sequence is deterministic. - delete.test.ts (new): covers happy-path deletion via URLSearchParams body and a no-op when thing_id is not present. - list.test.ts (new): covers empty-list response and insertion-order with monotonically increasing thing_id from the db idCounter. --- tests/routes/things/create.test.ts | 28 ++++++++----- tests/routes/things/delete.test.ts | 65 ++++++++++++++++++++++++++++++ tests/routes/things/list.test.ts | 35 ++++++++++++++++ 3 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 tests/routes/things/delete.test.ts create mode 100644 tests/routes/things/list.test.ts diff --git a/tests/routes/things/create.test.ts b/tests/routes/things/create.test.ts index 65e9935..a071050 100644 --- a/tests/routes/things/create.test.ts +++ b/tests/routes/things/create.test.ts @@ -1,19 +1,27 @@ import { getTestServer } from "tests/fixtures/get-test-server" import { test, expect } from "bun:test" -test("create a thing", async () => { +test("create a thing persists and lists deterministically", async () => { const { ky } = await getTestServer() - ky.post("things/create", { - json: { - name: "Thing1", - description: "Thing1 Description", - }, - }) + const createRes = await ky + .post("things/create", { + json: { + name: "Thing1", + description: "Thing1 Description", + }, + }) + .json<{ ok: boolean }>() - const data = await ky - .get("things/list") - .json<{ things: { name: string; description: string }[] }>() + expect(createRes).toEqual({ ok: true }) + + const data = await ky.get("things/list").json<{ + things: { thing_id: string; name: string; description: string }[] + }>() expect(data.things).toHaveLength(1) + expect(data.things[0].name).toBe("Thing1") + expect(data.things[0].description).toBe("Thing1 Description") + expect(typeof data.things[0].thing_id).toBe("string") + expect(data.things[0].thing_id.length).toBeGreaterThan(0) }) diff --git a/tests/routes/things/delete.test.ts b/tests/routes/things/delete.test.ts new file mode 100644 index 0000000..4993b2f --- /dev/null +++ b/tests/routes/things/delete.test.ts @@ -0,0 +1,65 @@ +import { getTestServer } from "tests/fixtures/get-test-server" +import { test, expect } from "bun:test" + +test("delete a thing removes it from the list", async () => { + const { ky } = await getTestServer() + + await ky + .post("things/create", { + json: { name: "ToDelete", description: "Will be removed" }, + }) + .json<{ ok: boolean }>() + + await ky + .post("things/create", { + json: { name: "ToKeep", description: "Stays" }, + }) + .json<{ ok: boolean }>() + + const before = await ky.get("things/list").json<{ + things: { thing_id: string; name: string; description: string }[] + }>() + expect(before.things).toHaveLength(2) + + const target = before.things.find((t) => t.name === "ToDelete") + expect(target).toBeDefined() + + const deleteRes = await ky + .post("things/delete", { + body: new URLSearchParams({ thing_id: target!.thing_id }), + }) + .json<{ ok: boolean }>() + expect(deleteRes).toEqual({ ok: true }) + + const after = await ky.get("things/list").json<{ + things: { thing_id: string; name: string; description: string }[] + }>() + expect(after.things).toHaveLength(1) + expect(after.things[0].name).toBe("ToKeep") + expect( + after.things.find((t) => t.thing_id === target!.thing_id), + ).toBeUndefined() +}) + +test("delete is a no-op when thing_id does not exist", async () => { + const { ky } = await getTestServer() + + await ky + .post("things/create", { + json: { name: "Solo", description: "only one" }, + }) + .json<{ ok: boolean }>() + + const res = await ky + .post("things/delete", { + body: new URLSearchParams({ thing_id: "does-not-exist" }), + }) + .json<{ ok: boolean }>() + expect(res).toEqual({ ok: true }) + + const after = await ky + .get("things/list") + .json<{ things: { thing_id: string; name: string }[] }>() + expect(after.things).toHaveLength(1) + expect(after.things[0].name).toBe("Solo") +}) diff --git a/tests/routes/things/list.test.ts b/tests/routes/things/list.test.ts new file mode 100644 index 0000000..c040766 --- /dev/null +++ b/tests/routes/things/list.test.ts @@ -0,0 +1,35 @@ +import { getTestServer } from "tests/fixtures/get-test-server" +import { test, expect } from "bun:test" + +test("list returns an empty array when no things exist", async () => { + const { ky } = await getTestServer() + + const data = await ky.get("things/list").json<{ + things: { thing_id: string; name: string; description: string }[] + }>() + + expect(data.things).toEqual([]) +}) + +test("list returns things in insertion order with incrementing thing_id", async () => { + const { ky } = await getTestServer() + + const names = ["Alpha", "Bravo", "Charlie"] + for (const name of names) { + await ky + .post("things/create", { + json: { name, description: `${name} desc` }, + }) + .json<{ ok: boolean }>() + } + + const data = await ky.get("things/list").json<{ + things: { thing_id: string; name: string; description: string }[] + }>() + + expect(data.things.map((t) => t.name)).toEqual(names) + // thing_id is generated from an incrementing counter + const ids = data.things.map((t) => Number(t.thing_id)) + expect(ids).toEqual([...ids].sort((a, b) => a - b)) + expect(new Set(ids).size).toBe(ids.length) +})