mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-11 07:06:06 +00:00
7605902324
* test(audit): add gremlins/rapid/coverage tooling + AUDIT.md scaffold * test(audit): hygiene sweep (race-clean except logger global; Finding #2) + smell inventory * test(audit): cover untested error/edge branches (TLS proxy+pin, migration tag cleanup=Finding #1) * test(audit): strengthen internal/sub link tests (dedup key, TLS/Reality mapping, clash well-formedness) * test(audit): property (rapid) + fuzz tests for joinHostPort/userinfo/pin/ParseLink * test(audit): tighten frontend subSortIndex rejection assertions + wire coverage * ci(audit): add shuffle gate + non-blocking race job (Finding #2) + fuzz-smoke; document mutation policy * chore(audit): gitignore frontend coverage output * test(audit): exhaustive whole-repo pass — strengthen 5 weak/fake tests (netproxy, CSP, modal per-protocol loops, schema coercions) * docs(contributing): add Testing section (conventions, race/shuffle, fuzz, mutation policy); drop AUDIT.md ledger * fix(logger,migration): guard logBuffer with mutex; execute legacy tag cleanup (tx.Exec); make CI race gate blocking * ci(mutation): add nightly scoped gremlins workflow (informational artifacts) * test(audit): strengthen runtime tests — baseURL scheme/port bounds, isNonEmptySlice, trafficReset * test(audit): strengthen clash tests — reality field mapping + tcp-header validation * test(audit): runtime — egress-proxy + content-type tests; drop redundant bp=='' branch * test(audit): strengthen link parser/helper tests (defaultPort, splitComma, base64, canonicalQuery, tls/reality/transport mapping) * test(audit): strengthen sub/xray/common/netsafe/mtproto/config/middleware tests (kill surviving mutants) * test(audit): raise timeout on protocol-iteration modal tests (heavy re-renders, slow on CI) * fix(logger): GetLogs returns at most c entries (off-by-one fix; addresses PR review) * perf(logger): snapshot logBuffer under lock so GetLogs doesn't block logging; clarify fuzz-seed docs (addresses PR review)
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
package sub
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
// shareLinkInbound builds a VLESS inbound with one client and the given stream
|
|
// settings, mirroring flowTestInbound but without forcing a flow.
|
|
func shareLinkInbound(streamSettings string) *model.Inbound {
|
|
return &model.Inbound{
|
|
Listen: "203.0.113.1",
|
|
Port: 443,
|
|
Protocol: model.VLESS,
|
|
Remark: "sharelink",
|
|
Settings: `{"clients":[{"id":"11111111-2222-4333-8444-555555555555","email":"user"}],"decryption":"none","encryption":"none"}`,
|
|
StreamSettings: streamSettings,
|
|
}
|
|
}
|
|
|
|
// TestGenVlessLink_TLSParamsMapped locks every field that applyShareTLSParams
|
|
// (service.go:1029) writes into a TLS share link. Without these assertions a mutant
|
|
// that drops `sni`, swaps a key, or skips `pcs`/`alpn`/`fp` survives the whole suite —
|
|
// the existing flow tests only check `flow=`.
|
|
func TestGenVlessLink_TLSParamsMapped(t *testing.T) {
|
|
stream := `{
|
|
"network":"tcp","security":"tls",
|
|
"tcpSettings":{"header":{"type":"none"}},
|
|
"tlsSettings":{
|
|
"serverName":"sni.example.com",
|
|
"alpn":["h2","http/1.1"],
|
|
"settings":{"fingerprint":"chrome","pinnedPeerCertSha256":["YWJj"]}
|
|
}
|
|
}`
|
|
s := &SubService{remarkModel: "-ieo"}
|
|
link := s.genVlessLink(shareLinkInbound(stream), "user")
|
|
|
|
// url.Values.Encode() percent-encodes values: "," -> %2C, "/" -> %2F.
|
|
wants := []string{
|
|
"security=tls",
|
|
"sni=sni.example.com",
|
|
"fp=chrome",
|
|
"alpn=h2%2Chttp%2F1.1",
|
|
"pcs=YWJj",
|
|
}
|
|
for _, w := range wants {
|
|
if !strings.Contains(link, w) {
|
|
t.Fatalf("TLS link missing %q\n got: %s", w, link)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestGenVlessLink_RealityParamsMapped locks the reality field mapping
|
|
// (applyShareRealityParams, service.go:1147). serverNames/shortIds are single-element
|
|
// so random.Num is deterministic (index 0); spx is random so it is asserted by prefix.
|
|
// Distinct pbk/sid values catch a pbk<->sid swap mutant.
|
|
func TestGenVlessLink_RealityParamsMapped(t *testing.T) {
|
|
stream := `{
|
|
"network":"tcp","security":"reality",
|
|
"tcpSettings":{"header":{"type":"none"}},
|
|
"realitySettings":{
|
|
"serverNames":["reality.example.com"],
|
|
"shortIds":["ab12cd"],
|
|
"settings":{"publicKey":"PBKvalue","fingerprint":"firefox"}
|
|
}
|
|
}`
|
|
s := &SubService{remarkModel: "-ieo"}
|
|
link := s.genVlessLink(shareLinkInbound(stream), "user")
|
|
|
|
wants := []string{
|
|
"security=reality",
|
|
"sni=reality.example.com",
|
|
"pbk=PBKvalue",
|
|
"sid=ab12cd",
|
|
"fp=firefox",
|
|
"spx=%2F", // "/" + random.Seq(15), percent-encoded leading slash
|
|
}
|
|
for _, w := range wants {
|
|
if !strings.Contains(link, w) {
|
|
t.Fatalf("reality link missing %q\n got: %s", w, link)
|
|
}
|
|
}
|
|
// A pbk<->sid swap must not silently pass: pbk must not carry the shortId.
|
|
if strings.Contains(link, "pbk=ab12cd") || strings.Contains(link, "sid=PBKvalue") {
|
|
t.Fatalf("reality pbk/sid mapping crossed: %s", link)
|
|
}
|
|
}
|