import assert from 'node:assert/strict'; import fs from 'node:fs/promises'; import path from 'node:path'; import { spawn, spawnSync } from 'node:child_process'; import http from 'node:http'; import test from 'node:test'; const root = path.resolve(import.meta.dirname, '..'); const read = relative => fs.readFile(path.join(root, relative), 'utf8'); test('upload MIME types are detected from content and not trusted request metadata', async () => { const upload = await read('src/middleware/upload.js'); const media = await read('src/services/media-service.js'); const pkg = JSON.parse(await read('package.json')); assert.equal(pkg.dependencies['file-type'], '22.0.1'); assert.equal(pkg.dependencies['@file-type/av'], '0.2.0'); assert.match(upload, /fileTypeFromFile/); assert.match(upload, /customDetectors:\s*\[detectAv\]/); assert.match(upload, /detectGpx/); assert.doesNotMatch(upload, /allowed\.has\(file\.mimetype\)/); assert.doesNotMatch(upload, /contentType ist erforderlich/); assert.match(media, /file\.detectedExtension/); }); test('Python API tools are syntax-valid and contain reusable 401 authentication handling', async () => { const toolsDir = path.join(root, 'tools/python'); const names = (await fs.readdir(toolsDir)).filter(name => name.endsWith('.py')); const expected = [ 'append_route.py', 'common.py', 'create_audio.py', 'create_picture.py', 'create_poi.py', 'create_route.py', 'delete_audio.py', 'delete_picture.py', 'delete_poi.py', 'delete_route.py', 'restore_route.py', 'update_audio.py', 'update_picture.py', 'update_poi.py', 'update_route.py' ]; assert.deepEqual( names.toSorted((left, right) => left.localeCompare(right)), expected.toSorted((left, right) => left.localeCompare(right)) ); const compile = spawnSync('python3', ['-m', 'py_compile', ...names.map(name => path.join(toolsDir, name))], { cwd: root, encoding: 'utf8' }); assert.equal(compile.status, 0, compile.stderr); await fs.rm(path.join(toolsDir, '__pycache__'), { recursive: true, force: true }); const common = await read('tools/python/common.py'); const sonar = await read('sonar-project.properties'); const requirements = await read('requirements-dev.txt'); const pkg = JSON.parse(await read('package.json')); assert.match(common, /class ApiClient/); assert.match(common, /error\.code != 401/); assert.match(common, /MAX_AUTH_ATTEMPTS/); assert.match(common, /self\.prompt_credentials\(\)/); assert.match(common, /getpass\.getpass/); assert.match(common, /os\.getenv\(API_USER_ENV/); assert.match(common, /os\.getenv\(API_PASSWORD_ENV/); assert.match(common, /def encode_multipart/); assert.match(common, /traceback\.print_exception/); assert.doesNotMatch(common, /Content-Type:.*application\/octet-stream/); assert.match(pkg.scripts['test:coverage'], /test:coverage:python/); assert.match(pkg.scripts['test:coverage:python'], /coverage\/python\/coverage\.xml/); assert.match(sonar, /sonar\.python\.coverage\.reportPaths=coverage\/python\/coverage\.xml/); assert.match(requirements, /^coverage==/m); }); test('Python client retries an intentionally simulated reverse-proxy 401 with trimmed environment credentials', async t => { const apiUser = 'local-test-user'; const apiPassword = 'local-test-password'; const expectedAuthorization = `Basic ${Buffer.from(`${apiUser}:${apiPassword}`).toString('base64')}`; const receivedAuthorization = []; const server = http.createServer((req, res) => { receivedAuthorization.push(req.headers.authorization || null); if (req.headers.authorization !== expectedAuthorization) { res.writeHead(401, { 'Content-Type': 'application/json', 'WWW-Authenticate': 'Basic realm="test"' }); res.end(JSON.stringify({ message: 'Zugangsdaten erforderlich.' })); return; } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true })); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); t.after(() => server.close()); const { port } = server.address(); const script = ` from common import ApiClient result = ApiClient('http://127.0.0.1:${port}').request('GET', '/protected') assert result == {'ok': True} `; const child = spawn('python3', ['-c', script], { env: { ...process.env, API_USER: ` ${apiUser} `, API_PASSWORD: ` ${apiPassword} `, PYTHONPATH: path.join(root, 'tools/python') }, stdio: ['ignore', 'pipe', 'pipe'] }); const stderr = []; child.stderr.on('data', chunk => stderr.push(chunk.toString())); const status = await new Promise(resolve => child.once('exit', resolve)); assert.equal(status, 0, stderr.join('')); assert.deepEqual(receivedAuthorization, [null, expectedAuthorization]); });