package traefik import ( "encoding/json" "testing" "gopkg.in/yaml.v3" ) func mustJSON(t *testing.T, v any) string { t.Helper() b, err := json.Marshal(v) if err != nil { t.Fatalf("json.Marshal: %v", err) } return string(b) } func sampleConfig() *Configuration { return &Configuration{HTTP: &HTTPConfiguration{ Routers: map[string]*Router{ "r": {EntryPoints: []string{"web"}, Service: "s", Rule: "Host(`a.de`)", TLS: &RouterTLS{}}, }, Services: map[string]*Service{ "s": {LoadBalancer: &ServersLoadBalancer{Servers: []Server{{URL: "http://10.0.0.1:80"}}}}, }, }} } func TestMarshal_JSON(t *testing.T) { got, err := sampleConfig().Marshal(FormatJSON) if err != nil { t.Fatalf("unerwarteter Fehler: %v", err) } want := `{"http":{"routers":{"r":{"entryPoints":["web"],"service":"s","rule":"Host(` + "`a.de`" + `)","tls":{}}},` + `"services":{"s":{"loadBalancer":{"servers":[{"url":"http://10.0.0.1:80"}]}}}}}` if string(got) != want { t.Errorf("JSON weicht ab\n got: %s\nwant: %s", got, want) } } func TestMarshal_YAML(t *testing.T) { got, err := sampleConfig().Marshal(FormatYAML) if err != nil { t.Fatalf("unerwarteter Fehler: %v", err) } want := "http:\n" + " routers:\n" + " r:\n" + " entryPoints:\n" + " - web\n" + " service: s\n" + " rule: Host(`a.de`)\n" + " tls: {}\n" + " services:\n" + " s:\n" + " loadBalancer:\n" + " servers:\n" + " - url: http://10.0.0.1:80\n" if string(got) != want { t.Errorf("YAML weicht ab\n got:\n%s\nwant:\n%s", got, want) } } func TestMarshal_Empty(t *testing.T) { for _, c := range []*Configuration{nil, {}} { j, err := c.Marshal(FormatJSON) if err != nil || string(j) != "{}" { t.Errorf("JSON = %q, %v; erwartet {}", j, err) } y, err := c.Marshal(FormatYAML) if err != nil || string(y) != "{}\n" { t.Errorf("YAML = %q, %v; erwartet {}\\n", y, err) } } } func TestMarshal_UnknownFormat(t *testing.T) { if _, err := sampleConfig().Marshal("toml"); err == nil { t.Error("erwartet Fehler für unbekanntes Format") } } // Die Ausgabe muss sich verlustfrei zurücklesen lassen – inklusive der // verschachtelten Middleware-Maps und der optionalen Felder. func TestMarshal_RoundTrip(t *testing.T) { orig, err := FromLabels("100.64.0.42", wordpressLabels()) if err != nil { t.Fatalf("FromLabels: %v", err) } orig.HTTP.Services["wp"].LoadBalancer.PassHostHeader = boolPtr(false) orig.HTTP.Routers["wp"].Priority = 7 decoders := map[Format]func([]byte, any) error{ FormatJSON: json.Unmarshal, FormatYAML: yaml.Unmarshal, } for f, decode := range decoders { t.Run(string(f), func(t *testing.T) { b, err := orig.Marshal(f) if err != nil { t.Fatalf("Marshal: %v", err) } var back Configuration if err := decode(b, &back); err != nil { t.Fatalf("Unmarshal: %v\n%s", err, b) } // Vergleich über JSON statt reflect.DeepEqual: yaml.v3 dekodiert // verschachtelte Maps in den Typ der äußeren Map (Middleware statt // map[string]any), inhaltlich ist das identisch. if got, want := mustJSON(t, &back), mustJSON(t, orig); got != want { t.Errorf("Round-Trip weicht ab\n got: %s\nwant: %s", got, want) } }) } } func TestParseFormat(t *testing.T) { tests := []struct { in string want Format wantErr bool }{ {"", FormatJSON, false}, {"json", FormatJSON, false}, {" JSON ", FormatJSON, false}, {"yaml", FormatYAML, false}, {"YML", FormatYAML, false}, {"toml", "", true}, } for _, tt := range tests { got, err := ParseFormat(tt.in) if (err != nil) != tt.wantErr || got != tt.want { t.Errorf("ParseFormat(%q) = %q, %v; erwartet %q, Fehler=%v", tt.in, got, err, tt.want, tt.wantErr) } } } func TestFormat_ContentType(t *testing.T) { if got := FormatJSON.ContentType(); got != "application/json" { t.Errorf("JSON: %q", got) } if got := FormatYAML.ContentType(); got != "application/yaml" { t.Errorf("YAML: %q", got) } }