All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m28s
Co-authored-by: Shaun Campbell <shaun@campbellwireless.net> Reviewed-on: #39 Co-authored-by: AI Agent <ai-agent@campbellwireless.net> Co-committed-by: AI Agent <ai-agent@campbellwireless.net>
152 lines
4.4 KiB
TypeScript
152 lines
4.4 KiB
TypeScript
import { beforeEach, afterEach, describe, expect, it } from 'vitest';
|
|
import { setupTestDb } from '../../tests/helpers.js';
|
|
import type { Database } from './db/types.js';
|
|
import {
|
|
createLocalUser,
|
|
deleteUser,
|
|
listUsers,
|
|
setLocalPasswordForUser,
|
|
updateUser,
|
|
upsertUserFromAuth,
|
|
upsertUserProfile
|
|
} from './users.js';
|
|
|
|
let database: Database;
|
|
beforeEach(() => {
|
|
database = setupTestDb();
|
|
});
|
|
|
|
afterEach(() => {
|
|
database.close();
|
|
});
|
|
|
|
describe('upsertUserFromAuth', () => {
|
|
it('creates a new user with profile data', () => {
|
|
const created = upsertUserFromAuth({
|
|
id: 'u1',
|
|
username: 'jdoe',
|
|
fullName: 'Jane Doe',
|
|
email: 'jane@example.com',
|
|
authSource: 'OIDC - Synology'
|
|
});
|
|
expect(created.id).toBe('u1');
|
|
expect(created.username).toBe('jdoe');
|
|
expect(created.full_name).toBe('Jane Doe');
|
|
expect(created.email).toBe('jane@example.com');
|
|
expect(created.auth_source).toBe('OIDC - Synology');
|
|
expect(listUsers()).toHaveLength(1);
|
|
});
|
|
|
|
it('updates existing users with new auth data', () => {
|
|
upsertUserFromAuth({
|
|
id: 'u1',
|
|
username: 'jdoe',
|
|
fullName: 'Jane Doe',
|
|
email: 'jane@example.com',
|
|
authSource: 'OIDC - Synology'
|
|
});
|
|
const updated = upsertUserFromAuth({
|
|
id: 'u1',
|
|
username: 'janed',
|
|
email: 'jane.doe@example.com'
|
|
});
|
|
expect(updated.username).toBe('janed');
|
|
expect(updated.email).toBe('jane.doe@example.com');
|
|
});
|
|
});
|
|
|
|
describe('upsertUserProfile', () => {
|
|
it('creates a user from profile details', () => {
|
|
const created = upsertUserProfile('u2', 'Sam Sample', 'sam@example.com');
|
|
expect(created.id).toBe('u2');
|
|
expect(created.full_name).toBe('Sam Sample');
|
|
expect(created.email).toBe('sam@example.com');
|
|
});
|
|
});
|
|
|
|
describe('updateUser', () => {
|
|
it('updates user fields and syncs self profile when present', () => {
|
|
upsertUserFromAuth({
|
|
id: 'u3',
|
|
username: 'sarah',
|
|
fullName: 'Sarah Lee',
|
|
email: 'sarah@example.com'
|
|
});
|
|
database.run(
|
|
`INSERT INTO people (id, user_id, first_name, last_name, email, is_self)
|
|
VALUES (?, ?, ?, ?, ?, 1)`,
|
|
['p1', 'u3', 'Sarah', 'Lee', 'sarah@example.com']
|
|
);
|
|
|
|
updateUser({ id: 'u3', username: 'slee', fullName: 'Sarah Smith', email: 'ss@example.com' });
|
|
|
|
const user = listUsers().find((item) => item.id === 'u3');
|
|
expect(user?.username).toBe('slee');
|
|
expect(user?.full_name).toBe('Sarah Smith');
|
|
expect(user?.email).toBe('ss@example.com');
|
|
|
|
const profile = database.get<{ first_name: string; last_name: string; email: string | null }>(
|
|
'SELECT first_name, last_name, email FROM people WHERE user_id = ? AND is_self = 1',
|
|
['u3']
|
|
);
|
|
expect(profile?.first_name).toBe('Sarah');
|
|
expect(profile?.last_name).toBe('Smith');
|
|
expect(profile?.email).toBe('ss@example.com');
|
|
});
|
|
});
|
|
|
|
describe('deleteUser', () => {
|
|
it('removes the user record', () => {
|
|
upsertUserFromAuth({ id: 'u4', username: 'delete-me', fullName: 'Delete Me' });
|
|
deleteUser('u4');
|
|
expect(listUsers().find((item) => item.id === 'u4')).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('createLocalUser', () => {
|
|
it('creates a local user with credentials', async () => {
|
|
const created = await createLocalUser({
|
|
username: 'local-user',
|
|
fullName: 'Local User',
|
|
email: 'local@example.com',
|
|
password: 'averysecurepassword'
|
|
});
|
|
expect(created.username).toBe('local-user');
|
|
expect(created.auth_source).toBe('Local');
|
|
const row = database.get<{ user_id: string; password_hash: string }>(
|
|
'SELECT user_id, password_hash FROM local_credentials WHERE user_id = ?',
|
|
[created.id]
|
|
);
|
|
expect(row?.user_id).toBe(created.id);
|
|
expect(row?.password_hash).toBeTruthy();
|
|
});
|
|
|
|
it('rejects duplicate usernames', async () => {
|
|
await createLocalUser({
|
|
username: 'dup-user',
|
|
fullName: 'Dup User',
|
|
password: 'averysecurepassword'
|
|
});
|
|
await expect(
|
|
createLocalUser({
|
|
username: 'dup-user',
|
|
fullName: 'Dup User Two',
|
|
password: 'averysecurepassword'
|
|
})
|
|
).rejects.toThrow('Username already exists');
|
|
});
|
|
});
|
|
|
|
describe('setLocalPasswordForUser', () => {
|
|
it('sets local credentials for an existing user', async () => {
|
|
upsertUserFromAuth({ id: 'u5', username: 'sarah', fullName: 'Sarah Lee' });
|
|
await setLocalPasswordForUser('u5', 'averysecurepassword');
|
|
const row = database.get<{ user_id: string; password_hash: string }>(
|
|
'SELECT user_id, password_hash FROM local_credentials WHERE user_id = ?',
|
|
['u5']
|
|
);
|
|
expect(row?.user_id).toBe('u5');
|
|
expect(row?.password_hash).toBeTruthy();
|
|
});
|
|
});
|