Files
Orchestrator/dhive/Projekt-KIQ-HP/server/__tests__/server.test.js
T
ankn 18f6e2732e test: add test suites for Projekt-KIQ-HP (12 tests) and andreknie.de validation (16 tests)
- Projekt-KIQ-HP: vitest + server API tests (health, login, auth, access-requests)
- andreknie.de: validation utility tests (email, contact, talk-request, newsletter, resource-download)
- db-planet-mcp-server: existing 7 tests confirmed working
2026-07-10 18:43:41 +02:00

271 lines
8.7 KiB
JavaScript

import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import express from 'express';
import cors from 'cors';
import cookieParser from 'cookie-parser';
import crypto from 'crypto';
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const TEST_DATA_DIR = join(__dirname, '..', 'data-test');
const TEST_DATA_FILE = join(TEST_DATA_DIR, 'access-requests.json');
function createTestApp() {
mkdirSync(TEST_DATA_DIR, { recursive: true });
writeFileSync(TEST_DATA_FILE, '[]', 'utf-8');
const sessions = new Map();
const ADMIN_USER = 'admin';
const ADMIN_PASSWORD = 'testpass';
const app = express();
app.use(cors());
app.use(express.json());
app.use(cookieParser('test-secret'));
function createSession(user) {
const token = crypto.randomBytes(32).toString('hex');
sessions.set(token, { user, createdAt: Date.now() });
return token;
}
function getSession(token) {
return sessions.get(token) || null;
}
function requireAuth(req, res, next) {
const token = req.cookies?.kiq_session;
if (!token) return res.status(401).json({ error: 'Nicht authentifiziert' });
const session = getSession(token);
if (!session) return res.status(401).json({ error: 'Sitzung abgelaufen' });
req.user = session.user;
next();
}
app.get('/api/health', (_req, res) => {
res.json({ status: 'ok', time: new Date().toISOString() });
});
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
if (username === ADMIN_USER && password === ADMIN_PASSWORD) {
const token = createSession({ username, role: 'admin' });
res.cookie('kiq_session', token, { httpOnly: true, sameSite: 'strict' });
return res.json({ user: { username, role: 'admin' } });
}
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
});
app.post('/api/logout', (req, res) => {
const token = req.cookies?.kiq_session;
if (token) sessions.delete(token);
res.clearCookie('kiq_session');
res.json({ ok: true });
});
app.get('/api/me', (req, res) => {
const token = req.cookies?.kiq_session;
if (!token) return res.json({ user: null });
const session = getSession(token);
if (!session) return res.json({ user: null });
res.json({ user: session.user });
});
app.post('/api/access-requests', (req, res) => {
const { user_data } = req.body;
if (!user_data || !user_data.name || !user_data.email || !user_data.company) {
return res.status(400).json({ error: 'Pflichtfelder fehlen' });
}
const requests = JSON.parse(readFileSync(TEST_DATA_FILE, 'utf-8'));
const newRequest = {
id: crypto.randomUUID(),
user_data,
status: 'pending',
created_at: new Date().toISOString(),
};
requests.push(newRequest);
writeFileSync(TEST_DATA_FILE, JSON.stringify(requests, null, 2), 'utf-8');
res.status(201).json({ ok: true, id: newRequest.id });
});
app.get('/api/access-requests', requireAuth, (_req, res) => {
const requests = JSON.parse(readFileSync(TEST_DATA_FILE, 'utf-8'));
requests.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
res.json(requests);
});
app.put('/api/access-requests/:id', requireAuth, (req, res) => {
const { id } = req.params;
const { status } = req.body;
if (!['pending', 'approved', 'rejected'].includes(status)) {
return res.status(400).json({ error: 'Ungültiger Status' });
}
const requests = JSON.parse(readFileSync(TEST_DATA_FILE, 'utf-8'));
const idx = requests.findIndex(r => r.id === id);
if (idx === -1) return res.status(404).json({ error: 'Anfrage nicht gefunden' });
requests[idx].status = status;
writeFileSync(TEST_DATA_FILE, JSON.stringify(requests, null, 2), 'utf-8');
res.json({ ok: true });
});
return app;
}
// --- Test Setup ---
let server;
let baseUrl;
beforeAll(async () => {
const app = createTestApp();
await new Promise((resolve) => {
server = app.listen(0, () => {
const addr = server.address();
baseUrl = `http://localhost:${addr.port}`;
resolve();
});
});
});
afterAll(() => {
if (server) server.close();
if (existsSync(TEST_DATA_DIR)) rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
beforeEach(() => {
writeFileSync(TEST_DATA_FILE, '[]', 'utf-8');
});
// --- Tests ---
describe('KIQ Server API', () => {
describe('GET /api/health', () => {
it('returns ok status', async () => {
const res = await fetch(`${baseUrl}/api/health`);
expect(res.status).toBe(200);
const data = await res.json();
expect(data.status).toBe('ok');
expect(data.time).toBeTruthy();
});
});
describe('POST /api/login', () => {
it('accepts valid credentials', async () => {
const res = await fetch(`${baseUrl}/api/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'testpass' }),
});
expect(res.status).toBe(200);
const data = await res.json();
expect(data.user.username).toBe('admin');
expect(data.user.role).toBe('admin');
});
it('rejects invalid credentials', async () => {
const res = await fetch(`${baseUrl}/api/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'wrong' }),
});
expect(res.status).toBe(401);
});
it('sets session cookie on success', async () => {
const res = await fetch(`${baseUrl}/api/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: 'admin', password: 'testpass' }),
});
const setCookie = res.headers.get('set-cookie');
expect(setCookie).toContain('kiq_session');
});
});
describe('GET /api/me', () => {
it('returns null user without session', async () => {
const res = await fetch(`${baseUrl}/api/me`);
expect(res.status).toBe(200);
const data = await res.json();
expect(data.user).toBeNull();
});
});
describe('POST /api/access-requests', () => {
it('creates a new access request', async () => {
const res = await fetch(`${baseUrl}/api/access-requests`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_data: {
name: 'Max Mustermann',
email: 'max@example.com',
company: 'Test GmbH',
role: 'Developer',
purpose: 'Testing',
},
}),
});
expect(res.status).toBe(201);
const data = await res.json();
expect(data.ok).toBe(true);
expect(data.id).toBeTruthy();
});
it('rejects when name is missing', async () => {
const res = await fetch(`${baseUrl}/api/access-requests`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_data: { email: 'x@x.com', company: 'X' } }),
});
expect(res.status).toBe(400);
});
it('rejects when email is missing', async () => {
const res = await fetch(`${baseUrl}/api/access-requests`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_data: { name: 'Max', company: 'X' } }),
});
expect(res.status).toBe(400);
});
it('rejects when company is missing', async () => {
const res = await fetch(`${baseUrl}/api/access-requests`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ user_data: { name: 'Max', email: 'x@x.com' } }),
});
expect(res.status).toBe(400);
});
it('rejects empty body', async () => {
const res = await fetch(`${baseUrl}/api/access-requests`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
});
expect(res.status).toBe(400);
});
});
describe('GET /api/access-requests (admin)', () => {
it('rejects without auth', async () => {
const res = await fetch(`${baseUrl}/api/access-requests`);
expect(res.status).toBe(401);
});
});
describe('PUT /api/access-requests/:id (admin)', () => {
it('rejects without auth', async () => {
const res = await fetch(`${baseUrl}/api/access-requests/fake-id`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status: 'approved' }),
});
expect(res.status).toBe(401);
});
});
});