mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-12 04:04:26 +00:00
The plugin catalog analyzer failed v1.0.28 with "invalid handler type: <nil>" (#151). Two defects combined. The analyzer feeds .traefik.yml testData to New() verbatim. The header values used quadruple braces on a false premise: no configuration path collapses them. Template validation rejected them, so New() failed. The testData now carries plain, valid templates. New() also returned failures through a multi-value tail call (return NewWithContext(...)). Yaegi v0.16.1 zeroes both results of that statement on the error path. The analyzer therefore saw (nil, nil) and reported the opaque handler-type message instead of the real validation error. New() now splits the return explicitly. Construction failures surface as real error messages under yaegi and in Traefik logs. The regression tests pin both fixes. README and CONFIGURATION.md now give correct escaping guidance: plain double braces everywhere; file provider users escape with a raw string. Quadruple braces do not work anywhere. Fixes #151.
115 lines
4.4 KiB
Go
115 lines
4.4 KiB
Go
package traefikoidc
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"os"
|
|
"testing"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Issue #151: the Traefik Plugin Catalog analyzer (piceus) failed v1.0.28 with
|
|
// "failed to run the plugin with Yaegi: invalid handler type: <nil>". Two
|
|
// independent defects combined to produce that opaque report:
|
|
//
|
|
// 1. The analyzer feeds .traefik.yml testData to New() RAW — paerser and
|
|
// mapstructure perform no brace collapsing, so the quadruple-brace header
|
|
// values ("{{{{.Claims.email}}}}") reached Config.Validate() verbatim and
|
|
// failed template parsing (`unexpected "{" in command`). The quadruple-brace
|
|
// convention (commit 2d1b04c) was based on a false premise; YAML does not
|
|
// collapse braces.
|
|
//
|
|
// 2. New() tail-called `return NewWithContext(...)`, converting the returned
|
|
// (*TraefikOidc, error) to (http.Handler, error) in the return statement.
|
|
// Under yaegi v0.16.1 (the interpreter piceus and Traefik embed) that
|
|
// conversion zeroes BOTH results when the error path is taken, so piceus
|
|
// saw (nil, nil), skipped its error branch, and reported the handler-type
|
|
// message instead of the real validation error. Compiled Go had the sibling
|
|
// defect: the typed-nil *TraefikOidc made New() return a NON-nil
|
|
// http.Handler alongside a non-nil error.
|
|
//
|
|
// These tests pin both fixes natively (no interpreter needed):
|
|
// TestIssue151_ManifestTestDataPassesAnalyzer pins (1) and
|
|
// TestIssue151_NewReturnsUntypedNilHandlerOnInvalidConfig pins (2).
|
|
|
|
// manifestTestData reads .traefik.yml and returns testData exactly as the
|
|
// analyzer sees it: parsed by a YAML parser, no template preprocessing.
|
|
func manifestTestData(t *testing.T) map[string]any {
|
|
t.Helper()
|
|
|
|
raw, err := os.ReadFile(".traefik.yml")
|
|
if err != nil {
|
|
t.Fatalf("read .traefik.yml: %v", err)
|
|
}
|
|
|
|
var manifest struct {
|
|
TestData map[string]any `yaml:"testData"`
|
|
}
|
|
if err := yaml.Unmarshal(raw, &manifest); err != nil {
|
|
t.Fatalf("parse .traefik.yml: %v", err)
|
|
}
|
|
if len(manifest.TestData) == 0 {
|
|
t.Fatal(".traefik.yml has no testData — the analyzer rejects manifests without it")
|
|
}
|
|
return manifest.TestData
|
|
}
|
|
|
|
// TestIssue151_ManifestTestDataPassesAnalyzer decodes the manifest's testData
|
|
// into a Config the same way piceus does (defaults from CreateConfig, then the
|
|
// raw testData values on top; JSON round-trip stands in for mapstructure) and
|
|
// requires the full analyzer contract: Validate() passes and New() yields a
|
|
// working handler with no error.
|
|
func TestIssue151_ManifestTestDataPassesAnalyzer(t *testing.T) {
|
|
testData := manifestTestData(t)
|
|
|
|
cfg := CreateConfig()
|
|
buf, err := json.Marshal(testData)
|
|
if err != nil {
|
|
t.Fatalf("marshal testData: %v", err)
|
|
}
|
|
if err := json.Unmarshal(buf, cfg); err != nil {
|
|
t.Fatalf("decode testData into Config: %v", err)
|
|
}
|
|
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatalf("manifest testData must pass Validate() — the catalog analyzer feeds it to New() raw (issue #151): %v", err)
|
|
}
|
|
|
|
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
|
|
h, err := New(context.Background(), next, cfg, "issue151-analyzer")
|
|
if err != nil {
|
|
t.Fatalf("New() with manifest testData must succeed (issue #151): %v", err)
|
|
}
|
|
if h == nil {
|
|
t.Fatal("New() with manifest testData returned a nil handler")
|
|
}
|
|
if closer, ok := h.(interface{ Close() error }); ok {
|
|
_ = closer.Close()
|
|
}
|
|
}
|
|
|
|
// TestIssue151_NewReturnsUntypedNilHandlerOnInvalidConfig requires New() to
|
|
// return an UNTYPED nil handler when construction fails. The previous
|
|
// `return NewWithContext(...)` tail call wrapped a typed-nil *TraefikOidc into
|
|
// the http.Handler interface (h != nil despite err != nil), and under yaegi the
|
|
// same statement zeroed the error as well, which is what turned every
|
|
// validation failure into Traefik's opaque "invalid handler type: <nil>".
|
|
func TestIssue151_NewReturnsUntypedNilHandlerOnInvalidConfig(t *testing.T) {
|
|
cfg := issue149BaseConfig()
|
|
cfg.Headers = []TemplatedHeader{
|
|
// Invalid template — the analyzer-side shape that triggered #151.
|
|
{Name: "X-User-Email", Value: `{{{{.Claims.email}}}}`},
|
|
}
|
|
|
|
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
|
|
h, err := New(context.Background(), next, cfg, "issue151-nil")
|
|
if err == nil {
|
|
t.Fatal("New() must reject an invalid header template")
|
|
}
|
|
if h != nil {
|
|
t.Fatalf("New() must return an untyped nil handler on error, got %T (typed nil leaks through the http.Handler interface and breaks the analyzer's error reporting)", h)
|
|
}
|
|
}
|