ファイル
btrc-hub/frontend/src/lib/postImportStorage.test.ts
T
2026-07-16 20:05:09 +09:00

127 行
4.5 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { cleanupExpiredPostImportSessions,
clearPostImportSourceDraft,
loadPostImportSession,
loadPostImportSourceDraft,
savePostImportSession,
savePostImportSourceDraft } from '@/lib/postImportSession'
import { buildPostImportRow } from '@/test/postImportFactories'
describe ('post import storage', () => {
beforeEach (() => {
sessionStorage.clear ()
vi.useRealTimers ()
})
it ('round-trips a valid session and source draft', () => {
const row = buildPostImportRow ({
attributes: { duration: '2.5' },
recoverable: true,
importStatus: 'pending',
validationErrors: { title: ['invalid'] } })
const skipped = buildPostImportRow ({
importStatus: 'skipped',
skipReason: 'existing',
existingPostId: 10 })
expect (savePostImportSession ('session', {
source: skipped.url,
rows: [row, skipped],
repairMode: 'all' })).toBe (true)
expect (loadPostImportSession ('session')).toMatchObject ({
version: 2,
source: skipped.url,
rows: [{
attributes: { duration: '2.5' },
recoverable: true,
importStatus: 'pending' },
{
importStatus: 'skipped',
skipReason: 'existing',
existingPostId: 10 }] })
expect (savePostImportSourceDraft ('https://example.com')).toBe (true)
expect (loadPostImportSourceDraft ()).toEqual ({ source: 'https://example.com' })
clearPostImportSourceDraft ()
expect (loadPostImportSourceDraft ()).toEqual ({ source: '' })
})
it ('rejects inconsistent post IDs and terminal statuses', () => {
const session = {
version: 2,
savedAt: new Date ().toISOString (),
source: '',
repairMode: 'all',
rows: [buildPostImportRow ()] }
const invalidRows = [
{ ...session.rows[0], skipReason: 'existing', existingPostId: undefined },
{ ...session.rows[0], existingPostId: 2, skipReason: undefined },
{ ...session.rows[0], importStatus: 'created', createdPostId: undefined },
{ ...session.rows[0], importStatus: 'failed', createdPostId: 3 },
{ ...session.rows[0], importStatus: 'created', recoverable: true },
{ ...session.rows[0], importStatus: 'skipped', recoverable: true },
{ ...session.rows[0], recoverable: true }]
for (const [index, row] of invalidRows.entries ())
{
sessionStorage.setItem (`post-import-session:invalid-${ index }`, JSON.stringify ({
...session,
rows: [row] }))
expect (loadPostImportSession (`invalid-${ index }`)).toBeNull ()
}
})
it ('rejects invalid attribute, provenance, tag-source, and snapshot data', () => {
const row = buildPostImportRow ()
const invalidRows = [
{ ...row, attributes: { title: [] } },
{ ...row, attributes: { unknown: 'value' } },
{ ...row, provenance: { title: 'mapped' } },
{ ...row, tagSources: { mapped: 'tag' } },
{ ...row, resetSnapshot: { ...row.resetSnapshot,
fieldWarnings: { title: 'warning' } } }]
invalidRows.forEach ((invalidRow, index) => {
sessionStorage.setItem (`post-import-session:shape-${ index }`, JSON.stringify ({
version: 2,
savedAt: new Date ().toISOString (),
source: '',
rows: [invalidRow],
repairMode: 'all' }))
expect (loadPostImportSession (`shape-${ index }`)).toBeNull ()
})
})
it ('removes expired and malformed sessions without touching current sessions', () => {
const current = {
version: 2,
savedAt: new Date ().toISOString (),
source: '',
rows: [buildPostImportRow ()],
repairMode: 'all' }
const expired = {
...current,
savedAt: new Date (Date.now () - 25 * 60 * 60 * 1000).toISOString () }
sessionStorage.setItem ('post-import-session:current', JSON.stringify (current))
sessionStorage.setItem ('post-import-session:expired', JSON.stringify (expired))
sessionStorage.setItem ('post-import-session:malformed', '{')
cleanupExpiredPostImportSessions ()
expect (sessionStorage.getItem ('post-import-session:current')).not.toBeNull ()
expect (sessionStorage.getItem ('post-import-session:expired')).toBeNull ()
expect (sessionStorage.getItem ('post-import-session:malformed')).toBeNull ()
})
it ('reports storage access failures without throwing', () => {
const onError = vi.fn ()
vi.spyOn (Storage.prototype, 'setItem').mockImplementation (() => {
throw new DOMException ('quota')
})
expect (savePostImportSourceDraft ('source', onError)).toBe (false)
expect (onError).toHaveBeenCalledWith ('ブラウザへ保存できませんでした.')
})
})