feat: add new assertions test type

This commit is contained in:
technofab 2026-10-15 19:27:05 +02:00
parent 6c4c1bace6
commit 17d08b9975
Signed by: technofab
SSH key fingerprint: SHA256:bV4h88OqS/AxjbPn66uUdvK9JsgIW4tv3vwJQ8tpMqQ
10 changed files with 603 additions and 16 deletions

View file

@ -5,6 +5,8 @@ import (
"fmt"
"reflect"
"regexp"
"sort"
"strings"
"sync"
"time"
@ -133,6 +135,8 @@ func (r *Runner) executeTest(spec types.TestSpec) types.TestResult {
r.handleUnitTest(&result, spec, actual)
case types.TestTypeScript:
r.handleScriptTest(&result, spec)
case types.TestTypeAssertions:
r.handleAssertionsTest(&result, spec)
default:
result.Status = types.StatusError
result.ErrorMessage = fmt.Sprintf("Invalid test type: %s", spec.Type)
@ -193,6 +197,62 @@ func (r *Runner) handleScriptTest(result *types.TestResult, spec types.TestSpec)
}
}
// handleAssertionsTest processes assertions type tests
func (r *Runner) handleAssertionsTest(result *types.TestResult, spec types.TestSpec) {
if len(spec.Assertions) == 0 {
result.Status = types.StatusError
result.ErrorMessage = "No assertions defined for test"
return
}
total := len(spec.Assertions)
failed := 0
errored := 0
names := make([]string, 0, total)
for name := range spec.Assertions {
names = append(names, name)
}
sort.Strings(names)
var sb strings.Builder
for _, name := range names {
assertRes := spec.Assertions[name]
if assertRes.Ok {
continue
}
if assertRes.Error {
errored++
} else {
failed++
}
if sb.Len() > 0 {
sb.WriteString("\n\n")
}
fmt.Fprintf(&sb, "❌ \"%s\"", name)
if assertRes.Message != "" {
fmt.Fprintf(&sb, "\n %s", assertRes.Message)
}
if assertRes.Expected != "" {
fmt.Fprintf(&sb, "\n Expected: %s", assertRes.Expected)
}
if assertRes.Actual != "" {
fmt.Fprintf(&sb, "\n Actual: %s", assertRes.Actual)
}
}
if errored > 0 {
result.Status = types.StatusError
result.ErrorMessage = sb.String()
} else if failed > 0 {
result.Status = types.StatusFailure
result.ErrorMessage = sb.String()
} else {
result.Status = types.StatusSuccess
}
}
// compareActualExpected performs the deep equality check and formats diffs
func (r *Runner) compareActualExpected(result *types.TestResult, actual, expected any) {
if reflect.DeepEqual(actual, expected) {

View file

@ -271,6 +271,71 @@ func TestRunner_executeTest(t *testing.T) {
wantStatus: types.StatusFailure,
wantErrMsgContains: "[exit code 1]\n[stdout]\nout on fail\n[stderr]\nerr on fail",
},
// --- Assertions Tests ---
{
name: "Assertions test all success",
spec: types.TestSpec{
Name: "AssertionsSuccess",
Type: types.TestTypeAssertions,
Assertions: map[string]types.AssertionResult{
"should be true": {Ok: true},
"should match": {Ok: true},
},
},
runnerConfig: Config{},
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {},
wantStatus: types.StatusSuccess,
},
{
name: "Assertions test with failure",
spec: types.TestSpec{
Name: "AssertionsFail",
Type: types.TestTypeAssertions,
Assertions: map[string]types.AssertionResult{
"should pass": {Ok: true},
"should fail": {
Ok: false,
Message: "Expected values to be equal",
Expected: "10",
Actual: "5",
},
},
},
runnerConfig: Config{},
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {},
wantStatus: types.StatusFailure,
wantErrMsgContains: "❌ \"should fail\"\n Expected values to be equal\n Expected: 10\n Actual: 5",
},
{
name: "Assertions test with eval error",
spec: types.TestSpec{
Name: "AssertionsError",
Type: types.TestTypeAssertions,
Assertions: map[string]types.AssertionResult{
"eval error": {
Ok: false,
Error: true,
Message: "Nix evaluation error (expression threw or aborted)",
},
},
},
runnerConfig: Config{},
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {},
wantStatus: types.StatusError,
wantErrMsgContains: "❌ \"eval error\"\n Nix evaluation error",
},
{
name: "Assertions test with empty assertions",
spec: types.TestSpec{
Name: "AssertionsEmpty",
Type: types.TestTypeAssertions,
Assertions: map[string]types.AssertionResult{},
},
runnerConfig: Config{},
setupMockServices: func(t *testing.T, mNix *mockNixService, mSnap *mockSnapshotService, s types.TestSpec, c Config) {},
wantStatus: types.StatusError,
wantErrMsgContains: "No assertions defined for test",
},
}
for _, tt := range tests {