129 lines
6.0 KiB
Go
129 lines
6.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"hash/fnv"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// The routes to root a review of ADR 0266 found (novox/hq ADR 0266 §7): an agent makes a repository of its
|
|
// own — or forks one the mesh builds from — commits a module.json naming a module the mesh runs everywhere
|
|
// (`sudo`, granting itself a rule without a password; `mesh-host`, the node-engine), and asks the `build` verb
|
|
// for it. Its outcome was registered under that name from the agent's repository, and the next push sent it.
|
|
|
|
// onTrunk is an outcome of a commit on its repository's trunk, as the build seat says it.
|
|
func onTrunk(id, repository, seat, path string, manifest map[string]any) link.BuildResult {
|
|
raw, _ := json.Marshal(manifest)
|
|
r := link.BuildResult{ID: id, Repository: "http://forge.internal:20000/" + repository + ".git", Path: path,
|
|
Ref: "main", On: "anchor", Commit: "c0ffee0123456789", Manifest: raw,
|
|
Trunk: "main", OnTrunk: true, Branches: []string{"main"}}
|
|
if seat != "" {
|
|
r.Source = &link.SourceOnSeat{Seat: seat, Repository: repository}
|
|
}
|
|
return r
|
|
}
|
|
|
|
// keptAsked keeps a build request as the asker would: through a verb, or at the terminal.
|
|
func keptAsked(t *testing.T, inv *inventory.Inventory, id, repository, path string, atTerminal bool) {
|
|
t.Helper()
|
|
if err := inv.RecordBuildRequest(t.Context(), inventory.BuildRequest{ID: id, Repository: repository, Seat: "git",
|
|
Path: path, For: "build", AtTerminal: atTerminal}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// theCatalogue is a mesh whose sudo is built from the catalogue repository and whose node-engine from its own.
|
|
|
|
// The operator at the terminal may still move a module, or add one from a new repository.
|
|
|
|
// The delivery's flow is untouched: a merge's rebuild of a module from its own repository, and a merge adding
|
|
// a module to a repository the catalogue builds from (novox/hq issue 300), are registered with no terminal.
|
|
|
|
// Through a verb, a build of a repository the catalogue builds nothing from is not even asked: the build node
|
|
// would run what the agent wrote.
|
|
|
|
// asTheOperator keeps a build as asked at the controller's terminal, as the operator's first build of a module
|
|
// from a repository the catalogue does not yet build from is (novox/hq ADR 0266), and hands it back.
|
|
func asTheOperator(t *testing.T, inv *inventory.Inventory, b link.BuildResult) link.BuildResult {
|
|
t.Helper()
|
|
repository, seat := b.Repository, ""
|
|
if b.Source != nil {
|
|
repository, seat = b.Source.Repository, b.Source.Seat
|
|
}
|
|
if err := inv.RecordBuildRequest(t.Context(), inventory.BuildRequest{ID: b.ID, Repository: repository, Seat: seat,
|
|
Path: b.Path, For: "build", AtTerminal: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// A rollback puts back only a build of the module's own repository: an agent's build of the module's name,
|
|
// recorded and refused, at the very commit the machine ran before (a fork carries it), is never registered by
|
|
// the back door of a failed gate.
|
|
|
|
// theForge is what the forge says in a test, by owner/name: a repository not named here is protected as a
|
|
// trunk must be, with an id of its own.
|
|
var theForge sync.Map
|
|
|
|
func init() {
|
|
askTheForge = func(_ context.Context, owner, repo, _ string) (forgeFacts, error) {
|
|
if said, ok := theForge.Load(owner + "/" + repo); ok {
|
|
switch f := said.(type) {
|
|
case error:
|
|
return forgeFacts{}, f
|
|
case forgeFacts:
|
|
return f, nil
|
|
}
|
|
}
|
|
h := fnv.New32a()
|
|
_, _ = h.Write([]byte(owner + "/" + repo))
|
|
return forgeFacts{ID: int64(h.Sum32()), Guarded: true}, nil
|
|
}
|
|
}
|
|
|
|
// A module's trunk the forge does not protect — direct pushes, no required status — or a forge that cannot say,
|
|
// registers nothing through a verb: the trunk rule means nothing on a branch anyone pushes to.
|
|
|
|
// A repository deleted and made again under the module's repository's name is another repository: the forge's
|
|
// id, recorded at registration, tells them apart.
|
|
|
|
// The terminal's mark is the operator's for the repository and path they asked: an outcome of another, under that
|
|
// build's id, is not theirs.
|
|
|
|
// The serving controller, and everything it starts, is never the terminal: a build asked in it reads as asked
|
|
// through the mesh even when no verb and no caller is named.
|
|
|
|
// The forge applies one rule to a branch: the rule named for it, else the first glob that covers it. A stronger
|
|
// rule later in the list is not what guards the branch, and is not read as if it were (the confirmation review).
|
|
func TestTheRuleJudgedIsTheOneTheForgeApplies(t *testing.T) {
|
|
guarded := protectionRule{Rule: "*", RequiredStatuses: []string{"mesh/merge-gate"}}
|
|
open := protectionRule{Rule: "main", Push: true, RequiredStatuses: []string{"mesh/merge-gate"}}
|
|
if f := judgedRule([]protectionRule{guarded, open}, "main"); f.Guarded {
|
|
t.Error("an exact rule letting pushes was passed over for a glob that guards")
|
|
}
|
|
if f := judgedRule([]protectionRule{{Rule: "ma*", RequiredStatuses: nil}, {Rule: "*", RequiredStatuses: []string{"x"}}},
|
|
"main"); f.Guarded || !strings.Contains(f.Why, "ma*") {
|
|
t.Errorf("the first glob covering the branch was not the one judged: %+v", f)
|
|
}
|
|
if f := judgedRule([]protectionRule{{Rule: "release/*"}, {Rule: "main", RequiredStatuses: []string{"x"}}}, "main"); !f.Guarded {
|
|
t.Errorf("the rule named for the branch was not judged: %+v", f)
|
|
}
|
|
if f := judgedRule(nil, "main"); f.Guarded {
|
|
t.Error("no rule is no protection")
|
|
}
|
|
}
|
|
|
|
// An id the forge could not give keeps the id already recorded.
|
|
|
|
// **The operator's Approve of a reviewed group registers a moved module from its new repository** (novox/hq ADR 0420 §7,
|
|
// issue 1025). sudo is built from the catalogue; a merge into the repository of node-engine's module, announced by
|
|
// mesh-delivery with the ask it merged on, brings its build from there. Accepted with the operator's Approve of the
|
|
// merged head; refused with no kept merge, a warrant for other heads, a Decline, a person who is not the operator, and
|
|
// through a verb.
|