package store import ( "testing" pb "git.ipng.ch/ipng/nginx-logtail/proto/logtailpb" ) // --- ParseStatusExpr --- func TestParseStatusExprEQ(t *testing.T) { n, op, ok := ParseStatusExpr("200") if !ok || n != 200 || op != pb.StatusOp_EQ { t.Fatalf("got (%d,%v,%v)", n, op, ok) } } func TestParseStatusExprExplicitEQ(t *testing.T) { for _, expr := range []string{"=200", "==200"} { n, op, ok := ParseStatusExpr(expr) if !ok || n != 200 || op != pb.StatusOp_EQ { t.Fatalf("expr %q: got (%d,%v,%v)", expr, n, op, ok) } } } func TestParseStatusExprNE(t *testing.T) { n, op, ok := ParseStatusExpr("!=200") if !ok || n != 200 || op != pb.StatusOp_NE { t.Fatalf("got (%d,%v,%v)", n, op, ok) } } func TestParseStatusExprGE(t *testing.T) { n, op, ok := ParseStatusExpr(">=400") if !ok || n != 400 || op != pb.StatusOp_GE { t.Fatalf("got (%d,%v,%v)", n, op, ok) } } func TestParseStatusExprGT(t *testing.T) { n, op, ok := ParseStatusExpr(">400") if !ok || n != 400 || op != pb.StatusOp_GT { t.Fatalf("got (%d,%v,%v)", n, op, ok) } } func TestParseStatusExprLE(t *testing.T) { n, op, ok := ParseStatusExpr("<=500") if !ok || n != 500 || op != pb.StatusOp_LE { t.Fatalf("got (%d,%v,%v)", n, op, ok) } } func TestParseStatusExprLT(t *testing.T) { n, op, ok := ParseStatusExpr("<500") if !ok || n != 500 || op != pb.StatusOp_LT { t.Fatalf("got (%d,%v,%v)", n, op, ok) } } func TestParseStatusExprEmpty(t *testing.T) { _, _, ok := ParseStatusExpr("") if ok { t.Fatal("expected ok=false for empty string") } } func TestParseStatusExprInvalid(t *testing.T) { for _, expr := range []string{"abc", "!=", ">=", "2xx"} { _, _, ok := ParseStatusExpr(expr) if ok { t.Fatalf("expr %q: expected ok=false", expr) } } } // --- MatchesFilter --- func compiledEQ(status int32) *CompiledFilter { v := status return CompileFilter(&pb.Filter{HttpResponse: &v, StatusOp: pb.StatusOp_EQ}) } func TestMatchesFilterNil(t *testing.T) { if !MatchesFilter(Tuple4{Website: "x"}, nil) { t.Fatal("nil filter should match everything") } if !MatchesFilter(Tuple4{Website: "x"}, &CompiledFilter{}) { t.Fatal("empty compiled filter should match everything") } } func TestMatchesFilterExactWebsite(t *testing.T) { w := "example.com" cf := CompileFilter(&pb.Filter{Website: &w}) if !MatchesFilter(Tuple4{Website: "example.com"}, cf) { t.Fatal("expected match") } if MatchesFilter(Tuple4{Website: "other.com"}, cf) { t.Fatal("expected no match") } } func TestMatchesFilterWebsiteRegex(t *testing.T) { re := "gouda.*" cf := CompileFilter(&pb.Filter{WebsiteRegex: &re}) if !MatchesFilter(Tuple4{Website: "gouda.example.com"}, cf) { t.Fatal("expected match") } if MatchesFilter(Tuple4{Website: "edam.example.com"}, cf) { t.Fatal("expected no match") } } func TestMatchesFilterURIRegex(t *testing.T) { re := "^/api/.*" cf := CompileFilter(&pb.Filter{UriRegex: &re}) if !MatchesFilter(Tuple4{URI: "/api/users"}, cf) { t.Fatal("expected match") } if MatchesFilter(Tuple4{URI: "/health"}, cf) { t.Fatal("expected no match") } } func TestMatchesFilterInvalidRegexMatchesNothing(t *testing.T) { re := "[invalid" cf := CompileFilter(&pb.Filter{WebsiteRegex: &re}) if MatchesFilter(Tuple4{Website: "anything"}, cf) { t.Fatal("invalid regex should match nothing") } } func TestMatchesFilterStatusEQ(t *testing.T) { cf := compiledEQ(200) if !MatchesFilter(Tuple4{Status: "200"}, cf) { t.Fatal("expected match") } if MatchesFilter(Tuple4{Status: "404"}, cf) { t.Fatal("expected no match") } } func TestMatchesFilterStatusNE(t *testing.T) { v := int32(200) cf := CompileFilter(&pb.Filter{HttpResponse: &v, StatusOp: pb.StatusOp_NE}) if MatchesFilter(Tuple4{Status: "200"}, cf) { t.Fatal("expected no match for 200 != 200") } if !MatchesFilter(Tuple4{Status: "404"}, cf) { t.Fatal("expected match for 404 != 200") } } func TestMatchesFilterStatusGE(t *testing.T) { v := int32(400) cf := CompileFilter(&pb.Filter{HttpResponse: &v, StatusOp: pb.StatusOp_GE}) if !MatchesFilter(Tuple4{Status: "400"}, cf) { t.Fatal("expected match: 400 >= 400") } if !MatchesFilter(Tuple4{Status: "500"}, cf) { t.Fatal("expected match: 500 >= 400") } if MatchesFilter(Tuple4{Status: "200"}, cf) { t.Fatal("expected no match: 200 >= 400") } } func TestMatchesFilterStatusLT(t *testing.T) { v := int32(400) cf := CompileFilter(&pb.Filter{HttpResponse: &v, StatusOp: pb.StatusOp_LT}) if !MatchesFilter(Tuple4{Status: "200"}, cf) { t.Fatal("expected match: 200 < 400") } if MatchesFilter(Tuple4{Status: "400"}, cf) { t.Fatal("expected no match: 400 < 400") } } func TestMatchesFilterStatusNonNumeric(t *testing.T) { cf := compiledEQ(200) if MatchesFilter(Tuple4{Status: "ok"}, cf) { t.Fatal("non-numeric status should not match") } } func TestMatchesFilterCombined(t *testing.T) { w := "example.com" v := int32(200) cf := CompileFilter(&pb.Filter{ Website: &w, HttpResponse: &v, StatusOp: pb.StatusOp_EQ, }) if !MatchesFilter(Tuple4{Website: "example.com", Status: "200"}, cf) { t.Fatal("expected match") } if MatchesFilter(Tuple4{Website: "other.com", Status: "200"}, cf) { t.Fatal("expected no match: wrong website") } if MatchesFilter(Tuple4{Website: "example.com", Status: "404"}, cf) { t.Fatal("expected no match: wrong status") } }