Browse Source

🧪 test: 增加测试用例

Pchen0 4 hours ago
parent
commit
a2961be64b
1 changed files with 160 additions and 0 deletions
  1. 160 0
      scripts/proxy-u-xxoo365-test.js

+ 160 - 0
scripts/proxy-u-xxoo365-test.js

@@ -0,0 +1,160 @@
+#!/usr/bin/env node
+'use strict'
+
+/**
+ * 测试通过 HTTP 代理访问 u.xxoo365.top(HTTPS)。
+ *
+ * 用法(项目根目录执行):
+ *   node scripts/proxy-u-xxoo365-test.js direct
+ *   node scripts/proxy-u-xxoo365-test.js proxy 103.217.191.15:30104
+ *   node scripts/proxy-u-xxoo365-test.js qg-fetch
+ *
+ * 可选环境变量:
+ *   TEST_URL=https://u.xxoo365.top/
+ *   TEST_TIMEOUT_MS=20000
+ *   TEST_METHOD=GET              # GET / HEAD
+ *   TEST_PATH=/health            # 会拼到 TEST_URL 的 origin 后
+ *   QG_AREA=... QG_AREA_EX=... QG_ISP=1|2|3 QG_DISTINCT=0|1
+ */
+
+const fs = require('fs')
+const path = require('path')
+const axios = require('axios')
+const HttpsProxyAgent = require('https-proxy-agent')
+
+const QG_GET_URL = 'https://share.proxy.qg.net/get'
+
+function readConfig() {
+    const cfgPath = path.join(__dirname, '..', 'config.json')
+    return JSON.parse(fs.readFileSync(cfgPath, 'utf8'))
+}
+
+function buildTargetUrl() {
+    const base = (process.env.TEST_URL || 'https://u.xxoo365.top/').trim()
+    const pathOverride = (process.env.TEST_PATH || '').trim()
+    if (!pathOverride) return base
+    const u = new URL(base)
+    u.pathname = pathOverride.startsWith('/') ? pathOverride : `/${pathOverride}`
+    return u.toString()
+}
+
+function normalizeMethod() {
+    const m = (process.env.TEST_METHOD || 'GET').toUpperCase()
+    return m === 'HEAD' ? 'HEAD' : 'GET'
+}
+
+function toProxyUrl(server, user, pass) {
+    const [host, portRaw] = String(server || '').trim().split(':')
+    const port = Number(portRaw)
+    if (!host || !Number.isFinite(port)) {
+        throw new Error(`代理地址非法: ${server}`)
+    }
+    let auth = ''
+    if (user) {
+        auth = `${encodeURIComponent(user)}:${encodeURIComponent(pass || '')}@`
+    }
+    return `http://${auth}${host}:${port}`
+}
+
+async function fetchProxyFromQg(cfg) {
+    const q = cfg.qgChannelProxy || {}
+    const key = String(q.extractKey || '').trim()
+    if (!key) {
+        throw new Error('config.json 缺少 qgChannelProxy.extractKey')
+    }
+    const params = { key, num: 1 }
+    const area = String(process.env.QG_AREA || '').trim()
+    const areaEx = String(process.env.QG_AREA_EX || '').trim()
+    if (area) params.area = area
+    if (areaEx) params.area_ex = areaEx
+    if (['1', '2', '3'].includes(String(process.env.QG_ISP || ''))) {
+        params.isp = Number(process.env.QG_ISP)
+    }
+    params.distinct = String(process.env.QG_DISTINCT || '1') !== '0'
+
+    const res = await axios.get(QG_GET_URL, {
+        params,
+        timeout: 20000,
+        proxy: false,
+        validateStatus: () => true
+    })
+    const body = res.data || {}
+    if (body.code !== 'SUCCESS' || !Array.isArray(body.data) || !body.data[0]?.server) {
+        throw new Error(`青果 /get 失败: ${body.code || JSON.stringify(body).slice(0, 180)}`)
+    }
+    return {
+        server: body.data[0].server,
+        proxy_ip: body.data[0].proxy_ip || null,
+        deadline: body.data[0].deadline || null,
+        request_id: body.request_id || null
+    }
+}
+
+async function probe(label, requestConfig) {
+    const timeout = Number(process.env.TEST_TIMEOUT_MS || 20000)
+    const url = buildTargetUrl()
+    const method = normalizeMethod()
+    const t0 = Date.now()
+    console.log(`\n=== ${label} ===`)
+    console.log('REQUEST:', method, url)
+
+    try {
+        const res = await axios({
+            url,
+            method,
+            timeout,
+            validateStatus: () => true,
+            ...requestConfig
+        })
+        const ms = Date.now() - t0
+        console.log('RESULT: OK', `${ms}ms`, 'HTTP', res.status)
+        if (method !== 'HEAD') {
+            const body = typeof res.data === 'string' ? res.data : JSON.stringify(res.data)
+            console.log('BODY_PREVIEW:', body.slice(0, 200).replace(/\s+/g, ' '))
+        }
+    } catch (e) {
+        const ms = Date.now() - t0
+        console.log('RESULT: FAIL', `${ms}ms`)
+        console.log('message:', e.message)
+        console.log('code:', e.code)
+        if (e.response) console.log('http_status:', e.response.status)
+    }
+}
+
+async function main() {
+    const mode = (process.argv[2] || 'direct').toLowerCase()
+    const cfg = readConfig()
+    const q = cfg.qgChannelProxy || {}
+    const user = String(process.env.QG_PROXY_USER || q.authUser || '').trim()
+    const pass = String(process.env.QG_PROXY_PASSWORD || q.authPassword || '').trim()
+    const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0'
+
+    if (mode === 'direct') {
+        await probe('直连', { proxy: false })
+        return
+    }
+
+    let server
+    if (mode === 'proxy') {
+        server = process.argv[3]
+        if (!server) throw new Error('proxy 模式需传 host:port')
+    } else if (mode === 'qg-fetch') {
+        const got = await fetchProxyFromQg(cfg)
+        server = got.server
+        console.log(
+            `[Qg] server=${got.server} proxy_ip=${got.proxy_ip || '—'} deadline=${got.deadline || '—'} request_id=${got.request_id || '—'}`
+        )
+    } else {
+        throw new Error('模式仅支持: direct | proxy <host:port> | qg-fetch')
+    }
+
+    const proxyUrl = toProxyUrl(server, user, pass)
+    const httpsAgent = new HttpsProxyAgent(proxyUrl, { rejectUnauthorized })
+    await probe(`代理 ${server}`, { proxy: false, httpsAgent })
+}
+
+main().catch(err => {
+    console.error(err.stack || err)
+    process.exit(1)
+})
+