Files
kochwas/tests/integration/wishlist.test.ts

144 lines
4.6 KiB
TypeScript
Raw Permalink Normal View History

import { describe, it, expect, beforeEach } from 'vitest';
import type Database from 'better-sqlite3';
import { openInMemoryForTest } from '../../src/lib/server/db';
import { createProfile } from '../../src/lib/server/profiles/repository';
import { insertRecipe } from '../../src/lib/server/recipes/repository';
import {
addToWishlist,
removeFromWishlist,
removeFromWishlistForAll,
listWishlist,
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
listWishlistProfileIds,
isOnMyWishlist,
countWishlistRecipes
} from '../../src/lib/server/wishlist/repository';
import type { Recipe } from '../../src/lib/types';
const recipe = (title: string, id?: null): Recipe => ({
id: id ?? null,
title,
description: null,
source_url: null,
source_domain: null,
image_path: null,
servings_default: 4,
servings_unit: null,
prep_time_min: null,
cook_time_min: null,
total_time_min: null,
cuisine: null,
category: null,
ingredients: [],
steps: [],
tags: []
});
let db: Database.Database;
beforeEach(() => {
db = openInMemoryForTest();
});
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
describe('per-user wishlist', () => {
it('adds and lists for a single profile', () => {
const r1 = insertRecipe(db, recipe('Carbonara'));
const p = createProfile(db, 'Hendrik');
addToWishlist(db, r1, p.id);
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
expect(isOnMyWishlist(db, r1, p.id)).toBe(true);
const list = listWishlist(db, p.id);
expect(list.length).toBe(1);
expect(list[0].title).toBe('Carbonara');
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
expect(list[0].wanted_by_count).toBe(1);
expect(list[0].wanted_by_names).toBe('Hendrik');
expect(list[0].on_my_wishlist).toBe(1);
});
it('aggregates multiple users per recipe', () => {
const r1 = insertRecipe(db, recipe('Pizza'));
const a = createProfile(db, 'Alice');
const b = createProfile(db, 'Bob');
const c = createProfile(db, 'Cara');
addToWishlist(db, r1, a.id);
addToWishlist(db, r1, b.id);
addToWishlist(db, r1, c.id);
const listFromA = listWishlist(db, a.id);
expect(listFromA.length).toBe(1);
expect(listFromA[0].wanted_by_count).toBe(3);
expect(listFromA[0].on_my_wishlist).toBe(1);
const ids = listWishlistProfileIds(db, r1);
expect(ids.sort()).toEqual([a.id, b.id, c.id].sort());
});
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
it('is idempotent on double-add for same profile', () => {
const r1 = insertRecipe(db, recipe('Pizza'));
const p = createProfile(db, 'A');
addToWishlist(db, r1, p.id);
addToWishlist(db, r1, p.id);
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
const list = listWishlist(db, p.id);
expect(list[0].wanted_by_count).toBe(1);
});
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
it('removes only my entry, keeps others', () => {
const r1 = insertRecipe(db, recipe('Salad'));
const a = createProfile(db, 'A');
const b = createProfile(db, 'B');
addToWishlist(db, r1, a.id);
addToWishlist(db, r1, b.id);
removeFromWishlist(db, r1, a.id);
expect(isOnMyWishlist(db, r1, a.id)).toBe(false);
expect(isOnMyWishlist(db, r1, b.id)).toBe(true);
expect(listWishlist(db, b.id)[0].wanted_by_count).toBe(1);
});
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
it('on_my_wishlist is 0 for profiles that did not wish', () => {
const r1 = insertRecipe(db, recipe('Curry'));
const a = createProfile(db, 'A');
const b = createProfile(db, 'B');
addToWishlist(db, r1, a.id);
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
const listFromB = listWishlist(db, b.id);
expect(listFromB[0].on_my_wishlist).toBe(0);
expect(listFromB[0].wanted_by_count).toBe(1);
});
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
it('cascades when recipe is deleted', () => {
const r1 = insertRecipe(db, recipe('X'));
const a = createProfile(db, 'A');
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
addToWishlist(db, r1, a.id);
db.prepare('DELETE FROM recipe WHERE id = ?').run(r1);
expect(listWishlist(db, a.id).length).toBe(0);
});
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
it('cascades when profile is deleted', () => {
const r1 = insertRecipe(db, recipe('X'));
const a = createProfile(db, 'A');
addToWishlist(db, r1, a.id);
db.prepare('DELETE FROM profile WHERE id = ?').run(a.id);
expect(listWishlist(db, null).length).toBe(0);
});
feat(wishlist): per-user Wünsche + Header-Badge mit Gesamtzahl Schema-Änderung (Migration 005): - Tabelle wishlist umgestellt auf PK (recipe_id, profile_id) - wishlist_like-Tabelle zusammengelegt — Liken WAR schon "will ich auch", also werden alle bestehenden Likes Memberships auf der neuen Tabelle. - Alt-Einträge mit added_by_profile_id werden migriert, anonyme gehen verloren (war inkonsistent, jetzt erzwingen wir profile_id NOT NULL). Repository: - listWishlist aggregiert pro Rezept: wanted_by_count, wanted_by_names (kommagetrennt), on_my_wishlist für das aktive Profil - listWishlistProfileIds(recipeId) für den Recipe-Page-Loader - countWishlistRecipes für das Header-Badge (DISTINCT recipe_id) - addToWishlist/removeFromWishlist/isOnMyWishlist alle mit profile_id als Pflicht API: - POST /api/wishlist: profile_id jetzt Pflicht (nullable raus) - DELETE /api/wishlist/[recipe_id]?profile_id=X (nur eigenes Entry) - /api/wishlist/[recipe_id]/like komplett entfernt (Konzept obsolet) - Neu: GET /api/wishlist/count → { count: <distinct recipes> } UI: - Header-Heart bekommt rotes Badge mit Zahl der Wunschliste-Rezepte. wishlistStore in $lib/client/wishlist.svelte.ts hält den Count reaktiv; Refresh auf Mount, nach Add/Remove, beim Öffnen der Wunschliste. - Recipe-Detail: Loader liefert wishlist_profile_ids; onMyWishlist ist ein $derived. Toggle fragt aktives Profil (alertAction sonst), mutiert die lokale Liste + ruft wishlistStore.refresh. - Wunschliste-Seite: Heart toggelt eigenen Wunsch, Count zeigt Gesamt- wünsche, kommagetrennte Namen zeigen "wer will". Trash-Button entfernt — Heart-off reicht jetzt. Tests (99 → 99, 8 neu geschrieben): - Per-User-Add/Remove, aggregierte Counts, on_my_wishlist, Cascades bei Recipe/Profile-Delete, countWishlistRecipes = DISTINCT.
2026-04-17 19:16:19 +02:00
it('countWishlistRecipes counts distinct recipes (not rows)', () => {
const r1 = insertRecipe(db, recipe('R1'));
const r2 = insertRecipe(db, recipe('R2'));
const a = createProfile(db, 'A');
const b = createProfile(db, 'B');
addToWishlist(db, r1, a.id);
addToWishlist(db, r1, b.id); // same recipe, different user
addToWishlist(db, r2, a.id);
expect(countWishlistRecipes(db)).toBe(2);
});
it('removeFromWishlistForAll drops every profile', () => {
const r1 = insertRecipe(db, recipe('R1'));
const a = createProfile(db, 'A');
const b = createProfile(db, 'B');
addToWishlist(db, r1, a.id);
addToWishlist(db, r1, b.id);
removeFromWishlistForAll(db, r1);
expect(listWishlistProfileIds(db, r1)).toEqual([]);
});
});