import { describe, it, expect, beforeEach, mock, spyOn } from 'bun:test' const successMock = mock(() => {}) const errorMock = mock(() => {}) globalThis.useNotificationStore = () => ({ add: () => 'test-id', markRead: () => {}, }) as any mock.module('~/composables/useNotification', () => ({ useNotification: () => ({ success: successMock, error: errorMock }), default: () => ({ success: successMock, error: errorMock }), })) mock.module('~/stores/notification', () => ({ useNotificationStore: () => ({ add: () => 'test-id', markRead: () => {}, }), })) // eslint-disable-next-line import/first import * as utils from '~/utils/index' // eslint-disable-next-line import/first import { usePresets } from '~/composables/usePresets' // eslint-disable-next-line import/first import type { Preset, PresetRequest } from '~/types/presets' // eslint-disable-next-line import/first import type { Pagination } from '~/types/responses' const mockPreset: Preset = { id: 1, name: 'Test Preset', description: 'Test description', folder: 'Downloads', template: '%(title)s.%(ext)s', cookies: 'cookie=value', cli: '--format best', default: false, priority: 10, } const mockPagination: Pagination = { page: 1, per_page: 50, total: 1, total_pages: 1, has_next: false, has_prev: false, } function createMockResponse({ ok, status, jsonData }: { ok: boolean; status: number; jsonData: any }) { return { ok, status, headers: new Headers(), redirected: false, statusText: '', type: 'basic', url: '', body: null, bodyUsed: false, clone() { return this }, async json() { return jsonData }, text: async () => JSON.stringify(jsonData), arrayBuffer: async () => new ArrayBuffer(0), blob: async () => new Blob(), formData: async () => new FormData(), } as Response } describe('usePresets', () => { beforeEach(() => { successMock.mockClear() errorMock.mockClear() }) describe('loadPresets', () => { it('loads presets with pagination successfully', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: { items: [mockPreset], pagination: mockPagination, }, }), ) const presets = usePresets() await presets.loadPresets() expect(presets.presets.value).toHaveLength(1) expect(presets.presets.value[0]).toEqual(mockPreset) expect(presets.pagination.value).toEqual(mockPagination) expect(presets.lastError.value).toBeNull() requestSpy.mockRestore() }) it('sorts presets by priority then name', async () => { const items = [ { ...mockPreset, id: 1, name: 'B', priority: 2 }, { ...mockPreset, id: 2, name: 'A', priority: 2 }, { ...mockPreset, id: 3, name: 'C', priority: 1 }, ] const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: { items, pagination: mockPagination, }, }), ) const presets = usePresets() await presets.loadPresets() const sorted = presets.presets.value expect(sorted[0].priority).toBe(2) expect(sorted[0].name).toBe('A') expect(sorted[1].priority).toBe(2) expect(sorted[1].name).toBe('B') expect(sorted[2].priority).toBe(1) expect(sorted[2].name).toBe('C') requestSpy.mockRestore() }) it('handles empty presets list', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: { items: [], pagination: { ...mockPagination, total: 0 }, }, }), ) const presets = usePresets() await presets.loadPresets() expect(presets.presets.value).toEqual([]) expect(presets.lastError.value).toBeNull() requestSpy.mockRestore() }) it('handles errors during load', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: false, status: 500, jsonData: { error: 'Server error' }, }), ) const presets = usePresets() await presets.loadPresets() expect(presets.lastError.value).toBeTruthy() requestSpy.mockRestore() }) }) describe('getPreset', () => { it('fetches a single preset successfully', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const presets = usePresets() const result = await presets.getPreset(1) expect(result).toEqual(mockPreset) expect(presets.lastError.value).toBeNull() requestSpy.mockRestore() }) it('handles 404 not found', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: false, status: 404, jsonData: { error: 'Preset not found' }, }), ) const presets = usePresets() const result = await presets.getPreset(999) expect(result).toBeNull() expect(presets.lastError.value).toBeTruthy() requestSpy.mockRestore() }) }) describe('createPreset', () => { it('creates a preset successfully', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const presets = usePresets() const initialTotal = presets.pagination.value.total const newPreset: PresetRequest = { name: 'New Preset', description: 'Desc', cli: '--format best', } const result = await presets.createPreset(newPreset) expect(result).toEqual(mockPreset) expect(presets.presets.value).toContainEqual(mockPreset) expect(presets.pagination.value.total).toBe(initialTotal + 1) requestSpy.mockRestore() }) it('calls callback on success', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const callback = mock(() => {}) const presets = usePresets() const newPreset: PresetRequest = { name: 'New Preset', description: 'Desc', cli: '--format best', } await presets.createPreset(newPreset, callback) expect(callback).toHaveBeenCalledWith({ success: true, error: null, detail: null, data: mockPreset, }) requestSpy.mockRestore() }) it('calls callback on error', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: false, status: 400, jsonData: { detail: [{ loc: ['name'], msg: 'Field required', type: 'value_error' }] }, }), ) const callback = mock(() => {}) const presets = usePresets() const newPreset: PresetRequest = { name: 'New Preset', description: 'Desc', cli: '--format best', } await presets.createPreset(newPreset, callback) expect(callback).toHaveBeenCalledWith( expect.objectContaining({ success: false, error: expect.any(String), }), ) requestSpy.mockRestore() }) }) describe('updatePreset', () => { it('updates a preset successfully', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const presets = usePresets() const result = await presets.updatePreset(1, mockPreset) expect(result).toEqual(mockPreset) requestSpy.mockRestore() }) it('strips id from update payload', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const presets = usePresets() await presets.updatePreset(1, { ...mockPreset, default: true }) const requestBody = JSON.parse((requestSpy.mock.calls[0][1] as any).body) expect(requestBody.id).toBeUndefined() expect(requestBody.default).toBe(false) requestSpy.mockRestore() }) }) describe('patchPreset', () => { it('patches a preset successfully', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: { ...mockPreset, priority: 20 }, }), ) const presets = usePresets() const result = await presets.patchPreset(1, { priority: 20 }) expect(result?.priority).toBe(20) requestSpy.mockRestore() }) it('strips id and default from patch payload', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const presets = usePresets() await presets.patchPreset(1, { id: 10, default: true }) const requestBody = JSON.parse((requestSpy.mock.calls[0][1] as any).body) expect(requestBody.id).toBeUndefined() expect(requestBody.default).toBe(false) requestSpy.mockRestore() }) }) describe('deletePreset', () => { it('deletes a preset successfully', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const presets = usePresets() const initialTotal = presets.pagination.value.total const result = await presets.deletePreset(mockPreset.id!) expect(result).toBe(true) expect(presets.pagination.value.total).toBe(Math.max(0, initialTotal - 1)) requestSpy.mockRestore() }) it('calls callback on delete success', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }), ) const callback = mock(() => {}) const presets = usePresets() await presets.deletePreset(1, callback) expect(callback).toHaveBeenCalledWith({ success: true, error: null, detail: null, data: true, }) requestSpy.mockRestore() }) it('calls callback on delete error', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: false, status: 404, jsonData: { error: 'Preset not found' }, }), ) const callback = mock(() => {}) const presets = usePresets() await presets.deletePreset(999, callback) expect(callback).toHaveBeenCalledWith( expect.objectContaining({ success: false, error: expect.any(String), data: false, }), ) requestSpy.mockRestore() }) }) describe('error handling', () => { it('throws when throwInstead is true', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: false, status: 500, jsonData: { error: 'Server error' }, }), ) const presets = usePresets() presets.throwInstead.value = true await expect(presets.loadPresets()).rejects.toThrow() requestSpy.mockRestore() }) it('clears error on clearError call', async () => { const requestSpy = spyOn(utils, 'request') requestSpy.mockResolvedValueOnce( createMockResponse({ ok: false, status: 500, jsonData: { error: 'Server error' }, }), ) const presets = usePresets() presets.throwInstead.value = false await presets.loadPresets() expect(presets.lastError.value).toBeTruthy() presets.clearError() expect(presets.lastError.value).toBeNull() requestSpy.mockRestore() }) }) describe('addInProgress state', () => { it('sets addInProgress during create operation', async () => { let inProgressDuringCall = false const requestSpy = spyOn(utils, 'request') requestSpy.mockImplementation(async () => { const presets = usePresets() inProgressDuringCall = presets.addInProgress.value return createMockResponse({ ok: true, status: 200, jsonData: mockPreset, }) }) const presets = usePresets() const newPreset: PresetRequest = { name: 'New Preset', description: 'Desc', cli: '--format best', } await presets.createPreset(newPreset) expect(inProgressDuringCall).toBe(true) expect(presets.addInProgress.value).toBe(false) requestSpy.mockRestore() }) }) })