package reverseproxy import ( "net/url" "testing" ) func TestMatchDomain(t *testing.T) { tests := []struct { pattern string host string matched bool }{ {"example.com", "example.com", true}, {"example.com", "example.com:80", true}, {"example.com", "example.com:443", true}, {"example.com", "sub.example.com", false}, {"*.example.com", "sub.example.com", true}, {"*.example.com", "api.sub.example.com", true}, {"*.example.com", "example.com", true}, {"*.example.com", "other.org", false}, {"*", "anything.local", true}, {"*", "192.168.1.1:8080", true}, {"", "anything.local", true}, {"192.168.1.100", "192.168.1.100", true}, {"192.168.1.100", "192.168.1.100:80", true}, {"192.168.1.100", "192.168.1.101", false}, } for _, tt := range tests { got := MatchDomain(tt.pattern, tt.host) if got != tt.matched { t.Errorf("MatchDomain(%q, %q) = %v; want %v", tt.pattern, tt.host, got, tt.matched) } } } func TestMatchPath(t *testing.T) { tests := []struct { rulePath string reqPath string matched bool }{ {"/", "/", true}, {"/", "/app1", true}, {"/", "/app1/module1", true}, {"/app1", "/app1", true}, {"/app1", "/app1/", true}, {"/app1", "/app1/module1", true}, {"/app1", "/app1/module1/attribute1", true}, {"/app1", "/app100", false}, {"/app1", "/other", false}, {"app1", "/app1", true}, {"/app1/", "/app1/module1", true}, } for _, tt := range tests { got := MatchPath(tt.rulePath, tt.reqPath) if got != tt.matched { t.Errorf("MatchPath(%q, %q) = %v; want %v", tt.rulePath, tt.reqPath, got, tt.matched) } } } func TestBuildTargetURL(t *testing.T) { tests := []struct { name string targetRaw string rulePath string reqPath string rawQuery string stripPath bool expected string }{ { name: "Root target preserving full path", targetRaw: "http://example.com", rulePath: "/", reqPath: "/test/item", rawQuery: "a=1", stripPath: false, expected: "http://example.com/test/item?a=1", }, { name: "Target with path, forwarding subfolders", targetRaw: "http://example.com/app1", rulePath: "/app1", reqPath: "/app1/module1/attribute1", rawQuery: "filter=all", stripPath: false, expected: "http://example.com/app1/module1/attribute1?filter=all", }, { name: "Target without path, stripPath true", targetRaw: "http://10.0.0.5:8080", rulePath: "/app1", reqPath: "/app1/module1/attribute1", rawQuery: "", stripPath: true, expected: "http://10.0.0.5:8080/module1/attribute1", }, { name: "Target without path, stripPath false", targetRaw: "http://10.0.0.5:8080", rulePath: "/app1", reqPath: "/app1/module1/attribute1", rawQuery: "", stripPath: false, expected: "http://10.0.0.5:8080/app1/module1/attribute1", }, { name: "Target with trailing slash, req root", targetRaw: "http://example.com/app1/", rulePath: "/app1", reqPath: "/app1", rawQuery: "", stripPath: false, expected: "http://example.com/app1/", }, { name: "Merging backend query and incoming query", targetRaw: "http://example.com/api?v=2", rulePath: "/api", reqPath: "/api/users", rawQuery: "page=1", stripPath: false, expected: "http://example.com/api/users?v=2&page=1", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { targetBase, err := url.Parse(tt.targetRaw) if err != nil { t.Fatalf("failed to parse target URL: %v", err) } dest := BuildTargetURL(targetBase, tt.rulePath, tt.reqPath, tt.rawQuery, tt.stripPath) if dest.String() != tt.expected { t.Errorf("BuildTargetURL() = %q; want %q", dest.String(), tt.expected) } }) } }