91 lines
3.6 KiB
Go
91 lines
3.6 KiB
Go
package link
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ed25519"
|
|
"encoding/json"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/testbus"
|
|
)
|
|
|
|
// The move's ask and answer keep the engine's field names (mesh-host internal/link,
|
|
// TestTheMovesAskKeepsItsFieldNamesAndContext): the row's fields beside a hand-over's, under a context of its own.
|
|
func TestTheMovesAskAndAnswerKeepTheirFieldNames(t *testing.T) {
|
|
keys := func(v any) string {
|
|
raw, _ := json.Marshal(v)
|
|
var m map[string]any
|
|
_ = json.Unmarshal(raw, &m)
|
|
var out []string
|
|
for k := range m {
|
|
out = append(out, k)
|
|
}
|
|
sort.Strings(out)
|
|
return strings.Join(out, " ")
|
|
}
|
|
ask := MoveSecretAsk{Module: "m", Name: "n", Provider: "p", Provision: "q", Consumer: "c", ConsumerModule: "cm",
|
|
Local: "l", Origin: "made", Generation: "g", MadeAt: time.Unix(1, 0).UTC(), Sealed: "s", SealedTo: "k"}
|
|
if got := keys(ask); got != "consumer consumer_module generation local made_at module name origin provider "+
|
|
"provision sealed sealed_to" {
|
|
t.Fatalf("the ask's fields are %q", got)
|
|
}
|
|
answer := MoveSecretAnswer{Said: "s", Refused: "r", Secret: "own/a/b/c", Generation: 1, Unchanged: true}
|
|
if got := keys(answer); got != "generation refused said secret unchanged" {
|
|
t.Fatalf("the answer's fields are %q", got)
|
|
}
|
|
if broker.AskMoveSecretSubject("laptop") != "mesh.node.laptop.ask.move-secret" ||
|
|
MoveSecretContext != "novox-mesh move-secret v1\n" {
|
|
t.Fatalf("the subject %q, the context %q", broker.AskMoveSecretSubject("laptop"), MoveSecretContext)
|
|
}
|
|
}
|
|
|
|
// A move is asked of the machine on its own subject, signed under its own context — never a hand-over's — naming
|
|
// no path and carrying the row beside the hand-over's fields; the engine's answer comes back whole.
|
|
func TestAMoveIsAskedOfTheMachineSignedUnderItsOwnContext(t *testing.T) {
|
|
conn, err := nats.Connect(testbus.URL(t))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(conn.Close)
|
|
signer, public := testSigner(t)
|
|
var heard HandOverAsk
|
|
var row MoveSecretAsk
|
|
engine, err := conn.Subscribe(broker.AskMoveSecretSubject("laptop"), func(msg *nats.Msg) {
|
|
var signed SignedHandOver
|
|
_ = json.Unmarshal(msg.Data, &signed)
|
|
if ed25519.Verify(public, append([]byte(HandOverContext), signed.Ask...), signed.Signature) ||
|
|
!ed25519.Verify(public, append([]byte(MoveSecretContext), signed.Ask...), signed.Signature) {
|
|
_ = msg.Respond([]byte(`{"refused":"not signed as a move"}`))
|
|
return
|
|
}
|
|
_ = json.Unmarshal(signed.Ask, &heard)
|
|
_ = json.Unmarshal(signed.Ask, &row)
|
|
body, _ := json.Marshal(MoveSecretAnswer{Said: "moved", Secret: "own/laptop/" + row.Module + "/" + row.Name,
|
|
Generation: 1})
|
|
_ = msg.Respond(body)
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = engine.Unsubscribe() })
|
|
a, err := AskMoveSecret(context.Background(), conn, signer, "laptop", "the move",
|
|
MoveSecretAsk{Module: "postgres", Name: "superuser-password", Origin: "made", Sealed: "c2Vh"}, 5*time.Second)
|
|
if err != nil || a.Secret != "own/laptop/postgres/superuser-password" || a.Generation != 1 {
|
|
t.Fatalf("answered %+v, %v", a, err)
|
|
}
|
|
if heard.Node != "laptop" || heard.Path != "" || heard.Nonce == "" || heard.Expires.IsZero() || heard.By != "the move" ||
|
|
row.Sealed != "c2Vh" || row.Origin != "made" {
|
|
t.Fatalf("the engine heard %+v %+v", heard, row)
|
|
}
|
|
if _, err := AskMoveSecret(context.Background(), conn, signer, "anchor", "the move", MoveSecretAsk{}, 2*time.Second); err == nil ||
|
|
!strings.Contains(err.Error(), "nothing was moved") {
|
|
t.Fatalf("a machine with no engine listening: %v", err)
|
|
}
|
|
}
|