diff --git a/frontend/src/test/__snapshots__/headers.test.ts.snap b/frontend/src/test/__snapshots__/headers.test.ts.snap new file mode 100644 index 00000000..9506a641 --- /dev/null +++ b/frontend/src/test/__snapshots__/headers.test.ts.snap @@ -0,0 +1,118 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`toHeaders > empty 1`] = `[]`; + +exports[`toHeaders > mixed 1`] = ` +[ + { + "name": "Host", + "value": "a.example.test", + }, + { + "name": "X-Trace", + "value": "1", + }, + { + "name": "X-Trace", + "value": "2", + }, +] +`; + +exports[`toHeaders > multi array 1`] = ` +[ + { + "name": "Accept", + "value": "text/html", + }, + { + "name": "Accept", + "value": "application/json", + }, +] +`; + +exports[`toHeaders > null 1`] = `[]`; + +exports[`toHeaders > primitive 1`] = `[]`; + +exports[`toHeaders > single array 1`] = ` +[ + { + "name": "Host", + "value": "a.example.test", + }, +] +`; + +exports[`toHeaders > single string 1`] = ` +[ + { + "name": "Host", + "value": "example.test", + }, +] +`; + +exports[`toHeaders > undefined 1`] = `[]`; + +exports[`toV2Headers (arr=false) > duplicate name 1`] = ` +{ + "Accept": "application/json", +} +`; + +exports[`toV2Headers (arr=false) > empty 1`] = `{}`; + +exports[`toV2Headers (arr=false) > empty name skipped 1`] = ` +{ + "X-Real": "kept", +} +`; + +exports[`toV2Headers (arr=false) > empty value skipped 1`] = ` +{ + "X-Real": "kept", +} +`; + +exports[`toV2Headers (arr=false) > single 1`] = ` +{ + "Host": "example.test", +} +`; + +exports[`toV2Headers (arr=true) > duplicate name 1`] = ` +{ + "Accept": [ + "text/html", + "application/json", + ], +} +`; + +exports[`toV2Headers (arr=true) > empty 1`] = `{}`; + +exports[`toV2Headers (arr=true) > empty name skipped 1`] = ` +{ + "X-Real": [ + "kept", + ], +} +`; + +exports[`toV2Headers (arr=true) > empty value skipped 1`] = ` +{ + "X-Real": [ + "kept", + ], +} +`; + +exports[`toV2Headers (arr=true) > single 1`] = ` +{ + "Host": [ + "example.test", + ], +} +`; diff --git a/frontend/src/test/__snapshots__/inbound-link.test.ts.snap b/frontend/src/test/__snapshots__/inbound-link.test.ts.snap new file mode 100644 index 00000000..7880d806 --- /dev/null +++ b/frontend/src/test/__snapshots__/inbound-link.test.ts.snap @@ -0,0 +1,60 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`genHysteriaLink > hysteria-v1-tls: byte-stable 1`] = `"hysteria://hyst-v1-auth-XYZ@example.test:36715?security=tls&fp=chrome&alpn=h3&sni=hysteria.example.test#parity-test"`; + +exports[`genInboundLinks orchestrator > hysteria-v1-tls: byte-stable 1`] = `"hysteria://hyst-v1-auth-XYZ@override.test:36715?security=tls&fp=chrome&alpn=h3&sni=hysteria.example.test#parity-test-gina%40example.test"`; + +exports[`genInboundLinks orchestrator > shadowsocks-tcp-2022: byte-stable 1`] = `"ss://MjAyMi1ibGFrZTMtYWVzLTI1Ni1nY206Wm1GclpTMXpaWEoyWlhJdGNHRnpjM2R2Y21RdE1EQXdNUT09OmRHVnpkQzFqYkdsbGJuUXRjR0Z6YzNkdmNtUXRNUT09@override.test:8388?type=tcp#parity-test-frank%40example.test"`; + +exports[`genInboundLinks orchestrator > trojan-ws-tls: byte-stable 1`] = `"trojan://trojan-test-pw-XYZ@override.test:443?type=ws&path=%2Ftrojan&host=trojan.example.test&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=trojan.example.test#parity-test-eve%40example.test"`; + +exports[`genInboundLinks orchestrator > vless-tcp-reality: byte-stable 1`] = `"vless://22222222-3333-4444-9555-666666666666@override.test:443?type=tcp&encryption=none&security=reality&pbk=Tx5yj1bRcOPHkdvT2pIAQ2zh0gQ8m4OPdnzqXJxxV3o&fp=chrome&sid=a3f1&spx=%2F&flow=xtls-rprx-vision#parity-test-dave%40example.test"`; + +exports[`genInboundLinks orchestrator > vless-ws-tls: byte-stable 1`] = `"vless://8c14d6f7-2e3b-4a91-9d24-3f7a6b8c1e02@override.test:443?type=ws&encryption=none&path=%2Fws&host=cdn.example.test&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=cdn.example.test#parity-test-alice%40example.test"`; + +exports[`genInboundLinks orchestrator > vmess-tcp-tls: byte-stable 1`] = `"vmess://ewogICJ2IjogIjIiLAogICJwcyI6ICJwYXJpdHktdGVzdC1jYXJvbEBleGFtcGxlLnRlc3QiLAogICJhZGQiOiAib3ZlcnJpZGUudGVzdCIsCiAgInBvcnQiOiA4NDQzLAogICJpZCI6ICIxMTExMTExMS0yMjIyLTQzMzMtODQ0NC01NTU1NTU1NTU1NTUiLAogICJzY3kiOiAiYXV0byIsCiAgIm5ldCI6ICJ0Y3AiLAogICJ0bHMiOiAidGxzIiwKICAidHlwZSI6ICJub25lIiwKICAic25pIjogInZtZXNzLmV4YW1wbGUudGVzdCIsCiAgImZwIjogImNocm9tZSIsCiAgImFscG4iOiAiaDIsaHR0cC8xLjEiCn0="`; + +exports[`genInboundLinks orchestrator > wireguard-server: byte-stable 1`] = ` +"[Interface] +PrivateKey = QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0= +Address = 10.0.0.2/32 +DNS = 1.1.1.1, 1.0.0.1 +MTU = 1420 + +# parity-test-1 +[Peer] +PublicKey = Piehk2n8UewhMHMyJiBS+Sxn/OK0FalyFW1GAGzokHM= +AllowedIPs = 0.0.0.0/0, ::/0 +Endpoint = override.test:51820 +PersistentKeepalive = 25 +" +`; + +exports[`genShadowsocksLink > shadowsocks-tcp-2022: byte-stable 1`] = `"ss://MjAyMi1ibGFrZTMtYWVzLTI1Ni1nY206Wm1GclpTMXpaWEoyWlhJdGNHRnpjM2R2Y21RdE1EQXdNUT09OmRHVnpkQzFqYkdsbGJuUXRjR0Z6YzNkdmNtUXRNUT09@example.test:8388?type=tcp#parity-test"`; + +exports[`genTrojanLink > trojan-ws-tls: byte-stable 1`] = `"trojan://trojan-test-pw-XYZ@example.test:443?type=ws&path=%2Ftrojan&host=trojan.example.test&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=trojan.example.test#parity-test"`; + +exports[`genVlessLink > vless-tcp-reality: byte-stable 1`] = `"vless://22222222-3333-4444-9555-666666666666@example.test:443?type=tcp&encryption=none&security=reality&pbk=Tx5yj1bRcOPHkdvT2pIAQ2zh0gQ8m4OPdnzqXJxxV3o&fp=chrome&sid=a3f1&spx=%2F&flow=xtls-rprx-vision#parity-test"`; + +exports[`genVlessLink > vless-ws-tls: byte-stable 1`] = `"vless://8c14d6f7-2e3b-4a91-9d24-3f7a6b8c1e02@example.test:443?type=ws&encryption=none&path=%2Fws&host=cdn.example.test&security=tls&fp=chrome&alpn=h2%2Chttp%2F1.1&sni=cdn.example.test#parity-test"`; + +exports[`genVmessLink > vmess-tcp-tls: byte-stable 1`] = `"vmess://ewogICJ2IjogIjIiLAogICJwcyI6ICJwYXJpdHktdGVzdCIsCiAgImFkZCI6ICJleGFtcGxlLnRlc3QiLAogICJwb3J0IjogODQ0MywKICAiaWQiOiAiMTExMTExMTEtMjIyMi00MzMzLTg0NDQtNTU1NTU1NTU1NTU1IiwKICAic2N5IjogImF1dG8iLAogICJuZXQiOiAidGNwIiwKICAidGxzIjogInRscyIsCiAgInR5cGUiOiAibm9uZSIsCiAgInNuaSI6ICJ2bWVzcy5leGFtcGxlLnRlc3QiLAogICJmcCI6ICJjaHJvbWUiLAogICJhbHBuIjogImgyLGh0dHAvMS4xIgp9"`; + +exports[`genWireguardLink + genWireguardConfig > wireguard-server: byte-stable 1`] = ` +{ + "config": "[Interface] +PrivateKey = QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0= +Address = 10.0.0.2/32 +DNS = 1.1.1.1, 1.0.0.1 +MTU = 1420 + +# wg-peer-1 +[Peer] +PublicKey = Piehk2n8UewhMHMyJiBS+Sxn/OK0FalyFW1GAGzokHM= +AllowedIPs = 0.0.0.0/0, ::/0 +Endpoint = wg.example.test:51820 +PersistentKeepalive = 25 +", + "link": "wireguard://QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0%3D@wg.example.test:51820?publickey=Piehk2n8UewhMHMyJiBS%2BSxn%2FOK0FalyFW1GAGzokHM%3D&address=10.0.0.2%2F32&mtu=1420#wg-peer-1", +} +`; diff --git a/frontend/src/test/__snapshots__/protocol-capabilities.test.ts.snap b/frontend/src/test/__snapshots__/protocol-capabilities.test.ts.snap new file mode 100644 index 00000000..7d8b1737 --- /dev/null +++ b/frontend/src/test/__snapshots__/protocol-capabilities.test.ts.snap @@ -0,0 +1,1681 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`protocol capability predicates > http-basic :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > http-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > hysteria2-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > mixed-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > shadowsocks-2022 :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": true, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: grpc/none 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: tcp/none 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > trojan-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > tunnel-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: grpc/none 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: grpc/reality 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: grpc/tls 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: tcp/none 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: tcp/reality 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": true, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: tcp/tls 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": true, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: xhttp/none 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: xhttp/reality 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vless-tcp-none :: xhttp/tls 1`] = ` +{ + "canEnableReality": true, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > vmess-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": true, + "canEnableTls": true, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: grpc/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: grpc/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: grpc/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: httpupgrade/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: httpupgrade/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: kcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: tcp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: tcp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: tcp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: ws/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: ws/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: xhttp/none 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: xhttp/reality 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; + +exports[`protocol capability predicates > wireguard-basic :: xhttp/tls 1`] = ` +{ + "canEnableReality": false, + "canEnableStream": false, + "canEnableTls": false, + "canEnableTlsFlow": false, + "canEnableVisionSeed": false, + "isSS2022": false, + "isSSMultiUser": true, +} +`; diff --git a/frontend/src/test/headers.test.ts b/frontend/src/test/headers.test.ts index a5ae1452..dcd3a098 100644 --- a/frontend/src/test/headers.test.ts +++ b/frontend/src/test/headers.test.ts @@ -1,10 +1,11 @@ import { describe, expect, it } from 'vitest'; import { getHeaderValue, toHeaders, toV2Headers, type HeaderEntry } from '@/lib/xray/headers'; -import { XrayCommonClass } from '@/models/inbound'; -// Shadow harness: the new pure helpers must agree byte-for-byte with the -// legacy XrayCommonClass static methods. Drift here is a regression. +// Pure-function tests for the header helpers. Snapshots were locked at +// the close of the legacy class migration — at that point toHeaders and +// toV2Headers were verified byte-equal to the legacy XrayCommonClass +// static methods. Drift past this baseline is a regression. const headerMapCases: Array<[string, unknown]> = [ ['null', null], @@ -17,10 +18,10 @@ const headerMapCases: Array<[string, unknown]> = [ ['mixed', { Host: 'a.example.test', 'X-Trace': ['1', '2'] }], ]; -describe('toHeaders parity with XrayCommonClass.toHeaders', () => { +describe('toHeaders', () => { for (const [label, input] of headerMapCases) { it(label, () => { - expect(toHeaders(input)).toEqual(XrayCommonClass.toHeaders(input)); + expect(toHeaders(input)).toMatchSnapshot(); }); } }); @@ -42,18 +43,18 @@ const entryCases: Array<[string, HeaderEntry[]]> = [ ]], ]; -describe('toV2Headers parity (arr=true)', () => { +describe('toV2Headers (arr=true)', () => { for (const [label, input] of entryCases) { it(label, () => { - expect(toV2Headers(input, true)).toEqual(XrayCommonClass.toV2Headers(input, true)); + expect(toV2Headers(input, true)).toMatchSnapshot(); }); } }); -describe('toV2Headers parity (arr=false)', () => { +describe('toV2Headers (arr=false)', () => { for (const [label, input] of entryCases) { it(label, () => { - expect(toV2Headers(input, false)).toEqual(XrayCommonClass.toV2Headers(input, false)); + expect(toV2Headers(input, false)).toMatchSnapshot(); }); } }); diff --git a/frontend/src/test/inbound-link.test.ts b/frontend/src/test/inbound-link.test.ts index b45590ee..6b34ef1e 100644 --- a/frontend/src/test/inbound-link.test.ts +++ b/frontend/src/test/inbound-link.test.ts @@ -12,18 +12,13 @@ import { genWireguardLink, resolveAddr, } from '@/lib/xray/inbound-link'; -import { Inbound as LegacyInbound } from '@/models/inbound'; import { InboundSchema } from '@/schemas/api/inbound'; import type { WireguardInboundSettings } from '@/schemas/protocols/inbound/wireguard'; -// Parity harness for the share-link extraction. For each full inbound -// fixture matching the protocol under test, we: -// 1. Parse with the Zod InboundSchema -> typed input for the new pure fn -// 2. Construct the legacy Inbound class via Inbound.fromJson(fixture) -// 3. Call both link generators with matching args -// 4. Assert the URLs match byte-for-byte -// Drift between the new pure fn and the legacy class method fails the -// test here, before the call sites in pages/ get swapped. +// Snapshot baseline for the share-link generators. Snapshots were locked +// at the close of the legacy class migration — at that point each +// generator was verified byte-equal to the corresponding legacy Inbound +// class method. Future drift past this baseline is a regression. const fullFixtures = import.meta.glob( './golden/fixtures/inbound-full/*.json', @@ -42,140 +37,108 @@ function fixturesForProtocol(protocol: string): Array<[string, Record a.localeCompare(b)); } -describe('genVmessLink parity', () => { +describe('genVmessLink', () => { const fixtures = fixturesForProtocol('vmess'); expect(fixtures.length, 'need at least one vmess full-inbound fixture').toBeGreaterThan(0); for (const [name, raw] of fixtures) { - it(`${name}: matches legacy Inbound.genVmessLink`, () => { + it(`${name}: byte-stable`, () => { const typed = InboundSchema.parse(raw); const settings = (raw as { settings: { clients: Array<{ id: string; security?: string }> } }).settings; const client = settings.clients[0]; - const address = 'example.test'; - const port = typed.port; - const remark = 'parity-test'; - - const newLink = genVmessLink({ + const link = genVmessLink({ inbound: typed, - address, - port, + address: 'example.test', + port: typed.port, forceTls: 'same', - remark, + remark: 'parity-test', clientId: client.id, security: client.security as never, externalProxy: null, }); - - const legacy = LegacyInbound.fromJson(raw); - const legacyLink = legacy.genVmessLink(address, port, 'same', remark, client.id, client.security, null); - - expect(newLink).toBe(legacyLink); + expect(link).toMatchSnapshot(); }); } }); -describe('genVlessLink parity', () => { +describe('genVlessLink', () => { const fixtures = fixturesForProtocol('vless'); expect(fixtures.length, 'need at least one vless full-inbound fixture').toBeGreaterThan(0); for (const [name, raw] of fixtures) { - it(`${name}: matches legacy Inbound.genVLESSLink`, () => { + it(`${name}: byte-stable`, () => { const typed = InboundSchema.parse(raw); const settings = (raw as { settings: { clients: Array<{ id: string; flow?: string }> } }).settings; const client = settings.clients[0]; - const address = 'example.test'; - const port = typed.port; - const remark = 'parity-test'; - - const newLink = genVlessLink({ + const link = genVlessLink({ inbound: typed, - address, - port, + address: 'example.test', + port: typed.port, forceTls: 'same', - remark, + remark: 'parity-test', clientId: client.id, flow: client.flow as never, externalProxy: null, }); - - const legacy = LegacyInbound.fromJson(raw); - const legacyLink = legacy.genVLESSLink(address, port, 'same', remark, client.id, client.flow, null); - - expect(newLink).toBe(legacyLink); + expect(link).toMatchSnapshot(); }); } }); -describe('genTrojanLink parity', () => { +describe('genTrojanLink', () => { const fixtures = fixturesForProtocol('trojan'); expect(fixtures.length, 'need at least one trojan full-inbound fixture').toBeGreaterThan(0); for (const [name, raw] of fixtures) { - it(`${name}: matches legacy Inbound.genTrojanLink`, () => { + it(`${name}: byte-stable`, () => { const typed = InboundSchema.parse(raw); const settings = (raw as { settings: { clients: Array<{ password: string }> } }).settings; const client = settings.clients[0]; - const address = 'example.test'; - const port = typed.port; - const remark = 'parity-test'; - - const newLink = genTrojanLink({ + const link = genTrojanLink({ inbound: typed, - address, - port, + address: 'example.test', + port: typed.port, forceTls: 'same', - remark, + remark: 'parity-test', clientPassword: client.password, externalProxy: null, }); - - const legacy = LegacyInbound.fromJson(raw); - const legacyLink = legacy.genTrojanLink(address, port, 'same', remark, client.password, null); - - expect(newLink).toBe(legacyLink); + expect(link).toMatchSnapshot(); }); } }); -describe('genHysteriaLink parity', () => { +describe('genHysteriaLink', () => { const fixtures = fixturesForProtocol('hysteria'); expect(fixtures.length, 'need at least one hysteria full-inbound fixture').toBeGreaterThan(0); for (const [name, raw] of fixtures) { - it(`${name}: matches legacy Inbound.genHysteriaLink`, () => { + it(`${name}: byte-stable`, () => { const typed = InboundSchema.parse(raw); const settings = (raw as { settings: { clients: Array<{ auth: string }> } }).settings; const client = settings.clients[0]; - const address = 'example.test'; - const port = typed.port; - const remark = 'parity-test'; - - const newLink = genHysteriaLink({ + const link = genHysteriaLink({ inbound: typed, - address, - port, - remark, + address: 'example.test', + port: typed.port, + remark: 'parity-test', clientAuth: client.auth, }); - - const legacy = LegacyInbound.fromJson(raw); - const legacyLink = legacy.genHysteriaLink(address, port, remark, client.auth); - - expect(newLink).toBe(legacyLink); + expect(link).toMatchSnapshot(); }); } }); -describe('genWireguardLink + genWireguardConfig parity', () => { +describe('genWireguardLink + genWireguardConfig', () => { const fixtures = fixturesForProtocol('wireguard'); expect(fixtures.length, 'need at least one wireguard full-inbound fixture').toBeGreaterThan(0); for (const [name, raw] of fixtures) { - it(`${name}: matches legacy getWireguardLink + getWireguardTxt`, () => { + it(`${name}: byte-stable`, () => { const typed = InboundSchema.parse(raw); if (typed.protocol !== 'wireguard') throw new Error('not a wireguard fixture'); // InboundSchema is an intersection of two DUs, so TS can't auto-narrow @@ -183,20 +146,21 @@ describe('genWireguardLink + genWireguardConfig parity', () => { // check; this cast just helps the type checker. const settings = typed.settings as WireguardInboundSettings; - const address = 'wg.example.test'; - const port = typed.port; - const remark = 'wg-peer-1'; - const peerIndex = 0; - - const newLink = genWireguardLink({ settings, address, port, remark, peerIndex }); - const newConfig = genWireguardConfig({ settings, address, port, remark, peerIndex }); - - const legacy = LegacyInbound.fromJson(raw); - const legacyLink = legacy.getWireguardLink(address, port, remark, peerIndex); - const legacyConfig = legacy.getWireguardTxt(address, port, remark, peerIndex); - - expect(newLink).toBe(legacyLink); - expect(newConfig).toBe(legacyConfig); + const link = genWireguardLink({ + settings, + address: 'wg.example.test', + port: typed.port, + remark: 'wg-peer-1', + peerIndex: 0, + }); + const config = genWireguardConfig({ + settings, + address: 'wg.example.test', + port: typed.port, + remark: 'wg-peer-1', + peerIndex: 0, + }); + expect({ link, config }).toMatchSnapshot(); }); } }); @@ -241,72 +205,53 @@ describe('resolveAddr precedence', () => { }); }); -describe('genInboundLinks orchestrator parity', () => { +describe('genInboundLinks orchestrator', () => { // Every full-inbound fixture should produce the same \r\n-joined link - // block as the legacy Inbound.genInboundLinks. Pass hostOverride - // explicitly so neither pipeline reaches for location.hostname. + // block at this baseline. const fixtures = Object.entries(fullFixtures) .map(([path, raw]): [string, Record] => [fixtureName(path), raw as Record]) .sort(([a], [b]) => a.localeCompare(b)); for (const [name, raw] of fixtures) { const protocol = (raw as { protocol?: string }).protocol; - // Skip protocols the legacy class can't dispatch (hysteria2 has no - // dispatch case; getSettings(protocol) returns null and crashes - // genHysteriaLink). Orchestrator-level parity covers the others. + // Skip hysteria2 — the legacy class had no dispatch case at the time + // the baseline was locked, so no snapshot exists. The new orchestrator + // covers it via its own logic and the genHysteriaLink unit test. if (protocol === 'hysteria2') continue; - it(`${name}: matches legacy Inbound.genInboundLinks`, () => { + it(`${name}: byte-stable`, () => { const typed = InboundSchema.parse(raw); - - const remark = 'parity-test'; - const hostOverride = 'override.test'; - const fallbackHostname = 'fallback.test'; - - const newBlock = genInboundLinks({ + const block = genInboundLinks({ inbound: typed, - remark, - hostOverride, - fallbackHostname, + remark: 'parity-test', + hostOverride: 'override.test', + fallbackHostname: 'fallback.test', }); - - const legacy = LegacyInbound.fromJson(raw); - const legacyBlock = legacy.genInboundLinks(remark, '-ieo', hostOverride); - - expect(newBlock).toBe(legacyBlock); + expect(block).toMatchSnapshot(); }); } }); -describe('genShadowsocksLink parity', () => { +describe('genShadowsocksLink', () => { const fixtures = fixturesForProtocol('shadowsocks'); expect(fixtures.length, 'need at least one shadowsocks full-inbound fixture').toBeGreaterThan(0); for (const [name, raw] of fixtures) { - it(`${name}: matches legacy Inbound.genSSLink`, () => { + it(`${name}: byte-stable`, () => { const typed = InboundSchema.parse(raw); const settings = (raw as { settings: { clients?: Array<{ password: string }> } }).settings; const client = settings.clients?.[0]; - const address = 'example.test'; - const port = typed.port; - const remark = 'parity-test'; - const clientPassword = client?.password ?? ''; - - const newLink = genShadowsocksLink({ + const link = genShadowsocksLink({ inbound: typed, - address, - port, + address: 'example.test', + port: typed.port, forceTls: 'same', - remark, - clientPassword, + remark: 'parity-test', + clientPassword: client?.password ?? '', externalProxy: null, }); - - const legacy = LegacyInbound.fromJson(raw); - const legacyLink = legacy.genSSLink(address, port, 'same', remark, clientPassword, null); - - expect(newLink).toBe(legacyLink); + expect(link).toMatchSnapshot(); }); } }); diff --git a/frontend/src/test/protocol-capabilities.test.ts b/frontend/src/test/protocol-capabilities.test.ts index 77520f77..c1082be4 100644 --- a/frontend/src/test/protocol-capabilities.test.ts +++ b/frontend/src/test/protocol-capabilities.test.ts @@ -1,7 +1,6 @@ /// import { describe, expect, it } from 'vitest'; -import { Inbound } from '@/models/inbound'; import { canEnableTls, canEnableReality, @@ -12,13 +11,10 @@ import { isSSMultiUser, } from '@/lib/xray/protocol-capabilities'; -// Parity harness for the capability predicates. For each golden fixture -// (protocol+settings), cross with a matrix of stream configurations -// (network × security), build the legacy Inbound class via fromJson, and -// assert each pure-function predicate matches the class method. -// -// Only the (protocol × stream-shape) cross matters here — the predicates -// never read sniffing/port/listen, so we hold those constant. +// Pure-function tests for the capability predicates. Each fixture × stream +// case is locked via snapshot — these were captured at the close of the +// legacy class migration and verified byte-equal to the legacy Inbound +// class instance methods. Drift past this baseline is a regression. const fixtures = import.meta.glob( './golden/fixtures/inbound/*.json', @@ -48,7 +44,7 @@ function fixtureName(path: string): string { return (path.split('/').pop() ?? path).replace(/\.json$/, ''); } -describe('protocol capability predicates: pure ↔ legacy parity', () => { +describe('protocol capability predicates', () => { const entries = Object.entries(fixtures).sort(([a], [b]) => a.localeCompare(b)); for (const [path, raw] of entries) { const name = fixtureName(path); @@ -57,28 +53,21 @@ describe('protocol capability predicates: pure ↔ legacy parity', () => { for (const stream of STREAM_CASES) { it(`${name} :: ${stream.network}/${stream.security}`, () => { - const wireConfig = { - port: 12345, - listen: '127.0.0.1', - protocol: fix.protocol, - settings: fix.settings, - streamSettings: { network: stream.network, security: stream.security }, - sniffing: {}, - }; - const legacy = Inbound.fromJson(wireConfig); const values = { protocol: fix.protocol, streamSettings: { network: stream.network, security: stream.security }, settings: fix.settings, }; - - expect(canEnableTls(values)).toBe(legacy.canEnableTls()); - expect(canEnableReality(values)).toBe(legacy.canEnableReality()); - expect(canEnableTlsFlow(values)).toBe(legacy.canEnableTlsFlow()); - expect(canEnableStream(values)).toBe(legacy.canEnableStream()); - expect(canEnableVisionSeed(values)).toBe(legacy.canEnableVisionSeed()); - expect(isSS2022(values)).toBe(legacy.isSS2022); - expect(isSSMultiUser(values)).toBe(legacy.isSSMultiUser); + const result = { + canEnableTls: canEnableTls(values), + canEnableReality: canEnableReality(values), + canEnableTlsFlow: canEnableTlsFlow(values), + canEnableStream: canEnableStream(values), + canEnableVisionSeed: canEnableVisionSeed(values), + isSS2022: isSS2022(values), + isSSMultiUser: isSSMultiUser(values), + }; + expect(result).toMatchSnapshot(); }); } } diff --git a/frontend/src/test/shadow.test.ts b/frontend/src/test/shadow.test.ts deleted file mode 100644 index b76dfe46..00000000 --- a/frontend/src/test/shadow.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -/// -import { describe, expect, it } from 'vitest'; - -import { Inbound } from '@/models/inbound'; -import { InboundSettingsSchema } from '@/schemas/protocols'; - -// Walks every inbound golden fixture through both pipelines: -// OLD: Inbound.Settings.fromJson(protocol, raw.settings).toJson() -// NEW: InboundSettingsSchema.parse(raw).settings -// Then canonicalizes (deep key-sort, undefined-strip via JSON round-trip) -// and asserts byte-equality. This is the safety net for Step 3d — once we -// start extracting class methods into lib/xray/* pure functions, any -// normalization drift trips a snapshot diff here. - -const fixtures = import.meta.glob( - './golden/fixtures/inbound/*.json', - { eager: true, import: 'default' }, -); - -type FixtureShape = { protocol: string; settings: unknown }; - -// The OLD panel class collapses hysteria + hysteria2 onto a single -// HysteriaSettings (distinguished only by `version`), so when a fixture -// carries the wire-level hysteria2 protocol literal we dispatch to the -// HYSTERIA branch on the legacy side. -function legacyProtocolFor(protocol: string): string { - if (protocol === 'hysteria2') return 'hysteria'; - return protocol; -} - -// Drops empty arrays and undefined/null fields, then sorts keys. The legacy -// class's toJson() omits optional fields whose value is the empty array -// (e.g. fallbacks: []); the Zod schema includes them because of .default([]). -// Both represent the same wire state, so we treat them as equivalent here. -function canonicalize(value: unknown): string { - function normalize(v: unknown): unknown { - if (Array.isArray(v)) { - const items = v.map(normalize).filter((x) => x !== undefined); - return items.length === 0 ? undefined : items; - } - if (v && typeof v === 'object') { - const entries = Object.entries(v as Record) - .map(([k, val]) => [k, normalize(val)] as const) - .filter(([, val]) => val !== undefined && val !== null) - .sort(([a], [b]) => a.localeCompare(b)); - return entries.length === 0 ? undefined : Object.fromEntries(entries); - } - return v; - } - return JSON.stringify(normalize(value) ?? null); -} - -function fixtureName(path: string): string { - const file = path.split('/').pop() ?? path; - return file.replace(/\.json$/, ''); -} - -describe('shadow parse: legacy class vs Zod schema', () => { - const entries = Object.entries(fixtures).sort(([a], [b]) => a.localeCompare(b)); - - for (const [path, raw] of entries) { - const fixture = raw as FixtureShape; - const name = fixtureName(path); - - it(`${name}: legacy toJson() and Zod parse converge`, () => { - const legacyInstance = Inbound.Settings.fromJson( - legacyProtocolFor(fixture.protocol), - fixture.settings, - ); - expect(legacyInstance, `legacy dispatch returned null for ${fixture.protocol}`).not.toBeNull(); - const legacyJson = legacyInstance.toJson(); - - const zodParsed = InboundSettingsSchema.parse(fixture); - - expect(canonicalize(zodParsed.settings)).toBe(canonicalize(legacyJson)); - }); - } -});