mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-10 12:46:15 -08:00
test(jassub): [PKG-JASSUB-01] freeze published contracts and vendor evidence
This commit is contained in:
1 parent
d44e81ce22
commit
2c73e2b105
20 files changed
+1949
-16
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { archiveFiles, ensureArchive, hash, readMember, refactorDir } from './releases.mjs'
|
||||
|
||||
export async function verifyJassubContract() {
|
||||
const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/jassub-release.json'), 'utf8'))
|
||||
const archives = new Map()
|
||||
for (const release of [baseline.release, ...baseline.previous]) {
|
||||
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}`)
|
||||
assert.deepEqual(JSON.parse(readMember(archive, 'package/package.json')), release.manifest)
|
||||
const core = release.historicalCore
|
||||
const coreSource = execFileSync('git', ['show', `${core.commit}:${core.file}`], { encoding: 'utf8' }).replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(coreSource), core.sha256LF)
|
||||
assert.equal(JSON.parse(coreSource).version, core.version)
|
||||
archives.set(release.version, archive)
|
||||
}
|
||||
const sources = new Map()
|
||||
for (const [file, expected] of Object.entries(baseline.source)) {
|
||||
const source = execFileSync('git', ['show', `${baseline.sourceCommit}:${file}`], { encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 }).replaceAll('\r\n', '\n')
|
||||
assert.equal(hash(source), expected, file)
|
||||
sources.set(file, source)
|
||||
}
|
||||
return { baseline, archives, sources }
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { Buffer } from 'node:buffer'
|
||||
import fs from 'node:fs'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Published and vendor provenance checks use the repository runner.
|
||||
import test from 'node:test'
|
||||
import { transformSync } from 'esbuild'
|
||||
import { verifyJassubContract } from './jassub-contract.mjs'
|
||||
import { archiveFiles, ensureArchive, hash, readMember } from './releases.mjs'
|
||||
|
||||
const contract = await verifyJassubContract()
|
||||
const releases = [contract.baseline.release, ...contract.baseline.previous]
|
||||
test('JASSUB freezes both actual releases, twelve members and nine Git text inputs', () => {
|
||||
assert.deepEqual(releases.map(item => item.version), ['1.1.0', '1.0.0'])
|
||||
assert.equal(releases.reduce((count, item) => count + Object.keys(item.files).length, 0), 12)
|
||||
assert.equal(contract.sources.size, 9)
|
||||
assert.deepEqual(releases.map(item => item.historicalCore.version), ['5.3.1', '5.3.1-beta.1'])
|
||||
for (const release of releases) assert.deepEqual(release.missingEntrypoints, {})
|
||||
})
|
||||
|
||||
test('JASSUB historical declarations require resource paths and incorrectly describe synchronous methods', () => {
|
||||
for (const release of releases) {
|
||||
const source = readMember(contract.archives.get(release.version), 'package/types/artplayer-plugin-jassub.d.ts').toString()
|
||||
for (const key of ['workerUrl', 'wasmUrl', 'modernWasmUrl']) assert.match(source, new RegExp(`${key}: string`))
|
||||
assert.match(source, /resize: \(force\?: boolean, width\?: number, height\?: number, top\?: number, left\?: number\) => Promise<void>/)
|
||||
assert.match(source, /setVideo: \(video: HTMLVideoElement\) => Promise<void>/)
|
||||
assert.match(source, /destroy: \(\) => Promise<void>/)
|
||||
assert.equal(source.match(/\[key: string\]: any/g).length, 2)
|
||||
assert.match(source, /export default artplayerPluginJassub/)
|
||||
}
|
||||
})
|
||||
|
||||
test('JASSUB published packages omit worker/WASM/font payloads while the demo uses independently hosted assets', () => {
|
||||
for (const release of releases)
|
||||
assert(!Object.keys(release.files).some(file => /\.(?:wasm|ttf|otf|woff2?)$|\/worker\//.test(file)))
|
||||
const demo = contract.sources.get('docs/assets/example/jassub.js')
|
||||
for (const resource of ['jassub-worker.js', 'jassub-worker.wasm', 'jassub-worker-modern.wasm', 'default.woff2']) assert(demo.includes(`/assets/jassub/${resource}`))
|
||||
for (const asset of contract.baseline.assets) {
|
||||
assert.equal(hash(fs.readFileSync(asset.file)), asset.sha256, asset.file)
|
||||
assert.equal(fs.statSync(asset.file).size, asset.bytes, asset.file)
|
||||
}
|
||||
})
|
||||
|
||||
test('JASSUB actual historical ESM artifacts expose only a lazy default factory', async () => {
|
||||
for (const release of releases) {
|
||||
const source = readMember(contract.archives.get(release.version), 'package/dist/artplayer-plugin-jassub.mjs').toString()
|
||||
const module = await import(`data:text/javascript;base64,${Buffer.from(source).toString('base64')}`)
|
||||
assert.deepEqual(Object.keys(module), ['default'])
|
||||
assert.equal(typeof module.default, 'function')
|
||||
assert.equal(typeof module.default({ workerUrl: '/worker.js' }), 'function')
|
||||
}
|
||||
})
|
||||
|
||||
test('JASSUB 1.8.8 comparison proves wrapper/worker/default font matches without claiming different WASM builds match', async () => {
|
||||
const upstream = JSON.parse(fs.readFileSync('refactor/baselines/jassub-vendor.json', 'utf8'))
|
||||
const archive = await ensureArchive(upstream)
|
||||
assert.deepEqual(archiveFiles(archive), Object.keys(upstream.files).sort())
|
||||
for (const [file, expected] of Object.entries(upstream.files)) assert.equal(hash(readMember(archive, file)), expected)
|
||||
const normalize = source => transformSync(source, { format: 'esm', target: 'es2020', legalComments: 'none' }).code
|
||||
assert.equal(normalize(contract.sources.get('packages/artplayer-plugin-jassub/src/jassub.es.js')), normalize(readMember(archive, 'package/dist/jassub.es.js').toString()))
|
||||
for (const file of ['packages/artplayer-plugin-jassub/worker/jassub-worker.js', 'docs/assets/jassub/jassub-worker.js'])
|
||||
assert.deepEqual(fs.readFileSync(file), readMember(archive, 'package/dist/jassub-worker.js'))
|
||||
assert.deepEqual(fs.readFileSync('docs/assets/jassub/default.woff2'), readMember(archive, 'package/dist/default.woff2'))
|
||||
for (const name of ['jassub-worker.wasm', 'jassub-worker-modern.wasm']) {
|
||||
const local = fs.readFileSync(`packages/artplayer-plugin-jassub/worker/${name}`)
|
||||
const reference = readMember(archive, `package/dist/${name}`)
|
||||
assert.deepEqual(local, fs.readFileSync(`docs/assets/jassub/${name}`))
|
||||
assert(!local.equals(reference))
|
||||
assert(WebAssembly.validate(local))
|
||||
assert(WebAssembly.validate(reference))
|
||||
}
|
||||
})
|
||||
|
||||
test('JASSUB font source clues cover every actual font without promoting embedded metadata to redistribution proof', () => {
|
||||
const metadata = JSON.parse(fs.readFileSync('refactor/baselines/jassub-font-metadata.json', 'utf8'))
|
||||
const files = contract.baseline.assets.filter(item => /\.(?:ttf|otf|woff2?)$/i.test(item.file))
|
||||
assert.equal(metadata.fonts.length, 12)
|
||||
assert.deepEqual(metadata.fonts.map(item => item.file).sort(), files.map(item => item.file).sort())
|
||||
for (const font of metadata.fonts) {
|
||||
assert.equal(hash(fs.readFileSync(font.file)), font.sha256)
|
||||
assert(font.names['1']?.length)
|
||||
}
|
||||
assert.match(metadata.scope, /not proof/)
|
||||
assert.deepEqual(metadata.tools, { fonttools: '4.60.1', brotli: '1.1.0' })
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Read font metadata only; embedded notices do not establish acquisition rights."""
|
||||
|
||||
import hashlib
|
||||
import importlib.metadata
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
|
||||
def inspect_fonts():
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
records = []
|
||||
for file in sorted((root / "docs/assets/jassub").rglob("*")):
|
||||
if file.suffix.lower() not in {".ttf", ".otf", ".woff", ".woff2"}:
|
||||
continue
|
||||
with TTFont(file, lazy=True) as font:
|
||||
names = {}
|
||||
for record in font["name"].names:
|
||||
if record.nameID in {0, 1, 2, 4, 5, 6, 8, 9, 11, 13, 14}:
|
||||
names.setdefault(str(record.nameID), set()).add(record.toUnicode())
|
||||
records.append({
|
||||
"file": file.relative_to(root).as_posix(),
|
||||
"bytes": file.stat().st_size,
|
||||
"sha256": hashlib.sha256(file.read_bytes()).hexdigest(),
|
||||
"names": {key: sorted(values) for key, values in names.items()},
|
||||
"fsType": font["OS/2"].fsType if "OS/2" in font else None,
|
||||
})
|
||||
return {
|
||||
"schemaVersion": 1,
|
||||
"task": "PKG-JASSUB-01",
|
||||
"tools": {name: importlib.metadata.version(name) for name in ["fonttools", "brotli"]},
|
||||
"scope": "Read-only embedded OpenType names and OS/2 fsType. These are source clues, not proof of original acquisition or redistribution authorization.",
|
||||
"fonts": records,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(json.dumps(inspect_fonts(), ensure_ascii=True, indent=2))
|
||||
Reference in new issue
Block a user