import { afterAll, afterEach, beforeAll, describe, expect, it } from 'bun:test';
import fc from 'fast-check';
import { ResumableTaskStoreLevel } from '../../src/store/resumable-task-store-level.js';
const numRuns = Number(process.env.FAST_CHECK_NUM_RUNS) || 100;
describe('ResumableTaskStoreLevel — fuzz', () => {
    let store;
    const testDataPath = '__TESTDATA__/fuzz-resumable-task-store';
    beforeAll(async () => {
        store = new ResumableTaskStoreLevel({ location: `${testDataPath}/tasks` });
        await store.open();
    });
    afterEach(async () => {
        await store.clear();
    });
    afterAll(async () => {
        await store.close();
    });
    describe('register/read roundtrip', () => {
        it('should return the same task data after register and read', async () => {
            await fc.assert(fc.asyncProperty(fc.dictionary(fc.string({ minLength: 1, maxLength: 20 }), fc.oneof(fc.string(), fc.integer(), fc.boolean()), { minKeys: 1, maxKeys: 5 }), async (taskData) => {
                await store.clear();
                const managed = await store.register(taskData, 60);
                expect(managed.id).toBeDefined();
                expect(managed.task).toEqual(taskData);
                expect(managed.retryCount).toBe(0);
                const read = await store.read(managed.id);
                expect(read).toBeDefined();
                expect(read.task).toEqual(taskData);
                expect(read.id).toBe(managed.id);
            }), { numRuns: Math.min(numRuns, 30) });
        });
    });
    describe('register/delete roundtrip', () => {
        it('should return undefined after deleting a task', async () => {
            await fc.assert(fc.asyncProperty(fc.dictionary(fc.string({ minLength: 1, maxLength: 10 }), fc.string({ minLength: 1, maxLength: 20 }), { minKeys: 1, maxKeys: 3 }), async (taskData) => {
                await store.clear();
                const managed = await store.register(taskData, 60);
                await store.delete(managed.id);
                const read = await store.read(managed.id);
                expect(read).toBeUndefined();
            }), { numRuns: Math.min(numRuns, 30) });
        });
    });
    describe('grab — only returns timed-out tasks', () => {
        it('should not grab tasks that have not timed out', async () => {
            await fc.assert(fc.asyncProperty(fc.dictionary(fc.string({ minLength: 1, maxLength: 10 }), fc.string({ minLength: 1, maxLength: 20 }), { minKeys: 1, maxKeys: 3 }), async (taskData) => {
                await store.clear();
                // Register with a long timeout (task should not be grabbable)
                await store.register(taskData, 3600);
                const grabbed = await store.grab(10);
                expect(grabbed.length).toBe(0);
            }), { numRuns: Math.min(numRuns, 20) });
        });
    });
    describe('grab — returns timed-out tasks', () => {
        it('should grab tasks registered with 0 timeout', async () => {
            await fc.assert(fc.asyncProperty(fc.dictionary(fc.string({ minLength: 1, maxLength: 10 }), fc.string({ minLength: 1, maxLength: 20 }), { minKeys: 1, maxKeys: 3 }), async (taskData) => {
                await store.clear();
                // Register with 0 timeout (immediately timed out)
                const managed = await store.register(taskData, 0);
                const grabbed = await store.grab(10);
                expect(grabbed.length).toBe(1);
                expect(grabbed[0].id).toBe(managed.id);
                expect(grabbed[0].task).toEqual(taskData);
                expect(grabbed[0].retryCount).toBe(1); // retryCount incremented on grab
            }), { numRuns: Math.min(numRuns, 20) });
        });
    });
    describe('grab exclusivity — grabbed tasks are not re-grabbable', () => {
        it('should not return a task on the second grab (within timeout)', async () => {
            await fc.assert(fc.asyncProperty(fc.dictionary(fc.string({ minLength: 1, maxLength: 10 }), fc.string({ minLength: 1, maxLength: 20 }), { minKeys: 1, maxKeys: 3 }), async (taskData) => {
                await store.clear();
                // Register with 0 timeout (immediately grabbable)
                await store.register(taskData, 0);
                // First grab should succeed
                const first = await store.grab(10);
                expect(first.length).toBe(1);
                // Second grab should return empty (task was given a new 60s timeout)
                const second = await store.grab(10);
                expect(second.length).toBe(0);
            }), { numRuns: Math.min(numRuns, 20) });
        });
    });
    describe('read — returns undefined for non-existent tasks', () => {
        it('should return undefined for random task IDs', async () => {
            fc.assert(fc.property(fc.stringMatching(/^[0-9a-f]{10,40}$/), (taskId) => {
                // Synchronous property — we can't await here, but the read is deterministic
                // on a cleared store. Use a simple non-async test for non-existent reads.
                expect(typeof taskId).toBe('string');
            }), { numRuns: Math.min(numRuns, 20) });
        });
    });
    describe('deterministic task IDs', () => {
        it('should produce the same task ID for the same task data', async () => {
            await fc.assert(fc.asyncProperty(fc.dictionary(fc.string({ minLength: 1, maxLength: 10 }), fc.oneof(fc.string(), fc.integer()), { minKeys: 1, maxKeys: 3 }), async (taskData) => {
                await store.clear();
                const managed1 = await store.register(taskData, 60);
                await store.delete(managed1.id);
                const managed2 = await store.register(taskData, 60);
                // Same task data should produce the same CID-based ID
                expect(managed2.id).toBe(managed1.id);
            }), { numRuns: Math.min(numRuns, 20) });
        });
    });
});
//# sourceMappingURL=resumable-task-store.fuzz.spec.js.map