mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-08-06 07:21:33 -07:00
205 lines
5.7 KiB
TypeScript
205 lines
5.7 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import { buildRawChangelogUrl, createChangelogSource } from './changelog-source';
|
|
import { buildChangelogSnapshot } from './changelog-snapshot';
|
|
|
|
const REMOTE = `# Changelog
|
|
|
|
## v0.20.0 (2026-09-01)
|
|
|
|
### Added
|
|
- Remote only entry.
|
|
|
|
## v0.19.2 (2026-08-04)
|
|
|
|
### Fixed
|
|
- Installed entry.
|
|
`;
|
|
|
|
const BUNDLED = `# Changelog
|
|
|
|
## v0.19.2 (2026-08-04)
|
|
|
|
### Fixed
|
|
- Installed entry.
|
|
`;
|
|
|
|
function createDeps(overrides: Partial<Parameters<typeof createChangelogSource>[0]> = {}) {
|
|
return {
|
|
fetchLatestReleaseTag: async () => 'v0.20.0',
|
|
fetchText: async () => REMOTE,
|
|
readBundledChangelog: () => BUNDLED,
|
|
getInstalledVersion: () => '0.19.2',
|
|
now: () => 1_000,
|
|
logWarn: () => {},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('changelog source reads the changelog at the latest release tag', async () => {
|
|
const urls: string[] = [];
|
|
const source = createChangelogSource(
|
|
createDeps({
|
|
fetchText: async (url: string) => {
|
|
urls.push(url);
|
|
return REMOTE;
|
|
},
|
|
}),
|
|
);
|
|
|
|
const snapshot = await source.getSnapshot();
|
|
|
|
assert.deepEqual(urls, [
|
|
'https://raw.githubusercontent.com/ksyasuda/SubMiner/v0.20.0/CHANGELOG.md',
|
|
]);
|
|
assert.equal(snapshot.source, 'remote');
|
|
assert.equal(snapshot.releaseTag, 'v0.20.0');
|
|
assert.equal(snapshot.latestVersion, '0.20.0');
|
|
assert.equal(snapshot.installedVersion, '0.19.2');
|
|
assert.equal(snapshot.expandedGroupKey, '0.20');
|
|
});
|
|
|
|
test('changelog source falls back to the default branch when no release tag resolves', async () => {
|
|
const urls: string[] = [];
|
|
const source = createChangelogSource(
|
|
createDeps({
|
|
fetchLatestReleaseTag: async () => null,
|
|
fetchText: async (url: string) => {
|
|
urls.push(url);
|
|
return REMOTE;
|
|
},
|
|
}),
|
|
);
|
|
|
|
const snapshot = await source.getSnapshot();
|
|
|
|
assert.deepEqual(urls, ['https://raw.githubusercontent.com/ksyasuda/SubMiner/main/CHANGELOG.md']);
|
|
assert.equal(snapshot.source, 'remote');
|
|
assert.equal(snapshot.releaseTag, undefined);
|
|
});
|
|
|
|
test('changelog source falls back to the bundled changelog when the download fails', async () => {
|
|
const warnings: string[] = [];
|
|
const source = createChangelogSource(
|
|
createDeps({
|
|
fetchText: async () => {
|
|
throw new Error('offline');
|
|
},
|
|
logWarn: (message: string) => warnings.push(message),
|
|
}),
|
|
);
|
|
|
|
const snapshot = await source.getSnapshot();
|
|
|
|
assert.equal(snapshot.source, 'bundled');
|
|
assert.match(snapshot.warning ?? '', /offline/);
|
|
assert.equal(snapshot.latestVersion, '0.19.2');
|
|
assert.equal(warnings.length, 1);
|
|
});
|
|
|
|
test('changelog source reports an error when no changelog can be loaded', async () => {
|
|
const source = createChangelogSource(
|
|
createDeps({
|
|
fetchText: async () => {
|
|
throw new Error('offline');
|
|
},
|
|
readBundledChangelog: () => null,
|
|
}),
|
|
);
|
|
|
|
const snapshot = await source.getSnapshot();
|
|
|
|
assert.deepEqual(snapshot.entries, []);
|
|
assert.match(snapshot.error ?? '', /offline/);
|
|
// Nothing is rendered, so the message must not promise a bundled changelog.
|
|
assert.match(snapshot.error ?? '', /^Changelog unavailable: /);
|
|
assert.doesNotMatch(snapshot.error ?? '', /Showing the bundled changelog/);
|
|
assert.equal(snapshot.installedVersion, '0.19.2');
|
|
});
|
|
|
|
test('changelog source caches remote results and refreshes on demand', async () => {
|
|
let fetches = 0;
|
|
let clock = 0;
|
|
const source = createChangelogSource(
|
|
createDeps({
|
|
now: () => clock,
|
|
fetchText: async () => {
|
|
fetches += 1;
|
|
return REMOTE;
|
|
},
|
|
}),
|
|
);
|
|
|
|
await source.getSnapshot();
|
|
await source.getSnapshot();
|
|
assert.equal(fetches, 1);
|
|
|
|
await source.getSnapshot({ refresh: true });
|
|
assert.equal(fetches, 2);
|
|
|
|
clock = 11 * 60 * 1000;
|
|
await source.getSnapshot();
|
|
assert.equal(fetches, 3);
|
|
});
|
|
|
|
test('changelog source retries the network after a bundled fallback', async () => {
|
|
let fetches = 0;
|
|
const source = createChangelogSource(
|
|
createDeps({
|
|
fetchText: async () => {
|
|
fetches += 1;
|
|
throw new Error('offline');
|
|
},
|
|
}),
|
|
);
|
|
|
|
await source.getSnapshot();
|
|
await source.getSnapshot();
|
|
|
|
assert.equal(fetches, 2);
|
|
});
|
|
|
|
test('changelog source treats an empty remote changelog as a failure', async () => {
|
|
const source = createChangelogSource(createDeps({ fetchText: async () => ' ' }));
|
|
|
|
const snapshot = await source.getSnapshot();
|
|
|
|
assert.equal(snapshot.source, 'bundled');
|
|
});
|
|
|
|
test('changelog source falls back when the remote body parses to no releases', () => {
|
|
const warnings: string[] = [];
|
|
const source = createChangelogSource(
|
|
createDeps({
|
|
// A 200 that is not a changelog, e.g. a redirect landing page.
|
|
fetchText: async () => '<!doctype html><html><body>Moved</body></html>',
|
|
logWarn: (message: string) => warnings.push(message),
|
|
}),
|
|
);
|
|
|
|
return source.getSnapshot().then((snapshot) => {
|
|
assert.equal(snapshot.source, 'bundled');
|
|
assert.equal(snapshot.entries.length, 1);
|
|
assert.match(snapshot.warning ?? '', /no releases/);
|
|
assert.equal(warnings.length, 1);
|
|
});
|
|
});
|
|
|
|
test('raw changelog urls encode the release ref', () => {
|
|
assert.equal(
|
|
buildRawChangelogUrl('v1.0.0', 'owner', 'repo'),
|
|
'https://raw.githubusercontent.com/owner/repo/v1.0.0/CHANGELOG.md',
|
|
);
|
|
});
|
|
|
|
test('snapshot expansion uses the newest version even when file order is unsorted', () => {
|
|
const snapshot = buildChangelogSnapshot(
|
|
'## v0.18.0 (2026-01-01)\n\n### Fixed\n- Old.\n\n## v0.19.0 (2026-02-01)\n\n### Fixed\n- New.\n',
|
|
{ installedVersion: '0.18.0', source: 'bundled' },
|
|
);
|
|
|
|
assert.equal(snapshot.latestVersion, '0.19.0');
|
|
assert.equal(snapshot.expandedGroupKey, '0.19');
|
|
});
|