|
| 1 | +// Copyright (c) Microsoft Corporation. |
| 2 | +// Licensed under the MIT License. |
| 3 | + |
| 4 | +package projectconfig |
| 5 | + |
| 6 | +import ( |
| 7 | + "github.com/invopop/jsonschema" |
| 8 | +) |
| 9 | + |
| 10 | +// TestDefinition is the new-shape [tests.X] declaration: one configuration of one |
| 11 | +// runner/harness with framework-specific options. Framework subtables are kept as |
| 12 | +// loosely-typed maps so the resolver can evolve their schemas without requiring |
| 13 | +// matching struct changes here. |
| 14 | +type TestDefinition struct { |
| 15 | + // Type identifies the framework/runner. Required, and constrained to the |
| 16 | + // closed enum in the schema tag at the schema layer. The loader still |
| 17 | + // accepts unknown values permissively; the resolver is the source of truth. |
| 18 | + Type string `toml:"type" json:"type" jsonschema:"required,title=Type,description=Test framework type,enum=pytest,enum=lisa,enum=tmt"` |
| 19 | + |
| 20 | + // Human-readable description. |
| 21 | + Description string `toml:"description,omitempty" json:"description,omitempty" jsonschema:"title=Description,description=Description of this test"` |
| 22 | + |
| 23 | + // Kind hints at what the test exercises (e.g. functional, performance). |
| 24 | + Kind []string `toml:"kind,omitempty" json:"kind,omitempty" jsonschema:"title=Kind,description=Test kind hints (e.g. functional or performance)"` |
| 25 | + |
| 26 | + // LongRunning hints to schedulers/policy that this test may take a long time. |
| 27 | + LongRunning bool `toml:"long-running,omitempty" json:"longRunning,omitempty" jsonschema:"title=Long running,description=Hints that this test may run for hours"` |
| 28 | + |
| 29 | + // RequiredCapabilities lists capability tokens an image must declare to be a |
| 30 | + // valid target for this test. Tokens are matched against [ImageCapabilities]. |
| 31 | + RequiredCapabilities []string `toml:"required-capabilities,omitempty" json:"requiredCapabilities,omitempty" jsonschema:"title=Required capabilities,description=Capability tokens the image must declare"` |
| 32 | + |
| 33 | + // Framework-specific subtables. Kept untyped so framework schema can evolve |
| 34 | + // independently of the dev-tools type definitions. |
| 35 | + Lisa map[string]any `toml:"lisa,omitempty" json:"lisa,omitempty" jsonschema:"title=LISA config,description=LISA-specific configuration"` |
| 36 | + Tmt map[string]any `toml:"tmt,omitempty" json:"tmt,omitempty" jsonschema:"title=TMT config,description=TMT-specific configuration"` |
| 37 | + Pytest map[string]any `toml:"pytest,omitempty" json:"pytest,omitempty" jsonschema:"title=Pytest config,description=pytest-specific configuration"` |
| 38 | +} |
| 39 | + |
| 40 | +// TestGroup is a [test-groups.X] declaration: a named bundle of test references that |
| 41 | +// images or components can target via a single name. |
| 42 | +type TestGroup struct { |
| 43 | + // Human-readable description. |
| 44 | + Description string `toml:"description,omitempty" json:"description,omitempty" jsonschema:"title=Description,description=Description of this test group"` |
| 45 | + |
| 46 | + // Tests is the ordered list of test or nested-group references that make up |
| 47 | + // the group's membership. |
| 48 | + Tests []TestRef `toml:"tests,omitempty" json:"tests,omitempty" jsonschema:"title=Tests,description=Member references (each is either {name=...} or {group=...})"` |
| 49 | +} |
| 50 | + |
| 51 | +// TestRef is a reference to either a test (by name) or another group (by name). |
| 52 | +// Exactly one of Name or Group should be set; semantic validation is the resolver's |
| 53 | +// responsibility. |
| 54 | +type TestRef struct { |
| 55 | + // Name references a [tests.X] entry. |
| 56 | + Name string `toml:"name,omitempty" json:"name,omitempty" jsonschema:"title=Name,description=Name of a test (mutually exclusive with group)"` |
| 57 | + |
| 58 | + // Group references a [test-groups.X] entry. |
| 59 | + Group string `toml:"group,omitempty" json:"group,omitempty" jsonschema:"title=Group,description=Name of a test group (mutually exclusive with name)"` |
| 60 | +} |
| 61 | + |
| 62 | +// ComponentTestsConfig holds the new-shape per-component tests block: |
| 63 | +// |
| 64 | +// tests.tests = [{ name = "..." }, { group = "..." }] |
| 65 | +type ComponentTestsConfig struct { |
| 66 | + // Tests is the list of test or test-group references that apply to the component. |
| 67 | + Tests []TestRef `toml:"tests,omitempty" json:"tests,omitempty" jsonschema:"title=Tests,description=Per-component test or test-group references"` |
| 68 | +} |
| 69 | + |
| 70 | +// JSONSchemaExtend tightens the generated schema for [TestRef] so editors can |
| 71 | +// flag refs that set neither or both of name/group. The runtime resolver |
| 72 | +// ([ErrInvalidTestRef]) is the source of truth; this keeps the schema in sync. |
| 73 | +func (TestRef) JSONSchemaExtend(schema *jsonschema.Schema) { |
| 74 | + // Exactly one of name|group: encoded as oneOf with `required` on each and |
| 75 | + // `not` excluding the other, which forbids both `{}` and `{name, group}`. |
| 76 | + schema.OneOf = []*jsonschema.Schema{ |
| 77 | + {Required: []string{"name"}, Not: &jsonschema.Schema{Required: []string{"group"}}}, |
| 78 | + {Required: []string{"group"}, Not: &jsonschema.Schema{Required: []string{"name"}}}, |
| 79 | + } |
| 80 | +} |
0 commit comments