import { describe, it, expect, vi, beforeEach } from 'vitest'; vi.mock('$lib/db', () => ({ prisma: { licenseKey: { findFirst: vi.fn(), update: vi.fn() } } })); import { prisma } from '$lib/db'; import { applySystemName, normalizeSystemName, SYSTEM_NAME_MAX_LENGTH } from './system-name'; const keyFindFirst = prisma.licenseKey.findFirst as ReturnType; const keyUpdate = prisma.licenseKey.update as ReturnType; beforeEach(() => { vi.clearAllMocks(); }); describe('normalizeSystemName', () => { it('trims and returns a case-folded copy', () => { expect(normalizeSystemName(' Living Room ')).toEqual({ ok: true, name: 'Living Room', lower: 'living room' }); }); it('accepts the length bounds', () => { expect(normalizeSystemName('a'.repeat(SYSTEM_NAME_MAX_LENGTH)).ok).toBe(true); expect(normalizeSystemName('a')).toEqual({ ok: true, name: 'a', lower: 'a' }); }); it('rejects empty, whitespace, and out-of-range values', () => { expect(normalizeSystemName('')).toEqual({ ok: false }); expect(normalizeSystemName(' ')).toEqual({ ok: false }); expect(normalizeSystemName('a'.repeat(SYSTEM_NAME_MAX_LENGTH + 1))).toEqual({ ok: false }); }); it('rejects non-strings', () => { expect(normalizeSystemName(undefined)).toEqual({ ok: false }); expect(normalizeSystemName(null)).toEqual({ ok: false }); expect(normalizeSystemName(42)).toEqual({ ok: false }); }); }); describe('applySystemName', () => { it('reports unchanged without writing when identical', async () => { const status = await applySystemName( { id: 'k1', userId: 'u1', systemName: 'Living Room' }, 'Living Room', 'living room' ); expect(status).toBe('unchanged'); expect(keyFindFirst).not.toHaveBeenCalled(); expect(keyUpdate).not.toHaveBeenCalled(); }); it('reports conflict when another license on the account holds the name', async () => { keyFindFirst.mockResolvedValue({ id: 'other' }); const status = await applySystemName( { id: 'k1', userId: 'u1', systemName: null }, 'Den', 'den' ); expect(status).toBe('conflict'); expect(keyUpdate).not.toHaveBeenCalled(); }); it('writes the name and its case-folded copy when free', async () => { keyFindFirst.mockResolvedValue(null); keyUpdate.mockResolvedValue({}); const status = await applySystemName( { id: 'k1', userId: 'u1', systemName: null }, 'Den', 'den' ); expect(status).toBe('ok'); expect(keyUpdate).toHaveBeenCalledWith({ where: { id: 'k1' }, data: { systemName: 'Den', systemNameLower: 'den' } }); }); it('skips the uniqueness check for a license with no account', async () => { keyUpdate.mockResolvedValue({}); const status = await applySystemName( { id: 'k1', userId: null, systemName: null }, 'Den', 'den' ); expect(status).toBe('ok'); expect(keyFindFirst).not.toHaveBeenCalled(); expect(keyUpdate).toHaveBeenCalled(); }); it('maps a unique-constraint violation to conflict', async () => { keyFindFirst.mockResolvedValue(null); keyUpdate.mockRejectedValue({ code: 'P2002' }); const status = await applySystemName( { id: 'k1', userId: 'u1', systemName: null }, 'Den', 'den' ); expect(status).toBe('conflict'); }); });