ci: test services in github actions
This commit is contained in:
parent
0fe85b2ae9
commit
cdb2b401ce
3 changed files with 147 additions and 0 deletions
24
.github/workflows/test.yml
vendored
24
.github/workflows/test.yml
vendored
|
@ -36,3 +36,27 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
- name: Run test script
|
||||
run: .github/test.sh api
|
||||
|
||||
check-services:
|
||||
name: test service functionality
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
services: ${{ steps.checkServices.outputs.service_list }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- id: checkServices
|
||||
run: npm ci && echo "service_list=$(node src/util/test-ci get-services)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
test-services:
|
||||
needs: check-services
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
service: ${{ fromJson(needs.check-services.outputs.services) }}
|
||||
name: "test service: ${{ matrix.service }}"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- run: npm ci && node src/util/test-ci run-tests-for ${{ matrix.service }}
|
42
src/modules/test.js
Normal file
42
src/modules/test.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { normalizeRequest } from "../modules/processing/request.js";
|
||||
import match from "./processing/match.js";
|
||||
import { extract } from "./processing/url.js";
|
||||
|
||||
export async function runTest(url, params, expect) {
|
||||
const normalized = normalizeRequest({ url, ...params });
|
||||
if (!normalized) {
|
||||
throw "invalid request";
|
||||
}
|
||||
|
||||
const parsed = extract(normalized.url);
|
||||
if (parsed === null) {
|
||||
throw `invalid url: ${normalized.url}`;
|
||||
}
|
||||
|
||||
const result = await match(
|
||||
parsed.host, parsed.patternMatch, "en", normalized
|
||||
);
|
||||
|
||||
let error = [];
|
||||
if (expect.status !== result.body.status) {
|
||||
const detail = `${expect.status} (expected) != ${result.body.status} (actual)`;
|
||||
error.push(`status mismatch: ${detail}`);
|
||||
}
|
||||
|
||||
if (expect.code !== result.status) {
|
||||
const detail = `${expect.code} (expected) != ${result.status} (actual)`;
|
||||
error.push(`status code mismatch: ${detail}`);
|
||||
}
|
||||
|
||||
if (error.length) {
|
||||
if (result.body.text) {
|
||||
error.push(`error message: ${result.body.text}`);
|
||||
}
|
||||
|
||||
throw error.join('\n');
|
||||
}
|
||||
|
||||
if (result.body.status === 'stream') {
|
||||
// TODO: stream testing
|
||||
}
|
||||
}
|
81
src/util/test-ci.js
Normal file
81
src/util/test-ci.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
import { env } from "../modules/config.js";
|
||||
import { runTest } from "../modules/test.js";
|
||||
import { loadLoc } from "../localization/manager.js";
|
||||
import { loadJSON } from "../modules/sub/loadFromFs.js";
|
||||
import { Red, Bright } from "../modules/sub/consoleText.js";
|
||||
|
||||
const tests = loadJSON('./src/util/tests.json');
|
||||
const services = loadJSON('./src/modules/processing/servicesConfig.json');
|
||||
|
||||
// services that are known to frequently fail due to external
|
||||
// factors (e.g. rate limiting)
|
||||
const finnicky = new Set(['bilibili', 'instagram', 'youtube'])
|
||||
|
||||
const action = process.argv[2];
|
||||
switch (action) {
|
||||
case "get-services":
|
||||
const fromConfig = Object.keys(services.config);
|
||||
|
||||
const missingTests = fromConfig.filter(
|
||||
service => !tests[service] || tests[service].length === 0
|
||||
);
|
||||
|
||||
if (missingTests.length) {
|
||||
console.error('services have no tests:', missingTests);
|
||||
console.log('[]');
|
||||
process.exitCode = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(fromConfig));
|
||||
break;
|
||||
|
||||
case "run-tests-for":
|
||||
const service = process.argv[3];
|
||||
let failed = false;
|
||||
|
||||
if (!tests[service]) {
|
||||
console.error('no such service:', service);
|
||||
}
|
||||
|
||||
await loadLoc();
|
||||
env.streamLifespan = 10000;
|
||||
env.apiURL = 'http://x';
|
||||
|
||||
for (const test of tests[service]) {
|
||||
const { name, url, params, expected } = test;
|
||||
const canFail = test.canFail || finnicky.has(service);
|
||||
|
||||
try {
|
||||
await runTest(url, params, expected);
|
||||
console.log(`${service}/${name}: ok`);
|
||||
|
||||
} catch(e) {
|
||||
failed = !canFail;
|
||||
|
||||
let failText = canFail ? `${Red('FAIL')} (ignored)` : Bright(Red('FAIL'));
|
||||
if (canFail && process.env.GITHUB_ACTION) {
|
||||
console.log(`::warning title=${service}/${name.replace(/,/g, ';')}::failed and was ignored`);
|
||||
}
|
||||
|
||||
console.error(`${service}/${name}: ${failText}`);
|
||||
const errorString = e.toString().split('\n');
|
||||
let c = '┃';
|
||||
errorString.forEach((line, index) => {
|
||||
line = line.replace('!=', Red('!='));
|
||||
|
||||
if (index === errorString.length - 1) {
|
||||
c = '┗';
|
||||
}
|
||||
|
||||
console.error(` ${c}`, line);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
process.exitCode = Number(failed);
|
||||
break;
|
||||
default:
|
||||
console.error('invalid action:', action);
|
||||
process.exitCode = 1;
|
||||
}
|
Loading…
Reference in a new issue