mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
224 lines
7.7 KiB
JavaScript
224 lines
7.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
import {
|
|
appendWindowsRuntimeGitHubOutputs,
|
|
readWindowsRuntimePin,
|
|
serializeWindowsRuntimePin,
|
|
validateWindowsRuntimePin,
|
|
WINDOWS_RUNTIME_LICENSE_CLAIM,
|
|
WINDOWS_RUNTIME_PIN_PATH,
|
|
} from './windows-runtime-pin.mjs';
|
|
import {
|
|
pinFromUpstreamRelease,
|
|
refreshWindowsRuntimePin,
|
|
runtimePinAgeDays,
|
|
selectNewestWindowsRuntimePin,
|
|
WINDOWS_RUNTIME_REFRESH_AFTER_DAYS,
|
|
} from './update-windows-runtime-pin.mjs';
|
|
|
|
const CURRENT_PIN = readWindowsRuntimePin();
|
|
|
|
function releaseFixture({
|
|
date = '2026-08-28',
|
|
commit = 'e8673660ab123456789012345678901234567890',
|
|
digest = '470437b5dc9f8c74092fdfab668e89bedf7b1a6385a53ffadf241a6a7a4c6ffb',
|
|
runId = '33215046953',
|
|
publishedAt = `${date}T22:32:29Z`,
|
|
} = {}) {
|
|
const compactDate = date.replaceAll('-', '');
|
|
const shortCommit = commit.slice(0, 10);
|
|
const tag = `${date}-${shortCommit}`;
|
|
const name = `mpv-dev-lgpl-x86_64-${compactDate}-git-${shortCommit}.7z`;
|
|
return {
|
|
draft: false,
|
|
prerelease: false,
|
|
tag_name: tag,
|
|
published_at: publishedAt,
|
|
body: [
|
|
`MPV Git commit: https://github.com/mpv-player/mpv/commit/${commit}`,
|
|
`Build Details: https://github.com/zhongfly/mpv-winbuild/actions/runs/${runId}`,
|
|
].join('\n'),
|
|
assets: [
|
|
{
|
|
name,
|
|
digest: `sha256:${digest}`,
|
|
browser_download_url: `https://github.com/zhongfly/mpv-winbuild/releases/download/${tag}/${name}`,
|
|
},
|
|
{
|
|
name: `mpv-dev-lgpl-x86_64-v3-${compactDate}-git-${shortCommit}.7z`,
|
|
digest: `sha256:${'a'.repeat(64)}`,
|
|
browser_download_url: 'https://example.invalid/v3.7z',
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function response({ ok = true, status = 200, json } = {}) {
|
|
return {
|
|
ok,
|
|
status,
|
|
statusText: ok ? 'OK' : 'Not Found',
|
|
json: async () => json,
|
|
};
|
|
}
|
|
|
|
function createPinFixture() {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'impv-win-pin-'));
|
|
const pinPath = path.join(root, 'windows-runtime-pin.json');
|
|
fs.writeFileSync(pinPath, serializeWindowsRuntimePin(CURRENT_PIN));
|
|
return { root, pinPath };
|
|
}
|
|
|
|
test('checked-in Windows runtime pin is internally consistent', () => {
|
|
assert.equal(WINDOWS_RUNTIME_PIN_PATH.endsWith('.json'), true);
|
|
assert.match(CURRENT_PIN.asset.sha256, /^[a-f0-9]{64}$/);
|
|
assert.match(CURRENT_PIN.asset.name, /^mpv-dev-lgpl-x86_64-/);
|
|
assert.doesNotMatch(CURRENT_PIN.asset.name, /-v3-/);
|
|
assert.equal(
|
|
CURRENT_PIN.upstream.licenseClaim,
|
|
WINDOWS_RUNTIME_LICENSE_CLAIM
|
|
);
|
|
});
|
|
|
|
test('pin validation rejects a softened license-verification statement', () => {
|
|
const invalid = structuredClone(CURRENT_PIN);
|
|
invalid.upstream.licenseClaim = 'Verified LGPL runtime.';
|
|
assert.throws(
|
|
() => validateWindowsRuntimePin(invalid),
|
|
/limited verification statement/
|
|
);
|
|
});
|
|
|
|
test('upstream release selection excludes the v3 archive and keeps evidence', () => {
|
|
const older = releaseFixture({
|
|
date: '2026-08-27',
|
|
commit: '182fa6ca49123456789012345678901234567890',
|
|
publishedAt: '2026-08-27T12:41:23Z',
|
|
});
|
|
const latest = releaseFixture();
|
|
const pin = selectNewestWindowsRuntimePin([older, latest]);
|
|
|
|
assert.equal(pin.releaseTag, latest.tag_name);
|
|
assert.equal(pin.asset.name, latest.assets[0].name);
|
|
assert.equal(pin.asset.sha256, latest.assets[0].digest.slice(7));
|
|
assert.equal(pin.upstream.mpvCommit, latest.body.match(/[a-f0-9]{40}/)[0]);
|
|
assert.doesNotMatch(pin.asset.name, /-v3-/);
|
|
});
|
|
|
|
test('upstream release must provide GitHub digest and build evidence', () => {
|
|
const missingDigest = releaseFixture();
|
|
delete missingDigest.assets[0].digest;
|
|
assert.throws(
|
|
() => pinFromUpstreamRelease(missingDigest),
|
|
/must expose a GitHub SHA-256 digest/
|
|
);
|
|
|
|
const missingEvidence = releaseFixture();
|
|
missingEvidence.body = '';
|
|
assert.throws(
|
|
() => pinFromUpstreamRelease(missingEvidence),
|
|
/lacks the expected mpv commit or build-run evidence/
|
|
);
|
|
});
|
|
|
|
test('young available pin does not query releases or rewrite the file', async () => {
|
|
const fixture = createPinFixture();
|
|
let requestCount = 0;
|
|
try {
|
|
const result = await refreshWindowsRuntimePin({
|
|
pinPath: fixture.pinPath,
|
|
now: new Date('2026-08-29T00:00:00Z'),
|
|
fetchImpl: async () => {
|
|
requestCount += 1;
|
|
return response();
|
|
},
|
|
});
|
|
assert.equal(result.changed, false);
|
|
assert.equal(result.reason, 'current');
|
|
assert.equal(requestCount, 1);
|
|
assert.equal(
|
|
fs.readFileSync(fixture.pinPath, 'utf8'),
|
|
serializeWindowsRuntimePin(CURRENT_PIN)
|
|
);
|
|
} finally {
|
|
fs.rmSync(fixture.root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('unavailable pin rotates to the newest downloadable release', async () => {
|
|
const fixture = createPinFixture();
|
|
const latest = releaseFixture();
|
|
const requests = [];
|
|
try {
|
|
const result = await refreshWindowsRuntimePin({
|
|
pinPath: fixture.pinPath,
|
|
now: new Date('2026-08-29T00:00:00Z'),
|
|
fetchImpl: async (url, options = {}) => {
|
|
requests.push([url, options.method ?? 'GET']);
|
|
if (url === CURRENT_PIN.asset.url) {
|
|
return response({ ok: false, status: 404 });
|
|
}
|
|
if (url.startsWith('https://api.github.com/')) {
|
|
return response({ json: [latest] });
|
|
}
|
|
return response();
|
|
},
|
|
});
|
|
assert.equal(result.changed, true);
|
|
assert.equal(result.reason, 'unavailable');
|
|
assert.equal(
|
|
readWindowsRuntimePin(fixture.pinPath).releaseTag,
|
|
latest.tag_name
|
|
);
|
|
assert.deepEqual(
|
|
requests.map((request) => request[1]),
|
|
['HEAD', 'GET', 'HEAD']
|
|
);
|
|
} finally {
|
|
fs.rmSync(fixture.root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('age threshold rotates an available pin before upstream retention', async () => {
|
|
const fixture = createPinFixture();
|
|
const latest = releaseFixture();
|
|
try {
|
|
const result = await refreshWindowsRuntimePin({
|
|
pinPath: fixture.pinPath,
|
|
now: new Date('2026-09-05T13:00:00Z'),
|
|
fetchImpl: async (url) =>
|
|
url.startsWith('https://api.github.com/')
|
|
? response({ json: [latest] })
|
|
: response(),
|
|
});
|
|
assert.equal(
|
|
runtimePinAgeDays(CURRENT_PIN, new Date('2026-09-05T13:00:00Z')) >=
|
|
WINDOWS_RUNTIME_REFRESH_AFTER_DAYS,
|
|
true
|
|
);
|
|
assert.equal(result.changed, true);
|
|
assert.equal(result.reason, 'age-threshold');
|
|
} finally {
|
|
fs.rmSync(fixture.root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('GitHub output exposes only the validated checked-in pin', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'impv-win-output-'));
|
|
const outputPath = path.join(root, 'output');
|
|
try {
|
|
appendWindowsRuntimeGitHubOutputs(CURRENT_PIN, outputPath);
|
|
const output = fs.readFileSync(outputPath, 'utf8');
|
|
assert.match(output, new RegExp(`sha256=${CURRENT_PIN.asset.sha256}`));
|
|
assert.match(
|
|
output,
|
|
new RegExp(`release-tag=${CURRENT_PIN.releaseTag}`)
|
|
);
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|