From 2cd9b47450e35de34fbd6c895953767ba5bbc322 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:04:27 +0200 Subject: [PATCH] feat(db): recipe_views table mit Profil-FK und Recent-Index Tracking-Tabelle fuer Sort-Option Zuletzt angesehen. Composite-PK (profile_id, recipe_id) erlaubt INSERT OR REPLACE per Default-Timestamp. Index nach profile_id + last_viewed_at DESC fuer Sort-Query. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../server/db/migrations/014_recipe_views.sql | 8 +++++ tests/integration/recipe-views.test.ts | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/lib/server/db/migrations/014_recipe_views.sql create mode 100644 tests/integration/recipe-views.test.ts diff --git a/src/lib/server/db/migrations/014_recipe_views.sql b/src/lib/server/db/migrations/014_recipe_views.sql new file mode 100644 index 0000000..657a716 --- /dev/null +++ b/src/lib/server/db/migrations/014_recipe_views.sql @@ -0,0 +1,8 @@ +CREATE TABLE recipe_views ( + profile_id INTEGER NOT NULL REFERENCES profile(id) ON DELETE CASCADE, + recipe_id INTEGER NOT NULL REFERENCES recipe(id) ON DELETE CASCADE, + last_viewed_at TEXT NOT NULL DEFAULT (datetime('now')), + PRIMARY KEY (profile_id, recipe_id) +); +CREATE INDEX idx_recipe_views_recent + ON recipe_views (profile_id, last_viewed_at DESC); diff --git a/tests/integration/recipe-views.test.ts b/tests/integration/recipe-views.test.ts new file mode 100644 index 0000000..b8d94d4 --- /dev/null +++ b/tests/integration/recipe-views.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from 'vitest'; +import { openInMemoryForTest } from '../../src/lib/server/db'; + +describe('014_recipe_views migration', () => { + it('creates recipe_views table with expected columns', () => { + const db = openInMemoryForTest(); + const cols = db.prepare("PRAGMA table_info(recipe_views)").all() as Array<{ + name: string; + type: string; + notnull: number; + pk: number; + }>; + const byName = Object.fromEntries(cols.map((c) => [c.name, c])); + expect(byName.profile_id?.type).toBe('INTEGER'); + expect(byName.profile_id?.notnull).toBe(1); + expect(byName.profile_id?.pk).toBe(1); + expect(byName.recipe_id?.type).toBe('INTEGER'); + expect(byName.recipe_id?.pk).toBe(2); + expect(byName.last_viewed_at?.type).toBe('TEXT'); + expect(byName.last_viewed_at?.notnull).toBe(1); + }); + + it('has index on (profile_id, last_viewed_at DESC)', () => { + const db = openInMemoryForTest(); + const idxList = db + .prepare("PRAGMA index_list(recipe_views)") + .all() as Array<{ name: string }>; + expect(idxList.some((i) => i.name === 'idx_recipe_views_recent')).toBe(true); + }); +});