mesh-delivery-contracts: the client of mesh-delivery-checker holds the rules of a verdict and a kept verdict that its holder and readers share (D-10, ADR 0634 §2, ADR 0625 §4; issue 1966)

This commit is contained in:
2026-11-05 14:37:13 +01:00
parent 530a4ddfde
commit 719ddabb14
4 changed files with 321 additions and 0 deletions
@@ -0,0 +1,222 @@
// Package meshdeliverycheckerclient holds the logic the holders and callers of the seat mesh-delivery-checker share over its
// contract (novox/hq ADR 0625 §4, ADR 0634 §2): the rules a verdict and a kept verdict must keep to be written or read. The
// checker writes them, and the view, the gate and the indexer read them; the contract's decoding is strict by schema only, so
// these rules are written once here and no caller copies them.
package meshdeliverycheckerclient
import (
"fmt"
"strings"
checker "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker"
"git.novox.be/novox/mesh-delivery/sdk/kept"
)
// The states of a verdict, the levels of a check and the compositions' states.
const (
VerdictRunning = "running"
VerdictPass = "pass"
VerdictFail = "fail"
LevelUnit = "unit"
LevelIntegration = "integration"
CompositionNotRun = "not-run"
CompositionPass = "pass"
CompositionFail = "fail"
)
// MaxFailures is the most failures a verdict names; it names the count of the rest beside them.
const MaxFailures = 50
func checkLevel(level string) error {
if level != LevelUnit && level != LevelIntegration {
return fmt.Errorf("meshdeliverycheckerclient: the level %q is not %s or %s", level, LevelUnit, LevelIntegration)
}
return nil
}
func isHex12(s string) bool { return len(s) == 12 && strings.Trim(s, "0123456789abcdef") == "" }
// CheckRepository refuses a repository that is not <owner>/<name> with exactly one slash, each part a key part.
func CheckRepository(repository string) error {
owner, name, ok := strings.Cut(repository, "/")
if !ok || strings.Contains(name, "/") {
return fmt.Errorf("meshdeliverycheckerclient: the repository %q is not <owner>/<name>, with exactly one slash", repository)
}
if err := kept.CheckName("repository owner", owner); err != nil {
return fmt.Errorf("meshdeliverycheckerclient: the repository %q: %w", repository, err)
}
if err := kept.CheckName("repository name", name); err != nil {
return fmt.Errorf("meshdeliverycheckerclient: the repository %q: %w", repository, err)
}
return nil
}
func checkComposition(c *checker.VerdictsByGroupHeadsValueComposition) error {
if c == nil {
return nil
}
switch c.State {
case CompositionNotRun:
if c.Sdk != "" || len(c.Machines) > 0 {
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition is not run and names no mesh-sdk commit or machines")
}
case CompositionPass:
if c.Sdk == "" {
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition passed and names no mesh-sdk commit")
}
case CompositionFail:
if c.Words == "" {
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition failed and gives no words")
}
default:
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition state %q is not %s, %s or %s", c.State, CompositionNotRun, CompositionPass, CompositionFail)
}
return nil
}
// CheckVerdict refuses a verdict a reader could not place: no group, heads that are not 12 lowercase hex digits, a repository
// both merged and read beside, a carried-from that carries nothing, a series change from a series to itself or named twice, a
// level that is neither unit nor integration or counts a negative number, a time below zero, a pass that says nothing of a
// level, a fail that names neither a test nor a machine, and failures on a verdict that did not fail.
func CheckVerdict(v checker.VerdictsByGroupHeadsValue) error {
const who = "meshdeliverycheckerclient: "
if v.Group == "" {
return fmt.Errorf(who + "a verdict names no group")
}
if !isHex12(v.Heads) {
return fmt.Errorf(who+"the verdict's heads hash %q is not 12 lowercase hex digits", v.Heads)
}
for repository, head := range v.Trunk {
if head == "" {
return fmt.Errorf(who+"the verdict's trunk head of %s is empty", repository)
}
}
for repository, commit := range v.Beside {
if commit == "" {
return fmt.Errorf(who+"the verdict's sibling commit of %s is empty", repository)
}
if _, ok := v.Trunk[repository]; ok {
return fmt.Errorf(who+"the verdict names %s as a trunk it merged and as a sibling it read", repository)
}
}
if len(v.CarriedFrom) > 0 {
if v.State == VerdictRunning {
return fmt.Errorf(who + "a running verdict is carried from nothing: carried-from is written with its end")
}
moved := false
for repository, commit := range v.CarriedFrom {
now, inTrunk := v.Trunk[repository]
if !inTrunk {
now = v.Beside[repository]
}
if commit == "" || now == "" {
return fmt.Errorf(who+"the verdict's carried-from names %s, which it judged on no commit of", repository)
}
moved = moved || commit != now
}
if !moved {
return fmt.Errorf(who + "the verdict's carried-from names the commits it is judged on: nothing moved, so nothing was carried")
}
}
changed := map[string]bool{}
for _, c := range v.Series {
if c.Module == "" || c.Artifact == "" || c.From == "" || c.To == "" {
return fmt.Errorf(who+"the verdict's series change %+v names no module, artifact, from or to", c)
}
if c.From == c.To {
return fmt.Errorf(who+"the verdict's series change of %s/%s is from %s to the same series", c.Module, c.Artifact, c.From)
}
if changed[c.Module+"/"+c.Artifact] {
return fmt.Errorf(who+"the verdict names the series change of %s/%s twice", c.Module, c.Artifact)
}
changed[c.Module+"/"+c.Artifact] = true
}
for level, r := range v.Levels {
if err := checkLevel(level); err != nil {
return err
}
if r.Run < 0 || r.Kept < 0 || r.Skipped < 0 {
return fmt.Errorf(who+"the verdict's %s level counts a negative number of tests", level)
}
}
if err := checkComposition(v.Composition); err != nil {
return err
}
seen := map[string]bool{}
for _, level := range v.OverBound {
if err := checkLevel(level); err != nil {
return fmt.Errorf(who+"the verdict's over-bound names: %w", err)
}
if seen[level] {
return fmt.Errorf(who+"the verdict's over-bound names the level %s twice", level)
}
seen[level] = true
}
if t := v.Took; t != nil {
for part, ms := range map[string]int64{"queue": t.Queue, "clone": t.Clone, "unit": t.Unit, "lint": t.Lint,
"integration-setup": t.IntegrationSetup, "integration-run": t.IntegrationRun, "composition": t.Composition} {
if ms < 0 {
return fmt.Errorf(who+"the verdict's time of the part %s is negative", part)
}
}
}
named := v.Failed != nil && (v.Failed.Test != "" || v.Failed.Machine != "")
if len(v.Failures) > 0 && v.State != VerdictFail {
return fmt.Errorf(who+"a %s verdict names no failures", v.State)
}
if len(v.Failures) > MaxFailures+1 {
return fmt.Errorf(who+"the verdict names %d failures, more than %d and the count of the rest", len(v.Failures), MaxFailures)
}
switch v.State {
case VerdictRunning:
if v.Failed != nil && *v.Failed != (checker.VerdictsByGroupHeadsValueFailed{}) {
return fmt.Errorf(who + "a running verdict names no failure")
}
case VerdictPass:
if v.Failed != nil && *v.Failed != (checker.VerdictsByGroupHeadsValueFailed{}) {
return fmt.Errorf(who + "a pass names no failure")
}
for _, level := range []string{LevelUnit, LevelIntegration} {
if _, ok := v.Levels[level]; !ok {
return fmt.Errorf(who+"a pass says nothing of the %s level", level)
}
}
case VerdictFail:
if !named {
return fmt.Errorf(who + "a fail names neither a test nor a machine")
}
default:
return fmt.Errorf(who+"the verdict state %q is not running, pass or fail", v.State)
}
return nil
}
// CheckKept refuses a kept verdict that is not a pass of a repository's package at a level, with its toolchain and its time:
// a failure is never kept.
func CheckKept(k checker.KeptByLevelDigestValue) error {
const who = "meshdeliverycheckerclient: "
if k.State != VerdictPass {
return fmt.Errorf(who+"a kept verdict is a pass, never %q: a failure is never kept", k.State)
}
if err := CheckRepository(k.Repository); err != nil {
return err
}
if k.Package == "" {
return fmt.Errorf(who + "a kept verdict names no package")
}
if err := checkLevel(k.Level); err != nil {
return err
}
if k.Tests < 0 || k.Took < 0 {
return fmt.Errorf(who + "a kept verdict counts a negative number of tests or a negative time")
}
if k.Toolchain == "" {
return fmt.Errorf(who + "a kept verdict names no toolchain version")
}
if k.At == "" {
return fmt.Errorf(who + "a kept verdict says no time")
}
return nil
}
@@ -0,0 +1,81 @@
package meshdeliverycheckerclient
import (
"strings"
"testing"
checker "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker"
)
type verdict = checker.VerdictsByGroupHeadsValue
func passing() verdict {
return verdict{Group: "g", Heads: "0123456789ab", State: VerdictPass, Trunk: map[string]string{"novox/a": "c1"},
Levels: map[string]checker.VerdictsByGroupHeadsValueLevelsValue{LevelUnit: {Run: 3}, LevelIntegration: {Run: 1}}}
}
// The rules a verdict keeps to be written or read: each case edits a passing verdict and names what the refusal says.
func TestAVerdictIsRefusedForWhatAReaderCouldNotPlace(t *testing.T) {
if err := CheckVerdict(passing()); err != nil {
t.Fatalf("a passing verdict is refused: %v", err)
}
for name, c := range map[string]struct {
edit func(*verdict)
says string
}{
"no group": {func(v *verdict) { v.Group = "" }, "no group"},
"heads of 11 digits": {func(v *verdict) { v.Heads = "0123456789a" }, "12 lowercase hex"},
"an empty trunk head": {func(v *verdict) { v.Trunk["novox/a"] = "" }, "trunk head"},
"merged and read": {func(v *verdict) { v.Beside = map[string]string{"novox/a": "c2"} }, "trunk it merged and as a sibling"},
"nothing carried": {func(v *verdict) { v.CarriedFrom = map[string]string{"novox/a": "c1"} }, "nothing moved"},
"a running carry": {func(v *verdict) { v.State, v.CarriedFrom = VerdictRunning, map[string]string{"novox/a": "c0"} }, "carried from nothing"},
"a series to itself": {func(v *verdict) {
v.Series = []checker.VerdictsByGroupHeadsValueSeriesItem{{Module: "m", Artifact: "a", From: "0.1", To: "0.1"}}
}, "same series"},
"an unknown level": {func(v *verdict) { v.Levels["smoke"] = checker.VerdictsByGroupHeadsValueLevelsValue{} }, "smoke"},
"a negative count": {func(v *verdict) { v.Levels[LevelUnit] = checker.VerdictsByGroupHeadsValueLevelsValue{Run: -1} }, "negative"},
"a negative time": {func(v *verdict) { v.Took = &checker.VerdictsByGroupHeadsValueTook{Unit: -1} }, "negative"},
"a pass without a level": {func(v *verdict) { delete(v.Levels, LevelIntegration) }, "integration level"},
"a pass that failed": {func(v *verdict) { v.Failed = &checker.VerdictsByGroupHeadsValueFailed{Test: "T"} }, "names no failure"},
"failures of a pass": {func(v *verdict) { v.Failures = []checker.VerdictsByGroupHeadsValueFailuresItem{{Test: "T"}} }, "names no failures"},
"a fail naming nothing": {func(v *verdict) { v.State = VerdictFail }, "neither a test nor a machine"},
"an unknown state": {func(v *verdict) { v.State = "maybe" }, "maybe"},
"a composition passed without a commit": {func(v *verdict) {
v.Composition = &checker.VerdictsByGroupHeadsValueComposition{State: CompositionPass}
}, "mesh-sdk commit"},
"the same level over bound twice": {func(v *verdict) { v.OverBound = []string{LevelUnit, LevelUnit} }, "twice"},
} {
v := passing()
c.edit(&v)
if err := CheckVerdict(v); err == nil || !strings.Contains(err.Error(), c.says) {
t.Errorf("%s: %v, want a refusal saying %q", name, err, c.says)
}
}
failed := passing()
failed.State, failed.Failed = VerdictFail, &checker.VerdictsByGroupHeadsValueFailed{Machine: "ace"}
if err := CheckVerdict(failed); err != nil {
t.Errorf("a fail naming a machine is refused: %v", err)
}
}
func TestAKeptVerdictIsAPassOfAPackageAtALevel(t *testing.T) {
ok := checker.KeptByLevelDigestValue{State: VerdictPass, Repository: "novox/mesh-sdk", Package: "./go/keys", Level: LevelUnit, Toolchain: "1.26.0", At: "2026-11-05T10:00:00Z"}
if err := CheckKept(ok); err != nil {
t.Fatalf("a kept pass is refused: %v", err)
}
for name, edit := range map[string]func(*checker.KeptByLevelDigestValue){
"a fail": func(k *checker.KeptByLevelDigestValue) { k.State = VerdictFail },
"no owner": func(k *checker.KeptByLevelDigestValue) { k.Repository = "mesh-sdk" },
"no package": func(k *checker.KeptByLevelDigestValue) { k.Package = "" },
"an unknown level": func(k *checker.KeptByLevelDigestValue) { k.Level = "smoke" },
"negative tests": func(k *checker.KeptByLevelDigestValue) { k.Tests = -1 },
"no toolchain": func(k *checker.KeptByLevelDigestValue) { k.Toolchain = "" },
"no time": func(k *checker.KeptByLevelDigestValue) { k.At = "" },
} {
k := ok
edit(&k)
if CheckKept(k) == nil {
t.Errorf("%s was taken", name)
}
}
}
@@ -0,0 +1,9 @@
module git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliverycheckerclient
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker v0.0.0
git.novox.be/novox/mesh-delivery/sdk v0.0.0
)
+9
View File
@@ -112,6 +112,15 @@
],
"series": "0.1"
},
{
"name": "go-meshdeliverycheckerclient",
"kind": "package",
"language": "go",
"paths": [
"go/clients/meshdeliverycheckerclient"
],
"series": "0.1"
},
{
"name": "go-gopackageregistryclient",
"kind": "package",