15 lines
3.2 KiB
JavaScript
15 lines
3.2 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import fs from 'node:fs/promises'
|
|
import {createHash} from 'node:crypto'
|
|
import {prepareImport,validateBrand,sqlPackage,SOURCE_PACK_SHA256,SOURCE_MIGRATION_SHA256,SOURCE_VERSION} from './prepare-native-brand-import.mjs'
|
|
const bytes=await fs.readFile(new URL('../lib/detail-generator/knowledge/enterprise-20261013.json',import.meta.url))
|
|
const migration=await fs.readFile(new URL('../prisma/migrations/20261013_detail_design_knowledge/migration.sql',import.meta.url)).catch(error=>{if(error.code==='ENOENT')return undefined;throw error})
|
|
const pack=JSON.parse(bytes)
|
|
test('accepted package retains exact known hash; historical SQL is verified when present',()=>{assert.equal(createHash('sha256').update(bytes).digest('hex'),SOURCE_PACK_SHA256);if(migration)assert.equal(createHash('sha256').update(migration).digest('hex'),SOURCE_MIGRATION_SHA256);assert.equal(pack.version,SOURCE_VERSION)})
|
|
test('all four migrated profiles keep source and all six visual rules unchanged',()=>{assert.equal(pack.brands.length,4);for(const p of pack.brands){const before=JSON.stringify(p);assert.equal(validateBrand(p),p);assert.equal(JSON.stringify(p),before)}})
|
|
test('missing provenance or fabricated rules are refused',()=>{const p=structuredClone(pack.brands[0]);p.source.files[0].sha256='0'.repeat(64);assert.throws(()=>validateBrand(p),/哈希/);const q=structuredClone(pack.brands[1]);delete q.visualRules.lighting;assert.throws(()=>validateBrand(q),/visualRules/);const r=structuredClone(pack.brands[2]);r.name='朴美';assert.throws(()=>validateBrand(r),/四个/)})
|
|
test('read-only dry-run cannot write and apply only inserts protected rows',()=>{const rows=pack.brands.map((profile,i)=>({id:'test-brand-'+i,name:profile.name,profile,revision:1}));const sql=sqlPackage(rows,'a'.repeat(64));assert.match(sql.dryRun,/READ ONLY/);assert.doesNotMatch(sql.dryRun,/\b(?:INSERT|UPDATE|DELETE|CREATE|ALTER|TRUNCATE)\s/i);assert.match(sql.apply,/LOCK TABLE public\.detail_generator_brand_profiles/);assert.match(sql.apply,/Brand already exists; refusing overwrite/);assert.doesNotMatch(sql.apply,/ON CONFLICT|\bUPDATE\s|CREATE TABLE|ALTER TABLE/i);assert.match(sql.apply,/INSERT INTO public\.detail_generator_brand_profiles/);assert.match(sql.verify,/p\.profile=d\.profile/)})
|
|
test('rollback requires original transaction, exact payload, revision and no references',()=>{const sql=sqlPackage(pack.brands.map((profile,i)=>({id:'test-brand-'+i,name:profile.name,profile,revision:1})),'b'.repeat(64)).rollback;for(const token of ['import_xmin','p.revision=1','p.profile=d.profile','jsonb_path_exists','refusing rollback','resource_id','SHARE MODE'])assert.ok(sql.includes(token),token);assert.doesNotMatch(sql,/CASCADE|DROP TABLE|TRUNCATE|UPDATE public\./i)})
|
|
test('changed package or missing original files cannot prepare an import',()=>{assert.throws(()=>prepareImport({packBytes:Buffer.from('{}'),migrationBytes:migration,originals:{}}),/知识包/);assert.throws(()=>prepareImport({packBytes:bytes,migrationBytes:migration,originals:{}}),/原文哈希/);assert.throws(()=>prepareImport({packBytes:bytes,migrationBytes:migration,originals:{},batchId:'not-a-uuid'}),/UUID/)})
|