From 2176e816f0ade18d55a890cd23897eadc4ab8b94 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Mon, 25 May 2026 23:26:27 +0200 Subject: [PATCH] test(frontend): broaden golden coverage to remaining inbounds + stream + security DUs Round out Step 3b. Four more inbound fixtures complete the protocol set (http with two accounts, mixed with socks-style auth, tunnel with a port map, hysteria v1). Two parallel test files cover the other DUs: stream.test.ts walks tcp/ws/grpc fixtures through NetworkSettingsSchema, and security.test.ts walks none/tls/reality through SecuritySettingsSchema. Snapshot count is now 16 across three test files. The reality fixture locks in the array form of serverNames/shortIds (the panel class stores them comma-joined internally but they ship as arrays on the wire). The TLS fixture pins the file-vs-inline cert DU on the file branch. Stream coverage for httpupgrade/xhttp/kcp and security mixed-with-stream combos follow in the next turn, alongside the shadow harness. --- .../test/__snapshots__/protocols.test.ts.snap | 75 +++++++++++++++++++ .../test/__snapshots__/security.test.ts.snap | 72 ++++++++++++++++++ .../test/__snapshots__/stream.test.ts.snap | 34 +++++++++ .../golden/fixtures/inbound/http-basic.json | 10 +++ .../fixtures/inbound/hysteria-basic.json | 20 +++++ .../golden/fixtures/inbound/mixed-basic.json | 11 +++ .../golden/fixtures/inbound/tunnel-basic.json | 13 ++++ .../test/golden/fixtures/security/none.json | 3 + .../fixtures/security/reality-basic.json | 22 ++++++ .../fixtures/security/tls-cert-file.json | 27 +++++++ .../golden/fixtures/stream/grpc-basic.json | 8 ++ .../test/golden/fixtures/stream/tcp-none.json | 4 + .../golden/fixtures/stream/ws-default.json | 12 +++ frontend/src/test/security.test.ts | 26 +++++++ frontend/src/test/stream.test.ts | 26 +++++++ 15 files changed, 363 insertions(+) create mode 100644 frontend/src/test/__snapshots__/security.test.ts.snap create mode 100644 frontend/src/test/__snapshots__/stream.test.ts.snap create mode 100644 frontend/src/test/golden/fixtures/inbound/http-basic.json create mode 100644 frontend/src/test/golden/fixtures/inbound/hysteria-basic.json create mode 100644 frontend/src/test/golden/fixtures/inbound/mixed-basic.json create mode 100644 frontend/src/test/golden/fixtures/inbound/tunnel-basic.json create mode 100644 frontend/src/test/golden/fixtures/security/none.json create mode 100644 frontend/src/test/golden/fixtures/security/reality-basic.json create mode 100644 frontend/src/test/golden/fixtures/security/tls-cert-file.json create mode 100644 frontend/src/test/golden/fixtures/stream/grpc-basic.json create mode 100644 frontend/src/test/golden/fixtures/stream/tcp-none.json create mode 100644 frontend/src/test/golden/fixtures/stream/ws-default.json create mode 100644 frontend/src/test/security.test.ts create mode 100644 frontend/src/test/stream.test.ts diff --git a/frontend/src/test/__snapshots__/protocols.test.ts.snap b/frontend/src/test/__snapshots__/protocols.test.ts.snap index 6682fe48..5ac334f2 100644 --- a/frontend/src/test/__snapshots__/protocols.test.ts.snap +++ b/frontend/src/test/__snapshots__/protocols.test.ts.snap @@ -1,5 +1,47 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`InboundSettingsSchema fixtures > parses http-basic byte-stably 1`] = ` +{ + "protocol": "http", + "settings": { + "accounts": [ + { + "pass": "proxypass", + "user": "proxyuser", + }, + { + "pass": "guest123", + "user": "guest", + }, + ], + "allowTransparent": false, + }, +} +`; + +exports[`InboundSettingsSchema fixtures > parses hysteria-basic byte-stably 1`] = ` +{ + "protocol": "hysteria", + "settings": { + "clients": [ + { + "auth": "hyst3ria-v1-token-XYZ", + "comment": "legacy v1", + "email": "hy1-client@example.test", + "enable": true, + "expiryTime": 0, + "limitIp": 0, + "reset": 0, + "subId": "hy1-001", + "tgId": 0, + "totalGB": 0, + }, + ], + "version": 1, + }, +} +`; + exports[`InboundSettingsSchema fixtures > parses hysteria2-basic byte-stably 1`] = ` { "protocol": "hysteria2", @@ -23,6 +65,23 @@ exports[`InboundSettingsSchema fixtures > parses hysteria2-basic byte-stably 1`] } `; +exports[`InboundSettingsSchema fixtures > parses mixed-basic byte-stably 1`] = ` +{ + "protocol": "mixed", + "settings": { + "accounts": [ + { + "pass": "sockspass", + "user": "socksuser", + }, + ], + "auth": "password", + "ip": "127.0.0.1", + "udp": true, + }, +} +`; + exports[`InboundSettingsSchema fixtures > parses shadowsocks-2022 byte-stably 1`] = ` { "protocol": "shadowsocks", @@ -73,6 +132,22 @@ exports[`InboundSettingsSchema fixtures > parses trojan-basic byte-stably 1`] = } `; +exports[`InboundSettingsSchema fixtures > parses tunnel-basic byte-stably 1`] = ` +{ + "protocol": "tunnel", + "settings": { + "allowedNetwork": "tcp,udp", + "followRedirect": false, + "portMap": { + "8080": "10.0.0.5:80", + "8443": "10.0.0.5:443", + }, + "rewriteAddress": "1.1.1.1", + "rewritePort": 53, + }, +} +`; + exports[`InboundSettingsSchema fixtures > parses vless-tcp-none byte-stably 1`] = ` { "protocol": "vless", diff --git a/frontend/src/test/__snapshots__/security.test.ts.snap b/frontend/src/test/__snapshots__/security.test.ts.snap new file mode 100644 index 00000000..bbca671f --- /dev/null +++ b/frontend/src/test/__snapshots__/security.test.ts.snap @@ -0,0 +1,72 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`SecuritySettingsSchema fixtures > parses none byte-stably 1`] = ` +{ + "security": "none", +} +`; + +exports[`SecuritySettingsSchema fixtures > parses reality-basic byte-stably 1`] = ` +{ + "realitySettings": { + "maxClientVer": "", + "maxTimediff": 0, + "minClientVer": "", + "mldsa65Seed": "", + "privateKey": "wM-2_oQRWXyLcXhV5q1ifTBcS3K8mYR3wQI3PqGFK1k", + "serverNames": [ + "yahoo.com", + "www.yahoo.com", + ], + "settings": { + "fingerprint": "chrome", + "mldsa65Verify": "", + "publicKey": "Tx5yj1bRcOPHkdvT2pIAQ2zh0gQ8m4OPdnzqXJxxV3o", + "serverName": "", + "spiderX": "/", + }, + "shortIds": [ + "a3f1", + "b8c2", + "d9e4", + ], + "show": false, + "target": "yahoo.com:443", + "xver": 0, + }, + "security": "reality", +} +`; + +exports[`SecuritySettingsSchema fixtures > parses tls-cert-file byte-stably 1`] = ` +{ + "security": "tls", + "tlsSettings": { + "alpn": [ + "h2", + "http/1.1", + ], + "certificates": [ + { + "buildChain": false, + "certificateFile": "/etc/ssl/certs/cdn.example.test.crt", + "keyFile": "/etc/ssl/private/cdn.example.test.key", + "oneTimeLoading": false, + "usage": "encipherment", + }, + ], + "cipherSuites": "", + "disableSystemRoot": false, + "echServerKeys": "", + "enableSessionResumption": false, + "maxVersion": "1.3", + "minVersion": "1.2", + "rejectUnknownSni": false, + "serverName": "cdn.example.test", + "settings": { + "echConfigList": "", + "fingerprint": "chrome", + }, + }, +} +`; diff --git a/frontend/src/test/__snapshots__/stream.test.ts.snap b/frontend/src/test/__snapshots__/stream.test.ts.snap new file mode 100644 index 00000000..616152af --- /dev/null +++ b/frontend/src/test/__snapshots__/stream.test.ts.snap @@ -0,0 +1,34 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`NetworkSettingsSchema fixtures > parses grpc-basic byte-stably 1`] = ` +{ + "grpcSettings": { + "authority": "grpc.example.test", + "multiMode": false, + "serviceName": "GunService", + }, + "network": "grpc", +} +`; + +exports[`NetworkSettingsSchema fixtures > parses tcp-none byte-stably 1`] = ` +{ + "network": "tcp", + "tcpSettings": {}, +} +`; + +exports[`NetworkSettingsSchema fixtures > parses ws-default byte-stably 1`] = ` +{ + "network": "ws", + "wsSettings": { + "acceptProxyProtocol": false, + "headers": { + "X-Forwarded-Proto": "https", + }, + "heartbeatPeriod": 30, + "host": "cdn.example.test", + "path": "/api/v2", + }, +} +`; diff --git a/frontend/src/test/golden/fixtures/inbound/http-basic.json b/frontend/src/test/golden/fixtures/inbound/http-basic.json new file mode 100644 index 00000000..c07c3fd9 --- /dev/null +++ b/frontend/src/test/golden/fixtures/inbound/http-basic.json @@ -0,0 +1,10 @@ +{ + "protocol": "http", + "settings": { + "accounts": [ + { "user": "proxyuser", "pass": "proxypass" }, + { "user": "guest", "pass": "guest123" } + ], + "allowTransparent": false + } +} diff --git a/frontend/src/test/golden/fixtures/inbound/hysteria-basic.json b/frontend/src/test/golden/fixtures/inbound/hysteria-basic.json new file mode 100644 index 00000000..77793d88 --- /dev/null +++ b/frontend/src/test/golden/fixtures/inbound/hysteria-basic.json @@ -0,0 +1,20 @@ +{ + "protocol": "hysteria", + "settings": { + "version": 1, + "clients": [ + { + "auth": "hyst3ria-v1-token-XYZ", + "email": "hy1-client@example.test", + "limitIp": 0, + "totalGB": 0, + "expiryTime": 0, + "enable": true, + "tgId": 0, + "subId": "hy1-001", + "comment": "legacy v1", + "reset": 0 + } + ] + } +} diff --git a/frontend/src/test/golden/fixtures/inbound/mixed-basic.json b/frontend/src/test/golden/fixtures/inbound/mixed-basic.json new file mode 100644 index 00000000..6754fe32 --- /dev/null +++ b/frontend/src/test/golden/fixtures/inbound/mixed-basic.json @@ -0,0 +1,11 @@ +{ + "protocol": "mixed", + "settings": { + "auth": "password", + "accounts": [ + { "user": "socksuser", "pass": "sockspass" } + ], + "udp": true, + "ip": "127.0.0.1" + } +} diff --git a/frontend/src/test/golden/fixtures/inbound/tunnel-basic.json b/frontend/src/test/golden/fixtures/inbound/tunnel-basic.json new file mode 100644 index 00000000..901afd32 --- /dev/null +++ b/frontend/src/test/golden/fixtures/inbound/tunnel-basic.json @@ -0,0 +1,13 @@ +{ + "protocol": "tunnel", + "settings": { + "rewriteAddress": "1.1.1.1", + "rewritePort": 53, + "portMap": { + "8080": "10.0.0.5:80", + "8443": "10.0.0.5:443" + }, + "allowedNetwork": "tcp,udp", + "followRedirect": false + } +} diff --git a/frontend/src/test/golden/fixtures/security/none.json b/frontend/src/test/golden/fixtures/security/none.json new file mode 100644 index 00000000..2cb07736 --- /dev/null +++ b/frontend/src/test/golden/fixtures/security/none.json @@ -0,0 +1,3 @@ +{ + "security": "none" +} diff --git a/frontend/src/test/golden/fixtures/security/reality-basic.json b/frontend/src/test/golden/fixtures/security/reality-basic.json new file mode 100644 index 00000000..f9434e2f --- /dev/null +++ b/frontend/src/test/golden/fixtures/security/reality-basic.json @@ -0,0 +1,22 @@ +{ + "security": "reality", + "realitySettings": { + "show": false, + "xver": 0, + "target": "yahoo.com:443", + "serverNames": ["yahoo.com", "www.yahoo.com"], + "privateKey": "wM-2_oQRWXyLcXhV5q1ifTBcS3K8mYR3wQI3PqGFK1k", + "minClientVer": "", + "maxClientVer": "", + "maxTimediff": 0, + "shortIds": ["a3f1", "b8c2", "d9e4"], + "mldsa65Seed": "", + "settings": { + "publicKey": "Tx5yj1bRcOPHkdvT2pIAQ2zh0gQ8m4OPdnzqXJxxV3o", + "fingerprint": "chrome", + "serverName": "", + "spiderX": "/", + "mldsa65Verify": "" + } + } +} diff --git a/frontend/src/test/golden/fixtures/security/tls-cert-file.json b/frontend/src/test/golden/fixtures/security/tls-cert-file.json new file mode 100644 index 00000000..1590f800 --- /dev/null +++ b/frontend/src/test/golden/fixtures/security/tls-cert-file.json @@ -0,0 +1,27 @@ +{ + "security": "tls", + "tlsSettings": { + "serverName": "cdn.example.test", + "minVersion": "1.2", + "maxVersion": "1.3", + "cipherSuites": "", + "rejectUnknownSni": false, + "disableSystemRoot": false, + "enableSessionResumption": false, + "certificates": [ + { + "certificateFile": "/etc/ssl/certs/cdn.example.test.crt", + "keyFile": "/etc/ssl/private/cdn.example.test.key", + "oneTimeLoading": false, + "usage": "encipherment", + "buildChain": false + } + ], + "alpn": ["h2", "http/1.1"], + "echServerKeys": "", + "settings": { + "fingerprint": "chrome", + "echConfigList": "" + } + } +} diff --git a/frontend/src/test/golden/fixtures/stream/grpc-basic.json b/frontend/src/test/golden/fixtures/stream/grpc-basic.json new file mode 100644 index 00000000..e1fb8824 --- /dev/null +++ b/frontend/src/test/golden/fixtures/stream/grpc-basic.json @@ -0,0 +1,8 @@ +{ + "network": "grpc", + "grpcSettings": { + "serviceName": "GunService", + "authority": "grpc.example.test", + "multiMode": false + } +} diff --git a/frontend/src/test/golden/fixtures/stream/tcp-none.json b/frontend/src/test/golden/fixtures/stream/tcp-none.json new file mode 100644 index 00000000..d0e3b193 --- /dev/null +++ b/frontend/src/test/golden/fixtures/stream/tcp-none.json @@ -0,0 +1,4 @@ +{ + "network": "tcp", + "tcpSettings": {} +} diff --git a/frontend/src/test/golden/fixtures/stream/ws-default.json b/frontend/src/test/golden/fixtures/stream/ws-default.json new file mode 100644 index 00000000..64cc3bdd --- /dev/null +++ b/frontend/src/test/golden/fixtures/stream/ws-default.json @@ -0,0 +1,12 @@ +{ + "network": "ws", + "wsSettings": { + "acceptProxyProtocol": false, + "path": "/api/v2", + "host": "cdn.example.test", + "headers": { + "X-Forwarded-Proto": "https" + }, + "heartbeatPeriod": 30 + } +} diff --git a/frontend/src/test/security.test.ts b/frontend/src/test/security.test.ts new file mode 100644 index 00000000..f9407315 --- /dev/null +++ b/frontend/src/test/security.test.ts @@ -0,0 +1,26 @@ +/// +import { describe, expect, it } from 'vitest'; + +import { SecuritySettingsSchema } from '@/schemas/protocols'; + +const securityFixtures = import.meta.glob( + './golden/fixtures/security/*.json', + { eager: true, import: 'default' }, +); + +function fixtureName(path: string): string { + const file = path.split('/').pop() ?? path; + return file.replace(/\.json$/, ''); +} + +describe('SecuritySettingsSchema fixtures', () => { + const entries = Object.entries(securityFixtures).sort(([a], [b]) => a.localeCompare(b)); + expect(entries.length, 'expected at least one fixture under golden/fixtures/security').toBeGreaterThan(0); + + for (const [path, raw] of entries) { + it(`parses ${fixtureName(path)} byte-stably`, () => { + const parsed = SecuritySettingsSchema.parse(raw); + expect(parsed).toMatchSnapshot(); + }); + } +}); diff --git a/frontend/src/test/stream.test.ts b/frontend/src/test/stream.test.ts new file mode 100644 index 00000000..7621a18c --- /dev/null +++ b/frontend/src/test/stream.test.ts @@ -0,0 +1,26 @@ +/// +import { describe, expect, it } from 'vitest'; + +import { NetworkSettingsSchema } from '@/schemas/protocols'; + +const streamFixtures = import.meta.glob( + './golden/fixtures/stream/*.json', + { eager: true, import: 'default' }, +); + +function fixtureName(path: string): string { + const file = path.split('/').pop() ?? path; + return file.replace(/\.json$/, ''); +} + +describe('NetworkSettingsSchema fixtures', () => { + const entries = Object.entries(streamFixtures).sort(([a], [b]) => a.localeCompare(b)); + expect(entries.length, 'expected at least one fixture under golden/fixtures/stream').toBeGreaterThan(0); + + for (const [path, raw] of entries) { + it(`parses ${fixtureName(path)} byte-stably`, () => { + const parsed = NetworkSettingsSchema.parse(raw); + expect(parsed).toMatchSnapshot(); + }); + } +});