import { describe, expect, it } from 'bun:test' import { lastSwitchableGroup, resolveChain } from './outbound-chain.ts' import type { RuntimeNode } from './outbound-chain.ts' /** 仿照真实那台机器:PROXY 手动选,选中了自动测速的 AUTO,AUTO 挑了一条订阅线路。 */ const runtime: Record = { PROXY: { now: 'AUTO', all: ['AUTO', 'DIRECT'] }, AUTO: { now: '[vless]Gomami', all: ['[vmess]DMIT', '[vless]Gomami'] }, '[vless]Gomami': {}, '[vmess]DMIT': {}, DIRECT: {}, } describe('解析流量最终走到哪条线', () => { it('一路跟着当前选中走到底', () => { expect(resolveChain(runtime, 'PROXY')).toEqual(['PROXY', 'AUTO', '[vless]Gomami']) }) it('起点就是线路时链条只有它自己', () => { expect(resolveChain(runtime, '[vless]Gomami')).toEqual(['[vless]Gomami']) }) it('没有落脚点时是空链条', () => { expect(resolveChain(runtime, null)).toEqual([]) }) it('落脚在一个内核不认识的名字上时也只返回它自己', () => { // 配置引用了一个不存在的出站时会走到这里,界面要能显示而不是崩掉 expect(resolveChain(runtime, '不存在的组')).toEqual(['不存在的组']) }) it('策略组互相引用成环时停下来,不会转死', () => { const looped: Record = { A: { now: 'B', all: ['B'] }, B: { now: 'A', all: ['A'] }, } expect(resolveChain(looped, 'A')).toEqual(['A', 'B']) }) it('组选中了自己时也停得下来', () => { expect(resolveChain({ A: { now: 'A', all: ['A'] } }, 'A')).toEqual(['A']) }) }) describe('找出用户能换选中的那一层', () => { it('取链条上最靠近线路的那个策略组', () => { // 换 AUTO 的选中才是「换线路」;换 PROXY 换的是「走哪套策略」 const chain = resolveChain(runtime, 'PROXY') expect(lastSwitchableGroup(runtime, chain)).toEqual({ group: 'AUTO', members: ['[vmess]DMIT', '[vless]Gomami'], now: '[vless]Gomami', }) }) it('链条上一个策略组都没有时返回空', () => { expect(lastSwitchableGroup(runtime, ['[vless]Gomami'])).toBeNull() expect(lastSwitchableGroup(runtime, [])).toBeNull() }) it('组存在但成员是空的,不算可切换', () => { // 筛选条件没匹配到任何线路时会出现这种组,给出一个点不动的列表只会让人困惑 expect(lastSwitchableGroup({ 空组: { all: [], now: '' } }, ['空组'])).toBeNull() }) })