import { beforeEach, describe, expect, it } from 'vitest'; import { MAP_FORMAT_VERSION, type MindMap } from '@/types/map'; import { db } from '@/db/database'; import { createMap, getMap, importMap } from '@/services/mapRepository'; import { MapFormatError, normalizeMapDocument, serializeMap, } from '@/services/mapSerialization'; /** Carte v1 telle qu'elle pouvait etre enregistree en IndexedDB avant la v2. */ function legacyStoredMap(id: string): Record { return { version: 1, id, name: 'Homelab v1', createdAt: '2026-01-01T00:00:00.000Z', updatedAt: '2026-01-02T00:00:00.000Z', nodes: [ { id: 'root', type: 'root', text: 'Infrastructure', position: { x: 0, y: 0 }, parentId: null, icon: '🖥️', style: { backgroundColor: '#eef2ff', width: 240, textAlign: 'center' }, }, { id: 'n1', type: 'standard', text: 'Reseau', position: { x: 260, y: 0 }, parentId: 'root', }, ], edges: [{ id: 'e-root-n1', source: 'root', target: 'n1' }], viewport: { x: 12, y: 34, zoom: 1.5 }, settings: { theme: 'ocean', layout: 'tree-vertical', branchStyle: 'curved', showGrid: false }, }; } beforeEach(async () => { await db.maps.clear(); await db.shares.clear(); await db.meta.clear(); }); describe('pipeline de normalisation', () => { it('normalise une carte v1 venue d IndexedDB comme un fichier v1', async () => { await db.maps.put(legacyStoredMap('stored-1') as unknown as MindMap); const fromStorage = await getMap('stored-1'); const fromFile = normalizeMapDocument(legacyStoredMap('stored-1'), { source: 'file' }); expect(fromStorage).toBeDefined(); expect(fromStorage?.nodes).toEqual(fromFile.nodes); expect(fromStorage?.settings).toEqual(fromFile.settings); expect(fromStorage?.version).toBe(MAP_FORMAT_VERSION); }); it('migre la largeur v1 d une carte stockee sans la reecrire en base', async () => { await db.maps.put(legacyStoredMap('stored-2') as unknown as MindMap); const opened = await getMap('stored-2'); expect(opened?.nodes[0]?.layout?.width).toBe(240); expect(opened?.nodes[0]?.layout?.widthMode).toBe('fixed'); expect(opened?.nodes[0]?.style?.width).toBeUndefined(); // La base contient toujours le document d'origine : rien n'a ete reecrit. const raw = (await db.maps.get('stored-2')) as unknown as Record; expect(raw.version).toBe(1); expect((raw.nodes as Array>)[0]?.style).toMatchObject({ width: 240 }); }); it('conserve le contenu et les reglages d une carte v1', async () => { await db.maps.put(legacyStoredMap('stored-3') as unknown as MindMap); const opened = await getMap('stored-3'); expect(opened?.name).toBe('Homelab v1'); expect(opened?.createdAt).toBe('2026-01-01T00:00:00.000Z'); expect(opened?.updatedAt).toBe('2026-01-02T00:00:00.000Z'); expect(opened?.nodes[0]?.icon).toBe('🖥️'); expect(opened?.nodes[0]?.style?.textAlign).toBe('center'); expect(opened?.settings.theme).toBe('ocean'); expect(opened?.settings.layout).toBe('tree-vertical'); expect(opened?.viewport).toEqual({ x: 12, y: 34, zoom: 1.5 }); }); it('ne modifie jamais le document fourni', () => { const original = legacyStoredMap('immutable'); const copy = JSON.parse(JSON.stringify(original)); normalizeMapDocument(original, { source: 'storage' }); expect(original).toEqual(copy); }); it('laisse une carte v2 inchangee', () => { const v2 = normalizeMapDocument(legacyStoredMap('x'), { source: 'file' }); const again = normalizeMapDocument(JSON.parse(serializeMap(v2)), { source: 'file' }); expect(again.nodes).toEqual(v2.nodes); expect(again.settings).toEqual(v2.settings); expect(again.version).toBe(MAP_FORMAT_VERSION); }); it('accepte une carte locale videe de ses noeuds', async () => { const map = await createMap('Carte videe'); await db.maps.put({ ...map, nodes: [], edges: [] }); const opened = await getMap(map.id); expect(opened?.nodes).toHaveLength(0); expect(opened?.name).toBe('Carte videe'); }); it('refuse un fichier sans noeud exploitable', () => { expect(() => normalizeMapDocument({ version: 1, id: 'x', nodes: [] })).toThrow(MapFormatError); }); it('adapte le message d erreur a la provenance', () => { expect(() => normalizeMapDocument('pas un objet', { source: 'file' })).toThrow(/fichier/i); expect(() => normalizeMapDocument('pas un objet', { source: 'storage' })).toThrow( /enregistree/i, ); expect(() => normalizeMapDocument('pas un objet', { source: 'share' })).toThrow(/partagee/i); }); it('fait passer un import par le meme pipeline', async () => { const normalised = normalizeMapDocument(legacyStoredMap('file-1'), { source: 'file' }); const stored = await importMap(normalised); const reopened = await getMap(stored.id); expect(reopened?.nodes).toEqual(normalised.nodes); expect(reopened?.version).toBe(MAP_FORMAT_VERSION); }); });