/** * users-file.test — uat-users.json round-trip, role coverage, and the enforced * gitignore block (idempotent, content-preserving). */ import { describe, it, expect, afterEach } from 'vitest'; import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { ensureUatGitignore, loadUsersFile, missingRoles, saveUsersFile, usersFileRelPath, type UatUsersFile, } from '../users-file.js'; const tmpDirs: string[] = []; const makeTmp = (): string => { const dir = mkdtempSync(join(tmpdir(), 'uat-users-')); tmpDirs.push(dir); return dir; }; afterEach(() => { while (tmpDirs.length) rmSync(tmpDirs.pop()!, { recursive: true, force: true }); }); const FILE: UatUsersFile = { version: '1', application: 'administration', apiUrl: 'http://localhost:5142', tenant: { name: 'UAT Tenant', slug: 'uat' }, createdAt: '2026-06-12T00:00:00.000Z', users: [{ role: 'admin', email: 'uat.admin@uat.local', password: 'Pw!1' }], }; describe('save/load round-trip', () => { it('persists and reloads (creating parent dirs)', () => { const root = makeTmp(); const abs = join(root, usersFileRelPath('administration')); saveUsersFile(abs, FILE); expect(loadUsersFile(abs)).toEqual(FILE); }); it('returns null on absent or invalid content', () => { const root = makeTmp(); const abs = join(root, 'x.json'); expect(loadUsersFile(abs)).toBeNull(); writeFileSync(abs, '{"version":"1"}'); expect(loadUsersFile(abs)).toBeNull(); }); }); describe('missingRoles', () => { it('lists uncovered roles, never anonymous, and treats null file as all-missing', () => { expect(missingRoles(FILE, ['admin', 'viewer', 'anonymous'])).toEqual(['viewer']); expect(missingRoles(null, ['admin', 'anonymous'])).toEqual(['admin']); }); }); describe('ensureUatGitignore', () => { it('appends the block once, preserving existing content', () => { const root = makeTmp(); writeFileSync(join(root, '.gitignore'), 'node_modules/\n'); expect(ensureUatGitignore(root)).toBe(true); const content = readFileSync(join(root, '.gitignore'), 'utf-8'); expect(content.startsWith('node_modules/\n')).toBe(true); expect(content).toContain('.application-test/uat/**/uat-users.json'); expect(content).toContain('.application-test/uat/**/runs/'); expect(ensureUatGitignore(root)).toBe(false); // idempotent expect(readFileSync(join(root, '.gitignore'), 'utf-8')).toBe(content); }); it('creates .gitignore when absent', () => { const root = makeTmp(); expect(ensureUatGitignore(root)).toBe(true); expect(readFileSync(join(root, '.gitignore'), 'utf-8')).toContain('uat-users.json'); }); });