test(recipes): add action tests and harden create/update form actions
- Add try-catch around JSON.parse with fail(400) for malformed input - Validate effort against allowed values ['Easy','Medium','Hard'] - Fix NaN risk: Number(serves)||undefined instead of Number(serves) - Add action tests for create/update: validation, JSON.parse crash, success, API error Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,3 +51,104 @@ describe('new recipe page — load', () => {
|
||||
expect(result.recipe).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('new recipe page — create action', () => {
|
||||
let actions: any;
|
||||
|
||||
const makeFormData = (overrides: Record<string, string | string[]> = {}) => {
|
||||
const base: Record<string, string | string[]> = {
|
||||
name: 'Test Rezept',
|
||||
effort: 'Easy',
|
||||
tagIds: ['t1'],
|
||||
ingredientsJson: '[]',
|
||||
stepsJson: '[]',
|
||||
...overrides
|
||||
};
|
||||
const fd = new FormData();
|
||||
for (const [key, val] of Object.entries(base)) {
|
||||
if (Array.isArray(val)) {
|
||||
for (const v of val) fd.append(key, v);
|
||||
} else {
|
||||
fd.append(key, val);
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
mockGet.mockReset();
|
||||
mockPost.mockReset();
|
||||
vi.resetModules();
|
||||
const mod = await import('./+page.server');
|
||||
actions = mod.actions;
|
||||
});
|
||||
|
||||
it('returns fail(422) when name is missing', async () => {
|
||||
const result = await actions.create({
|
||||
request: { formData: async () => makeFormData({ name: '' }) },
|
||||
fetch: vi.fn()
|
||||
} as any);
|
||||
expect(result.status).toBe(422);
|
||||
});
|
||||
|
||||
it('returns fail(422) when effort is missing', async () => {
|
||||
const result = await actions.create({
|
||||
request: { formData: async () => makeFormData({ effort: '' }) },
|
||||
fetch: vi.fn()
|
||||
} as any);
|
||||
expect(result.status).toBe(422);
|
||||
});
|
||||
|
||||
it('returns fail(422) when effort is not a valid value', async () => {
|
||||
const result = await actions.create({
|
||||
request: { formData: async () => makeFormData({ effort: 'InvalidEffort' }) },
|
||||
fetch: vi.fn()
|
||||
} as any);
|
||||
expect(result.status).toBe(422);
|
||||
});
|
||||
|
||||
it('returns fail(422) when no tagIds', async () => {
|
||||
const result = await actions.create({
|
||||
request: { formData: async () => makeFormData({ tagIds: [] }) },
|
||||
fetch: vi.fn()
|
||||
} as any);
|
||||
expect(result.status).toBe(422);
|
||||
});
|
||||
|
||||
it('returns fail(400) when ingredientsJson is invalid JSON', async () => {
|
||||
const result = await actions.create({
|
||||
request: { formData: async () => makeFormData({ ingredientsJson: 'not-json' }) },
|
||||
fetch: vi.fn()
|
||||
} as any);
|
||||
expect(result.status).toBe(400);
|
||||
});
|
||||
|
||||
it('returns fail(400) when stepsJson is invalid JSON', async () => {
|
||||
const result = await actions.create({
|
||||
request: { formData: async () => makeFormData({ stepsJson: '{broken' }) },
|
||||
fetch: vi.fn()
|
||||
} as any);
|
||||
expect(result.status).toBe(400);
|
||||
});
|
||||
|
||||
it('calls POST /v1/recipes with correct body on success', async () => {
|
||||
mockPost.mockResolvedValue({ error: undefined });
|
||||
const fd = makeFormData({
|
||||
ingredientsJson: JSON.stringify([{ name: 'Spaghetti', quantity: 200, unit: 'g' }]),
|
||||
stepsJson: JSON.stringify(['Kochen'])
|
||||
});
|
||||
await actions.create({ request: { formData: async () => fd }, fetch: vi.fn() } as any).catch(
|
||||
() => {}
|
||||
);
|
||||
expect(mockPost).toHaveBeenCalledWith('/v1/recipes', expect.objectContaining({ body: expect.objectContaining({ name: 'Test Rezept', effort: 'Easy' }) }));
|
||||
});
|
||||
|
||||
it('returns fail(500) when API returns error', async () => {
|
||||
mockPost.mockResolvedValue({ error: { status: 500 } });
|
||||
const result = await actions.create({
|
||||
request: { formData: async () => makeFormData() },
|
||||
fetch: vi.fn()
|
||||
} as any);
|
||||
expect(result.status).toBe(500);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user