import path from "node:path"; import { env } from "../config.js"; import { runTest } from "../misc/run-test.js"; import { loadJSON } from "../misc/load-from-fs.js"; import { Red, Bright } from "../misc/console-text.js"; import { randomizeCiphers } from "../misc/randomize-ciphers.js"; import { services } from "../processing/service-config.js"; const getTestPath = service => path.join('./src/util/tests/', `./${service}.json`); const getTests = (service) => loadJSON(getTestPath(service)); // services that are known to frequently fail due to external // factors (e.g. rate limiting) const finnicky = new Set(['bilibili', 'instagram', 'facebook', 'youtube']); const runTestsFor = async (service) => { const tests = getTests(service); let softFails = 0, fails = 0; if (!tests) { throw "no such service: " + service; } for (const test of tests) { 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) { softFails += !canFail; fails++; 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); }); } } return { fails, softFails }; } const printHeader = (service, padLen) => { const padding = padLen - service.length; service = service.padEnd(1 + service.length + padding, ' '); console.log(service + '='.repeat(50)); } const action = process.argv[2]; switch (action) { case "get-services": const fromConfig = Object.keys(services); const missingTests = fromConfig.filter( service => { const tests = getTests(service); return !tests || tests.length === 0 } ); if (missingTests.length) { console.error('services have no tests:', missingTests); process.exitCode = 1; break; } console.log(JSON.stringify(fromConfig)); break; case "run-tests-for": const service = process.argv[3]; env.streamLifespan = 10000; env.apiURL = 'http://x/'; randomizeCiphers(); try { const { softFails } = await runTestsFor(service); process.exitCode = Number(!!softFails); } catch(e) { console.error(e); process.exitCode = 1; break; } break; default: const maxHeaderLen = Object.keys(services).reduce((n, v) => v.length > n ? v.length : n, 0); const failCounters = {}; env.streamLifespan = 10000; env.apiURL = 'http://x/'; randomizeCiphers(); for (const service in services) { printHeader(service, maxHeaderLen); const { fails, softFails } = await runTestsFor(service); failCounters[service] = fails; console.log(); if (!process.exitCode && softFails) process.exitCode = 1; } console.log('='.repeat(50 + maxHeaderLen)); console.log( Bright('total fails:'), Object.values(failCounters).reduce((a, b) => a + b) ); for (const [ service, fails ] of Object.entries(failCounters)) { if (fails) console.log(`${Bright(service)} fails: ${fails}`); } }