mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
153 lines
4.5 KiB
JavaScript
153 lines
4.5 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const ts = require('typescript');
|
|
const axios = require('axios');
|
|
|
|
function fixture(pathname = '/login', baseUrl = '/') {
|
|
const storage = new Map([['token', 'expired-token']]);
|
|
const pushes = [];
|
|
const history = { location: { pathname }, push: (p) => pushes.push(p) };
|
|
const config = { authKey: 'token', baseUrl };
|
|
const mocks = {
|
|
axios,
|
|
'react-intl-universal': { get: (s) => s },
|
|
antd: {
|
|
message: { config() {}, error() {} },
|
|
notification: { error() {} },
|
|
},
|
|
'./config': config,
|
|
'@umijs/max': { history },
|
|
'./httpError': { getErrorDetails: () => [] },
|
|
};
|
|
const source = fs.readFileSync(
|
|
path.join(__dirname, '../../src/utils/http.tsx'),
|
|
'utf8',
|
|
);
|
|
const { outputText } = ts.transpileModule(source, {
|
|
compilerOptions: {
|
|
module: ts.ModuleKind.CommonJS,
|
|
jsx: ts.JsxEmit.React,
|
|
esModuleInterop: true,
|
|
},
|
|
});
|
|
const module = { exports: {} };
|
|
new Function('require', 'module', 'exports', 'localStorage', outputText)(
|
|
(name) => {
|
|
assert.ok(Object.hasOwn(mocks, name), name);
|
|
return mocks[name];
|
|
},
|
|
module,
|
|
module.exports,
|
|
{
|
|
getItem: (k) => storage.get(k) ?? null,
|
|
removeItem: (k) => storage.delete(k),
|
|
},
|
|
);
|
|
return { request: module.exports.request, storage, pushes };
|
|
}
|
|
|
|
for (const pathname of ['/login', '/dashboard']) {
|
|
test(`401 clears expired credentials on ${pathname}`, async () => {
|
|
const { request, storage, pushes } = fixture(pathname);
|
|
await assert.rejects(
|
|
request.get('/api/user', {
|
|
adapter: async (config) => {
|
|
throw new axios.AxiosError(
|
|
'Unauthorized',
|
|
'ERR_BAD_REQUEST',
|
|
config,
|
|
null,
|
|
{ status: 401, data: { message: 'expired' }, config },
|
|
);
|
|
},
|
|
}),
|
|
);
|
|
assert.equal(storage.has('token'), false);
|
|
assert.deepEqual(pushes, pathname === '/login' ? [] : ['/login']);
|
|
});
|
|
}
|
|
for (const baseUrl of ['/', '/panel/']) {
|
|
test(`health is anonymous while protected requests keep credentials (${baseUrl})`, async () => {
|
|
const { request } = fixture('/login', baseUrl);
|
|
const headers = [];
|
|
const adapter = async (config) => {
|
|
headers.push(config.headers.get('Authorization'));
|
|
return { status: 200, data: { code: 200 }, config };
|
|
};
|
|
await request.get(`${baseUrl}api/health`, { adapter });
|
|
await request.get(`${baseUrl}api/user`, { adapter });
|
|
assert.deepEqual(headers, [undefined, 'Bearer expired-token']);
|
|
});
|
|
}
|
|
|
|
function healthFixture(get) {
|
|
const source = fs.readFileSync(
|
|
path.join(__dirname, '../../src/layouts/index.tsx'),
|
|
'utf8',
|
|
);
|
|
const ast = ts.createSourceFile(
|
|
'index.tsx',
|
|
source,
|
|
ts.ScriptTarget.Latest,
|
|
true,
|
|
ts.ScriptKind.TSX,
|
|
);
|
|
let initializer;
|
|
function visit(node) {
|
|
if (
|
|
ts.isVariableDeclaration(node) &&
|
|
node.name.getText(ast) === 'getHealthStatus'
|
|
)
|
|
initializer = node.initializer;
|
|
ts.forEachChild(node, visit);
|
|
}
|
|
visit(ast);
|
|
assert.ok(initializer, 'layout health bootstrap exists');
|
|
const { outputText } = ts.transpileModule(
|
|
`const bootstrap = ${initializer.getText(ast)};`,
|
|
{
|
|
compilerOptions: { target: ts.ScriptTarget.ES2020 },
|
|
},
|
|
);
|
|
const events = [];
|
|
const bootstrap = new Function(
|
|
'request',
|
|
'config',
|
|
'history',
|
|
'window',
|
|
'getSystemInfo',
|
|
'setInitLoading',
|
|
`${outputText}; return bootstrap;`,
|
|
)(
|
|
{ get },
|
|
{ apiPrefix: '/api/' },
|
|
{ push: (p) => events.push(p) },
|
|
{ location: { reload: () => events.push('reload') } },
|
|
() => events.push('system'),
|
|
(value) => events.push(['loading', value]),
|
|
);
|
|
return { bootstrap, events };
|
|
}
|
|
for (const status of [401, 503, undefined]) {
|
|
test(`health failure ${
|
|
status ?? 'network'
|
|
} settles without reloading`, async () => {
|
|
const { bootstrap, events } = healthFixture(async () => {
|
|
throw status ? { response: { status } } : new Error('Network Error');
|
|
});
|
|
bootstrap();
|
|
await new Promise(setImmediate);
|
|
assert.deepEqual(events, ['/error', ['loading', false]]);
|
|
});
|
|
}
|
|
test('healthy bootstrap continues loading system information', async () => {
|
|
const { bootstrap, events } = healthFixture(async () => ({
|
|
data: { status: 'ok' },
|
|
}));
|
|
bootstrap();
|
|
await new Promise(setImmediate);
|
|
assert.deepEqual(events, ['system', ['loading', false]]);
|
|
});
|