get_subscribe/worker/sub.test.mjs
2026-09-21 02:10:43 +00:00

96 lines
5.0 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import worker, { refreshAll } from './sub.mjs';
const commit = 'a'.repeat(40);
const clash = 'proxies:\n - {name: test, type: ss, server: example.com, port: 443}\n';
const report = JSON.stringify({ checked_at: '2026-09-08T00:00:00Z', method: 'TCP connect', counts: {
reachable: { clash: 1, v2ray: 1 }, unreachable: { clash: 0, v2ray: 0 }, untested: { clash: 0, v2ray: 0 },
} });
function storage() {
const objects = new Map();
return { objects, SUBSCRIBE_BUCKET: {
async head(key) { return objects.get(key) || null; },
async get(key) {
const item = objects.get(key);
return item ? { ...item, body: new Response(item.text).body, httpEtag: '"etag"' } : null;
},
async put(key, text, options) { objects.set(key, { text, ...options }); },
} };
}
function upstream(url) {
if (url.startsWith('https://api.github.com/')) return Response.json({ object: { sha: commit } });
assert.ok(url.startsWith(`https://raw.githubusercontent.com/ermaozi/get_subscribe/${commit}/subscribe/`));
if (url.endsWith('health.json')) return new Response(report);
const empty = /\/(unreachable|untested)\//.test(url);
return new Response(url.endsWith('.yml') ? (empty ? 'proxies: []\n' : clash) : (empty ? '' : 'vmess://test\n'));
}
test('syncs one commit, all routes serve R2, unchanged revision does not download again', async t => {
const env = storage();
const fetch = t.mock.method(globalThis, 'fetch', async url => upstream(url));
const result = await refreshAll(env);
assert.equal(result.files, 9);
assert.equal(env.objects.size, 9);
assert.equal(fetch.mock.callCount(), 10);
for (const group of ['', 'reachable/', 'unreachable/', 'untested/']) {
for (const kind of ['clash', 'v2ray']) {
const response = await worker.fetch(new Request(`https://www.ermao.net/sub/${group}${kind}/ermao.net`), env);
assert.equal(response.status, 200);
assert.equal(response.headers.get('X-Git-Commit'), commit);
const title = Buffer.from(response.headers.get('Profile-Title').slice(7), 'base64').toString('utf-8');
assert.match(title, /^ermao.net · /);
assert.equal(response.headers.get('Profile-Web-Page-Url'), 'https://www.ermao.net/');
assert.match(response.headers.get('Content-Disposition'), /ermao\.net-/);
await response.text();
}
}
const health = await worker.fetch(new Request('https://www.ermao.net/sub/health.json'), env);
assert.deepEqual(await health.json(), JSON.parse(report));
assert.equal((await refreshAll(env)).unchanged, true);
assert.equal(fetch.mock.callCount(), 11);
});
test('invalid upstream file leaves existing objects unchanged; reads work during outage', async t => {
const env = storage();
env.objects.set('files/subscribe/clash.yml', { text: clash, customMetadata: {} });
t.mock.method(globalThis, 'fetch', async url => url.endsWith('health.json') ? new Response('<html>error</html>') : upstream(url));
await assert.rejects(refreshAll(env));
assert.equal(env.objects.size, 1);
assert.equal(env.objects.get('files/subscribe/clash.yml').text, clash);
const response = await worker.fetch(new Request('https://www.ermao.net/sub/clash/ermao.net'), env);
assert.equal(response.status, 200);
assert.equal(await response.text(), clash);
});
test('rejects invalid commits, excessive responses, unknown paths and unsupported methods', async t => {
const env = storage();
const fetch = t.mock.method(globalThis, 'fetch', async () => Response.json({ object: { sha: '../other' } }));
await assert.rejects(refreshAll(env), /Invalid upstream commit/);
fetch.mock.mockImplementation(async url => url.startsWith('https://api.github.com/')
? upstream(url) : new Response('x'.repeat(4 * 1024 * 1024 + 1)));
await assert.rejects(refreshAll(env), /size limit/);
assert.equal(env.objects.size, 0);
assert.equal((await worker.fetch(new Request('https://example.com/sub/nope'), env)).status, 404);
assert.equal((await worker.fetch(new Request('https://example.com/sub/refresh/all', { method: 'POST' }), env)).status, 405);
});
test('preserves refresh endpoints, HEAD, ETag and scheduled handler', async t => {
const env = storage();
t.mock.method(globalThis, 'fetch', async url => upstream(url));
const refresh = await worker.fetch(new Request('https://example.com/sub/refresh/all'), env);
assert.equal(refresh.status, 200);
assert.equal((await refresh.json()).commit, commit);
const head = await worker.fetch(new Request('https://example.com/sub/clash/ermao.net', { method: 'HEAD' }), env);
assert.equal(await head.text(), '');
const cached = await worker.fetch(new Request('https://example.com/sub/clash/ermao.net', { headers: { 'If-None-Match': '"etag"' } }), env);
assert.equal(cached.status, 304);
const pending = [];
await worker.scheduled({}, env, { waitUntil(promise) { pending.push(promise); } });
await Promise.all(pending);
const subscription = await worker.fetch(new Request('https://example.com/sub/refresh/v2ray'), env);
assert.equal(await subscription.text(), 'vmess://test\n');
});