140 lines
4.5 KiB
Go
140 lines
4.5 KiB
Go
//go:build integration
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
|
|
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
|
"github.com/nats-io/nats.go/jetstream"
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/testbus"
|
|
)
|
|
|
|
// builderOnTheBus is the builder's and the indexer's buckets on a test's own bus, which the controller's connection for
|
|
// the moment (servingBus) reaches: what a take-in from a key reads (R4e, issue 1851).
|
|
type builderOnTheBus struct {
|
|
js *broker.JetStream
|
|
builds, modules, manifests jetstream.KeyValue
|
|
}
|
|
|
|
func aBuilderOnTheBus(t *testing.T) builderOnTheBus {
|
|
t.Helper()
|
|
js, err := broker.Dial(testbus.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(js.Close)
|
|
api, err := jetstream.New(js.Conn())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx := t.Context()
|
|
b := builderOnTheBus{js: js}
|
|
for _, c := range []struct {
|
|
into *jetstream.KeyValue
|
|
bucket string
|
|
history uint8
|
|
}{
|
|
{&b.builds, sdkdelivery.BuilderBuildsBucket, sdkdelivery.BuilderBuildsHistory},
|
|
{&b.modules, sdkdelivery.ModulesBucket, sdkdelivery.ModulesHistory},
|
|
{&b.manifests, sdkdelivery.ManifestsBucket, 1},
|
|
} {
|
|
kv, err := api.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: c.bucket, History: c.history})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
*c.into = kv
|
|
}
|
|
servingBus.Store(js)
|
|
t.Cleanup(func() { servingBus.Store(nil) })
|
|
withTrunkList(t, true)
|
|
// The indexer on this bus is read, not the "not held" a mesh with no bus reads (aMeshOnTheStore).
|
|
wasIndexed := indexedModule
|
|
indexedModule = theIndexedModule
|
|
t.Cleanup(func() { indexedModule = wasIndexed })
|
|
return b
|
|
}
|
|
|
|
// indexed writes the indexer's trunk value of module at commit, naming fp, and its manifest: a module of one archive.
|
|
func (b builderOnTheBus) indexed(t *testing.T, module, version, repository, path, commit, fp string) string {
|
|
t.Helper()
|
|
keys, trunk := aKeyFor(module, version, fp, repository, path, commit)
|
|
raw := keys.manifests[trunk.Manifest]
|
|
value, err := sdkdelivery.EncodeManifest(sdkdelivery.Manifest{Module: module, JSON: raw})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := b.manifests.Put(context.Background(), trunk.Manifest, value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
trunk.FP = "src1:" + fp[len("src2:"):]
|
|
encoded, err := sdkdelivery.EncodeModule(trunk)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := b.modules.Put(context.Background(), module, encoded); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return trunk.Manifest
|
|
}
|
|
|
|
// built writes the builder's key of module at fp: done, its one archive named with a reference, or in another state.
|
|
func (b builderOnTheBus) built(t *testing.T, module, version, fp, manifest, state string) {
|
|
t.Helper()
|
|
keys, _ := aKeyFor(module, version, fp, "", "", "")
|
|
v := keys.keys[module].Build
|
|
v.Manifest = manifest
|
|
if state != sdkdelivery.BuildDone {
|
|
v = sdkdelivery.BuilderBuild{State: state, By: "ace"}
|
|
if state == sdkdelivery.BuildFailed {
|
|
v.Error = "digests differ: ace sha256:1, g14 sha256:2"
|
|
}
|
|
}
|
|
value, err := sdkdelivery.EncodeBuilderBuild(v)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
key, err := sdkdelivery.BuilderBuildKey(module, fp)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := b.builds.Put(context.Background(), key, value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// noBuildAsked fails the test if anything asks build-agent for a build (ADR 0501 §6, ADR 0503 §6).
|
|
func noBuildAsked(t *testing.T) {
|
|
t.Helper()
|
|
was := askABuild
|
|
askABuild = func(_ context.Context, _ buildSource, path, _ string) (string, error) {
|
|
t.Errorf("a build was asked of build-agent, at %q", path)
|
|
return "", context.Canceled
|
|
}
|
|
t.Cleanup(func() { askABuild = was })
|
|
}
|
|
|
|
// asksWithPaths records every build a merge asks for, as repository, path and ref. The merge window's goroutine and
|
|
// the test's own may ask at once, so the record is written under a lock (issue 1543).
|
|
func asksWithPaths(t *testing.T) *[][3]string {
|
|
t.Helper()
|
|
asksOfEveryModule(t) // the one ask left is the builder's own (ADR 0453 §5): these tests are of what a walk does around it
|
|
var asked [][3]string
|
|
var mu sync.Mutex
|
|
was := askABuild
|
|
askABuild = func(_ context.Context, source buildSource, path, ref string) (string, error) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
asked = append(asked, [3]string{source.Repository, path, ref})
|
|
return "b-" + path, nil
|
|
}
|
|
t.Cleanup(func() { askABuild = was })
|
|
return &asked
|
|
}
|
|
|
|
// theIndexedModule is the look at the indexer as the controller makes it, kept before any test replaces it.
|
|
var theIndexedModule = indexedModule
|