Files
nezha_domains/integration/agentcompat/internal/evidence/results.go
T
2026-07-20 04:50:03 +00:00

106 lines
3.4 KiB
Go

package evidence
import (
"encoding/json"
"fmt"
"github.com/nezhahq/nezha/integration/agentcompat/internal/contract"
)
type ScenarioResult struct {
Name string `json:"name"`
Passed bool `json:"passed"`
Assertions []Assertion `json:"assertions,omitempty"`
Error string `json:"error,omitempty"`
}
type Assertion struct {
Name string `json:"name"`
Passed bool `json:"passed"`
Details string `json:"details,omitempty"`
}
type Results struct {
Profile string `json:"profile"`
Passed bool `json:"passed"`
Scenarios []ScenarioResult `json:"scenarios"`
}
func (results Results) Validate() error {
if results.Profile == "" || len(results.Scenarios) == 0 {
return fmt.Errorf("results fields are incomplete")
}
if _, err := contract.ProfileByName(results.Profile); err != nil {
return fmt.Errorf("results profile is invalid: %w", err)
}
allPassed := true
seenScenarios := make(map[string]struct{}, len(results.Scenarios))
for _, scenario := range results.Scenarios {
if _, err := contract.NewScenario(scenario.Name); err != nil {
return fmt.Errorf("scenario name is invalid: %w", err)
}
if _, exists := seenScenarios[scenario.Name]; exists {
return fmt.Errorf("scenario name is duplicated")
}
seenScenarios[scenario.Name] = struct{}{}
if len(scenario.Assertions) == 0 {
return fmt.Errorf("scenario must contain assertions")
}
assertionsPassed := true
seenAssertions := make(map[string]struct{}, len(scenario.Assertions))
for _, assertion := range scenario.Assertions {
if assertion.Name == "" {
return fmt.Errorf("assertion name is required")
}
if _, exists := seenAssertions[assertion.Name]; exists {
return fmt.Errorf("assertion name is duplicated")
}
seenAssertions[assertion.Name] = struct{}{}
assertionsPassed = assertionsPassed && assertion.Passed
}
if scenario.Passed != assertionsPassed {
return fmt.Errorf("scenario pass state is inconsistent with assertions")
}
if !scenario.Passed {
if scenario.Error == "" {
return fmt.Errorf("failed scenario error is required")
}
allPassed = false
} else if scenario.Error != "" {
return fmt.Errorf("passed scenario cannot contain an error")
}
}
if results.Passed != allPassed {
return fmt.Errorf("results pass state is inconsistent")
}
return nil
}
func MarshalResults(results Results) ([]byte, error) {
if err := results.Validate(); err != nil {
return nil, err
}
return json.Marshal(results)
}
func (results Results) MarshalJSON() ([]byte, error) {
type resultsWire Results
redacted := resultsWire{Profile: results.Profile, Passed: results.Passed, Scenarios: make([]ScenarioResult, 0, len(results.Scenarios))}
for _, scenario := range results.Scenarios {
assertions := make([]Assertion, 0, len(scenario.Assertions))
for _, assertion := range scenario.Assertions {
assertions = append(assertions, Assertion{Name: assertion.Name, Passed: assertion.Passed, Details: Redact(assertion.Details)})
}
redacted.Scenarios = append(redacted.Scenarios, ScenarioResult{Name: scenario.Name, Passed: scenario.Passed, Assertions: assertions, Error: Redact(scenario.Error)})
}
return json.Marshal(redacted)
}
func scenarioResultNames(scenarios []ScenarioResult) []string {
names := make([]string, 0, len(scenarios))
for _, scenario := range scenarios {
names = append(names, scenario.Name)
}
return names
}