feat(api): /api/recipes/all akzeptiert sort=viewed + profile_id
VALID_SORTS um 'viewed' erweitert. parseProfileId-Helper analog zu /api/wishlist. Wert wird an listAllRecipesPaginated als 5. Param durchgereicht. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,19 @@ import {
|
||||
type AllRecipesSort
|
||||
} from '$lib/server/recipes/search-local';
|
||||
|
||||
const VALID_SORTS = new Set<AllRecipesSort>(['name', 'rating', 'cooked', 'created']);
|
||||
const VALID_SORTS = new Set<AllRecipesSort>([
|
||||
'name',
|
||||
'rating',
|
||||
'cooked',
|
||||
'created',
|
||||
'viewed'
|
||||
]);
|
||||
|
||||
function parseProfileId(raw: string | null): number | null {
|
||||
if (!raw) return null;
|
||||
const n = Number(raw);
|
||||
return Number.isInteger(n) && n > 0 ? n : null;
|
||||
}
|
||||
|
||||
export const GET: RequestHandler = async ({ url }) => {
|
||||
const sortRaw = (url.searchParams.get('sort') ?? 'name') as AllRecipesSort;
|
||||
@@ -17,6 +29,7 @@ export const GET: RequestHandler = async ({ url }) => {
|
||||
// one round-trip so document height matches and scroll-restore lands.
|
||||
const limit = Math.min(200, Math.max(1, Number(url.searchParams.get('limit') ?? 10)));
|
||||
const offset = Math.max(0, Number(url.searchParams.get('offset') ?? 0));
|
||||
const hits = listAllRecipesPaginated(getDb(), sortRaw, limit, offset);
|
||||
const profileId = parseProfileId(url.searchParams.get('profile_id'));
|
||||
const hits = listAllRecipesPaginated(getDb(), sortRaw, limit, offset, profileId);
|
||||
return json({ sort: sortRaw, limit, offset, hits });
|
||||
};
|
||||
|
||||
@@ -237,3 +237,34 @@ describe('POST /api/recipes/[id]/view', () => {
|
||||
).rejects.toMatchObject({ status: 404 });
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GET /api/recipes/all — sort=viewed + profile_id
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
import { GET as allGet } from '../../src/routes/api/recipes/all/+server';
|
||||
|
||||
describe('GET /api/recipes/all sort=viewed', () => {
|
||||
it('passes profile_id through and returns viewed-order hits', async () => {
|
||||
const db = openInMemoryForTest();
|
||||
testDb.current = db;
|
||||
const profile = createProfile(db, 'Test');
|
||||
const a = seedRecipe(db, 'Apfel');
|
||||
const b = seedRecipe(db, 'Birne');
|
||||
recordView(db, profile.id, b);
|
||||
await new Promise((r) => setTimeout(r, 1100));
|
||||
recordView(db, profile.id, a);
|
||||
|
||||
const url = new URL(`http://localhost/api/recipes/all?sort=viewed&profile_id=${profile.id}&limit=10`);
|
||||
const res = await allGet({ url } as never);
|
||||
expect(res.status).toBe(200);
|
||||
const body = await res.json();
|
||||
expect(body.sort).toBe('viewed');
|
||||
expect(body.hits.map((h: { id: number }) => h.id)).toEqual([a, b]);
|
||||
});
|
||||
|
||||
it('400 on invalid sort', async () => {
|
||||
const url = new URL('http://localhost/api/recipes/all?sort=invalid');
|
||||
await expect(allGet({ url } as never)).rejects.toMatchObject({ status: 400 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user