diff --git a/modules/caddyhttp/push/handler_test.go b/modules/caddyhttp/push/handler_test.go new file mode 100644 index 000000000..c51e38e42 --- /dev/null +++ b/modules/caddyhttp/push/handler_test.go @@ -0,0 +1,129 @@ +package push + +import ( + "testing" +) + +func TestIsRemoteResource(t *testing.T) { + tests := []struct { + name string + resource string + want bool + }{ + {name: "http URL", resource: "http://example.com/style.css", want: true}, + {name: "https URL", resource: "https://example.com/script.js", want: true}, + {name: "protocol-relative", resource: "//cdn.example.com/lib.js", want: true}, + {name: "absolute path", resource: "/style.css", want: false}, + {name: "relative path", resource: "images/logo.png", want: false}, + {name: "empty string", resource: "", want: false}, + {name: "just slash", resource: "/", want: false}, + {name: "http in path", resource: "/http://example.com", want: false}, + {name: "single slash prefix", resource: "/script.js", want: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := isRemoteResource(tt.resource) + if got != tt.want { + t.Errorf("isRemoteResource(%q) = %v, want %v", tt.resource, got, tt.want) + } + }) + } +} + +func TestParseLinkHeaderFormats(t *testing.T) { + tests := []struct { + name string + header string + wantCount int + wantURIs []string + }{ + { + name: "single link", + header: "; rel=preload; as=style", + wantCount: 1, + wantURIs: []string{"/style.css"}, + }, + { + name: "multiple links", + header: "; as=style,; as=script", + wantCount: 2, + wantURIs: []string{"/style.css", "/script.js"}, + }, + { + name: "empty header", + header: "", + wantCount: 0, + }, + { + name: "no angle brackets", + header: "/style.css; rel=preload", + wantCount: 0, + }, + { + name: "link with nopush", + header: "; rel=preload; nopush", + wantCount: 1, + wantURIs: []string{"/style.css"}, + }, + { + name: "link with multiple params", + header: "; rel=preload; as=font; crossorigin", + wantCount: 1, + wantURIs: []string{"/font.woff2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resources := parseLinkHeader(tt.header) + if len(resources) != tt.wantCount { + t.Fatalf("parseLinkHeader(%q) returned %d resources, want %d", tt.header, len(resources), tt.wantCount) + } + for i, uri := range tt.wantURIs { + if i >= len(resources) { + break + } + if resources[i].uri != uri { + t.Errorf("resources[%d].uri = %q, want %q", i, resources[i].uri, uri) + } + } + }) + } +} + +func TestParseLinkHeaderParams(t *testing.T) { + resources := parseLinkHeader("; rel=preload; as=style") + if len(resources) != 1 { + t.Fatalf("expected 1 resource, got %d", len(resources)) + } + r := resources[0] + if r.params["rel"] != "preload" { + t.Errorf("params['rel'] = %q, want 'preload'", r.params["rel"]) + } + if r.params["as"] != "style" { + t.Errorf("params['as'] = %q, want 'style'", r.params["as"]) + } +} + +func TestParseLinkHeaderNopush(t *testing.T) { + resources := parseLinkHeader("; rel=preload; nopush") + if len(resources) != 1 { + t.Fatalf("expected 1 resource, got %d", len(resources)) + } + r := resources[0] + if _, ok := r.params["nopush"]; !ok { + t.Error("expected 'nopush' param to exist") + } +} + +func TestHandlerCaddyModule(t *testing.T) { + h := Handler{} + info := h.CaddyModule() + if info.ID != "http.handlers.push" { + t.Errorf("CaddyModule().ID = %v, want 'http.handlers.push'", info.ID) + } + if info.New == nil { + t.Fatal("CaddyModule().New is nil") + } +}