import { beforeEach, describe, expect, it, vi } from 'vitest' import { fetchPostChanges, fetchPosts, switchPostFilterLocale, toggleViewedFlg, updatePost } from '@/lib/posts' import type { FetchPostsParams } from '@/types' const api = vi.hoisted (() => ({ apiDelete: vi.fn (), apiGet: vi.fn (), apiPost: vi.fn (), apiPut: vi.fn (), })) vi.mock ('@/lib/api', () => api) const baseParams: FetchPostsParams = { url: '', title: '', tags: '', match: 'all', originalCreatedFrom: '', originalCreatedTo: '', createdFrom: '', createdTo: '', updatedFrom: '', updatedTo: '', page: 1, limit: 20, order: 'updated_at:desc', } describe ('posts API functions', () => { beforeEach (() => { vi.clearAllMocks () }) it ('switches current tag names without adding identity state to the URL', async () => { api.apiGet.mockResolvedValueOnce ({ tags: 'シロシ not:nico:shared' }) const path = await switchPostFilterLocale ({ location: { pathname: '/ja/counts/posts', search: '?tags=source+not%3Anico%3Ashared&match=any', hash: '#table' }, params: {}, locale: 'ja', targetLocale: 'ain-Kana' }) expect (api.apiGet).toHaveBeenCalledWith ('/posts/tag_filters', { locale: 'ja', params: { tags: 'source not:nico:shared', target_locale: 'ain-Kana' } }) const url = new URL (path, 'https://example.test') expect (url.pathname).toBe ('/ain-Kana/counts/posts') expect (Object.fromEntries (url.searchParams)).toEqual ({ tags: 'シロシ not:nico:shared', match: 'any' }) expect (url.hash).toBe ('#table') }) it ('maps post search parameters to backend snake_case names', async () => { api.apiGet.mockResolvedValueOnce ({ posts: [], count: 0 }) await fetchPosts ({ ...baseParams, title: 'title', tags: 'a b', originalCreatedFrom: '2026-01-01', updatedTo: '2026-02-01', }) expect (api.apiGet).toHaveBeenCalledWith ( '/posts', { locale: 'ja', params: { title: 'title', tags: 'a b', match: 'all', original_created_from: '2026-01-01', updated_to: '2026-02-01', page: 1, limit: 20, order: 'updated_at:desc', }, }, ) }) it ('updates posts with version and merge controls', async () => { api.apiPut.mockResolvedValueOnce ({ id: 5 }) await updatePost ( { id: 5, title: 'new title', tags: 'tag', duration: null, parentPostIds: '1 2', originalCreatedFrom: null, originalCreatedBefore: '2026-01-02T00:00:00Z', }, { baseVersionNo: 7, force: true, merge: false }, ) expect (api.apiPut).toHaveBeenCalledWith ( '/posts/5', { title: 'new title', tags: 'tag', duration: null, parent_post_ids: '1 2', original_created_from: null, original_created_before: '2026-01-02T00:00:00Z', }, { locale: 'ja', params: { base_version_no: '7', force: '1', merge: '0', }, }, ) }) it ('uses the viewed endpoint method matching the requested state', async () => { await toggleViewedFlg ('9', true) await toggleViewedFlg ('9', false) expect (api.apiPost).toHaveBeenCalledWith ('/posts/9/viewed') expect (api.apiDelete).toHaveBeenCalledWith ('/posts/9/viewed') }) it ('keeps optional post history filters out when blank', async () => { api.apiGet.mockResolvedValueOnce ({ versions: [], count: 0 }) await fetchPostChanges ({ page: 2, limit: 50 }) expect (api.apiGet).toHaveBeenCalledWith ( '/posts/versions', { locale: 'ja', params: { page: 2, limit: 50 } }, ) }) it ('maps an explicit external tag history filter to external_tag', async () => { api.apiGet.mockResolvedValueOnce ({ versions: [], count: 0 }) await fetchPostChanges ({ externalTag: '7', page: 2, limit: 50, }) expect (api.apiGet).toHaveBeenCalledWith ( '/posts/versions', { locale: 'ja', params: { external_tag: '7', page: 2, limit: 50, }, }, ) }) })