import { describe, expect, test } from 'bun:test' import { describeProbeFailure, findTarget, isChallenge, judge, rayId, REACH_TARGETS, } from './reachability.ts' const openai = REACH_TARGETS[0]! const gemini = REACH_TARGETS[2]! const headers = (init: Record = {}) => new Headers(init) describe('REACH_TARGETS', () => { test('标识不重名,否则接口按 id 取目标会取错', () => { const ids = REACH_TARGETS.map((t) => t.id) expect(new Set(ids).size).toBe(ids.length) }) test('findTarget 认不出来的标识返回 null', () => { expect(findTarget('openai')?.label).toBe('OpenAI') expect(findTarget('nonesuch')).toBeNull() }) }) describe('judge', () => { test('无鉴权探测拿到 401 算可达,并说明这是预期结果', () => { const { verdict, message } = judge(openai, 401, headers(), '') expect(verdict).toBe('reachable') expect(message).toBe('HTTP 401,目标可达') }) test('2xx 不需要解释,只报状态码', () => { expect(judge(gemini, 200, headers(), '{}')).toEqual({ verdict: 'reachable', message: 'HTTP 200', }) }) test('白名单之外的状态码算不通', () => { const { verdict, message } = judge(gemini, 403, headers(), '') expect(verdict).toBe('unreachable') expect(message).toBe('HTTP 403,不是预期的状态码') }) test('429 单独说是限流,不和连不上混在一起', () => { expect(judge(openai, 429, headers(), '').verdict).toBe('throttled') }) test('Cloudflare 拦截优先于状态码白名单', () => { // 429 在 openai 的白名单外,但结论要是 challenge 而不是 throttled: // 前者要换出口 IP,后者只要等 const { verdict, message } = judge( openai, 429, headers({ 'cf-mitigated': 'challenge', 'cf-ray': '8f0a1b2c3d4e5f60-LAX' }), '', ) expect(verdict).toBe('challenge') expect(message).toContain('8f0a1b2c3d4e5f60-LAX') }) }) describe('isChallenge', () => { test('只有 403 和 429 才可能是拦截页', () => { const body = 'window._cf_chl_opt' expect(isChallenge(403, headers(), body)).toBe(true) expect(isChallenge(429, headers(), body)).toBe(true) expect(isChallenge(200, headers(), body)).toBe(false) }) test('没有 cf-mitigated 头时认正文里的标记', () => { expect(isChallenge(403, headers(), 'Just a Moment...')).toBe(true) }) test('普通的 403 不是拦截页', () => { expect(isChallenge(403, headers(), '{"error":"forbidden"}')).toBe(false) }) test('正文只看前 4 KB,标记在那之后不算', () => { expect(isChallenge(403, headers(), 'x'.repeat(5000) + 'just a moment')).toBe(false) }) }) describe('rayId', () => { test('优先取响应头', () => { expect(rayId(headers({ 'cf-ray': ' abc123-LAX ' }), 'cf-ray: zzz')).toBe('abc123-LAX') }) test('没有响应头时从正文里找', () => { expect(rayId(headers(), 'Cloudflare Ray ID: cf-ray: abc123')).toBe('abc123') }) test('都没有就是空串', () => { expect(rayId(headers(), 'nothing here')).toBe('') }) }) describe('describeProbeFailure', () => { test('超时说等了多久', () => { const error = new Error('timed out') error.name = 'TimeoutError' expect(describeProbeFailure(7890, 10_000, error)).toBe('10 秒内没有响应') }) test('本机端口没人监听时指向内核,不照抄 Bun 那句指向目标网址的话', () => { const error = Object.assign(new Error('Unable to connect. Is the computer able to access the url?'), { code: 'ConnectionRefused', }) expect(describeProbeFailure(7890, 10_000, error)).toBe('本机 7890 端口没有响应,内核可能没在运行') }) test('其余原因照原文给出,不猜', () => { expect(describeProbeFailure(7890, 10_000, new Error('TLS handshake failed'))).toBe( '连接失败:TLS handshake failed', ) }) })