Release v2.5.6

This commit is contained in:
2026-08-05 19:38:16 +01:00
parent a8ef35b287
commit 9f831f04bc
161 changed files with 8487 additions and 1295 deletions
@@ -0,0 +1,193 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const registerUsersRoutes = require('../src/web/routes/admin/users');
test('user create route allows users without roles', async () => {
const handlers = {};
const app = {
get() {},
post(path, ...routeHandlers) {
handlers[path] = routeHandlers;
}
};
let insertedArgs = null;
let syncedRoles = null;
const deps = {
pool: {
async query(sql) {
if (sql.includes('SELECT id FROM a_users WHERE username = ? LIMIT 1')) {
return [[]];
}
return [[]];
},
async getConnection() {
return {
async beginTransaction() {},
async query(sql, args) {
if (sql.includes('INSERT INTO a_users')) {
insertedArgs = args;
return [{ insertId: 11 }];
}
return [[]];
},
async commit() {},
async rollback() {},
release() {}
};
}
},
common: {
validateMaxLength(value) {
return String(value || '').trim();
},
fetchDuplicateName: async () => null
},
pages: {
renderUsersAddPage() {
return '';
}
},
formatDashboardDate: () => '',
getAuditUserId: () => 1,
hashPassword: () => ({ hash: 'hash', salt: 'salt', iterations: 1 }),
validatePasswordStrength: () => '',
readArrayField: () => [],
rbacData: {
fetchRoles: async () => ([]),
fetchUsersWithRolesPage: async () => ({ users: [], totalItems: 0, currentPage: 1 }),
syncUserRoles: async (_connection, userId, roleIds) => {
syncedRoles = { userId, roleIds };
}
},
requirePermission() {
return function (_req, _res, next) {
next();
};
}
};
registerUsersRoutes(app, deps);
const routeHandlers = handlers['/users'];
assert.equal(Array.isArray(routeHandlers), true);
const req = {
body: {
name: 'No Roles User',
username: 'noroles',
password: 'ValidPass!1',
confirm_password: 'ValidPass!1'
},
currentUser: { id: 1 }
};
const res = {
redirect(url) {
this.redirectedTo = url;
},
status() {
return this;
},
send() {
return this;
}
};
await routeHandlers[1](req, res, () => {});
assert.ok(insertedArgs);
assert.deepEqual(syncedRoles, { userId: 11, roleIds: [] });
assert.equal(res.redirectedTo, '/users/11/edit?message=User%20created.');
});
test('user create save and new goes to the blank create page', async () => {
const handlers = {};
const app = {
get() {},
post(path, ...routeHandlers) {
handlers[path] = routeHandlers;
}
};
const deps = {
pool: {
async query(sql) {
if (sql.includes('SELECT id FROM a_users WHERE username = ? LIMIT 1')) {
return [[]];
}
return [[]];
},
async getConnection() {
return {
async beginTransaction() {},
async query(sql) {
if (sql.includes('INSERT INTO a_users')) {
return [{ insertId: 15 }];
}
return [[]];
},
async commit() {},
async rollback() {},
release() {}
};
}
},
common: {
validateMaxLength(value) {
return String(value || '').trim();
},
fetchDuplicateName: async () => null
},
pages: {
renderUsersAddPage() {
return '';
}
},
formatDashboardDate: () => '',
getAuditUserId: () => 1,
hashPassword: () => ({ hash: 'hash', salt: 'salt', iterations: 1 }),
validatePasswordStrength: () => '',
readArrayField: () => [],
rbacData: {
fetchRoles: async () => ([]),
fetchUsersWithRolesPage: async () => ({ users: [], totalItems: 0, currentPage: 1 }),
syncUserRoles: async () => {}
},
requirePermission() {
return function (_req, _res, next) {
next();
};
}
};
registerUsersRoutes(app, deps);
const routeHandlers = handlers['/users'];
const req = {
body: {
name: 'No Roles User',
username: 'noroles',
password: 'ValidPass!1',
confirm_password: 'ValidPass!1',
save_action: 'new'
},
currentUser: { id: 1 }
};
const res = {
redirect(url) {
this.redirectedTo = url;
},
status() {
return this;
},
send() {
return this;
}
};
await routeHandlers[1](req, res, () => {});
assert.equal(res.redirectedTo, '/users/new?message=User%20created.');
});