fix(tests): add missing Sentry mock event fields across 14 spec files; fix test:coverage semicolon
`@sentry/sveltekit` wraps load functions and reads `event.request.method` and `event.url.pathname`. Mock events that omitted `request` or `url` threw `TypeError: Cannot read properties of undefined` on every invocation, silently masking 86 test failures on main. Two root causes fixed: - Added `request: new Request(...)` (and `url: new URL(...)` where absent) to all mock event objects in 14 `*.server.spec.ts` files - Changed `;` to `&&` in the `test:coverage` npm script so a failing server run propagates its exit code instead of being swallowed by the client run All 576 server-project tests now pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,14 +19,22 @@ describe('admin/groups layout load', () => {
|
||||
{ id: 'g1', name: 'Admins', permissions: ['ADMIN'] },
|
||||
{ id: 'g2', name: 'Editors', permissions: ['WRITE_ALL'] }
|
||||
]);
|
||||
const result = await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
const result = await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/groups'),
|
||||
url: new URL('http://localhost/admin/groups')
|
||||
});
|
||||
expect(result.groups).toHaveLength(2);
|
||||
expect(result.groups[0].name).toBe('Admins');
|
||||
});
|
||||
|
||||
it('returns an empty array when the API returns nothing', async () => {
|
||||
mockApi([]);
|
||||
const result = await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
const result = await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/groups'),
|
||||
url: new URL('http://localhost/admin/groups')
|
||||
});
|
||||
expect(result.groups).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -35,7 +43,11 @@ describe('admin/groups layout load', () => {
|
||||
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
|
||||
typeof createApiClient
|
||||
>);
|
||||
await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/groups'),
|
||||
url: new URL('http://localhost/admin/groups')
|
||||
});
|
||||
expect(mockGet).toHaveBeenCalledWith('/api/groups');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,26 +26,46 @@ beforeEach(() => vi.clearAllMocks());
|
||||
describe('admin layout load — permission check', () => {
|
||||
it('throws 403 when user has no admin permission', async () => {
|
||||
await expect(
|
||||
load({ fetch: vi.fn() as unknown as typeof fetch, locals: { user: noPermUser } })
|
||||
load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin'),
|
||||
url: new URL('http://localhost/admin'),
|
||||
locals: { user: noPermUser }
|
||||
})
|
||||
).rejects.toMatchObject({ status: 403 });
|
||||
});
|
||||
|
||||
it('throws 403 when user is undefined', async () => {
|
||||
await expect(
|
||||
load({ fetch: vi.fn() as unknown as typeof fetch, locals: { user: undefined } })
|
||||
load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin'),
|
||||
url: new URL('http://localhost/admin'),
|
||||
locals: { user: undefined }
|
||||
})
|
||||
).rejects.toMatchObject({ status: 403 });
|
||||
});
|
||||
|
||||
it('throws 403 when user has no groups', async () => {
|
||||
await expect(
|
||||
load({ fetch: vi.fn() as unknown as typeof fetch, locals: { user: { groups: [] } } })
|
||||
load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin'),
|
||||
url: new URL('http://localhost/admin'),
|
||||
locals: { user: { groups: [] } }
|
||||
})
|
||||
).rejects.toMatchObject({ status: 403 });
|
||||
});
|
||||
|
||||
it('allows access for a user with ADMIN_TAG only', async () => {
|
||||
mockApi([], [], []);
|
||||
await expect(
|
||||
load({ fetch: vi.fn() as unknown as typeof fetch, locals: { user: tagAdminUser } })
|
||||
load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin'),
|
||||
url: new URL('http://localhost/admin'),
|
||||
locals: { user: tagAdminUser }
|
||||
})
|
||||
).resolves.toBeDefined();
|
||||
});
|
||||
|
||||
@@ -63,6 +83,8 @@ describe('admin layout load — permission check', () => {
|
||||
|
||||
const result = await load({
|
||||
fetch: mockFetch as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin'),
|
||||
url: new URL('http://localhost/admin'),
|
||||
locals: { user: adminUser }
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,12 @@ describe('admin/ocr/[personId] — load', () => {
|
||||
data: { runs: [], personNames: { [personId]: 'Anna Müller' } }
|
||||
});
|
||||
|
||||
const result = (await load({ params: { personId }, fetch } as never))!;
|
||||
const result = (await load({
|
||||
params: { personId },
|
||||
fetch,
|
||||
request: new Request('http://localhost/admin/ocr/123'),
|
||||
url: new URL('http://localhost/admin/ocr/123')
|
||||
} as never))!;
|
||||
|
||||
expect(result.history.personNames?.[personId]).toBe('Anna Müller');
|
||||
});
|
||||
@@ -27,7 +32,12 @@ describe('admin/ocr/[personId] — load', () => {
|
||||
});
|
||||
|
||||
await expect(
|
||||
load({ params: { personId: 'unknown-id' }, fetch } as never)
|
||||
load({
|
||||
params: { personId: 'unknown-id' },
|
||||
fetch,
|
||||
request: new Request('http://localhost/admin/ocr/unknown-id'),
|
||||
url: new URL('http://localhost/admin/ocr/unknown-id')
|
||||
} as never)
|
||||
).rejects.toMatchObject({ status: 404 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,11 @@ describe('admin/ocr/global — load', () => {
|
||||
data: { runs: [{ id: 'run1' }], personNames: {} }
|
||||
});
|
||||
|
||||
const result = (await load({ fetch } as never))!;
|
||||
const result = (await load({
|
||||
fetch,
|
||||
request: new Request('http://localhost/admin/ocr/global'),
|
||||
url: new URL('http://localhost/admin/ocr/global')
|
||||
} as never))!;
|
||||
|
||||
expect(result.history.runs).toHaveLength(1);
|
||||
});
|
||||
@@ -22,6 +26,12 @@ describe('admin/ocr/global — load', () => {
|
||||
it('throws error when API call fails', async () => {
|
||||
mockApi.GET.mockResolvedValue({ response: { ok: false, status: 500 }, error: {} });
|
||||
|
||||
await expect(load({ fetch } as never)).rejects.toMatchObject({ status: 500 });
|
||||
await expect(
|
||||
load({
|
||||
fetch,
|
||||
request: new Request('http://localhost/admin/ocr/global'),
|
||||
url: new URL('http://localhost/admin/ocr/global')
|
||||
} as never)
|
||||
).rejects.toMatchObject({ status: 500 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,11 @@ describe('admin/ocr — load', () => {
|
||||
data: { availableBlocks: 10, ocrServiceAvailable: true, senderModels: [] }
|
||||
});
|
||||
|
||||
const result = (await load({ fetch } as never))!;
|
||||
const result = (await load({
|
||||
fetch,
|
||||
request: new Request('http://localhost/admin/ocr'),
|
||||
url: new URL('http://localhost/admin/ocr')
|
||||
} as never))!;
|
||||
|
||||
expect(result.trainingInfo.availableBlocks).toBe(10);
|
||||
expect(result.trainingInfo.ocrServiceAvailable).toBe(true);
|
||||
@@ -23,6 +27,12 @@ describe('admin/ocr — load', () => {
|
||||
it('throws 503 when OCR API call fails', async () => {
|
||||
mockApi.GET.mockResolvedValue({ response: { ok: false, status: 503 }, error: {} });
|
||||
|
||||
await expect(load({ fetch } as never)).rejects.toMatchObject({ status: 503 });
|
||||
await expect(
|
||||
load({
|
||||
fetch,
|
||||
request: new Request('http://localhost/admin/ocr'),
|
||||
url: new URL('http://localhost/admin/ocr')
|
||||
} as never)
|
||||
).rejects.toMatchObject({ status: 503 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ describe('tags/[id] — load function', () => {
|
||||
const result = await load({
|
||||
params: { id: 't1' },
|
||||
parent: async () => ({ tags: [{ id: 't1', name: 'Test', documentCount: 0 }] }),
|
||||
request: new Request('http://localhost/admin/tags/t1'),
|
||||
url
|
||||
} as never);
|
||||
expect((result as { mergeSuccess: boolean }).mergeSuccess).toBe(true);
|
||||
@@ -31,6 +32,7 @@ describe('tags/[id] — load function', () => {
|
||||
const result = await load({
|
||||
params: { id: 't1' },
|
||||
parent: async () => ({ tags: [{ id: 't1', name: 'Test', documentCount: 0 }] }),
|
||||
request: new Request('http://localhost/admin/tags/t1'),
|
||||
url
|
||||
} as never);
|
||||
expect((result as { mergeSuccess: boolean }).mergeSuccess).toBe(false);
|
||||
|
||||
@@ -44,14 +44,22 @@ const sampleTree = [
|
||||
describe('admin/tags layout load', () => {
|
||||
it('returns the tree list', async () => {
|
||||
mockTreeApi(sampleTree);
|
||||
const result = await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
const result = await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/tags'),
|
||||
url: new URL('http://localhost/admin/tags')
|
||||
});
|
||||
expect(result.tree).toHaveLength(2);
|
||||
expect(result.tree[0].name).toBe('Familie');
|
||||
});
|
||||
|
||||
it('returns an empty tree when the API returns nothing', async () => {
|
||||
mockTreeApi([]);
|
||||
const result = await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
const result = await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/tags'),
|
||||
url: new URL('http://localhost/admin/tags')
|
||||
});
|
||||
expect(result.tree).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -60,13 +68,21 @@ describe('admin/tags layout load', () => {
|
||||
vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
|
||||
typeof createApiClient
|
||||
>);
|
||||
await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/tags'),
|
||||
url: new URL('http://localhost/admin/tags')
|
||||
});
|
||||
expect(mockGet).toHaveBeenCalledWith('/api/tags/tree');
|
||||
});
|
||||
|
||||
it('flattens the tree into a flat tags array', async () => {
|
||||
mockTreeApi(sampleTree);
|
||||
const result = await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
const result = await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/tags'),
|
||||
url: new URL('http://localhost/admin/tags')
|
||||
});
|
||||
// Both parent and child should be in the flat array
|
||||
expect(result.tags).toHaveLength(3);
|
||||
expect(result.tags.map((t) => t.name)).toContain('Eltern');
|
||||
@@ -74,14 +90,22 @@ describe('admin/tags layout load', () => {
|
||||
|
||||
it('preserves parentId on child tags in the flat array', async () => {
|
||||
mockTreeApi(sampleTree);
|
||||
const result = await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
const result = await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/tags'),
|
||||
url: new URL('http://localhost/admin/tags')
|
||||
});
|
||||
const child = result.tags.find((t) => t.name === 'Eltern');
|
||||
expect(child?.parentId).toBe('parent1');
|
||||
});
|
||||
|
||||
it('sets parentId to undefined on root tags in the flat array', async () => {
|
||||
mockTreeApi(sampleTree);
|
||||
const result = await load({ fetch: vi.fn() as unknown as typeof fetch });
|
||||
const result = await load({
|
||||
fetch: vi.fn() as unknown as typeof fetch,
|
||||
request: new Request('http://localhost/admin/tags'),
|
||||
url: new URL('http://localhost/admin/tags')
|
||||
});
|
||||
const root = result.tags.find((t) => t.name === 'Familie');
|
||||
expect(root?.parentId).toBeUndefined();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user