test(danmuku-mask): [PKG-MASK-01] freeze published contracts and model provenance

This commit is contained in:
Harvey Zhao committed 2026-09-13 17:36:11 +08:00
1 parent 8ec9fbe83a
commit 7d44bce06f
13 files changed
+1138 -13

No files matched your search

+107
View File
@@ -0,0 +1,107 @@
import assert from 'node:assert/strict'
import { execFileSync } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import lockfile from '@yarnpkg/lockfile'
import { archiveFiles, ensureArchive, hash, readMember, refactorDir } from './releases.mjs'
const workspace = path.dirname(refactorDir.replace(/[\\/]$/u, ''))
const prefix = 'packages/artplayer-plugin-danmuku-mask/'
function gitBytes(commit, file) {
return execFileSync('git', ['show', `${commit}:${file}`], { cwd: workspace, maxBuffer: 32 * 1024 * 1024 })
}
function gitText(commit, file) {
return gitBytes(commit, file).toString().replaceAll('\r\n', '\n')
}
export async function verifyDanmukuMaskContract() {
const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/danmuku-mask-release.json'), 'utf8'))
const registryBytes = fs.readFileSync(path.join(refactorDir, 'baselines/danmuku-mask-registry.json'))
assert.equal(hash(registryBytes), baseline.registrySnapshot.bodySha256)
const registry = JSON.parse(registryBytes)
assert.deepEqual(registry['dist-tags'], baseline.registrySnapshot.distTags)
assert.deepEqual(Object.keys(registry.versions).filter(version => /^\d+\.\d+\.\d+$/u.test(version)), baseline.registrySnapshot.stableVersions)
const archives = new Map()
for (const release of [baseline.release, ...baseline.previous]) {
const metadata = registry.versions[release.version]
assert.equal(metadata.dist.integrity, release.integrity)
assert.equal(metadata.dist.tarball, release.tarball)
assert.equal(metadata.gitHead, release.registryGitHead)
const archive = await ensureArchive(release)
assert.deepEqual(archiveFiles(archive), Object.keys(release.files).sort())
for (const [file, expected] of Object.entries(release.files))
assert.equal(hash(readMember(archive, file)), expected, `${release.version}/${file}`)
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
assert.deepEqual(manifest, release.manifest)
assert.equal(manifest.version, release.version)
assert.equal(manifest.name, release.name)
for (const [field, target] of Object.entries(release.entrypoints)) {
assert.equal(manifest[field] ?? null, target)
if (target)
assert(Object.hasOwn(release.files, `package/${target.replace(/^\.\//u, '')}`), `${release.version}/${field}`)
}
function targetExists(target) {
if (typeof target === 'string')
assert(Object.hasOwn(release.files, `package/${target.replace(/^\.\//u, '')}`))
else if (target && typeof target === 'object')
Object.values(target).forEach(targetExists)
}
targetExists(manifest.exports)
for (const association of [release.registrySourceManifest, release.historicalCore]) {
assert.equal(association.commit, release.registryGitHead)
const text = gitText(association.commit, association.file)
assert.equal(hash(text), association.sha256LF)
assert.equal(JSON.parse(text).version, association.version)
}
assert.equal(release.registrySourceManifest.versionMatchesRelease, release.registrySourceManifest.version === release.version)
for (const comparison of release.registrySourceComparison) {
const text = gitText(release.registryGitHead, comparison.file)
assert.equal(hash(text), comparison.sha256LF)
assert.equal(comparison.matchesArchiveBytes, hash(text) === release.files[`package/${comparison.file.slice(prefix.length)}`])
}
archives.set(release.version, archive)
}
assert.deepEqual([...archives.keys()].sort(), [...baseline.registrySnapshot.stableVersions].sort())
const sources = new Map()
for (const [file, expected] of Object.entries(baseline.source)) {
const text = gitText(baseline.sourceCommit, file)
assert.equal(hash(text), expected, file)
sources.set(file, text)
}
for (const file of baseline.observations.latestArchiveMatchesFrozenSourceFiles)
assert.equal(hash(sources.get(file)), baseline.release.files[`package/${file.slice(prefix.length)}`])
const locked = gitText(baseline.sdk.lock.commit, baseline.sdk.lock.file)
assert.equal(hash(locked), baseline.sdk.lock.sha256LF)
const parsed = lockfile.parse(locked)
assert.equal(parsed.type, 'success')
const manifest = JSON.parse(sources.get(`${prefix}package.json`))
for (const dependency of baseline.sdk.dependencies) {
assert.equal(manifest.dependencies[dependency.name], dependency.range)
const entry = parsed.object[dependency.key]
for (const field of ['version', 'resolved', 'integrity'])
assert.equal(entry[field], dependency[field])
const installed = fs.readFileSync(path.join(workspace, dependency.installedManifest.file))
assert.equal(hash(installed), dependency.installedManifest.sha256)
assert.equal(JSON.parse(installed).version, dependency.version)
}
for (const [file, digest] of Object.entries(baseline.sdk.adapterFiles))
assert.equal(hash(fs.readFileSync(path.join(workspace, file))), digest, file)
for (const asset of baseline.sdk.assets) {
const bytes = gitBytes(baseline.sourceCommit, asset.file)
assert.equal(bytes.length, asset.bytes)
assert.equal(hash(bytes), asset.sha256)
const installed = fs.readFileSync(path.join(workspace, asset.installedFile))
assert.equal(hash(installed), asset.installedSha256)
assert.equal(asset.matchesInstalled, asset.sha256 === asset.installedSha256)
}
assert(sources.get(`${prefix}src/index.js`).includes(baseline.sdk.defaultSolutionPath))
assert(sources.get('docs/assets/example/danmuku.mask.js').includes(baseline.sdk.demoSolutionPath))
return { baseline, archives, sources }
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
const { baseline, archives, sources } = await verifyDanmukuMaskContract()
console.log(JSON.stringify({ sourceCommit: baseline.sourceCommit, sourceFiles: sources.size, releases: [...archives.keys()], dependencies: baseline.sdk.dependencies.map(item => `${item.name}@${item.version}`), existingAssets: baseline.sdk.assets.length, validation: 'Archive SHA512/SHA256, complete entrypoints, Git associations, frozen Yarn versions, installed SDK source and existing asset bytes; no model execution' }, null, 2))
}
@@ -0,0 +1,170 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
// eslint-disable-next-line test/no-import-node-test -- Frozen package contract verification.
import test from 'node:test'
import vm from 'node:vm'
import { transform } from 'esbuild'
import { verifyDanmukuMaskContract } from './danmuku-mask-contract.mjs'
import { readMember } from './releases.mjs'
const contract = await verifyDanmukuMaskContract()
const source = contract.sources.get('packages/artplayer-plugin-danmuku-mask/src/index.js')
async function controlledSource(option) {
const frames = new Map()
const configs = []
const calls = []
const listeners = new Map()
const style = {}
const pixels = new Uint8ClampedArray([255, 255, 255, 255, 250, 255, 255, 255])
let id = 0
const canvas = {
width: 0,
height: 0,
getContext: () => ({ getImageData: () => ({ data: pixels }), putImageData: value => calls.push(['pixels', [...value.data]]) }),
toDataURL: () => 'data:image/png;base64,controlled',
}
const segmenter = { segmentPeople: async () => [{}] }
const body = {
SupportedModels: { MediaPipeSelfieSegmentation: 'selfie' },
createSegmenter: async (model, config) => {
configs.push({ model, config: { ...config } })
return segmenter
},
toBinaryMask: async (...args) => {
calls.push(['mask', ...args])
return {}
},
drawMask: async (...args) => { calls.push(['draw', args[3], args[4]]) },
}
const tf = { setBackend: async value => calls.push(['backend', value]) }
const module = { exports: {} }
vm.runInNewContext((await transform(source, { format: 'cjs', target: 'es2020' })).code, {
module,
exports: module.exports,
require(name) {
if (name === '@tensorflow-models/body-segmentation')
return body
if (name === '@tensorflow/tfjs-core')
return tf
assert(['@tensorflow/tfjs-backend-webgl', '@tensorflow/tfjs-backend-cpu'].includes(name))
return {}
},
document: { createElement: (tag) => {
assert.equal(tag, 'canvas')
return canvas
} },
requestAnimationFrame: (callback) => {
frames.set(++id, callback)
return id
},
cancelAnimationFrame: id => frames.delete(id),
console,
}, { timeout: 1000 })
const art = { template: { $video: { paused: false, ended: false, videoWidth: 2, videoHeight: 1 }, $danmuku: { style } }, on: (name, callback) => listeners.set(name, callback) }
const result = module.exports.default(option)(art)
return { result, listeners, frames, configs, calls, style }
}
test('Danmuku Mask freezes all published stable archive members and their actual Git associations', () => {
assert.deepEqual([...contract.archives.keys()].sort(), ['1.0.0', '1.1.0'])
for (const release of [contract.baseline.release, ...contract.baseline.previous]) {
assert.equal(Object.keys(release.files).length, 6)
assert.deepEqual(release.missingEntrypoints, [])
}
assert.equal(contract.baseline.release.registrySourceManifest.version, '1.0.1')
assert.equal(contract.baseline.release.registrySourceManifest.versionMatchesRelease, false)
assert.equal(contract.baseline.observations.latestArchiveMatchesFrozenSourceFiles.length, 6)
})
test('Both published declarations retain optional options, synchronous registration, async start and void stop', () => {
const declaration = contract.sources.get('packages/artplayer-plugin-danmuku-mask/types/artplayer-plugin-danmuku-mask.d.ts')
for (const archive of contract.archives.values())
assert.equal(readMember(archive, 'package/types/artplayer-plugin-danmuku-mask.d.ts').toString(), declaration)
assert(declaration.includes('(option?: Option) => (art: Artplayer) => Result'))
assert(declaration.includes('start: () => Promise<void>'))
assert(declaration.includes('stop: () => void'))
assert(declaration.includes('export default artplayerPluginDanmukuMask'))
})
test('Actual main and legacy exports preserve historical CommonJS and browser global forms without starting a model', () => {
for (const [version, archive] of contract.archives) {
for (const suffix of ['js', 'legacy.js']) {
const bytes = readMember(archive, `package/dist/artplayer-plugin-danmuku-mask.${suffix}`).toString()
for (const form of ['cjs', 'global']) {
const module = { exports: {} }
const environment = {
console,
fetch() { throw new Error('Network is forbidden in this export probe') },
}
environment.global = environment
environment.self = environment
if (form === 'cjs')
Object.assign(environment, { module, exports: module.exports })
else environment.window = environment
vm.runInNewContext(bytes, environment, { timeout: 5000 })
const value = form === 'cjs' ? module.exports : environment.artplayerPluginDanmukuMask
const oldCommonJs = version === '1.0.0' && form === 'cjs'
assert.equal(typeof value, oldCommonJs ? 'object' : 'function')
assert.equal(typeof value.default, oldCommonJs ? 'function' : 'undefined')
const factory = oldCommonJs ? value.default : value
const events = []
const style = {}
const result = factory()({
template: { $video: {}, $danmuku: { style } },
on: name => events.push(name),
})
assert.equal(result.name, 'artplayerPluginDanmukuMask')
assert.equal(typeof result.start, 'function')
assert.equal(typeof result.stop, 'function')
assert.equal(result.stop(), undefined)
assert.equal(style.maskImage, 'none')
assert.deepEqual(events, ['ready', 'destroy'])
}
}
}
})
test('Frozen owned source keeps its defaults, ready/destroy hooks and alpha mask pixel boundary without a real SDK', async () => {
const env = await controlledSource()
assert.equal(env.result.name, 'artplayerPluginDanmukuMask')
assert.deepEqual([...env.listeners.keys()], ['ready', 'destroy'])
const pending = env.result.start()
assert.equal(typeof pending.then, 'function')
await pending
await new Promise(resolve => setImmediate(resolve))
assert.deepEqual(env.configs, [{ model: 'selfie', config: { runtime: 'mediapipe', modelType: 'general', solutionPath: contract.baseline.sdk.defaultSolutionPath, modelSelection: 1, smoothSegmentation: true, minDetectionConfidence: 0.5, minTrackingConfidence: 0.5, selfieMode: false } }])
assert.deepEqual(env.style, { maskMode: 'alpha', maskSize: 'contain', maskRepeat: 'no-repeat', backgroundSize: 'contain', backgroundRepeat: 'no-repeat', maskImage: 'url(data:image/png;base64,controlled)' })
assert.deepEqual(env.calls.find(call => call[0] === 'draw'), ['draw', 1, 3])
assert.deepEqual(env.calls.find(call => call[0] === 'pixels'), ['pixels', [255, 255, 255, 0, 250, 255, 255, 255]])
assert.equal(env.frames.size, 1)
assert.equal(env.result.stop(), undefined)
assert.equal(env.frames.size, 0)
assert.equal(env.style.maskImage, 'none')
})
test('Frozen zero numeric options use historical OR defaults while false smoothSegmentation is retained', async () => {
const env = await controlledSource({ solutionPath: '/local-model', modelSelection: 0, smoothSegmentation: false, minDetectionConfidence: 0, minTrackingConfidence: 0, foregroundThreshold: 0, opacity: 0, maskBlurAmount: 0, drawContour: true, selfieMode: true })
await env.result.start()
await new Promise(resolve => setImmediate(resolve))
assert.deepEqual(env.configs[0].config, { runtime: 'mediapipe', modelType: 'general', solutionPath: '/local-model', modelSelection: 1, smoothSegmentation: false, minDetectionConfidence: 0.5, minTrackingConfidence: 0.5, selfieMode: true })
assert.deepEqual(env.calls.find(call => call[0] === 'draw'), ['draw', 1, 3])
const mask = env.calls.find(call => call[0] === 'mask')
assert.equal(mask[4], true)
assert.equal(mask[5], 0.5)
env.result.stop()
})
test('SDK-08 distinguishes the frozen Yarn adapter and local resource bytes from unversioned CDN resolution', () => {
assert.deepEqual(contract.baseline.sdk.dependencies.map(item => item.version), ['0.1.1675465747', '1.0.2', '4.22.0', '4.22.0', '4.22.0', '4.22.0'])
assert.equal(contract.baseline.sdk.assets.length, 12)
assert(contract.baseline.sdk.assets.every(asset => asset.matchesInstalled))
const adapter = fs.readFileSync('node_modules/@tensorflow-models/body-segmentation/dist/selfie_segmentation_mediapipe/segmenter.js', 'utf8')
assert.match(adapter, /switch \(config\.modelType\)/u)
assert.match(adapter, /case 'general':[\s\S]*?modelSelection = 0;/u)
for (const ignored of ['modelSelection', 'smoothSegmentation', 'minDetectionConfidence', 'minTrackingConfidence', 'selfieMode'])
assert.equal(adapter.includes(`config.${ignored}`), false, `Do not claim ${ignored} is consumed by this adapter`)
assert(adapter.includes('return solutionPath + "/" + path;'))
assert(adapter.includes('this.selfieSegmentationSolution.close();'))
assert.equal(source.includes('.dispose('), false, 'The historical plugin does not dispose its segmenter')
})