feat(16-04): update all service tests to pass userId and add isolation tests

- Destructure { db, userId } from createTestDb() in all 8 service test files
- Pass userId to every service function call
- Add cross-user isolation tests for items, categories, threads, setups
- Add composite unique constraint test for categories
- Update verifyApiKey assertions to check { userId } return
- Update verifyAccessToken assertions to check { userId } return
- Pass userId to exchangeCode and refreshAccessToken calls
This commit is contained in:
2026-04-05 11:01:51 +02:00
parent 884bec0b35
commit 5b702a0e98
8 changed files with 659 additions and 288 deletions

View File

@@ -9,16 +9,17 @@ import { createTestDb } from "../helpers/db.ts";
describe("CSV Service", () => {
let db: any;
let userId: number;
beforeEach(async () => {
db = await createTestDb();
({ db, userId } = await createTestDb());
});
// ── Export ────────────────────────────────────────────────────────────────
describe("exportItemsCsv", () => {
it("returns correct headers on empty collection", async () => {
const csv = await exportItemsCsv(db);
const csv = await exportItemsCsv(db, userId);
const lines = csv.split("\n");
expect(lines[0]).toBe(
"name,quantity,weightGrams,priceCents,category,notes,productUrl",
@@ -27,7 +28,7 @@ describe("CSV Service", () => {
});
it("exports items with correct values", async () => {
await createItem(db, {
await createItem(db, userId, {
name: "Tent",
weightGrams: 1200,
priceCents: 35000,
@@ -36,7 +37,7 @@ describe("CSV Service", () => {
productUrl: "https://example.com/tent",
});
const csv = await exportItemsCsv(db);
const csv = await exportItemsCsv(db, userId);
const lines = csv.split("\n");
expect(lines).toHaveLength(2);
expect(lines[1]).toContain("Tent");
@@ -48,42 +49,46 @@ describe("CSV Service", () => {
});
it("properly escapes fields with commas", async () => {
await createItem(db, {
await createItem(db, userId, {
name: "Tent, Ultralight",
categoryId: 1,
});
const csv = await exportItemsCsv(db);
const csv = await exportItemsCsv(db, userId);
const lines = csv.split("\n");
expect(lines[1]).toContain('"Tent, Ultralight"');
});
it("properly escapes fields with double quotes", async () => {
await createItem(db, {
await createItem(db, userId, {
name: 'He said "great tent"',
categoryId: 1,
});
const csv = await exportItemsCsv(db);
const csv = await exportItemsCsv(db, userId);
const lines = csv.split("\n");
expect(lines[1]).toContain('"He said ""great tent"""');
});
it("exports multiple items", async () => {
await createItem(db, { name: "Tent", categoryId: 1 });
await createItem(db, { name: "Sleeping Bag", categoryId: 1 });
await createItem(db, userId, { name: "Tent", categoryId: 1 });
await createItem(db, userId, {
name: "Sleeping Bag",
categoryId: 1,
});
const csv = await exportItemsCsv(db);
const csv = await exportItemsCsv(db, userId);
const lines = csv.split("\n");
expect(lines).toHaveLength(3); // header + 2 items
});
it("exports quantity correctly", async () => {
// Insert directly to set quantity > 1 (createItem service defaults to 1)
await db.insert(items)
.values({ name: "Bolt", categoryId: 1, quantity: 4 });
await db
.insert(items)
.values({ name: "Bolt", categoryId: 1, quantity: 4, userId });
const csv = await exportItemsCsv(db);
const csv = await exportItemsCsv(db, userId);
const lines = csv.split("\n");
const fields = lines[1].split(",");
// quantity is second field
@@ -101,7 +106,7 @@ describe("CSV Service", () => {
"Sleeping Bag,1,800,25000,Camping,,",
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(2);
expect(result.errors).toHaveLength(0);
});
@@ -112,7 +117,7 @@ describe("CSV Service", () => {
"Helmet,1,350,12000,Cycling,,",
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(1);
expect(result.createdCategories).toContain("Cycling");
expect(result.errors).toHaveLength(0);
@@ -125,7 +130,7 @@ describe("CSV Service", () => {
"Spork,1,,,uncategorized,,",
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(1);
expect(result.createdCategories).toHaveLength(0);
});
@@ -137,7 +142,7 @@ describe("CSV Service", () => {
"Tent,1,1200,,,",
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(1);
expect(result.errors).toHaveLength(1);
expect(result.errors[0]).toMatch(/missing required field "name"/);
@@ -149,7 +154,7 @@ describe("CSV Service", () => {
"Tent,1200,35000,Camping,,",
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(1);
expect(result.errors).toHaveLength(0);
});
@@ -160,7 +165,7 @@ describe("CSV Service", () => {
"Tent,,,,,",
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(1);
expect(result.errors).toHaveLength(0);
});
@@ -171,13 +176,13 @@ describe("CSV Service", () => {
'"Tent, Ultralight",1,1200,,,',
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(1);
expect(result.errors).toHaveLength(0);
});
it("returns zero imported on empty CSV", async () => {
const result = await importItemsCsv(db, "");
const result = await importItemsCsv(db, userId, "");
expect(result.imported).toBe(0);
expect(result.errors).toHaveLength(0);
});
@@ -188,7 +193,7 @@ describe("CSV Service", () => {
"Tent,1,,,,",
].join("\n");
const result = await importItemsCsv(db, csv);
const result = await importItemsCsv(db, userId, csv);
expect(result.imported).toBe(1);
expect(result.createdCategories).toHaveLength(0);
});