mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-08 05:04:21 +08:00
🛸 Sync 2026-10-27 02:27:08
This commit is contained in:
1 parent
b3798acf79
commit
8b63d5d4a9
78 files changed
+2288
-518
No files matched your search
@@ -0,0 +1,64 @@
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
|
||||
const lua = process.env.MINIGATE_TEST_LUA;
|
||||
const controller = path.resolve(__dirname, '../luasrc/controller/minigate.lua');
|
||||
|
||||
test('visitor aggregation distinguishes blocked, upstream, legacy and failed requests', { skip: !lua }, () => {
|
||||
const input = [
|
||||
"local raw, response, limit = '', nil, '50'",
|
||||
"package.preload['luci.sys'] = function() return {exec=function() return raw end} end",
|
||||
"luci = {http={formvalue=function() return limit end, prepare_content=function() end, write_json=function(v) response=v end}}",
|
||||
'dofile(' + JSON.stringify(controller) + ')',
|
||||
"local real_time = os.time",
|
||||
"os.time = function(v) return v and real_time(v) or real_time({year=2026,month=10,day=26,hour=12,min=0,sec=0}) end",
|
||||
"local function row(ip, status, upstream, domain, time)",
|
||||
"raw = raw .. '{\"time\":\"' .. (time or '2026-10-26T11:59:00+08:00') .. '\",\"client\":\"' .. ip .. '\",\"domain\":\"' .. (domain or 'app.example.com') .. '\"' ..",
|
||||
"(status and ',\"status\":' .. status or '') .. (upstream and ',\"upstream_status\":\"' .. upstream .. '\"' or '') .. '}\\n'",
|
||||
"end",
|
||||
"row('mixed', 200, '200')",
|
||||
"row('mixed', 444, '-')",
|
||||
"row('mixed', 308, '-')",
|
||||
"row('blocked', 444, '-')",
|
||||
"row('default', 400, '-', '_')",
|
||||
"row('upstream403', 403, '403')",
|
||||
"row('upstream401', 401, '401')",
|
||||
"row('legacy200', 200)",
|
||||
"row('legacy403', 403)",
|
||||
"row('failure', 502, '502')",
|
||||
"row('closed', 499, '-')",
|
||||
"row('missing', 200, '200')",
|
||||
"row('missing', nil, '200', '')",
|
||||
"row('old', 200, '200', nil, '2026-10-26T11:54:59+08:00')",
|
||||
"row('future', 200, '200', nil, '2026-10-26T12:01:00+08:00')",
|
||||
"raw = raw .. 'invalid line\\n'",
|
||||
"luci.controller.minigate.action_proxy_access()",
|
||||
"local by_ip = {}",
|
||||
"for _, v in ipairs(response.visitors) do",
|
||||
"by_ip[v.ip] = v",
|
||||
"assert(v.count == v.blocked_count + v.forwarded_count + v.other_count)",
|
||||
"assert(v.online == nil)",
|
||||
"end",
|
||||
"local v = by_ip.mixed",
|
||||
"assert(v.count == 3 and v.blocked_count == 1 and v.forwarded_count == 1 and v.other_count == 1)",
|
||||
"assert(v.outcome == 'responded' and v.status == 308)",
|
||||
"assert(by_ip.blocked.outcome == 'blocked' and by_ip.blocked.recent)",
|
||||
"assert(by_ip.default.outcome == 'blocked')",
|
||||
"assert(by_ip.upstream403.outcome == 'forwarded' and by_ip.upstream403.status == 403)",
|
||||
"assert(by_ip.upstream401.outcome == 'forwarded')",
|
||||
"assert(by_ip.legacy200.outcome == 'responded')",
|
||||
"assert(by_ip.legacy403.outcome == 'error')",
|
||||
"assert(by_ip.failure.outcome == 'error' and by_ip.closed.outcome == 'error')",
|
||||
"assert(by_ip.missing.outcome == 'unknown' and by_ip.missing.status == nil and by_ip.missing.domain == '')",
|
||||
"assert(not by_ip.old.recent and not by_ip.future.recent)",
|
||||
"limit = 'invalid'",
|
||||
"luci.controller.minigate.action_proxy_access()",
|
||||
"assert(#response.visitors == 5)",
|
||||
"print('visitor aggregation passed')"
|
||||
].join('\n');
|
||||
const result = spawnSync(lua, ['-'], { encoding: 'utf8', input });
|
||||
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||
assert.match(result.stdout, /aggregation passed/);
|
||||
});
|
||||
@@ -78,6 +78,13 @@ test('real nginx enforces source policy before upstream, redirect and websocket'
|
||||
assert.equal(fixture.reached(), reached, '拒绝的请求不得到达后端');
|
||||
assert.match(await request(fixture.redirectPath, '14.196.0.1'), /308 Permanent Redirect/);
|
||||
assert.match(await request(fixture.socketPath, '14.196.0.1', { websocket: true }), /101 Switching Protocols/);
|
||||
const rows = fs.readFileSync(path.join(fixture.dir, 'access.log'), 'utf8').trim().split('\n').map(JSON.parse);
|
||||
assert.ok(rows.some(row => row.status === 200 && row.upstream_status === '200'));
|
||||
assert.ok(rows.some(row => row.status === 101 && row.upstream_status === '101'));
|
||||
assert.ok(rows.some(row => row.status === 308 && !row.upstream_status));
|
||||
const blocked = rows.filter(row => row.status === 444);
|
||||
assert.equal(blocked.length, 18);
|
||||
assert.ok(blocked.every(row => !row.upstream_status), '被拒绝请求没有上游响应');
|
||||
});
|
||||
|
||||
test('real nginx permits all sources when disabled and fails closed if selected data is missing', { skip: !nginx }, async t => {
|
||||
|
||||
@@ -102,6 +102,35 @@ test('overview polling pauses in a hidden tab and refreshes when visible again',
|
||||
assert.deepEqual(page.requests.map(item => item.url).sort(), ['au', 'su']);
|
||||
});
|
||||
|
||||
test('overview distinguishes request results and never labels blocked requests as online', () => {
|
||||
const page = fakePage(overviewScript);
|
||||
const visitors = ['blocked', 'forwarded', 'responded', 'error', 'unknown'].map((outcome, i) => ({
|
||||
ip: '198.51.100.' + (i + 1), last_time: '2026-10-26T11:59:00+08:00', domain: 'app.example.com',
|
||||
outcome, status: [444, 403, 308, 502, null][i], recent: i === 0,
|
||||
count: 6, blocked_count: i === 0 ? 0 : 1, forwarded_count: 2, other_count: 3
|
||||
}));
|
||||
take(page, 'au').callback(null, { visitors });
|
||||
const html = page.element('mg-v-list').innerHTML;
|
||||
for (const label of ['已拦截', '已转发', '已响应', '请求失败', '结果未知', 'HTTP 444', 'HTTP 403',
|
||||
'无状态码', '近5分钟有请求', '5分钟内无新请求', '总计 6', '拦截 1 · 转发 2 · 其他 3', '拦截 0 · 转发 2 · 其他 3']) {
|
||||
assert.ok(html.includes(label), label);
|
||||
}
|
||||
assert.doesNotMatch(html, /在线|离线|mg-dot/);
|
||||
});
|
||||
|
||||
test('overview renders legacy visitor responses as unknown and escapes log content', () => {
|
||||
const page = fakePage(overviewScript);
|
||||
take(page, 'au').callback(null, { visitors: [{
|
||||
ip: '198.51.100.1', domain: '<img src=x onerror=alert(1)>', count: 1,
|
||||
last_time: '2026-10-26T11:59:00+08:00', online: true
|
||||
}] });
|
||||
const html = page.element('mg-v-list').innerHTML;
|
||||
assert.match(html, /结果未知/);
|
||||
assert.match(html, /请求时间未知/);
|
||||
assert.match(html, /<img/);
|
||||
assert.doesNotMatch(html, /<img|已转发|在线|离线|拦截 undefined/);
|
||||
});
|
||||
|
||||
test('login guard renders empty JSON objects as empty lists and uses POST for changes', () => {
|
||||
const page = fakePage(loginGuardScript);
|
||||
take(page, 'lu').callback(null, { enabled: '1', running: true, banned: {}, banned_total: 0, watching: {}, watching_total: 0, threshold: 3 });
|
||||
|
||||
Reference in new issue
Block a user