Clients: the kept and preview keys of the checker's states and the candidates key of the catalog's (ADR 0634 §2; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 16:42:19 +01:00
parent dd6f899b12
commit b79b94b19d
4 changed files with 132 additions and 0 deletions
@@ -3,10 +3,12 @@ package meshcatalogclient
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"
"strings"
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-sdk/go/keys"
)
// FingerprintSet is the `fpset` of a candidate's pull requests: the first 12 hex digits of the sha256 over the lines
@@ -36,3 +38,25 @@ func FingerprintSet(pulls []seat.CandidatesByGroupValuePullsItem) string {
sum := sha256.Sum256([]byte(strings.Join(all, "\n")))
return hex.EncodeToString(sum[:])[:12]
}
// CandidatesKey is the key, in the state candidates, of a group: `<group>` escaped (keys.EscapeName), the branch name the
// group's pull requests share (a slash is allowed); a lone pull request is a group of one under its branch name.
func CandidatesKey(group string) (string, error) {
if group == "" {
return "", fmt.Errorf("meshcatalogclient: the group name is empty")
}
return seat.CandidatesByGroupKey(keys.EscapeName(group)), nil
}
// ParseCandidatesKey is the inverse of CandidatesKey.
func ParseCandidatesKey(key string) (string, error) {
part, ok := seat.ParseCandidatesByGroupKey(key)
if !ok {
return "", fmt.Errorf("meshcatalogclient: %q is not a candidates key", key)
}
group, err := keys.UnescapeName(part)
if err != nil {
return "", fmt.Errorf("meshcatalogclient: %q is not a candidates key: %w", key, err)
}
return group, nil
}
@@ -156,3 +156,19 @@ func TestVerdictsAreTakenAsTheReadersTookThem(t *testing.T) {
t.Error("ModulesKey accepts a dotted name")
}
}
func TestCandidatesKeyEscapesTheGroupAndReadsBack(t *testing.T) {
key, err := CandidatesKey("fix/1.2")
if err != nil {
t.Fatal(err)
}
if key == "fix/1.2" {
t.Errorf("the group is not escaped: %q", key)
}
if group, err := ParseCandidatesKey(key); err != nil || group != "fix/1.2" {
t.Errorf("read back = %q, %v", group, err)
}
if _, err := CandidatesKey(""); err == nil {
t.Error("an empty group keys")
}
}
@@ -192,3 +192,32 @@ func TestTheCodecsWriteAtTheVersionAndReadBackThroughTheRules(t *testing.T) {
t.Error("a preview with no merge commit and no conflict is written")
}
}
// The kept and preview keys: a key reads back to its parts, and a part that does not key is refused.
func TestKeptAndPreviewKeysReadBack(t *testing.T) {
digest := strings.Repeat("ab", 32)
key, err := KeptKey(LevelUnit, digest)
if err != nil || key != "unit."+digest {
t.Fatalf("kept key = %q, %v", key, err)
}
if level, d, err := ParseKeptKey(key); err != nil || level != LevelUnit || d != digest {
t.Errorf("kept key read back = %q %q, %v", level, d, err)
}
for _, bad := range []string{"fast." + digest, "unit.XY", "unit"} {
if _, _, err := ParseKeptKey(bad); err == nil {
t.Errorf("kept key %q is read", bad)
}
}
key, err = PreviewKey("novox/mesh-sdk", "h1", "t1")
if err != nil {
t.Fatal(err)
}
if repo, h, tr, err := ParsePreviewKey(key); err != nil || repo != "novox/mesh-sdk" || h != "h1" || tr != "t1" {
t.Errorf("preview key %q read back = %q %q %q, %v", key, repo, h, tr, err)
}
for _, bad := range []string{"novox.h1.t1", "a/b.h1", "a/b.h1.t1.x"} {
if _, _, _, err := ParsePreviewKey(bad); err == nil {
t.Errorf("preview key %q is read", bad)
}
}
}
@@ -3,6 +3,7 @@ package meshdeliverycheckerclient
import (
"encoding/json"
"fmt"
"strings"
checker "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker"
"git.novox.be/novox/mesh-sdk/go/keys"
@@ -85,3 +86,65 @@ func decode(data []byte, into any) error {
}
return nil
}
// KeptKey is the key, in the state kept, of a kept verdict: `<level>.<digest>`, the level unit or integration and the digest,
// lowercase hex, of everything that package's tests read at that level as the language's build module names it.
func KeptKey(level, digest string) (string, error) {
if err := checkLevel(level); err != nil {
return "", err
}
if digest == "" || strings.Trim(digest, "0123456789abcdef") != "" {
return "", fmt.Errorf("meshdeliverycheckerclient: %q is not a digest, lowercase hex", digest)
}
return checker.KeptByLevelDigestKey(level, digest), nil
}
// ParseKeptKey is the inverse of KeptKey.
func ParseKeptKey(key string) (level, digest string, err error) {
level, digest, ok := checker.ParseKeptByLevelDigestKey(key)
if !ok {
return "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a kept key, <level>.<digest>", key)
}
if _, err = KeptKey(level, digest); err != nil {
return "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a kept key: %w", key, err)
}
return level, digest, nil
}
// PreviewKey is the key, in the state previews, of a head merged onto a trunk commit: `<repository>.<head>.<trunk>`, the
// repository's owner and name each escaped (keys.EscapeName).
func PreviewKey(repository, head, trunk string) (string, error) {
if err := CheckRepository(repository); err != nil {
return "", err
}
if err := keys.CheckName("head", head); err != nil {
return "", err
}
if err := keys.CheckName("trunk commit", trunk); err != nil {
return "", err
}
owner, name, _ := strings.Cut(repository, "/")
return checker.PreviewsByRepositoryHeadTrunkKey(keys.EscapeName(owner)+"/"+keys.EscapeName(name), head, trunk), nil
}
// ParsePreviewKey is the inverse of PreviewKey.
func ParsePreviewKey(key string) (repository, head, trunk string, err error) {
repo, head, trunk, ok := checker.ParsePreviewsByRepositoryHeadTrunkKey(key)
if !ok || strings.Contains(trunk, ".") {
return "", "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a preview key, <repository>.<head>.<trunk>", key)
}
owner, name, found := strings.Cut(repo, "/")
if !found {
return "", "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a preview key: the repository has no slash", key)
}
owner, err1 := keys.UnescapeName(owner)
name, err2 := keys.UnescapeName(name)
if err1 != nil || err2 != nil {
return "", "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a preview key: the repository does not unescape", key)
}
repository = owner + "/" + name
if _, err := PreviewKey(repository, head, trunk); err != nil {
return "", "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a preview key: %w", key, err)
}
return repository, head, trunk, nil
}