@@ -794,6 +794,168 @@ func TestDBCatalog(t *testing.T) {
794794 assert .Contains (t , err .Error (), "invalid model name" )
795795 })
796796 })
797+
798+ t .Run ("TestGetFilterOptions" , func (t * testing.T ) {
799+ // Create models with various properties for filter options testing
800+ model1 := & models.CatalogModelImpl {
801+ TypeID : apiutils .Of (int32 (catalogModelTypeID )),
802+ Attributes : & models.CatalogModelAttributes {
803+ Name : apiutils .Of ("filter-options-model-1" ),
804+ ExternalID : apiutils .Of ("filter-opt-1" ),
805+ },
806+ Properties : & []mr_models.Properties {
807+ {Name : "source_id" , StringValue : apiutils .Of ("filter-test-source" )},
808+ {Name : "license" , StringValue : apiutils .Of ("MIT" )},
809+ {Name : "provider" , StringValue : apiutils .Of ("HuggingFace" )},
810+ {Name : "maturity" , StringValue : apiutils .Of ("stable" )},
811+ {Name : "library_name" , StringValue : apiutils .Of ("transformers" )},
812+ {Name : "language" , StringValue : apiutils .Of (`["python", "rust"]` )},
813+ {Name : "tasks" , StringValue : apiutils .Of (`["text-classification", "token-classification"]` )},
814+ },
815+ }
816+
817+ model2 := & models.CatalogModelImpl {
818+ TypeID : apiutils .Of (int32 (catalogModelTypeID )),
819+ Attributes : & models.CatalogModelAttributes {
820+ Name : apiutils .Of ("filter-options-model-2" ),
821+ ExternalID : apiutils .Of ("filter-opt-2" ),
822+ },
823+ Properties : & []mr_models.Properties {
824+ {Name : "source_id" , StringValue : apiutils .Of ("filter-test-source" )},
825+ {Name : "license" , StringValue : apiutils .Of ("Apache-2.0" )},
826+ {Name : "provider" , StringValue : apiutils .Of ("OpenAI" )},
827+ {Name : "maturity" , StringValue : apiutils .Of ("experimental" )},
828+ {Name : "library_name" , StringValue : apiutils .Of ("openai" )},
829+ {Name : "language" , StringValue : apiutils .Of (`["python", "javascript"]` )},
830+ {Name : "tasks" , StringValue : apiutils .Of (`["text-generation", "conversational"]` )},
831+ {Name : "readme" , StringValue : apiutils .Of ("This is a very long readme that exceeds 100 characters and should be excluded from filter options because it's too verbose for filtering purposes." )},
832+ },
833+ }
834+
835+ model3 := & models.CatalogModelImpl {
836+ TypeID : apiutils .Of (int32 (catalogModelTypeID )),
837+ Attributes : & models.CatalogModelAttributes {
838+ Name : apiutils .Of ("filter-options-model-3" ),
839+ ExternalID : apiutils .Of ("filter-opt-3" ),
840+ },
841+ Properties : & []mr_models.Properties {
842+ {Name : "source_id" , StringValue : apiutils .Of ("filter-test-source" )},
843+ {Name : "license" , StringValue : apiutils .Of ("MIT" )},
844+ {Name : "provider" , StringValue : apiutils .Of ("PyTorch" )},
845+ {Name : "maturity" , StringValue : apiutils .Of ("stable" )},
846+ {Name : "language" , StringValue : apiutils .Of (`["python"]` )},
847+ {Name : "tasks" , StringValue : apiutils .Of (`["image-classification"]` )},
848+ {Name : "logo" , StringValue : apiutils .Of ("https://example.com/logo.png" )},
849+ {Name : "license_link" , StringValue : apiutils .Of ("https://example.com/license" )},
850+ },
851+ }
852+
853+ _ , err := catalogModelRepo .Save (model1 )
854+ require .NoError (t , err )
855+ _ , err = catalogModelRepo .Save (model2 )
856+ require .NoError (t , err )
857+ _ , err = catalogModelRepo .Save (model3 )
858+ require .NoError (t , err )
859+
860+ // Test GetFilterOptions
861+ filterOptions , err := dbCatalog .GetFilterOptions (ctx )
862+ require .NoError (t , err )
863+ require .NotNil (t , filterOptions )
864+ require .NotNil (t , filterOptions .Filters )
865+
866+ filters := * filterOptions .Filters
867+
868+ // Should include short properties
869+ assert .Contains (t , filters , "license" )
870+ assert .Contains (t , filters , "provider" )
871+ assert .Contains (t , filters , "maturity" )
872+ assert .Contains (t , filters , "library_name" )
873+ assert .Contains (t , filters , "language" )
874+ assert .Contains (t , filters , "tasks" )
875+
876+ // Should exclude internal/verbose fields
877+ assert .NotContains (t , filters , "source_id" , "source_id should be excluded" )
878+ assert .NotContains (t , filters , "logo" , "logo should be excluded" )
879+ assert .NotContains (t , filters , "license_link" , "license_link should be excluded" )
880+ assert .NotContains (t , filters , "readme" , "readme should be excluded (too long)" )
881+
882+ licenseFilter := filters ["license" ]
883+ assert .Equal (t , "string" , licenseFilter .Type )
884+ assert .NotNil (t , licenseFilter .Values )
885+ assert .GreaterOrEqual (t , len (licenseFilter .Values ), 2 , "Should have at least MIT and Apache-2.0" )
886+
887+ // Convert to string slice for easier checking
888+ licenseValues := make ([]string , 0 )
889+ for _ , v := range licenseFilter .Values {
890+ if strVal , ok := v .(string ); ok {
891+ licenseValues = append (licenseValues , strVal )
892+ }
893+ }
894+ assert .Contains (t , licenseValues , "MIT" )
895+ assert .Contains (t , licenseValues , "Apache-2.0" )
896+
897+ // Verify provider filter options
898+ providerFilter := filters ["provider" ]
899+ assert .Equal (t , "string" , providerFilter .Type )
900+ providerValues := make ([]string , 0 )
901+ for _ , v := range providerFilter .Values {
902+ if strVal , ok := v .(string ); ok {
903+ providerValues = append (providerValues , strVal )
904+ }
905+ }
906+ assert .Contains (t , providerValues , "HuggingFace" )
907+ assert .Contains (t , providerValues , "OpenAI" )
908+ assert .Contains (t , providerValues , "PyTorch" )
909+
910+ // Verify JSON array fields are properly parsed and expanded
911+ languageFilter := filters ["language" ]
912+ assert .Equal (t , "string" , languageFilter .Type )
913+ languageValues := make ([]string , 0 )
914+ for _ , v := range languageFilter .Values {
915+ if strVal , ok := v .(string ); ok {
916+ languageValues = append (languageValues , strVal )
917+ }
918+ }
919+ // Should contain individual values from JSON arrays
920+ assert .Contains (t , languageValues , "python" )
921+ assert .Contains (t , languageValues , "rust" )
922+ assert .Contains (t , languageValues , "javascript" )
923+
924+ // Verify tasks are properly expanded
925+ tasksFilter := filters ["tasks" ]
926+ assert .Equal (t , "string" , tasksFilter .Type )
927+ tasksValues := make ([]string , 0 )
928+ for _ , v := range tasksFilter .Values {
929+ if strVal , ok := v .(string ); ok {
930+ tasksValues = append (tasksValues , strVal )
931+ }
932+ }
933+ assert .Contains (t , tasksValues , "text-classification" )
934+ assert .Contains (t , tasksValues , "token-classification" )
935+ assert .Contains (t , tasksValues , "text-generation" )
936+ assert .Contains (t , tasksValues , "conversational" )
937+ assert .Contains (t , tasksValues , "image-classification" )
938+
939+ // Verify no duplicates
940+ pythonCount := 0
941+ for _ , v := range languageValues {
942+ if v == "python" {
943+ pythonCount ++
944+ }
945+ }
946+ assert .Equal (t , 1 , pythonCount , "python should appear only once (deduplicated)" )
947+
948+ // Verify maturity options
949+ maturityFilter := filters ["maturity" ]
950+ maturityValues := make ([]string , 0 )
951+ for _ , v := range maturityFilter .Values {
952+ if strVal , ok := v .(string ); ok {
953+ maturityValues = append (maturityValues , strVal )
954+ }
955+ }
956+ assert .Contains (t , maturityValues , "stable" )
957+ assert .Contains (t , maturityValues , "experimental" )
958+ })
797959}
798960
799961// Helper functions to get type IDs from database
0 commit comments