Split the unit level from the integration level in cmd, builder, link, catalogue, inventory; cheap fixtures for the archive-entry bound, the registry waits and the catalogue copy (issue 895)
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/catalogue"
|
||||
"github.com/novox/mesh-controller/internal/conditions"
|
||||
"github.com/novox/mesh-controller/internal/link"
|
||||
)
|
||||
@@ -15,98 +12,9 @@ import (
|
||||
// the condition store's first kind (to-be 45 §2). On 2026-10-05 the identity provider refused every
|
||||
// consumer for a day and status called the mesh well (04-ISSUES/179): this is that day, told to the
|
||||
// controller the way the provider now tells it.
|
||||
func TestAProviderFailingAConsumerBreaksAllWellUntilItRecovers(t *testing.T) {
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
register(t, open, catalogue.Manifest{Module: "idp", Version: "1",
|
||||
Receives: map[string]string{"oidc-client": "/var/lib/mesh/idp/mesh.json"}})
|
||||
if _, err := assign(ctx, open, "anchor", "idp"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
kept := standings{keeper: func() *conditions.Keeper { return conditionsFrom }}
|
||||
since := time.Now().Add(-23 * time.Hour)
|
||||
failing := link.Standing{Module: "idp", Failing: true, Provider: "oidc-client", ProviderNode: "anchor",
|
||||
Consumer: "mesh_laptop_dashboard", Node: "laptop", Class: "credentials-rejected",
|
||||
Error: `Keycloak token request failed: 401 {"error":"invalid_grant"}`, Since: since, Attempts: 31000}
|
||||
if _, err := kept.Stood(ctx, failing); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
asked, err := theThreeQuestions(ctx, open)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if asked.well() {
|
||||
t.Fatal("a mesh whose identity provider fails a consumer reads as well")
|
||||
}
|
||||
said := printed(t, func() error { return printStatus(asked) })
|
||||
for _, want := range []string{"1 open condition(s)", "provider.idp.anchor.mesh_laptop_dashboard.failing",
|
||||
"idp on anchor keeps failing mesh_laptop_dashboard on laptop", "credentials-rejected", "31000 attempt(s)"} {
|
||||
if !strings.Contains(said, want) {
|
||||
t.Fatalf("status does not say %q:\n%s", want, said)
|
||||
}
|
||||
}
|
||||
if strings.Contains(said, "all doing what they were told") {
|
||||
t.Fatalf("status said all well beside a failing provider:\n%s", said)
|
||||
}
|
||||
if !strings.HasPrefix(said, "1 open condition(s)") {
|
||||
t.Fatalf("status does not lead with what is open:\n%s", said)
|
||||
}
|
||||
body, err := statusAsJSON(asked)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var doc struct {
|
||||
Conditions []conditions.Condition `json:"conditions"`
|
||||
Failing []conditions.Condition `json:"failing"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &doc); err != nil || len(doc.Failing) != 1 || len(doc.Conditions) != 1 ||
|
||||
!strings.Contains(doc.Failing[0].Evidence[0].Said, "invalid_grant") {
|
||||
t.Fatalf("the document does not carry it: %v\n%s", err, body)
|
||||
}
|
||||
// Both machines' `node show` name it: where the provider runs, and where the consumer is.
|
||||
for _, node := range []string{"anchor", "laptop"} {
|
||||
shown := printed(t, func() error { return showNode(ctx, open, node) })
|
||||
if !strings.Contains(shown, "open condition(s) about this machine") || !strings.Contains(shown, "mesh_laptop_dashboard") {
|
||||
t.Fatalf("node show %s does not name it:\n%s", node, shown)
|
||||
}
|
||||
}
|
||||
|
||||
// Recovered: gone, and the mesh may be well again as far as this is concerned.
|
||||
failing.Failing = false
|
||||
if cleared, err := kept.Stood(ctx, failing); err != nil || !cleared {
|
||||
t.Fatalf("%v %v", cleared, err)
|
||||
}
|
||||
asked, err = theThreeQuestions(ctx, open)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(asked.conditions) != 0 {
|
||||
t.Fatalf("a recovered consumer is still named: %+v", asked.conditions)
|
||||
}
|
||||
}
|
||||
|
||||
// A provider no longer assigned where it ran has nothing running to fail anybody: its last word is
|
||||
// cleared on the next look, said as resolved by the assignment.
|
||||
func TestAnUnassignedProvidersLastWordIsCleared(t *testing.T) {
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
kept := standings{keeper: func() *conditions.Keeper { return conditionsFrom }}
|
||||
if _, err := kept.Stood(ctx, link.Standing{Module: "gone", Failing: true,
|
||||
ProviderNode: "anchor", Consumer: "x", Since: time.Now()}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
all, err := conditionsFrom.Open(ctx)
|
||||
if err != nil || len(all) != 1 {
|
||||
t.Fatalf("%+v %v", all, err)
|
||||
}
|
||||
if err := unassignedProviders(ctx, open.inventory, conditionsFrom, all); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if all, _ := conditionsFrom.Open(ctx); len(all) != 0 {
|
||||
t.Fatalf("%+v", all)
|
||||
}
|
||||
}
|
||||
|
||||
// **The store away holds a recovery** (ADR 0224 §3): a standing that cannot be kept is asked again.
|
||||
func TestAStandingTheStoreCannotKeepIsAskedAgain(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user