Files
ridebus-admin/backend/test/boardDevicesApi.test.js
Zakhar Timoshenko 360b684019 feat: implement board device service with CRUD operations and validation
- Add boardDeviceService for managing device pairing, status, claiming, and revocation.
- Introduce boardDeviceSettings for default settings and validation logic.
- Create integration tests for device lifecycle, concurrent claims, and updates.
- Add unit tests for device pairing, claiming, and settings validation.
- Implement API tests for device endpoints with JWT authentication.
- Develop in-memory repository for testing purposes.
- Create frontend components for managing devices, including a form for device settings.
- Style DevicesPage with CSS for better user experience.
2026-10-10 00:47:26 +03:00

29 lines
2.8 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
process.env.JWT_SECRET = 'board-device-test-secret-only';
require.cache[require.resolve('../src/config/database')] = { exports: { query: async () => ({ rows: [] }) } };
const express = require('express');
const jwt = require('jsonwebtoken');
const { createBoardDevicesRouter } = require('../src/routes/boardDevices');
const { createBoardDeviceService } = require('../src/services/boardDeviceService');
const { defaults } = require('../src/services/boardDeviceSettings');
const { memoryRepository } = require('./helpers/boardDeviceMemoryRepository');
test('public pairing and device credentials cannot access admin endpoints; JWT administrator and city are required', async t => {
const service = createBoardDeviceService(memoryRepository()); const app = express(); app.use(express.json());
app.use('/api/v1/board-devices', createBoardDevicesRouter(service, { cityMiddleware: (req, _res, next) => { req.city = { id: Number(req.get('X-City-Id') || 1) }; next(); } }));
const server = await new Promise(resolve => { const s = app.listen(0, '127.0.0.1', () => resolve(s)); }); t.after(() => new Promise(resolve => server.close(resolve)));
const base = `http://127.0.0.1:${server.address().port}/api/v1/board-devices`;
const post = (path, body, headers = {}) => fetch(base + path, { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify(body) });
const admin = { Authorization: `Bearer ${jwt.sign({ id: 7, role: 'admin' }, process.env.JWT_SECRET)}`, 'X-City-Id': '1' };
const pair = await (await post('/pairings', { width: 879, height: 593 })).json();
assert.match(pair.code, /^\d{4}$/); assert.equal((await fetch(base + '/admin')).status, 401);
assert.equal((await fetch(base + '/admin', { headers: { 'X-Board-Token': pair.token } })).status, 401);
assert.equal((await fetch(base + '/admin', { headers: { Authorization: `Bearer ${jwt.sign({ id: 7, role: 'user' }, process.env.JWT_SECRET)}` } })).status, 403);
assert.equal((await fetch(base + '/self', { headers: admin })).status, 401);
assert.equal((await post('/admin/claim', { code: pair.code, name: 'A', settings: defaults }, admin)).status, 200);
const status = await fetch(base + '/self', { headers: { 'X-Board-Token': pair.token } }); assert.equal(status.headers.get('cache-control'), 'no-store'); assert.equal((await status.json()).state, 'paired');
assert.equal((await post('/admin/claim', { code: pair.code, name: 'A', settings: defaults }, admin)).status, 409);
const list = await (await fetch(base + '/admin', { headers: admin })).json(); assert.equal(list.length, 1); assert.equal(list[0].token_hash, undefined);
assert.equal((await (await fetch(base + '/admin', { headers: { ...admin, 'X-City-Id': '2' } })).json()).length, 0);
});