Files
qinglong/test/back/linux-mirror-result.test.cjs
T
whyour be877cfddc fix: 修复完整镜像源地址及 CRLF 任务导入 (#3093)
* fix: address six verified legacy issue regressions

* fix: limit issue patch to mirror URLs and CRLF imports

* fix: preserve APT formats and third-party repositories
2026-10-13 01:11:35 +08:00

177 lines
6.1 KiB
JavaScript

const assert = require('node:assert/strict');
const test = require('node:test');
const { spawn, spawnSync } = require('node:child_process');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
const load = require('../helpers/load-security-module.cjs');
for (const scenario of [
{
name: 'stderr warning with exit zero',
code: 'process.stderr.write("sudo: warning\\n")',
saved: true,
},
{
name: 'nonzero exit without stderr',
code: 'process.exitCode = 7',
saved: false,
},
{
name: 'signal termination',
code: 'process.kill(process.pid, "SIGTERM")',
saved: false,
},
{ name: 'spawn failure', shell: '/nonexistent-ql-test-shell', saved: false },
]) {
test(`Linux mirror persistence follows the process result: ${scenario.name}`, async () => {
const logger = { info() {}, error() {} };
const ScheduleService = load('back/services/schedule.ts', {
'../shared/cronScheduler': {},
'../shared/pLimit': { runWithSystemLimit: async (_params, fn) => fn() },
'cross-spawn': {
spawn: () =>
spawn(
process.execPath,
['-e', scenario.code || ''],
scenario.shell ? { shell: scenario.shell } : {},
),
},
}).default;
const schedule = new ScheduleService(logger);
let completion;
const runTask = schedule.runTask.bind(schedule);
schedule.runTask = (...args) => (completion = runTask(...args));
const messages = [],
writes = [];
const SystemService = load('back/services/system.ts', {
os: { platform: () => 'linux' },
'../config': {},
'../config/const': {},
'../config/util': { updateLinuxMirrorFile: async () => 'fixture' },
'../data/dependence': {},
'../data/notify': {},
'../data/system': {},
'../schedule/client': {},
'../shared/pLimit': {},
'../shared/i18n': {},
'../shared/schedulerMutationLock': {},
'./notify': class {},
'./schedule': ScheduleService,
'./sock': class {},
}).default;
const service = new SystemService(logger, schedule, {
sendMessage: (message) => messages.push(message),
});
service.getSystemConfig = async () => ({ id: 1, info: { lang: 'en-US' } });
service.updateAuthDb = async (doc) => writes.push(doc);
let ended = 0;
await service.updateLinuxMirror(
{ linuxMirror: 'https://mirror.example' },
undefined,
() => ended++,
);
const result = await completion;
assert.equal(ended, 1);
assert.equal(writes.length, scenario.saved ? 1 : 0);
assert.ok(messages.some((message) => message.status === 'completed'));
if (scenario.saved) {
assert.equal(result.code, 0);
assert.ok(
messages.some((message) => message.message.includes('sudo: warning')),
);
assert.deepEqual(writes[0].info, {
lang: 'en-US',
linuxMirror: 'https://mirror.example',
});
}
});
}
const hasApt = spawnSync('apt-get', ['--version']).status === 0;
for (const distribution of ['Debian', 'Ubuntu']) {
for (const existingMirror of [false, true]) {
test(
`${distribution} mirror update rejects failed index downloads with ${
existingMirror ? 'an existing' : 'an empty'
} source`,
{ skip: !hasApt },
async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'ql-apt-result-'));
t.after(() => fs.rm(root, { recursive: true, force: true }));
const parts = path.join(root, 'parts');
await fs.mkdir(parts);
await fs.mkdir(path.join(root, 'lists', 'partial'), {
recursive: true,
});
const source = path.join(root, 'sources.list');
await fs.writeFile(
source,
'deb http://127.0.0.1:1/debian bookworm main\n',
);
// Load only fixture configuration, including all writable APT paths.
const aptConfig = path.join(root, 'apt.conf');
await fs.writeFile(
aptConfig,
`Dir::Etc::main "/dev/null";
Dir::Etc::parts "${parts}";
Dir::Etc::sourcelist "${source}";
Dir::Etc::sourceparts "${parts}";
Dir::State::lists "${path.join(root, 'lists')}";
Dir::Cache "${path.join(root, 'cache')}";
Dir::Log "${path.join(root, 'logs')}";
Acquire::Retries "0";
Acquire::http::Proxy "DIRECT";
Acquire::http::Timeout "1";
`,
);
const util = load('back/config/util.ts', {
'fs/promises': {
readFile: async (file) =>
file === '/etc/os-release'
? `${distribution}\nVERSION_CODENAME=bookworm\n`
: existingMirror
? 'URIs: http://old.example/debian\n'
: '',
},
os: { platform: () => 'linux' },
'./index': {},
'./const': {},
'./share': {},
'./container': { isInContainer: () => false },
'../loaders/logger': {},
'../shared/utils': {},
'../shared/fileAccess': {},
'../data/dependence': {},
});
const previousOS = process.env.QL_OS_TYPE;
let command;
try {
process.env.QL_OS_TYPE = distribution.toLowerCase();
command = await util.updateLinuxMirrorFile('http://127.0.0.1:1');
} finally {
if (previousOS === undefined) delete process.env.QL_OS_TYPE;
else process.env.QL_OS_TYPE = previousOS;
}
// Exercise the generated updater without modifying host source files.
const [program, ...args] = command
.slice(command.lastIndexOf('&&') + 2)
.trim()
.split(/\s+/);
assert.equal(program, 'apt-get');
const options = {
encoding: 'utf8',
timeout: 10000,
env: { ...process.env, APT_CONFIG: aptConfig },
};
const baseline = spawnSync('apt-get', ['update'], options);
assert.equal(baseline.status, 0, baseline.stderr);
assert.match(baseline.stderr, /Failed to fetch/);
const result = spawnSync(program, args, options);
assert.equal(result.status, 100, result.stderr);
assert.match(result.stderr, /Failed to fetch/);
},
);
}
}