This commit is contained in:
seb
2026-11-02 00:19:21 +01:00
parent 55f04de8a8
commit d0a33b1f82
35 changed files with 4919 additions and 3739 deletions

View File

@@ -0,0 +1,92 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import LibDashStore from '../src/index.js';
import { BetterSqlite3Provider } from '../src/providers/sqlite3/index.js';
async function open(tempDir) {
const provider = new BetterSqlite3Provider({ filename: join(tempDir, 'test.db'), storageDir: join(tempDir, 'storage') });
await provider.initialize();
return { provider, store: await new LibDashStore(provider) };
}
const tempDir = mkdtempSync(join(tmpdir(), 'test-cu-roles-'));
try {
console.log('--- Testing companyuser roles + autoinvite ---');
let { provider, store } = await open(tempDir);
const adminId = await store.google_auth.add('admin@example.com');
const userId = await store.google_auth.add('user@example.com');
const company = await store.company.create({ name: 'Roles Corp', iso639: 'de', taxrate: 19, reducedtaxrate: 7, googleAuthId: adminId });
await store.google_auth.setSelectedCompany(adminId, company.id);
// creator is admin
let members = await store.companyuser.list(adminId);
assert.deepEqual(members.map((m) => [m.email, m.role]), [['admin@example.com', 'admin']]);
assert.equal((await store.company.list(adminId))[0].role, 'admin');
// non-member cannot select the company
await assert.rejects(store.google_auth.setSelectedCompany(userId, company.id), /Not authorized/);
// admin adds existing user
await assert.rejects(store.companyuser.addByEmail(adminId, { email: 'nobody@example.com' }), /User not found/);
await assert.rejects(store.companyuser.addByEmail(adminId, { email: 'user@example.com', role: 'root' }), /Invalid role/);
const added = await store.companyuser.addByEmail(adminId, { email: 'User@Example.com' });
assert.equal(added.role, 'user');
await assert.rejects(store.companyuser.addByEmail(adminId, { email: 'user@example.com' }), /already in company/);
// user can now select it, but is not admin
await store.google_auth.setSelectedCompany(userId, company.id);
await assert.rejects(store.companyuser.list(userId), /Not authorized/);
await assert.rejects(store.companyuser.addByEmail(userId, { email: 'admin@example.com' }), /Not authorized/);
await assert.rejects(store.companyuser.setRole(userId, added.id, 'admin'), /Not authorized/);
await assert.rejects(store.companyuser.remove(userId, added.id), /Not authorized/);
await assert.rejects(store.autoinvite.add(userId, { email: 'x@example.com' }), /Not authorized/);
// last admin guard
const adminCu = members[0].id;
await assert.rejects(store.companyuser.setRole(adminId, adminCu, 'user'), /last admin/);
await assert.rejects(store.companyuser.remove(adminId, adminCu), /last admin/);
await store.companyuser.setRole(adminId, added.id, 'admin');
await store.companyuser.setRole(adminId, adminCu, 'user');
await store.companyuser.setRole(userId, adminCu, 'admin');
await store.companyuser.setRole(userId, added.id, 'user');
await assert.rejects(store.companyuser.setRole(adminId, 99999, 'user'), /Company user not found/);
// autoinvite
await assert.rejects(store.autoinvite.add(adminId, { email: 'user@example.com' }), /already registered/);
await assert.rejects(store.autoinvite.add(adminId, { email: 'bad' }), /Invalid email/);
await store.autoinvite.add(adminId, { email: 'New@Example.com', role: 'admin' });
await assert.rejects(store.autoinvite.add(adminId, { email: 'new@example.com' }), /already exists/);
await store.autoinvite.add(adminId, { email: 'gone@example.com' });
assert.equal((await store.autoinvite.list(adminId)).length, 2);
await store.autoinvite.remove(adminId, (await store.autoinvite.list(adminId)).find((i) => i.email === 'gone@example.com').id);
await assert.rejects(store.autoinvite.remove(adminId, 99999), /Invitation not found/);
const newId = await store.google_auth.add('new@example.com');
assert.deepEqual(await store.autoinvite.list(adminId), []);
await store.google_auth.setSelectedCompany(newId, company.id);
members = await store.companyuser.list(adminId);
assert.equal(members.find((m) => m.email === 'new@example.com').role, 'admin');
// remove clears selection
const userCu = members.find((m) => m.email === 'user@example.com');
await store.companyuser.remove(adminId, userCu.id);
assert.equal((await store.google_auth.getById(userId)).selectedCompanyId, null);
// downgrade → upgrade through the migration engine
await provider.close?.();
const env = { ...process.env, DB_PROVIDER: 'sqlite', SQLITE_PATH: join(tempDir, 'test.db') };
execFileSync('node', ['scripts/downgrade.js', '5'], { env, stdio: 'inherit' });
({ provider, store } = await open(tempDir).catch((e) => { throw e; }));
// opening upgrades to latest again; roles reset to admin for all members
const rows = (await provider.query('SELECT role FROM companyuser')).rows;
assert.ok(rows.length > 0 && rows.every((r) => r.role === 'admin'));
assert.deepEqual((await provider.query('SELECT * FROM autoinvite')).rows, []);
await provider.close?.();
console.log('OK');
} finally {
rmSync(tempDir, { recursive: true, force: true });
}