23 Commits

Author SHA1 Message Date
0bbbe0064b Merge pull request 'f105_umstellung_seaweedS3' (#32) from f105_umstellung_seaweedS3 into development
All checks were successful
InnoHub Processor/tatort/pipeline/head This commit looks good
Reviewed-on: #32
2025-09-04 15:17:38 +02:00
92f5f8e5ed Merge pull request 'f100_backend_api-endpoints_tests' (#31) from f100_backend_api-endpoints_tests into development
All checks were successful
InnoHub Processor/tatort/pipeline/head This commit looks good
Reviewed-on: #31
2025-09-04 15:16:49 +02:00
e935adf8df hotfix: viewer cannot access Vorgang with correct PIN, temp remove check if logged-in
All checks were successful
InnoHub Processor/tatort/pipeline/head This commit looks good
2025-09-04 12:44:33 +02:00
d007513e82 formatting 2025-09-04 10:56:11 +02:00
b9c03831cb switch from minio to seaweed S3 storage: configs and buckets (dev vs. prod, refactoring magic strings) 2025-09-04 10:55:45 +02:00
1e85df9127 remove unrelated View tests 2025-09-04 09:59:46 +02:00
3d22aab5b3 stop tracking tatort.db
All checks were successful
InnoHub Processor/tatort/pipeline/head This commit looks good
2025-09-04 08:11:38 +02:00
5a1b27f81c Merge branch 'development' into f094_create_tests 2025-09-03 13:05:02 +02:00
f18ef07116 add tests for API endpoint: vorgang/[vorgang]/vorgangPIN 2025-09-03 10:16:18 +02:00
821b8a6440 refactoring: extract common locals.user definition and translatation of comments 2025-09-03 08:41:57 +02:00
e0b490a353 add tests for API endpoint: users 2025-09-03 08:31:53 +02:00
1e96f13d22 remove unused import 2025-09-02 11:49:11 +02:00
c222d75ac5 add tests for API endpoint: user 2025-09-02 11:10:07 +02:00
877c350824 rename test file for ViewAngemeldet 2025-09-02 10:58:38 +02:00
418e91c504 add tests for API endpoint: list/[vorgang]/[tatort] 2025-09-02 10:47:08 +02:00
9be9676f6c add more tests for API endpoint: list/[vorgang], HEAD and DELETE 2025-08-29 10:19:45 +02:00
52bffcd4f0 reorder import, vi.mock is being hoisted 2025-08-29 08:25:52 +02:00
66a4c014ad add tests for API endpoint: list/[vorgang] 2025-08-28 12:43:56 +02:00
a85ef8eab1 rename api-list test file to use CamelCase 2025-08-28 12:40:56 +02:00
9bb691055a add tests for API endpoint: list vorgang 2025-08-28 12:09:43 +02:00
0aa49643ca add tests for API endpoint: list vorgang 2025-08-28 12:03:40 +02:00
cfe00b5424 Add Landing Page View tests, admin and viewer access 2025-08-28 10:09:34 +02:00
b43cfe345a configure vite.config to include toplevel ´tests´ folder 2025-08-27 13:56:39 +02:00
17 changed files with 568 additions and 32 deletions

View File

@@ -1,10 +1,10 @@
{
"minio": {
"endPoint": "api-s3.innovation-hub-niedersachsen.de",
"endPoint": "sws3.innovation-hub-niedersachsen.de",
"port": 443,
"useSSL": true,
"accessKey": "GxKhfnfkNvlDU7qzsz0D",
"secretKey": "cqSM5rIRr4MPtqzu2sNKgmB9k2OghPbyxwAWogeM"
"accessKey": "wjpKrmaqXra99rX3D61H",
"secretKey": "fTPi0u0FR6Lv9Y9IKydWv6WM0EA5XrsK008HCt9u"
},
"jwt": {
"secret": "@S2!q@@wXz$dCQ8JoVsHLpzaJ6JCfB",

Binary file not shown.

View File

@@ -1,9 +1,9 @@
import { client } from '$lib/minio';
import { client, BUCKET } from '$lib/minio';
export default async function vorgangNumberOccupied (vorgangNumber: string): Promise<boolean> {
export default async function vorgangNumberOccupied(vorgangNumber: string): Promise<boolean> {
const prefix = `${vorgangNumber}`;
const promise: Promise<boolean> = new Promise((resolve) => {
const stream = client.listObjectsV2('tatort', prefix, false, '');
const stream = client.listObjectsV2(BUCKET, prefix, false, '');
stream.on('data', () => {
stream.destroy();
resolve(true);

View File

@@ -7,6 +7,10 @@ import config from '$lib/config';
/** export const client = new Minio.Client(config.minio); */
export const client = new Client(config.minio);
export const BUCKET = 'tatort';
const isProd = process.env.NODE_ENV == 'production';
const BUCKET = isProd ? 'tatort' : 'tatort-dev';
export { BUCKET };
export const TOKENFILENAME = '__perm__';
export const CONFIGFILENAME = 'config.json';

View File

@@ -1,5 +1,5 @@
import { Readable } from 'stream';
import { client } from '$lib/minio';
import { BUCKET, client } from '$lib/minio';
import { fail } from '@sveltejs/kit';
import { v4 as uuidv4 } from 'uuid';
@@ -17,10 +17,10 @@ const isRequiredFieldValid = (value: unknown) => {
export const actions = {
url: async ({ request }: { request: Request }) => {
const data = await request.formData();
const vorgangName: string | null = data.get('vorgang') as string;
const vorgangName: string | null = data.get('vorgang') as string;
const crimeName: string | null = data.get('name') as string;
const type: string | null = data.get('type') as string;
const vorgangPIN: string | null = data.get('vorgangPIN') as string;
const type: string | null = data.get('type') as string;
const vorgangPIN: string | null = data.get('vorgangPIN') as string;
const fileName: string | null = data.get('fileName') as string;
const vorgangExists = vorgangNameExists(vorgangName);
@@ -51,7 +51,7 @@ export const actions = {
objectName += '.glb';
}
const url = await client.presignedPutObject('tatort', objectName);
const url = await client.presignedPutObject(BUCKET, objectName);
return { url };
},
@@ -95,7 +95,7 @@ export const actions = {
const vorgang = data.vorgang;
const name = data.name;
const url = await client.presignedPutObject('tatort', `${vorgang}/${name}`, 60);
const url = await client.presignedPutObject(BUCKET, `${vorgang}/${name}`, 60);
return { url };
},
@@ -106,7 +106,7 @@ export const actions = {
const stream = data.file.stream();
const metaData = { 'Content-Type': 'model-gtlf-binary', 'X-VorgangsNr': '4711' };
const result = new Promise((resolve, reject) => {
client.putObject('tatort', name, Readable.from(stream), metaData, function (err, etag) {
client.putObject(BUCKET, name, Readable.from(stream), metaData, function (err, etag) {
if (err) return reject(err);
resolve(etag);
});

View File

@@ -1,8 +1,8 @@
import { client } from '$lib/minio';
import { BUCKET, client } from '$lib/minio';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
const { vorgang, tatort } = params;
const url = await client.presignedUrl('GET', 'tatort', `${vorgang}/${tatort}`);
const url = await client.presignedUrl('GET', BUCKET, `${vorgang}/${tatort}`);
return { url };
}
};

View File

@@ -2,9 +2,6 @@ import { getVorgaenge } from '$lib/server/vorgangService';
import { json } from '@sveltejs/kit';
export async function GET({ locals }) {
if (!locals.user) {
return json({ error: 'Unauthorized' }, { status: 401 });
}
const vorgaenge = getVorgaenge();

View File

@@ -1,4 +1,4 @@
import { client } from '$lib/minio';
import { BUCKET, client } from '$lib/minio';
import {
deleteVorgangByToken,
getCrimesListByToken,
@@ -11,7 +11,7 @@ export async function DELETE({ params }) {
const object_list = await new Promise((resolve, reject) => {
const res = [];
const items_str = client.listObjects('tatort', vorgangToken, true);
const items_str = client.listObjects(BUCKET, vorgangToken, true);
items_str.on('data', (obj) => {
res.push(obj.name);
@@ -24,7 +24,7 @@ export async function DELETE({ params }) {
});
});
await client.removeObjects('tatort', object_list);
await client.removeObjects(BUCKET, object_list);
deleteVorgangByToken(vorgangToken);
return new Response(null, { status: 204 });
@@ -45,9 +45,6 @@ export async function HEAD({ params }) {
}
export async function GET({ params, locals }) {
if (!locals.user) {
return json({ error: 'Unauthorized' }, { status: 401 });
}
try {
const vorgangToken = params.vorgang;

View File

@@ -40,10 +40,9 @@ export async function PUT({ params, request }) {
const vorgangToken = params.vorgang;
// prepare copy, incl. check if new name exists already
const crimeOldName = data['oldName'];
const crimeS3FullBucketPathOld = `/tatort/${vorgangToken}/${crimeOldName}`;
const crimeS3FullBucketPathOld = `/${BUCKET}/${vorgangToken}/${crimeOldName}`;
const crimeNewName = `${vorgangToken}/${data['newName']}`;
if (!crimeOldName || !crimeNewName) {
@@ -51,14 +50,14 @@ export async function PUT({ params, request }) {
}
try {
await client.statObject('tatort', crimeNewName);
await client.statObject(BUCKET, crimeNewName);
return json({ msg: 'Die Datei existiert bereits.' }, { status: 400 });
} catch (error) {
console.log(error, 'continue operation');
}
await client.copyObject('tatort', crimeNewName, crimeS3FullBucketPathOld);
await client.removeObject('tatort', `${vorgangToken}/${crimeOldName}`);
await client.copyObject(BUCKET, crimeNewName, crimeS3FullBucketPathOld);
await client.removeObject(BUCKET, `${vorgangToken}/${crimeOldName}`);
return json({ success: 'success' }, { status: 200 });
}

64
tests/APIList.test.ts Normal file
View File

@@ -0,0 +1,64 @@
import { describe, test, expect, vi } from 'vitest';
import { GET } from '../src/routes/api/list/+server';
import { getVorgaenge } from '$lib/server/vorgangService';
// Mocks
vi.mock('$lib/server/vorgangService', () => ({
getVorgaenge: vi.fn()
}));
const event = {
locals: {
user: { id: 'admin', admin: true }
}
};
describe('API-Endpoints: list', () => {
test('Unerlaubter Zugriff', async () => {
const event = {
locals: {
user: null
}
};
const response = await GET(event);
expect(response.status).toBe(401);
const json = await response.json();
const errorObj = { error: 'Unauthorized' }
expect(json).toEqual(errorObj);
});
test('Leere Liste wenn keine Vorgänge existieren', async () => {
vi.mocked(getVorgaenge).mockReturnValueOnce([]);
const response = await GET(event);
expect(response.status).toBe(200);
const json = await response.json();
expect(json).toEqual([]);
});
test('Liste mit existierenden Vorgängen', async () => {
const testVorgaenge = [
{
vorgangToken: '19f1d34e-4f31-48e8-830f-c4e42c29085e',
vorgangName: 'xyz-123',
vorgangPIN: 'pin-123'
},
{
vorgangToken: '7596e4d5-c51f-482d-a4aa-ff76434305fc',
vorgangName: 'vorgang-2',
vorgangPIN: 'pin-2'
}
];
vi.mocked(getVorgaenge).mockReturnValueOnce(testVorgaenge);
const response = await GET(event);
expect(response.status).toBe(200);
const json = await response.json();
expect(json).toEqual(testVorgaenge);
});
});

View File

@@ -0,0 +1,136 @@
import { describe, test, expect, vi } from 'vitest';
import { DELETE, GET, HEAD } from '../src/routes/api/list/[vorgang]/+server';
import {
getCrimesListByToken,
vorgangNameExists,
deleteVorgangByToken
} from '$lib/server/vorgangService';
import { client } from '$lib/minio';
import { EventEmitter } from 'events';
// Mocks
vi.mock('$lib/server/vorgangService', () => ({
getCrimesListByToken: vi.fn(),
vorgangNameExists: vi.fn(),
deleteVorgangByToken: vi.fn()
}));
vi.mock('$lib/minio', () => ({
client: {
listObjects: vi.fn(),
removeObjects: vi.fn()
}
}));
const MockEvent = {
params: { vorgang: '123' },
locals: {
user: { id: 'admin', admin: true }
}
};
describe('API-Endpoints: list/[vorgang]', () => {
test('Unerlaubter Zugriff', async () => {
const event = {
locals: {
user: null
}
};
const response = await GET(event);
expect(response.status).toBe(401);
const json = await response.json();
const errorObj = { error: 'Unauthorized' };
expect(json).toEqual(errorObj);
});
test('Vorgang ohne Tatorte', async () => {
const testCrimesList = [];
vi.mocked(getCrimesListByToken).mockReturnValueOnce(testCrimesList);
const response = await GET(MockEvent);
expect(response.status).toBe(200);
const json = await response.json();
expect(json).toEqual(testCrimesList);
});
test('Vorgang mit Tatorte', async () => {
const testCrimesList = [
{
name: 'model-A',
lastModified: '2025-08-28T09:44:12.453Z',
etag: '558f35716f6af953f9bb5d75f6d77e6a',
size: 8947140,
prefix: '7596e4d5-c51f-482d-a4aa-ff76434305fc',
show_button: true
},
{
name: 'model-z',
lastModified: '2025-08-28T10:37:20.142Z',
etag: '43e3989c32c4682bee407baaf83b6fa0',
size: 35788560,
prefix: '7596e4d5-c51f-482d-a4aa-ff76434305fc',
show_button: true
}
];
vi.mocked(getCrimesListByToken).mockReturnValueOnce(testCrimesList);
const response = await GET(MockEvent);
expect(response.status).toBe(200);
const json = await response.json();
expect(json).toEqual(testCrimesList);
});
test('Vorgang existiert via HEAD', async () => {
const vorgangExists = true;
vi.mocked(vorgangNameExists).mockReturnValueOnce(vorgangExists);
const response = await HEAD(MockEvent);
expect(response.status).toBe(200);
const textContent = await response.text();
expect(textContent).toEqual('');
});
test('Vorgang existiert nicht via HEAD', async () => {
const vorgangExists = false;
vi.mocked(vorgangNameExists).mockReturnValueOnce(vorgangExists);
const response = await HEAD(MockEvent);
expect(response.status).toBe(404);
const textContent = await response.text();
expect(textContent).toEqual('');
});
test('Lösche Vorgang und dazugehörige S3 Objekte', async () => {
// Mock data
const fakeStream = new EventEmitter();
vi.mocked(client.listObjects).mockReturnValue(fakeStream);
vi.mocked(client.removeObjects).mockResolvedValue(undefined);
vi.mocked(deleteVorgangByToken).mockReturnValueOnce(undefined);
const responsePromise = DELETE(MockEvent);
const fakeCrimeNames = [
`${MockEvent.params.vorgang}/file1.glb`,
`${MockEvent.params.vorgang}/file2.glb`
];
// simulate data stream
fakeStream.emit('data', { name: fakeCrimeNames[0] });
fakeStream.emit('data', { name: fakeCrimeNames[1] });
fakeStream.emit('end');
const response = await responsePromise;
expect(client.removeObjects).toHaveBeenCalledWith('tatort', fakeCrimeNames);
expect(deleteVorgangByToken).toHaveBeenCalledWith(MockEvent.params.vorgang);
expect(response.status).toBe(204);
});
});

View File

@@ -0,0 +1,93 @@
import { describe, test, expect, vi } from 'vitest';
import { DELETE, PUT } from '../src/routes/api/list/[vorgang]/[tatort]/+server';
import { BUCKET, client } from '$lib/minio';
// Mock data and methods
const fakeVorgangToken = `c399423a-ba37-4fe1-bbdf-80e5881168ff`;
const fakeCrimeOldName = `model-A`;
const fakeCrimeNewName = 'model-Z';
const fakeCrimePath = `${fakeVorgangToken}/${fakeCrimeOldName}`;
const fullFakeCrimePath = `/${BUCKET}/${fakeCrimePath}`;
const fakeCrimeAPIURL = `http://localhost:5173/api/list/${fakeCrimePath}`;
vi.mock('$lib/minio', () => ({
client: {
removeObject: vi.fn(),
statObject: vi.fn(),
copyObject: vi.fn()
},
BUCKET: 'tatort'
}));
describe('API-Endpoints: list/[vorgang]/[tatort]', () => {
test('Löschen von Tatorten', async () => {
const request = new Request(fakeCrimeAPIURL);
const response = await DELETE({ request });
expect(client.removeObject).toHaveBeenCalledWith(BUCKET, fakeCrimePath);
expect(response.status).toBe(204);
const responseBody = await response.text();
expect(responseBody).toBe('');
});
test('Umbennen von Tatorten: Erfolgreich', async () => {
const request = new Request(fakeCrimeAPIURL, {
method: 'PUT',
body: JSON.stringify({
oldName: fakeCrimeOldName,
newName: fakeCrimeNewName
})
});
const params = { vorgang: fakeVorgangToken };
// Mock Datei nicht gefunden
client.statObject.mockRejectedValueOnce(new Error('NotFound'));
const response = await PUT({ params, request });
const fakeCrimeNewPath = `${fakeVorgangToken}/${fakeCrimeNewName}`;
expect(client.statObject).toHaveBeenCalledWith(BUCKET, fakeCrimeNewPath);
expect(client.copyObject).toHaveBeenCalledWith(BUCKET, fakeCrimeNewPath, fullFakeCrimePath);
expect(client.removeObject).toHaveBeenCalledWith(BUCKET, fakeCrimePath);
expect(response.status).toBe(200);
});
test('Umbennen von Tatorten: Fehlende(r) Name', async () => {
const request = new Request(fakeCrimeAPIURL, {
method: 'PUT',
body: JSON.stringify({
oldName: '',
newName: ''
})
});
const params = { vorgang: fakeVorgangToken };
const response = await PUT({ params, request });
expect(response.status).toBe(400);
});
test('Umbennen von Tatorten: Existierender Name', async () => {
const request = new Request(fakeCrimeAPIURL, {
method: 'PUT',
body: JSON.stringify({
oldName: fakeCrimeOldName,
newName: fakeCrimeNewName
})
});
const params = { vorgang: fakeVorgangToken };
// Datei existiert bereits
client.statObject.mockResolvedValueOnce({});
const response = await PUT({ params, request });
expect(response.status).toBe(400);
const fakeCrimeNewPath = `${fakeVorgangToken}/${fakeCrimeNewName}`;
expect(client.statObject).toHaveBeenCalledWith(BUCKET, fakeCrimeNewPath);
expect(client.copyObject).not.toHaveBeenCalled();
expect(client.removeObject).not.toHaveBeenCalled();
});
});

40
tests/APIUser.test.ts Normal file
View File

@@ -0,0 +1,40 @@
import { describe, test, expect } from 'vitest';
import { GET } from '../src/routes/api/user/+server';
const id = 'admin';
describe('API-Endpoints: User ist Admin', () => {
test('User ist Admin', async () => {
const admin = true;
const event = {
locals: {
user: { id, admin }
}
};
const fakeResult = { admin };
const response = await GET(event);
expect(response.status).toBe(200);
const json = await response.json();
expect(json).toEqual(fakeResult);
});
test('User ist kein Admin', async () => {
const admin = false;
const event = {
locals: {
user: { id, admin }
}
};
const fakeResult = { admin };
const response = await GET(event);
expect(response.status).toBe(200);
const json = await response.json();
expect(json).toEqual(fakeResult);
});
});

163
tests/APIUsers.test.ts Normal file
View File

@@ -0,0 +1,163 @@
import { describe, test, expect, vi, beforeEach } from 'vitest';
import { GET, POST } from '../src/routes/api/users/+server';
import bcrypt from 'bcrypt';
import { addUser, getUsers } from '$lib/server/userService';
vi.mock('$lib/server/userService', () => ({
addUser: vi.fn(),
getUsers: vi.fn()
}));
vi.mock('bcrypt', () => ({
default: {
hashSync: vi.fn()
}
}));
describe('API-Endpoint: Users', () => {
test('Unerlaubter Zugriff', async () => {
const event = {
locals: {
user: null
}
};
const response = await GET(event);
expect(response.status).toBe(401);
const errorMessage = { error: 'Unauthorized' };
const json = await response.json();
expect(json).toEqual(errorMessage);
});
// [INFO] Test auf keine User nicht notwendig, da immer min. ein User vorhanden
// Mock eingelogter User bzw. stelle locals.user zur Verfügung
const fakeLoggedInUser = { id: 'admin', admin: true };
const mockLocals = {
user: fakeLoggedInUser
};
test('Rufe Liste aller User ab', async () => {
const fakeLoggedInUser = { id: 'admin', admin: true };
const event = {
locals: {
user: fakeLoggedInUser
}
};
const fakeResult = [{ userId: 42, userName: 'admin' }];
getUsers.mockReturnValueOnce(fakeResult);
const response = await GET(event);
expect(response.status).toBe(200);
const json = await response.json();
expect(json).toEqual(fakeResult);
});
test('Füge Benutzer hinzu: Erfolgreich', async () => {
// Mocke Parameter und Funktionen von Drittparteien
const fakeUsersAPIURL = `http://localhost:5173/api/users`;
const fakeUserID = 42;
const fakeUsername = 'admin';
const fakeUserPassword = 'pass-123';
const mockRequest = new Request(fakeUsersAPIURL, {
method: 'POST',
body: JSON.stringify({
userName: fakeUsername,
userPassword: fakeUserPassword
})
});
const mockedHash = 'mocked-hash';
bcrypt.hashSync.mockReturnValueOnce(mockedHash);
const mockedRowInfo = {
changes: 1,
lastInsertRowid: fakeUserID
};
addUser.mockReturnValueOnce(mockedRowInfo);
const response = await POST({
request: mockRequest,
locals: mockLocals
});
expect(response.status).toBe(201);
const fakeResult = { userId: fakeUserID, userName: fakeUsername };
const json = await response.json();
expect(json).toEqual(fakeResult);
expect(addUser).toHaveBeenCalledWith(fakeUsername, mockedHash);
});
test('Füge Benutzer hinzu: Fehlender Name oder Passwort', async () => {
// Mocke Parameter und Funktionen von Drittparteien
const fakeUsersAPIURL = `http://localhost:5173/api/users`;
const fakeUserID = 42;
const fakeUsername = '';
const fakeUserPassword = '';
const mockRequest = new Request(fakeUsersAPIURL, {
method: 'POST',
body: JSON.stringify({
userName: fakeUsername,
userPassword: fakeUserPassword
})
});
const response = await POST({
request: mockRequest,
locals: mockLocals
});
expect(response.status).toBe(400);
const errorMessage = { error: 'Missing input' };
const json = await response.json();
expect(json).toEqual(errorMessage);
expect(addUser).not.toHaveBeenCalled();
});
test('Füge Benutzer hinzu: Nicht erfolgreich, keine Datenbankänderung', async () => {
// Mocke Parameter und Funktionen von Drittparteien
const fakeUsersAPIURL = `http://localhost:5173/api/users`;
const fakeUserID = 42;
const fakeUsername = 'admin';
const fakeUserPassword = 'pass-123';
const mockRequest = new Request(fakeUsersAPIURL, {
method: 'POST',
body: JSON.stringify({
userName: fakeUsername,
userPassword: fakeUserPassword
})
});
const mockedHash = 'mocked-hash';
bcrypt.hashSync.mockReturnValueOnce(mockedHash);
const mockedRowInfo = {
changes: 0,
lastInsertRowid: fakeUserID
};
addUser.mockReturnValueOnce(mockedRowInfo);
const response = await POST({
request: mockRequest,
locals: mockLocals
});
expect(response.status).toBe(400);
const body = await response.text();
expect(body).toEqual('');
expect(addUser).toHaveBeenCalledWith(fakeUsername, mockedHash);
});
});

View File

@@ -0,0 +1,43 @@
import { describe, test, expect, vi } from 'vitest';
import { GET } from '../src/routes/api/vorgang/[vorgang]/vorgangPIN/+server';
import { db } from '$lib/server/dbService';
const mockEvent = {
params: { vorgang: '123' }
};
vi.mock('$lib/server/dbService', () => ({
db: {
prepare: vi.fn()
}
}));
describe('API-Endpoint: Vorgang-PIN', () => {
test('Vorgang PIN: Erfolgreich', async () => {
// only interested in PIN value
const mockPIN = 'pin-123';
const mockRow = { pin: mockPIN };
const getMock = vi.fn().mockReturnValue(mockRow);
db.prepare.mockReturnValue({ get: getMock });
const response = await GET(mockEvent);
expect(response.status).toBe(200);
const body = await response.text();
expect(body).toEqual(mockPIN);
});
test('Vorgang PIN: Nicht erfolgreich', async () => {
const mockRow = {};
const getMock = vi.fn().mockReturnValue(mockRow);
db.prepare.mockReturnValue({ get: getMock });
const response = await GET(mockEvent);
expect(response.status).toBe(404);
const body = await response.text();
expect(body).toEqual('');
});
});

View File

@@ -13,7 +13,7 @@ export default defineConfig({
name: 'client',
environment: 'jsdom',
clearMocks: true,
include: ['src/**/*.svelte.{test,spec}.{js,ts}'],
include: ['tests/**/*.{test,spec}.{js,ts}', 'src/**/*.svelte.{test,spec}.{js,ts}'],
exclude: ['src/lib/server/**'],
setupFiles: ['./vitest-setup-client.ts']
}