aboutsummaryrefslogtreecommitdiff
path: root/src/lib/server/db/groceries.test.ts
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-07-27 17:18:40 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-07-27 17:18:40 +0200
commit6722589b515ccee267212cf783fc0ebbaa20cd90 (patch)
tree1220bf320a47dc5fe8a438da8443c788555f8edb /src/lib/server/db/groceries.test.ts
parent6cde16e66ddcb95f55aa2195910260d9b1faa88b (diff)
feat: catalog section + deduplicate items by name
- Show all items split into List (unchecked) and Catalog (checked) sections - Tapping a catalog item unchecks it (returns to list) - Add action now upserts: unchecks existing item instead of creating duplicate - UNIQUE constraint on groceries.name as defense-in-depth - Tests for dedup behavior (insert, re-add from catalog, no-op on list)
Diffstat (limited to 'src/lib/server/db/groceries.test.ts')
-rw-r--r--src/lib/server/db/groceries.test.ts73
1 files changed, 73 insertions, 0 deletions
diff --git a/src/lib/server/db/groceries.test.ts b/src/lib/server/db/groceries.test.ts
new file mode 100644
index 0000000..b4f5050
--- /dev/null
+++ b/src/lib/server/db/groceries.test.ts
@@ -0,0 +1,73 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { drizzle } from 'drizzle-orm/libsql';
+import { createClient } from '@libsql/client';
+import { groceries } from '$lib/server/db/schema';
+import { eq } from 'drizzle-orm';
+
+describe('groceries add', () => {
+ const client = createClient({ url: ':memory:' });
+ const db = drizzle(client, { schema: { groceries } });
+
+ beforeEach(async () => {
+ await db.run(`DROP TABLE IF EXISTS groceries`);
+ await db.run(`CREATE TABLE groceries (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL,
+ checked INTEGER NOT NULL DEFAULT 0,
+ created_at TEXT NOT NULL
+ )`);
+ });
+
+ async function addItem(name: string) {
+ const existing = await db
+ .select()
+ .from(groceries)
+ .where(eq(groceries.name, name))
+ .limit(1);
+
+ if (existing.length > 0) {
+ await db
+ .update(groceries)
+ .set({ checked: false })
+ .where(eq(groceries.id, existing[0].id));
+ } else {
+ await db.insert(groceries).values({
+ name,
+ createdAt: new Date().toISOString(),
+ });
+ }
+ }
+
+ it('creates a new item when name is new', async () => {
+ await addItem('milk');
+
+ const items = await db.select().from(groceries);
+ expect(items).toHaveLength(1);
+ expect(items[0].name).toBe('milk');
+ expect(items[0].checked).toBe(false);
+ });
+
+ it('unchecks an existing checked item instead of creating duplicate', async () => {
+ const now = new Date().toISOString();
+ await db
+ .insert(groceries)
+ .values({ name: 'milk', checked: true, createdAt: now });
+ await addItem('milk');
+
+ const items = await db.select().from(groceries);
+ expect(items).toHaveLength(1);
+ expect(items[0].checked).toBe(false);
+ });
+
+ it('keeps an unchecked item unchanged (already on list)', async () => {
+ const now = new Date().toISOString();
+ await db
+ .insert(groceries)
+ .values({ name: 'milk', checked: false, createdAt: now });
+ await addItem('milk');
+
+ const items = await db.select().from(groceries);
+ expect(items).toHaveLength(1);
+ expect(items[0].checked).toBe(false);
+ });
+});