import { describe, expect, it, vi } from 'vitest';
import * as Y from 'yjs';
import { createPrismaDocumentPersistence, } from '../../realtime/persistence.js';
/** Build a minimal mock that records calls and lets tests stub return values. */
function makeMockDb() {
    const findUnique = vi.fn();
    const upsert = vi.fn(async (args) => ({
        documentId: args.where.documentId,
        version: 1,
    }));
    const db = {
        documentCRDT: { findUnique, upsert },
    };
    return { db, findUnique, upsert };
}
function makeYjsState(seed) {
    const doc = new Y.Doc();
    seed(doc);
    const state = Y.encodeStateAsUpdate(doc);
    doc.destroy();
    return state;
}
describe('createPrismaDocumentPersistence — load', () => {
    it('returns null when no row exists', async () => {
        const { db, findUnique } = makeMockDb();
        findUnique.mockResolvedValueOnce(null);
        const p = createPrismaDocumentPersistence(db);
        const result = await p.load('doc-1');
        expect(result).toBeNull();
        expect(findUnique).toHaveBeenCalledWith({
            where: { documentId: 'doc-1' },
            select: { state: true },
        });
    });
    it('returns the stored bytes when a row exists', async () => {
        const { db, findUnique } = makeMockDb();
        const stored = makeYjsState((d) => d.getText('t').insert(0, 'hello'));
        findUnique.mockResolvedValueOnce({ state: stored });
        const p = createPrismaDocumentPersistence(db);
        const result = await p.load('doc-1');
        expect(result).not.toBeNull();
        // Round-trip the bytes through Yjs to confirm content survives.
        const doc = new Y.Doc();
        Y.applyUpdate(doc, result);
        expect(doc.getText('t').toString()).toBe('hello');
        doc.destroy();
    });
    it('returns a defensive copy so callers cannot mutate the source', async () => {
        const { db, findUnique } = makeMockDb();
        const stored = new Uint8Array([1, 2, 3]);
        findUnique.mockResolvedValueOnce({ state: stored });
        const p = createPrismaDocumentPersistence(db);
        const result = await p.load('doc-1');
        result[0] = 99;
        expect(stored[0]).toBe(1);
    });
});
describe('createPrismaDocumentPersistence — save', () => {
    it('upserts with state + stateVector + version increment', async () => {
        const { db, upsert } = makeMockDb();
        const state = makeYjsState((d) => d.getText('t').insert(0, 'hi'));
        const p = createPrismaDocumentPersistence(db);
        await p.save('doc-1', state);
        expect(upsert).toHaveBeenCalledTimes(1);
        const args = upsert.mock.calls[0][0];
        expect(args.where).toEqual({ documentId: 'doc-1' });
        expect(args.create.documentId).toBe('doc-1');
        expect(args.create.state).toBeInstanceOf(Uint8Array);
        expect(args.create.stateVector).toBeInstanceOf(Uint8Array);
        expect(args.create.version).toBe(1);
        expect(args.update.version).toEqual({ increment: 1 });
        expect(args.update.state).toBeInstanceOf(Uint8Array);
        expect(args.update.stateVector).toBeInstanceOf(Uint8Array);
    });
    it('writes a stateVector that matches the encoded document', async () => {
        const { db, upsert } = makeMockDb();
        const seed = (d) => d.getText('t').insert(0, 'abc');
        const state = makeYjsState(seed);
        const p = createPrismaDocumentPersistence(db);
        await p.save('doc-1', state);
        const writtenStateVector = upsert.mock.calls[0][0].create.stateVector;
        // Reconstruct the expected stateVector from the same state bytes and
        // compare byte-by-byte.
        const verify = new Y.Doc();
        Y.applyUpdate(verify, state);
        const expected = Y.encodeStateVector(verify);
        verify.destroy();
        expect(Array.from(writtenStateVector)).toEqual(Array.from(expected));
    });
    it('honours options.lastUpdatedById when set', async () => {
        const { db, upsert } = makeMockDb();
        const p = createPrismaDocumentPersistence(db, { lastUpdatedById: 'user_42' });
        await p.save('doc-1', makeYjsState((d) => d.getText('t').insert(0, 'x')));
        const args = upsert.mock.calls[0][0];
        expect(args.create.lastUpdatedById).toBe('user_42');
        expect(args.update.lastUpdatedById).toBe('user_42');
    });
    it('defaults lastUpdatedById to null when no option is set', async () => {
        const { db, upsert } = makeMockDb();
        const p = createPrismaDocumentPersistence(db);
        await p.save('doc-1', makeYjsState((d) => d.getText('t').insert(0, 'x')));
        const args = upsert.mock.calls[0][0];
        expect(args.create.lastUpdatedById).toBeNull();
        expect(args.update.lastUpdatedById).toBeNull();
    });
    it('makes a defensive copy of the state bytes', async () => {
        const { db, upsert } = makeMockDb();
        const original = makeYjsState((d) => d.getText('t').insert(0, 'abc'));
        const p = createPrismaDocumentPersistence(db);
        await p.save('doc-1', original);
        const written = upsert.mock.calls[0][0].create.state;
        // Mutating the original after save should not affect the buffer Prisma got.
        const beforeFirst = written[0];
        original[0] = 99;
        expect(written[0]).toBe(beforeFirst);
    });
    it('propagates database errors so the binding can flag them via onError', async () => {
        const { db, upsert } = makeMockDb();
        upsert.mockRejectedValueOnce(new Error('boom'));
        const p = createPrismaDocumentPersistence(db);
        await expect(p.save('doc-1', makeYjsState((d) => d.getText('t').insert(0, 'x')))).rejects.toThrow('boom');
    });
});
//# sourceMappingURL=persistence.test.js.map