From e2c82af118afc175aa6202e84cc81e1a3a4d5c17 Mon Sep 17 00:00:00 2001 From: Vijay Date: Fri, 11 Sep 2026 22:16:23 +0530 Subject: [PATCH] fix(core): propagate filterQuery in GetInferenceServices and GetServeModels Assisted-by: Gemini AI Signed-off-by: Vijay --- internal/core/inference_service.go | 1 + internal/core/inference_service_test.go | 186 ++++++++++++++++++++++++ internal/core/serve_model.go | 1 + internal/core/serve_model_test.go | 129 ++++++++++++++++ internal/db/models/inference_service.go | 8 + 5 files changed, 325 insertions(+) diff --git a/internal/core/inference_service.go b/internal/core/inference_service.go index f524014a6c..cdbe5848fc 100644 --- a/internal/core/inference_service.go +++ b/internal/core/inference_service.go @@ -138,6 +138,7 @@ func (b *ModelRegistryService) GetInferenceServices(listOptions api.ListOptions, OrderBy: listOptions.OrderBy, SortOrder: listOptions.SortOrder, NextPageToken: listOptions.NextPageToken, + FilterQuery: listOptions.FilterQuery, }, Runtime: runtime, ParentResourceID: parentResourceID, diff --git a/internal/core/inference_service_test.go b/internal/core/inference_service_test.go index ca7a310074..0eb7513798 100644 --- a/internal/core/inference_service_test.go +++ b/internal/core/inference_service_test.go @@ -1092,3 +1092,189 @@ func TestInferenceServiceRoundTrip(t *testing.T) { assert.Equal(t, "new_value", finalProps["new_prop"].MetadataStringValue.StringValue) }) } + +// TestGetInferenceServicesWithFilterQuery verifies that the filterQuery parameter +// is correctly propagated and applied when listing InferenceServices. +// +// Regression test for: filterQuery silently ignored on GET /inference_services +// Root cause was missing FilterQuery field propagation in GetInferenceServices +// and missing GetRestEntityType() on InferenceServiceListOptions. +func TestGetInferenceServicesWithFilterQuery(t *testing.T) { + _service, cleanup := SetupModelRegistryService(t) + defer cleanup() + + // Create shared prerequisites + rm, err := _service.UpsertRegisteredModel(&openapi.RegisteredModel{Name: "filter-test-rm"}) + require.NoError(t, err) + + env, err := _service.UpsertServingEnvironment(&openapi.ServingEnvironment{Name: "filter-test-env"}) + require.NoError(t, err) + + // Create inference services with distinct properties for filtering + type svcDef struct { + name string + runtime string + extID string + } + svcDefs := []svcDef{ + {"fraud-detector", "tensorflow", "ext-fraud-001"}, + {"image-classifier", "pytorch", "ext-image-002"}, + {"nlp-pipeline", "tensorflow", "ext-nlp-003"}, + {"recommendation-engine", "sklearn", "ext-rec-004"}, + } + for _, svc := range svcDefs { + name := svc.name + rt := svc.runtime + eid := svc.extID + _, err := _service.UpsertInferenceService(&openapi.InferenceService{ + Name: &name, + ExternalId: &eid, + Runtime: &rt, + ServingEnvironmentId: *env.Id, + RegisteredModelId: *rm.Id, + }) + require.NoError(t, err) + } + + testCases := []struct { + name string + filterQuery string + expectedCount int + expectedNames []string + }{ + { + name: "Filter by exact name", + filterQuery: "name = 'fraud-detector'", + expectedCount: 1, + expectedNames: []string{"fraud-detector"}, + }, + { + name: "Filter by name pattern", + filterQuery: "name LIKE '%-detector'", + expectedCount: 1, + expectedNames: []string{"fraud-detector"}, + }, + { + name: "Filter by externalId", + filterQuery: "externalId = 'ext-image-002'", + expectedCount: 1, + expectedNames: []string{"image-classifier"}, + }, + { + name: "Filter by runtime - tensorflow", + filterQuery: "runtime = 'tensorflow'", + expectedCount: 2, + expectedNames: []string{"fraud-detector", "nlp-pipeline"}, + }, + { + name: "Filter by runtime - pytorch", + filterQuery: "runtime = 'pytorch'", + expectedCount: 1, + expectedNames: []string{"image-classifier"}, + }, + { + name: "Complex filter with AND", + filterQuery: "runtime = 'tensorflow' AND name = 'fraud-detector'", + expectedCount: 1, + expectedNames: []string{"fraud-detector"}, + }, + { + name: "Complex filter with OR", + filterQuery: "runtime = 'pytorch' OR runtime = 'sklearn'", + expectedCount: 2, + expectedNames: []string{"image-classifier", "recommendation-engine"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + pageSize := int32(20) + fq := tc.filterQuery + result, err := _service.GetInferenceServices(api.ListOptions{ + PageSize: &pageSize, + FilterQuery: &fq, + }, nil, nil) + + require.NoError(t, err) + require.NotNil(t, result) + + var matchedNames []string + for _, item := range result.Items { + if slices.Contains(tc.expectedNames, *item.Name) { + matchedNames = append(matchedNames, *item.Name) + } + } + + assert.Equal(t, tc.expectedCount, len(matchedNames), + "filterQuery %q: expected %d items, got %d (filter may be silently ignored)", + tc.filterQuery, tc.expectedCount, len(matchedNames)) + assert.ElementsMatch(t, tc.expectedNames, matchedNames, + "filterQuery %q: unexpected items returned", tc.filterQuery) + }) + } + + t.Run("Invalid filter syntax returns error", func(t *testing.T) { + invalidFilter := "invalid <<