const assert = require('node:assert/strict'); const http = require('node:http'); const test = require('node:test'); const loginMigration = require('../migrations/20261027000000-add-login-to-users'); const { validateUser, validateCustomer, validateCar, } = require('../lib/api'); const app = require('../app'); test('user validation requires a login and an eight-character password', () => { assert.deepEqual(validateUser({ login: 'jan', email: 'jan@example.com', password: 'Haslo123!', first_name: 'Jan', last_name: 'Kowalski', }, true), {}); assert.equal(validateUser({ password: 'short' }, false).password, 'Password must be between 8 and 72 characters.'); }); test('customer validation enforces company name, phone and Polish postal code formats', () => { const errors = validateCustomer({ type: 'company', first_name: 'Jan', last_name: 'Kowalski', email: 'jan@example.com', phone: '123', postal_code: '12345', birth_date: '2024-99-99', }, true); assert.ok(errors.company_name); assert.ok(errors.phone); assert.ok(errors.postal_code); assert.ok(errors.birth_date); assert.equal(validateCustomer({ first_name: null }, false).first_name, 'Must be a string of at most 100 characters.'); }); test('car validation normalizes identifiers and rejects invalid VINs', () => { const data = { customer_id: '1', make: 'Toyota', model: 'Corolla', production_year: 2020, vin: '1HGCM82633A004352', registration_number: 'wa 12345', fuel_type: 'petrol', }; assert.deepEqual(validateCar(data, true), {}); assert.equal(data.vin, '1HGCM82633A004352'); assert.equal(data.registration_number, 'WA12345'); assert.ok(validateCar({ vin: 'INVALID' }, false).vin); }); test('login migration backfills existing users and adds a unique login index', async () => { const calls = []; const queryInterface = { describeTable: async () => ({ email: {} }), addColumn: async (...args) => calls.push(['addColumn', ...args]), sequelize: { query: async (...args) => calls.push(['query', ...args]) }, changeColumn: async (...args) => calls.push(['changeColumn', ...args]), showIndex: async () => [], addIndex: async (...args) => calls.push(['addIndex', ...args]), }; await loginMigration.up(queryInterface, { STRING: (length) => `STRING(${length})` }); assert.deepEqual(calls.map(([name]) => name), ['addColumn', 'query', 'changeColumn', 'addIndex']); assert.match(calls[1][1], /SET `login` = `email`/); assert.equal(calls[3][3].unique, true); }); test('HTTP app exposes OpenAPI docs and challenges unauthenticated API requests', async (t) => { const server = http.createServer(app); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); t.after(() => new Promise((resolve, reject) => { server.close((error) => error ? reject(error) : resolve()); })); const baseUrl = `http://127.0.0.1:${server.address().port}`; const docs = await fetch(`${baseUrl}/docs`); assert.equal(docs.status, 200); assert.match(await docs.text(), /swagger-ui/); const spec = await fetch(`${baseUrl}/openapi.yaml`); assert.equal(spec.status, 200); assert.match(await spec.text(), /scheme: basic/); const protectedResponse = await fetch(`${baseUrl}/api/auth/me`); assert.equal(protectedResponse.status, 401); assert.match(protectedResponse.headers.get('www-authenticate'), /^Basic/); });