92 lines
3.4 KiB
JavaScript
92 lines
3.4 KiB
JavaScript
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/);
|
|
});
|