test(persons): cover review form actions in server spec
Extend the WRITE_ALL-guard spec to a full matrix for each of the four form actions (confirm, delete, merge, rename): happy path (backend 200), required-field validation where applicable (merge without targetPersonId, rename without lastName), backend 403, backend 404, and the unauthorized guard from the previous commit. Mirrors the shape of frontend/src/routes/persons/page.server.spec.ts. 18 tests, all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -45,8 +45,39 @@ function runAction(
|
|||||||
} as any);
|
} as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('persons/review confirm action — WRITE_ALL guard', () => {
|
describe('persons/review confirm action', () => {
|
||||||
it('returns fail(403) when the user lacks WRITE_ALL', async () => {
|
it('returns { success: true } on backend 200', async () => {
|
||||||
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
|
||||||
|
const result = await runAction(actions.confirm, fd, writer);
|
||||||
|
|
||||||
|
expect(apiCall).toHaveBeenCalledOnce();
|
||||||
|
expect(result).toEqual({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) on backend 403', async () => {
|
||||||
|
mockApi({ ok: false, status: 403, error: { code: 'FORBIDDEN' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
|
||||||
|
const result = await runAction(actions.confirm, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 403 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(404) on backend 404', async () => {
|
||||||
|
mockApi({ ok: false, status: 404, error: { code: 'NOT_FOUND' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-missing');
|
||||||
|
|
||||||
|
const result = await runAction(actions.confirm, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 404 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) when the user lacks WRITE_ALL (server-side guard)', async () => {
|
||||||
const apiCall = mockApi({ ok: true, status: 200 });
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('id', 'p-1');
|
fd.append('id', 'p-1');
|
||||||
@@ -58,8 +89,39 @@ describe('persons/review confirm action — WRITE_ALL guard', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('persons/review delete action — WRITE_ALL guard', () => {
|
describe('persons/review delete action', () => {
|
||||||
it('returns fail(403) when the user lacks WRITE_ALL', async () => {
|
it('returns { success: true } on backend 200', async () => {
|
||||||
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
|
||||||
|
const result = await runAction(actions.delete, fd, writer);
|
||||||
|
|
||||||
|
expect(apiCall).toHaveBeenCalledOnce();
|
||||||
|
expect(result).toEqual({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) on backend 403', async () => {
|
||||||
|
mockApi({ ok: false, status: 403, error: { code: 'FORBIDDEN' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
|
||||||
|
const result = await runAction(actions.delete, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 403 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(404) on backend 404', async () => {
|
||||||
|
mockApi({ ok: false, status: 404, error: { code: 'NOT_FOUND' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-missing');
|
||||||
|
|
||||||
|
const result = await runAction(actions.delete, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 404 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) when the user lacks WRITE_ALL (server-side guard)', async () => {
|
||||||
const apiCall = mockApi({ ok: true, status: 200 });
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('id', 'p-1');
|
fd.append('id', 'p-1');
|
||||||
@@ -71,8 +133,53 @@ describe('persons/review delete action — WRITE_ALL guard', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('persons/review merge action — WRITE_ALL guard', () => {
|
describe('persons/review merge action', () => {
|
||||||
it('returns fail(403) when the user lacks WRITE_ALL', async () => {
|
it('returns { success: true } on backend 200', async () => {
|
||||||
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
fd.append('targetPersonId', 'p-2');
|
||||||
|
|
||||||
|
const result = await runAction(actions.merge, fd, writer);
|
||||||
|
|
||||||
|
expect(apiCall).toHaveBeenCalledOnce();
|
||||||
|
expect(result).toEqual({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(400) when targetPersonId is missing', async () => {
|
||||||
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
|
||||||
|
const result = await runAction(actions.merge, fd, writer);
|
||||||
|
|
||||||
|
expect(apiCall).not.toHaveBeenCalled();
|
||||||
|
expect(result).toMatchObject({ status: 400 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) on backend 403', async () => {
|
||||||
|
mockApi({ ok: false, status: 403, error: { code: 'FORBIDDEN' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
fd.append('targetPersonId', 'p-2');
|
||||||
|
|
||||||
|
const result = await runAction(actions.merge, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 403 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(404) on backend 404', async () => {
|
||||||
|
mockApi({ ok: false, status: 404, error: { code: 'NOT_FOUND' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
fd.append('targetPersonId', 'p-missing');
|
||||||
|
|
||||||
|
const result = await runAction(actions.merge, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 404 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) when the user lacks WRITE_ALL (server-side guard)', async () => {
|
||||||
const apiCall = mockApi({ ok: true, status: 200 });
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('id', 'p-1');
|
fd.append('id', 'p-1');
|
||||||
@@ -85,8 +192,53 @@ describe('persons/review merge action — WRITE_ALL guard', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('persons/review rename action — WRITE_ALL guard', () => {
|
describe('persons/review rename action', () => {
|
||||||
it('returns fail(403) when the user lacks WRITE_ALL', async () => {
|
it('returns { success: true } on backend 200', async () => {
|
||||||
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
fd.append('lastName', 'Smith');
|
||||||
|
|
||||||
|
const result = await runAction(actions.rename, fd, writer);
|
||||||
|
|
||||||
|
expect(apiCall).toHaveBeenCalledOnce();
|
||||||
|
expect(result).toEqual({ success: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(400) when lastName is missing', async () => {
|
||||||
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
|
||||||
|
const result = await runAction(actions.rename, fd, writer);
|
||||||
|
|
||||||
|
expect(apiCall).not.toHaveBeenCalled();
|
||||||
|
expect(result).toMatchObject({ status: 400 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) on backend 403', async () => {
|
||||||
|
mockApi({ ok: false, status: 403, error: { code: 'FORBIDDEN' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-1');
|
||||||
|
fd.append('lastName', 'Smith');
|
||||||
|
|
||||||
|
const result = await runAction(actions.rename, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 403 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(404) on backend 404', async () => {
|
||||||
|
mockApi({ ok: false, status: 404, error: { code: 'NOT_FOUND' } });
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('id', 'p-missing');
|
||||||
|
fd.append('lastName', 'Smith');
|
||||||
|
|
||||||
|
const result = await runAction(actions.rename, fd, writer);
|
||||||
|
|
||||||
|
expect(result).toMatchObject({ status: 404 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns fail(403) when the user lacks WRITE_ALL (server-side guard)', async () => {
|
||||||
const apiCall = mockApi({ ok: true, status: 200 });
|
const apiCall = mockApi({ ok: true, status: 200 });
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('id', 'p-1');
|
fd.append('id', 'p-1');
|
||||||
@@ -98,18 +250,3 @@ describe('persons/review rename action — WRITE_ALL guard', () => {
|
|||||||
expect(result).toMatchObject({ status: 403 });
|
expect(result).toMatchObject({ status: 403 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sanity: writers still pass through (no 403 from the guard). Full happy-path coverage lives
|
|
||||||
// in the action-by-action describe blocks added later.
|
|
||||||
describe('persons/review confirm action — writer passes guard', () => {
|
|
||||||
it('does NOT short-circuit with 403 when the user has WRITE_ALL', async () => {
|
|
||||||
const apiCall = mockApi({ ok: true, status: 200 });
|
|
||||||
const fd = new FormData();
|
|
||||||
fd.append('id', 'p-1');
|
|
||||||
|
|
||||||
const result = await runAction(actions.confirm, fd, writer);
|
|
||||||
|
|
||||||
expect(apiCall).toHaveBeenCalled();
|
|
||||||
expect(result).toEqual({ success: true });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user