127 行
4.5 KiB
TypeScript
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 ('ブラウザへ保存できませんでした.')
|
|
})
|
|
})
|