pip install pytest pydanticFrom the project root:
pytest tests/ -vtests/
├── test_config_schema.py -- Config schema validation tests
└── test_experiment.py -- DoE experiment parser and summary tests
Validates the Pydantic config schema (llmdbenchmark/parser/config_schema.py) against the real defaults and scenario files.
| Test class | What it covers |
|---|---|
TestDefaultsValidation |
defaults.yaml passes validation, produces expected model values |
TestScenarioValidation |
Every scenario in config/scenarios/ (merged with defaults) passes validation |
TestTypoDetection |
Misspelled keys in decode, model, vllmCommon, harness, prefill.vllm are caught |
TestTypeErrors |
Constraint violations (gpuMemoryUtilization > 1, negative replicas, negative waitTimeout) |
TestNonBlocking |
validate_config() returns a list on valid, invalid, garbage, and empty input -- never raises |
TestAllowSections |
extra="allow" sections accept arbitrary keys (GPU resources, flags, top-level) |
TestScenarioOnlyFields |
Fields used by scenarios but absent from defaults are accepted |
Validates the DoE experiment parser (llmdbenchmark/experiment/parser.py) and summary tracker (llmdbenchmark/experiment/summary.py).
| Test class | What it covers |
|---|---|
TestDottedToNested |
dotted_to_nested() converts flat dotted-key dicts to nested structures |
TestParseExperimentWithSetup |
Parsing experiment YAML with setup + treatments sections |
TestSetupConstantsOverrideOrder |
Treatment-specific values override setup.constants |
TestParseExperimentWithoutSetup |
Run-only experiments (no setup section) -- backward compat |
TestParseExperimentEdgeCases |
Missing files, invalid YAML, auto-generated names, empty sections |
TestParseRealExperimentFiles |
All 4 experiment YAMLs in workload/experiments/ parse correctly |
TestTieredPrefixCacheExperiment |
Detailed validation of tiered-prefix-cache setup treatments and matrix |
TestPrecisePrefixCacheAwareExperiment |
Routing plugin setup treatments and matrix |
TestPdDisaggregationExperiment |
9 fractional factorial treatments, modelservice/standalone split |
TestExperimentPlanProperties |
total_matrix computation for various setup/run combinations |
TestTreatmentResult |
to_dict() serialization for success and failure cases |
TestExperimentSummary |
Result recording, YAML serialization, summary table output |
TestSetupTreatment |
Dataclass defaults and override storage |
For end-to-end testing against a live cluster, util/test-scenarios.sh runs standup/teardown cycles across scenarios:
util/test-scenarios.sh --stable # Run known-stable scenarios
util/test-scenarios.sh --trouble # Run scenarios that have had issues
util/test-scenarios.sh --all # Run all scenarios
util/test-scenarios.sh --ms-only # Modelservice scenarios only
util/test-scenarios.sh --sa-only # Standalone scenarios onlyThis is useful for validating that template changes do not break deployment across different scenario configurations.
The config schema validates defaults.yaml and all scenario files automatically. When you make changes to templates or config, run the tests to catch regressions.
TestScenarioValidation auto-discovers every *.yaml file under config/scenarios/examples/ and config/scenarios/guides/. New scenarios are picked up automatically -- no test changes needed.
If the new scenario introduces a key that does not exist in defaults.yaml or the schema, the test will fail with a validation warning showing the unrecognized key. To fix:
- Add the field to the appropriate model in
llmdbenchmark/parser/config_schema.py(e.g.VllmCommonConfig,DeploymentBaseConfig, etc.) - Use
Optionalwith aNonedefault for fields that are not indefaults.yaml - Add a targeted test in
TestScenarioOnlyFieldsto document the field
- Add the field to the corresponding Pydantic model in
config_schema.py - Match the type and default value from
defaults.yaml - Run
pytest tests/ -vto confirm defaults still pass
If the key is in a section with STRICT_CONFIG (extra="forbid"), omitting it from the schema will cause TestDefaultsValidation to fail.
The schema is designed for incremental adoption. To model a new top-level section (e.g. standalone, storage, gateway):
- Define the Pydantic model(s) in
config_schema.pyusingSTRICT_CONFIG - Add the field to
BenchmarkConfig(the root model) - Run
pytest tests/ -v-- this validates the new model against defaults and all scenarios - If scenarios use keys not in defaults for this section, add them as optional fields
- For sections that accept arbitrary user-defined keys (like
pluginsCustomConfig), useLENIENT_CONFIG
Templates consume the merged config dict. The schema does not validate templates directly, but it ensures the config feeding them is well-formed. If a new template requires new config keys:
- Add the keys to
defaults.yaml - Add corresponding fields to the schema (see "Adding a new key" above)
- The existing
TestScenarioValidationwill catch any scenario that sets these keys incorrectly
TestDefaultsValidationfails: A key was added/renamed/removed indefaults.yamlbut not in the schemaTestScenarioValidationfails for a specific scenario: That scenario uses a key the schema does not recognize -- add it to the model as optionalTestTypoDetectionfails: The schema is too lenient for that section -- check ifSTRICT_CONFIGis appliedTestAllowSectionsfails: A section that should be extensible is usingSTRICT_CONFIGinstead ofLENIENT_CONFIG