@@ -244,52 +244,6 @@ def extract_custom_property_values(custom_properties: dict[str, Any]) -> dict[st
244244 return extracted
245245
246246
247- def validate_custom_properties_structure (custom_properties : dict [str , Any ]) -> bool :
248- """
249- Validate that custom properties follow the expected MetadataStringValue structure.
250-
251- Args:
252- custom_properties: Dictionary of custom properties from API response
253-
254- Returns:
255- True if all custom properties have valid structure, False otherwise
256- """
257- if not custom_properties :
258- LOGGER .info ("No custom properties found - structure validation skipped" )
259- return True
260-
261- expected_keys = ["size" , "tensor_type" , "variant_group_id" ]
262-
263- for key in expected_keys :
264- if key in custom_properties :
265- prop_data = custom_properties [key ]
266-
267- if not isinstance (prop_data , dict ):
268- LOGGER .error (f"Custom property '{ key } ' is not a dictionary: { prop_data } " )
269- return False
270-
271- if "metadataType" not in prop_data :
272- LOGGER .error (f"Custom property '{ key } ' missing 'metadataType' field" )
273- return False
274-
275- if prop_data .get ("metadataType" ) != "MetadataStringValue" :
276- LOGGER .error (f"Custom property '{ key } ' has unexpected metadataType: { prop_data .get ('metadataType' )} " )
277- return False
278-
279- if "string_value" not in prop_data :
280- LOGGER .error (f"Custom property '{ key } ' missing 'string_value' field" )
281- return False
282-
283- if not isinstance (prop_data .get ("string_value" ), str ):
284- LOGGER .error (f"Custom property '{ key } ' string_value is not a string: { prop_data .get ('string_value' )} " )
285- return False
286-
287- LOGGER .info (f"Custom property '{ key } ' has valid structure: '{ prop_data .get ('string_value' )} '" )
288-
289- LOGGER .info ("All custom properties have valid structure" )
290- return True
291-
292-
293247def validate_custom_properties_match_metadata (api_custom_properties : dict [str , str ], metadata : dict [str , Any ]) -> bool :
294248 """
295249 Compare API custom properties with metadata.json values.
@@ -345,130 +299,6 @@ def get_metadata_from_catalog_pod(model_catalog_pod: Pod, model_name: str) -> di
345299 raise
346300
347301
348- def validate_filter_options_structure (
349- response : dict [Any , Any ], expected_properties : set [str ] | None = None
350- ) -> Tuple [bool , List [str ]]:
351- """
352- Comprehensive validation of filter_options response structure.
353-
354- Validates:
355- - Top-level structure (filters object)
356- - All property types and their required fields
357- - Core properties presence (if specified)
358- - String properties: type, values array, distinct values
359- - Numeric properties: type, range object, min/max validity
360-
361- Args:
362- response: The API response to validate
363- expected_properties: Optional set of core properties that must be present
364-
365- Returns:
366- Tuple of (is_valid, list_of_errors)
367- """
368- errors = []
369-
370- # Validate top-level structure
371- if not isinstance (response , dict ):
372- errors .append ("Response should be a dictionary" )
373- return False , errors
374-
375- if "filters" not in response :
376- errors .append ("Response should contain 'filters' object" )
377- return False , errors
378-
379- filters = response ["filters" ]
380- if not isinstance (filters , dict ):
381- errors .append ("Filters should be a dictionary" )
382- return False , errors
383-
384- if not filters :
385- errors .append ("Filters object should not be empty" )
386- return False , errors
387-
388- # Validate expected core properties if specified
389- if expected_properties :
390- for prop in expected_properties :
391- if prop not in filters :
392- errors .append (f"Core property '{ prop } ' should be present in filter options" )
393-
394- # Validate each property structure
395- for prop_name , prop_data in filters .items ():
396- if not isinstance (prop_data , dict ):
397- errors .append (f"Property '{ prop_name } ' should be a dictionary" )
398- continue
399-
400- if "type" not in prop_data :
401- errors .append (f"Property '{ prop_name } ' should have 'type' field" )
402- continue
403-
404- prop_type = prop_data ["type" ]
405- if not isinstance (prop_type , str ) or not prop_type .strip ():
406- errors .append (f"Type for '{ prop_name } ' should be a non-empty string" )
407- continue
408-
409- # Validate string properties
410- if prop_type == "string" :
411- if "values" not in prop_data :
412- errors .append (f"String property '{ prop_name } ' should have 'values' array" )
413- continue
414-
415- values = prop_data ["values" ]
416- if not isinstance (values , list ):
417- errors .append (f"Values for '{ prop_name } ' should be a list" )
418- continue
419-
420- if not values :
421- errors .append (f"Values array for '{ prop_name } ' should not be empty" )
422- continue
423-
424- # Validate individual values
425- for i , value in enumerate (values ):
426- if not isinstance (value , str ):
427- errors .append (f"Value at index { i } for '{ prop_name } ' should be string, got: { type (value )} " )
428- elif not value .strip ():
429- errors .append (f"Value at index { i } for '{ prop_name } ' should not be empty or whitespace" )
430-
431- # Check for distinct values (no duplicates)
432- try :
433- if len (values ) != len (set (values )):
434- errors .append (f"Values for '{ prop_name } ' should be distinct (found duplicates)" )
435- except TypeError :
436- errors .append (f"Values for '{ prop_name } ' should be a list of strings, found unhashable type" )
437-
438- # Validate numeric properties - checking multiple type names since we don't know what the API will return
439- elif prop_type in ["number" , "numeric" , "float" , "integer" , "int" ]:
440- if "range" not in prop_data :
441- errors .append (f"Numeric property '{ prop_name } ' should have 'range' object" )
442- continue
443-
444- range_obj = prop_data ["range" ]
445- if not isinstance (range_obj , dict ):
446- errors .append (f"Range for '{ prop_name } ' should be a dictionary" )
447- continue
448-
449- # Check min/max presence
450- if "min" not in range_obj :
451- errors .append (f"Range for '{ prop_name } ' should have 'min' value" )
452- if "max" not in range_obj :
453- errors .append (f"Range for '{ prop_name } ' should have 'max' value" )
454-
455- if "min" in range_obj and "max" in range_obj :
456- min_val = range_obj ["min" ]
457- max_val = range_obj ["max" ]
458-
459- # Validate min/max are numeric
460- if not isinstance (min_val , (int , float )):
461- errors .append (f"Min value for '{ prop_name } ' should be numeric, got: { type (min_val )} " )
462- if not isinstance (max_val , (int , float )):
463- errors .append (f"Max value for '{ prop_name } ' should be numeric, got: { type (max_val )} " )
464-
465- # Validate logical relationship (min <= max)
466- if isinstance (min_val , (int , float )) and isinstance (max_val , (int , float )) and min_val > max_val :
467- errors .append (f"Min value ({ min_val } ) should be <= max value ({ max_val } ) for '{ prop_name } '" )
468-
469- return len (errors ) == 0 , errors
470-
471-
472302def compare_filter_options_with_database (
473303 api_filters : dict [str , Any ], db_properties : dict [str , list [str ]], excluded_fields : set [str ]
474304) -> Tuple [bool , List [str ]]:
@@ -653,19 +483,3 @@ def verify_labels_match(expected_labels: List[Dict[str, Any]], api_labels: List[
653483 break
654484
655485 assert found , f"Expected label not found in API response: { expected_label } "
656-
657-
658- def validate_source_status (catalog : dict [str , Any ], expected_status : str ) -> None :
659- """
660- Validate the status field of a catalog source.
661-
662- Args:
663- catalog: The catalog source dictionary from API response
664- expected_status: The expected status value (e.g., "available", "disabled", "error")
665-
666- Raises:
667- AssertionError: If status field does not match expected value
668- """
669- assert catalog .get ("status" ) == expected_status , (
670- f"Source '{ catalog .get ('id' )} ' status should be '{ expected_status } ', got: { catalog .get ('status' )} "
671- )
0 commit comments