package globals import ( "encoding/json" "net/http" "net/http/httptest" "testing" ) func TestGostClientChainOperations(t *testing.T) { var ( created *GostChainConfig deleted []string saved bool ) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() if !ok || username != "user" || password != "pass" { t.Errorf("unexpected auth: ok=%v username=%q password=%q", ok, username, password) http.Error(w, "unauthorized", http.StatusUnauthorized) return } switch { case r.Method == http.MethodGet && r.URL.Path == "/api/config/chains": _ = json.NewEncoder(w).Encode(map[string]any{ "count": 2, "list": []map[string]any{ {"name": "old-a"}, {"name": "old-b"}, }, }) case r.Method == http.MethodPost && r.URL.Path == "/api/config/chains": if err := json.NewDecoder(r.Body).Decode(&created); err != nil { t.Errorf("Decode chain failed: %v", err) http.Error(w, "bad request", http.StatusBadRequest) return } _, _ = w.Write([]byte(`{}`)) case r.Method == http.MethodDelete && r.URL.Path == "/api/config/chains/old-a": deleted = append(deleted, "old-a") _, _ = w.Write([]byte(`{}`)) case r.Method == http.MethodDelete && r.URL.Path == "/api/config/chains/old-b": deleted = append(deleted, "old-b") _, _ = w.Write([]byte(`{}`)) case r.Method == http.MethodPost && r.URL.Path == "/api/config": saved = true _, _ = w.Write([]byte(`{}`)) default: t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path) http.NotFound(w, r) } })) defer server.Close() client := NewGost(server.URL, 9700, "/api", "user", "pass") chains, err := client.ListChains() if err != nil { t.Fatalf("ListChains returned error: %v", err) } if len(chains) != 2 || chains[0].Name != "old-a" || chains[1].Name != "old-b" { t.Fatalf("unexpected chains: %#v", chains) } if err := client.DeleteChain(chains[0].Name); err != nil { t.Fatalf("DeleteChain old-a returned error: %v", err) } if err := client.DeleteChain(chains[1].Name); err != nil { t.Fatalf("DeleteChain old-b returned error: %v", err) } if len(deleted) != 2 { t.Fatalf("unexpected deleted chains: %#v", deleted) } err = client.CreateChain(&GostChainConfig{ Name: "edge-a", Hops: []GostHopConfig{{ Nodes: []GostNodeConfig{{ Addr: "192.0.2.1:1080", Connector: GostConnectorConfig{Type: "socks5"}, Dialer: GostDialerConfig{Type: "tcp"}, }}, }}, }) if err != nil { t.Fatalf("CreateChain returned error: %v", err) } if created == nil || created.Name != "edge-a" { t.Fatalf("unexpected created chain: %#v", created) } if len(created.Hops) != 1 || len(created.Hops[0].Nodes) != 1 { t.Fatalf("unexpected created chain hops: %#v", created.Hops) } node := created.Hops[0].Nodes[0] if node.Addr != "192.0.2.1:1080" || node.Connector.Type != "socks5" || node.Dialer.Type != "tcp" { t.Fatalf("unexpected created node: %#v", node) } if err := client.SaveConfig(); err != nil { t.Fatalf("SaveConfig returned error: %v", err) } if !saved { t.Fatal("expected SaveConfig request") } }