| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- package model
- import (
- "encoding/json"
- "strings"
- "testing"
- )
- func settingsVersion(t *testing.T, settings string) any {
- t.Helper()
- var parsed map[string]any
- if err := json.Unmarshal([]byte(settings), &parsed); err != nil {
- t.Fatalf("unmarshal settings: %v", err)
- }
- return parsed["version"]
- }
- func TestHealHysteriaVersion(t *testing.T) {
- tests := []struct {
- name string
- settings string
- wantChanged bool
- wantVersion any
- }{
- {
- name: "legacy v1 row",
- settings: `{"version":1,"clients":[{"auth":"tok","email":"a@x"}]}`,
- wantChanged: true,
- wantVersion: float64(2),
- },
- {
- name: "no version at all",
- settings: `{"clients":[{"auth":"tok","email":"a@x"}]}`,
- wantChanged: true,
- wantVersion: float64(2),
- },
- {
- name: "already v2",
- settings: `{"version":2,"clients":[]}`,
- wantChanged: false,
- wantVersion: float64(2),
- },
- {
- name: "version as a string",
- settings: `{"version":"1","clients":[]}`,
- wantChanged: true,
- wantVersion: float64(2),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- healed, changed := HealHysteriaVersion(tt.settings)
- if changed != tt.wantChanged {
- t.Fatalf("changed = %v, want %v", changed, tt.wantChanged)
- }
- if got := settingsVersion(t, healed); got != tt.wantVersion {
- t.Fatalf("version = %#v, want %#v", got, tt.wantVersion)
- }
- })
- }
- }
- func TestHealHysteriaVersionKeepsClients(t *testing.T) {
- healed, changed := HealHysteriaVersion(`{"version":1,"clients":[{"auth":"tok","email":"a@x"}]}`)
- if !changed {
- t.Fatal("a v1 row must be healed")
- }
- if !strings.Contains(healed, `"auth": "tok"`) || !strings.Contains(healed, `"email": "a@x"`) {
- t.Fatalf("healing dropped client data: %s", healed)
- }
- }
- func TestHealHysteriaVersionLeavesUnparsableSettings(t *testing.T) {
- const broken = `{"version":1,`
- healed, changed := HealHysteriaVersion(broken)
- if changed || healed != broken {
- t.Fatalf("unparsable settings must be left alone, got changed=%v %q", changed, healed)
- }
- if healed, changed := HealHysteriaVersion(""); changed || healed != "" {
- t.Fatalf("empty settings must be left alone, got changed=%v %q", changed, healed)
- }
- }
- func TestHealHysteriaStreamVersion(t *testing.T) {
- healed, changed := HealHysteriaStreamVersion(`{"network":"hysteria","hysteriaSettings":{"version":1,"udpIdleTimeout":60}}`)
- if !changed {
- t.Fatal("a v1 transport must be healed")
- }
- var parsed map[string]any
- if err := json.Unmarshal([]byte(healed), &parsed); err != nil {
- t.Fatalf("unmarshal: %v", err)
- }
- hysteria, _ := parsed["hysteriaSettings"].(map[string]any)
- if hysteria["version"] != float64(2) {
- t.Fatalf("version = %#v, want 2", hysteria["version"])
- }
- if hysteria["udpIdleTimeout"] != float64(60) {
- t.Fatalf("healing dropped transport settings: %#v", hysteria)
- }
- }
- func TestHealHysteriaStreamVersionWithoutHysteriaSettings(t *testing.T) {
- const stream = `{"network":"tcp","tcpSettings":{}}`
- healed, changed := HealHysteriaStreamVersion(stream)
- if changed || healed != stream {
- t.Fatalf("a stream without hysteriaSettings must be left alone, got changed=%v %q", changed, healed)
- }
- }
- // TestGenXrayInboundConfigHealsHysteriaVersion is the regression for a stored
- // v1 row: xray-core answers "version != 2" and rejects the whole config, so
- // every other inbound on the server stays offline until the row is fixed.
- func TestGenXrayInboundConfigHealsHysteriaVersion(t *testing.T) {
- in := Inbound{
- Protocol: Hysteria,
- Port: 36715,
- Listen: "127.0.0.1",
- Tag: "in-hysteria",
- Settings: `{"version":1,"clients":[{"auth":"tok","email":"a@x"}]}`,
- StreamSettings: `{"network":"hysteria","hysteriaSettings":{"version":1,"udpIdleTimeout":60}}`,
- }
- cfg := in.GenXrayInboundConfig()
- if got := settingsVersion(t, string(cfg.Settings)); got != float64(2) {
- t.Fatalf("generated settings.version = %#v, want 2", got)
- }
- var stream map[string]any
- if err := json.Unmarshal(cfg.StreamSettings, &stream); err != nil {
- t.Fatalf("unmarshal generated streamSettings: %v", err)
- }
- hysteria, _ := stream["hysteriaSettings"].(map[string]any)
- if hysteria["version"] != float64(2) {
- t.Fatalf("generated hysteriaSettings.version = %#v, want 2", hysteria["version"])
- }
- if !strings.Contains(in.Settings, `"version":1`) {
- t.Fatal("the stored row must keep its own value; only the generated config is healed")
- }
- }
- func TestGenXrayInboundConfigLeavesOtherProtocolsAlone(t *testing.T) {
- in := Inbound{
- Protocol: VLESS,
- Port: 443,
- Tag: "in-vless",
- Settings: `{"clients":[],"decryption":"none"}`,
- }
- if got := settingsVersion(t, string(in.GenXrayInboundConfig().Settings)); got != nil {
- t.Fatalf("a non-hysteria inbound must not gain a version key, got %#v", got)
- }
- }
|