AS17 (a, b): a revert is a want going back to an earlier fingerprint by the assignment revisions' numbers; a kept data's form change is read from the entries' manifests
Builds: design 47 G §298, §300, §303, §213; lead answer A-217 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package assigngraph
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
)
|
||||
|
||||
// Reverted says a want of module on node at wantFp is a revert (design 47 G §298, §300, A-217): the module had wantFp in an earlier
|
||||
// assignment graph revision than the one it now runs from, by the revisions' own numbers. The revision it runs from is the first kept
|
||||
// that lists runFp for the module on the node; a want is a revert when it was listed before that, and never listed since.
|
||||
func Reverted(ctx context.Context, o Objects, node, module, runFp, wantFp string) (bool, error) {
|
||||
if runFp == "" || wantFp == "" || runFp == wantFp {
|
||||
return false, nil
|
||||
}
|
||||
names, err := o.Names(ctx)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("the objects %s: %w", RevisionsObjects, err)
|
||||
}
|
||||
var numbers []int64
|
||||
for _, n := range names {
|
||||
if r, ok := trunkOf(n); ok {
|
||||
numbers = append(numbers, r)
|
||||
}
|
||||
}
|
||||
slices.Sort(numbers)
|
||||
var ranFrom, lastWanted int64
|
||||
for _, r := range numbers {
|
||||
rev, err := ReadRevision(ctx, o, r)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, ref := range rev.Entries {
|
||||
if ref.Node != node || ref.Module != module {
|
||||
continue
|
||||
}
|
||||
e, err := ReadEntry(ctx, o, ref.Digest)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
switch e.Fp {
|
||||
case runFp:
|
||||
if ranFrom == 0 {
|
||||
ranFrom = r
|
||||
}
|
||||
case wantFp:
|
||||
lastWanted = r
|
||||
}
|
||||
}
|
||||
}
|
||||
return ranFrom != 0 && lastWanted != 0 && lastWanted < ranFrom, nil
|
||||
}
|
||||
|
||||
// FormChanges are the kept data whose `form` differs between the running entry's manifest and the want's (design 47 G §295 to §298,
|
||||
// A-217): the entry's manifest whole is read as a manifest and compared by catalogue.FormChanges, so a kept data's form is read from
|
||||
// the entry's kept declarations (catalogue.KeptDeclarations), never from a field of the want.
|
||||
func FormChanges(running, applied meshcatalog.CatalogEntry) ([]catalogue.FormChange, error) {
|
||||
before, err := manifestOf(running)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the running manifest of %s: %w", running.Module, err)
|
||||
}
|
||||
after, err := manifestOf(applied)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the manifest of %s: %w", applied.Module, err)
|
||||
}
|
||||
return catalogue.FormChanges(before, after), nil
|
||||
}
|
||||
|
||||
func manifestOf(e meshcatalog.CatalogEntry) (catalogue.Manifest, error) {
|
||||
var m catalogue.Manifest
|
||||
raw, err := json.Marshal(e.Manifest)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
err = json.Unmarshal(raw, &m)
|
||||
return m, err
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package assigngraph
|
||||
|
||||
import (
|
||||
"context"
|
||||
mma "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/clients/meshcatalogclient"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog"
|
||||
)
|
||||
|
||||
// Design 47 G §298, §300, A-217 (AS17): a want is a revert when the module had its fingerprint in an earlier assignment graph revision than
|
||||
// the one it now runs from; a kept data whose form changed is read from the entries' manifests.
|
||||
func TestAWantGoingBackToAnEarlierFingerprintIsARevert(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
o := memObjects{}
|
||||
at := time.Now()
|
||||
for _, fp := range []string{"src2:a", "src2:b", "src2:c"} {
|
||||
in := Inputs{Halves: map[string]meshcatalogclient.Half{"main": half("main", 1, meshcatalog.CatalogEntry{Module: "web", Fp: fp})},
|
||||
Placements: map[string]mma.Assignments{"ace": {Node: "ace", Modules: []mma.PlacedModule{{Module: "web"}}}}, FirstBranch: func(string) string { return "main" }}
|
||||
if _, _, err := Keep(ctx, o, Compute(in), "test", at); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, c := range []struct {
|
||||
run, want string
|
||||
revert bool
|
||||
}{{"src2:c", "src2:a", true}, {"src2:c", "src2:b", true}, {"src2:a", "src2:c", false}, {"src2:b", "src2:b", false}, {"src2:c", "src2:zz", false}} {
|
||||
if got, err := Reverted(ctx, o, "ace", "web", c.run, c.want); err != nil || got != c.revert {
|
||||
t.Errorf("runs %s, wanted %s: revert %v %v, want %v", c.run, c.want, got, err, c.revert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAKeptDatasFormIsReadFromTheEntrysManifest(t *testing.T) {
|
||||
entry := func(form int) meshcatalog.CatalogEntry {
|
||||
return meshcatalog.CatalogEntry{Module: "web", Manifest: map[string]any{"module": "web",
|
||||
"state": []any{map[string]any{"name": "sessions", "form": form}}}}
|
||||
}
|
||||
same, err := FormChanges(entry(1), entry(1))
|
||||
if err != nil || len(same) != 0 {
|
||||
t.Fatalf("same form: %v %v", same, err)
|
||||
}
|
||||
got, err := FormChanges(entry(1), entry(2))
|
||||
if err != nil || len(got) != 1 || got[0].Name != "sessions" {
|
||||
t.Errorf("changed form: %+v %v", got, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user