mirror of
https://gh-proxy.org/https://github.com/klinsmaya/portal-template-default.git
synced 2026-11-11 02:27:17 +08:00
98 lines
2.9 KiB
JavaScript
98 lines
2.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import { GitHubClient } from '../factory-lib.mjs';
|
|
|
|
const client = () =>
|
|
new GitHubClient({
|
|
token: 'test-only',
|
|
repository: 'owner/factory',
|
|
retryDelays: [0, 0],
|
|
});
|
|
|
|
function replyWith(t, replies) {
|
|
const calls = [];
|
|
t.mock.method(globalThis, 'fetch', async (url, options) => {
|
|
calls.push(`${options.method} ${new URL(url).pathname}`);
|
|
const reply = replies.shift();
|
|
if (reply instanceof Error) throw reply;
|
|
return new Response(JSON.stringify(reply.body ?? {}), {
|
|
status: reply.status,
|
|
});
|
|
});
|
|
t.mock.method(console, 'warn', () => {});
|
|
return calls;
|
|
}
|
|
|
|
test('content-addressed git object POSTs retry a short outage when asked to', async (t) => {
|
|
for (const route of ['/git/blobs', '/git/trees', '/git/commits']) {
|
|
const calls = replyWith(t, [
|
|
{ status: 502 },
|
|
new TypeError('fetch failed'),
|
|
{ status: 201, body: { sha: 'abc' } },
|
|
]);
|
|
assert.deepEqual(
|
|
await client().request('POST', route, {
|
|
body: {},
|
|
contentAddressed: true,
|
|
}),
|
|
{ sha: 'abc' },
|
|
);
|
|
assert.equal(calls.length, 3, route);
|
|
t.mock.restoreAll();
|
|
}
|
|
});
|
|
|
|
test('a POST without the opt-in is still sent once', async (t) => {
|
|
const calls = replyWith(t, [{ status: 502 }, { status: 201 }]);
|
|
await assert.rejects(
|
|
client().request('POST', '/git/blobs', { body: {} }),
|
|
/failed \(502\)/,
|
|
);
|
|
assert.equal(calls.length, 1);
|
|
});
|
|
|
|
test('the opt-in is refused for any other POST and for ref updates', async (t) => {
|
|
const calls = replyWith(t, []);
|
|
for (const [method, route] of [
|
|
['POST', '/git/refs'],
|
|
['POST', '/issues/1/comments'],
|
|
['PATCH', '/git/refs/heads/gh-pages'],
|
|
['GET', '/git/blobs'],
|
|
])
|
|
await assert.rejects(
|
|
client().request(method, route, { contentAddressed: true }),
|
|
/contentAddressed retries apply only to POST/,
|
|
`${method} ${route}`,
|
|
);
|
|
// Refused before anything reached the network.
|
|
assert.equal(calls.length, 0);
|
|
});
|
|
|
|
test('the shared commit helpers opt in for objects and never for the ref', async () => {
|
|
const { readFileSync } = await import('node:fs');
|
|
const path = await import('node:path');
|
|
for (const file of ['report-pages.mjs', 'evaluation-registry.mjs']) {
|
|
const source = readFileSync(
|
|
path.resolve(import.meta.dirname, '..', file),
|
|
'utf8',
|
|
);
|
|
const posts = [
|
|
...source.matchAll(
|
|
// Up to the end of the statement: bodies nest braces and parentheses.
|
|
/request\(\s*'POST',\s*'(\/git\/[a-z]+)'[^;]*;/g,
|
|
),
|
|
];
|
|
assert.ok(posts.length >= 3, file);
|
|
for (const [call, route] of posts) {
|
|
assert.match(route, /^\/git\/(blobs|trees|commits)$/, file);
|
|
assert.match(call, /contentAddressed:\s*true/, `${file} ${route}`);
|
|
}
|
|
assert.doesNotMatch(
|
|
source,
|
|
/'\/git\/refs[^']*'[^)]*contentAddressed/,
|
|
`${file} never retries a ref update`,
|
|
);
|
|
}
|
|
});
|