import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { setActivePinia, createPinia } from 'pinia' import { useGalleryStore } from '../src/stores/gallery.js' function stubFetch(handler) { globalThis.fetch = vi.fn(async (url, init) => { const { status, body } = handler(url, init) return { ok: status >= 200 && status < 300, status, statusText: String(status), text: async () => (body == null ? '' : JSON.stringify(body)) } }) } const EMPTY = { images: [], date_groups: [], next_cursor: null } describe('gallery store: tag/post filter exclusivity', () => { beforeEach(() => setActivePinia(createPinia())) afterEach(() => vi.restoreAllMocks()) it('setPostFilter sets post_id and clears tag_id', () => { const s = useGalleryStore() stubFetch(() => ({ status: 200, body: EMPTY })) s.setTagFilter(3) expect(s.filter.tag_id).toBe(3) s.setPostFilter(7) expect(s.filter.post_id).toBe(7) expect(s.filter.tag_id).toBe(null) }) it('setTagFilter clears post_id', () => { const s = useGalleryStore() stubFetch(() => ({ status: 200, body: EMPTY })) s.setPostFilter(7) s.setTagFilter(3) expect(s.filter.tag_id).toBe(3) expect(s.filter.post_id).toBe(null) }) it('loadMore sends exactly the active filter param', async () => { const s = useGalleryStore() const urls = [] stubFetch((url) => { urls.push(url); return { status: 200, body: EMPTY } }) s.setPostFilter(7) await s.loadMore() const scrollCall = urls.filter(u => u.includes('/api/gallery/scroll')).pop() expect(scrollCall).toContain('post_id=7') expect(scrollCall).not.toContain('tag_id=') }) it('setTagFilter resolves the tag name for the filter chip', async () => { const s = useGalleryStore() stubFetch((url) => { if (url.includes('/api/tags/7')) { return { status: 200, body: { id: 7, name: 'Asuka', kind: 'character' } } } return { status: 200, body: EMPTY } // scroll + timeline }) s.setTagFilter(7) expect(s.filter.tag_id).toBe(7) await vi.waitFor(() => expect(s.filterTagName).toBe('Asuka')) }) it('loadInitial issues exactly one scroll request at the initial limit', async () => { const s = useGalleryStore() const urls = [] // Non-null cursor: the old 10Ă—serial-batch loop would have fired ten // requests here. The single-fetch path must fire exactly one. stubFetch((url) => { urls.push(url) return { status: 200, body: { images: [], date_groups: [], next_cursor: 'c1' } } }) await s.loadInitial() const scrollCalls = urls.filter(u => u.includes('/api/gallery/scroll')) expect(scrollCalls).toHaveLength(1) expect(scrollCalls[0]).toContain('limit=50') }) })