Initial release of Web Tools 0.0.5
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
normalizeNavigation,
|
||||
validateNavigation,
|
||||
writeJsonAtomic,
|
||||
} from '../server/navigation.mjs'
|
||||
|
||||
const temporaryDirectories = []
|
||||
afterEach(async () => {
|
||||
await Promise.all(temporaryDirectories.splice(0).map((directory) => rm(directory, { recursive: true, force: true })))
|
||||
})
|
||||
|
||||
const catalog = {
|
||||
categories: [{ id: 'encoding' }, { id: 'crypto' }],
|
||||
tools: [
|
||||
{ id: 'hex', category: 'encoding' },
|
||||
{ id: 'base64', category: 'encoding' },
|
||||
{ id: 'cmac', category: 'crypto' },
|
||||
],
|
||||
}
|
||||
const defaults = {
|
||||
version: 1,
|
||||
categories: [
|
||||
{ id: 'encoding', tools: ['hex', 'base64'] },
|
||||
{ id: 'crypto', tools: ['cmac'] },
|
||||
],
|
||||
}
|
||||
|
||||
describe('navigation config', () => {
|
||||
it('preserves custom order and appends tools added in a later release', () => {
|
||||
const current = { version: 1, categories: [{ id: 'encoding', tools: ['base64'] }] }
|
||||
expect(normalizeNavigation(current, catalog, defaults)).toEqual({
|
||||
version: 1,
|
||||
categories: [
|
||||
{ id: 'encoding', tools: ['base64', 'hex'] },
|
||||
{ id: 'crypto', tools: ['cmac'] },
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects duplicate or incomplete configurations', () => {
|
||||
expect(() => validateNavigation({ version: 1, categories: [{ id: 'encoding', tools: ['hex', 'hex'] }] }, catalog)).toThrow('重复')
|
||||
expect(() => validateNavigation({ version: 1, categories: [{ id: 'encoding', tools: ['hex'] }, { id: 'crypto', tools: ['cmac'] }] }, catalog)).toThrow('全部')
|
||||
})
|
||||
|
||||
it('writes valid JSON through an atomic replacement', async () => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), 'web-tools-navigation-'))
|
||||
temporaryDirectories.push(directory)
|
||||
const filePath = path.join(directory, 'navigation.json')
|
||||
await writeJsonAtomic(filePath, defaults)
|
||||
expect(JSON.parse(await readFile(filePath, 'utf8'))).toEqual(defaults)
|
||||
})
|
||||
|
||||
it('closes the handle and removes the temporary file when writing fails', async () => {
|
||||
const calls = []
|
||||
const failure = new Error('disk full')
|
||||
const operations = {
|
||||
mkdir: async () => undefined,
|
||||
open: async (temporaryPath) => ({
|
||||
writeFile: async () => { throw failure },
|
||||
sync: async () => undefined,
|
||||
close: async () => { calls.push('close') },
|
||||
temporaryPath,
|
||||
}),
|
||||
rename: async () => { calls.push('rename') },
|
||||
unlink: async (temporaryPath) => { calls.push(`unlink:${temporaryPath}`) },
|
||||
}
|
||||
await expect(writeJsonAtomic('/virtual/navigation.json', defaults, operations)).rejects.toBe(failure)
|
||||
expect(calls[0]).toBe('close')
|
||||
expect(calls[1]).toMatch(/^unlink:\/virtual\/navigation\.json\..+\.tmp$/)
|
||||
expect(calls).not.toContain('rename')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user