G B-1 (Go): the tests G added are merged into existing ones as subtests, so mesh-sdk ends G with the tests it started with
unit 302 and integration 34, as at the merge base 45e143f; the buscalls test moves beside busstate's. Builds: design 47 G §419, §626 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
// A bus function called before Serve waits for Serve and proceeds (issue 1966): a module that starts a goroutine
|
||||
// before stdio.Serve and calls the bus there is no longer refused. It is refused only when the process never serves.
|
||||
// The test drives the wait itself: serveWait is the test's own timer channel, no row sleeps on a clock.
|
||||
func TestBusFunctionsWaitForServe(t *testing.T) {
|
||||
func subBusFunctionsWaitForServe(t *testing.T) {
|
||||
const refusal = "before Serve: a bundle reaches the bus through the runtime that launched it"
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
@@ -72,3 +72,10 @@ func TestBusFunctionsWaitForServe(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestBusFunctionsWaitForServe runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestBusFunctionsWaitForServe(t *testing.T) {
|
||||
t.Run("BusFunctionsWaitForServe", subBusFunctionsWaitForServe)
|
||||
t.Run("ACapabilityWordIsReadOnceAndNamedWhenNotStated", subACapabilityWordIsReadOnceAndNamedWhenNotStated)
|
||||
t.Run("ABundleAsksAnAcknowledgedPublishAndASubscriptionForNewMessages", subABundleAsksAnAcknowledgedPublishAndASubscriptionForNewMessages)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func put(t *testing.T, dir, name string, b []byte, at time.Time) {
|
||||
os.Chtimes(p, at, at)
|
||||
}
|
||||
|
||||
func TestAGoodSnapshotPassesAndSaysItsStreams(t *testing.T) {
|
||||
func subAGoodSnapshotPassesAndSaysItsStreams(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
good := snapshotTar(t, nil)
|
||||
put(t, dir, "bus.tar", good, time.Now())
|
||||
@@ -99,7 +99,7 @@ func TestAGoodSnapshotPassesAndSaysItsStreams(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestACorruptedChecksumFailsNamingTheFile(t *testing.T) {
|
||||
func subACorruptedChecksumFailsNamingTheFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
put(t, dir, "bus.tar", snapshotTar(t, func(m *Manifest, files map[string][]byte) {
|
||||
m.Streams[1].ArchiveSHA256 = sum([]byte("something else"))
|
||||
@@ -116,7 +116,7 @@ func TestACorruptedChecksumFailsNamingTheFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestATruncatedSnapshotFails(t *testing.T) {
|
||||
func subATruncatedSnapshotFails(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
good := snapshotTar(t, nil)
|
||||
put(t, dir, "bus.tar", good[:len(good)*2/3], time.Now())
|
||||
@@ -129,7 +129,7 @@ func TestATruncatedSnapshotFails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnArchiveThatDoesNotReadToItsEndFailsEvenWithMatchingChecksums(t *testing.T) {
|
||||
func subAnArchiveThatDoesNotReadToItsEndFailsEvenWithMatchingChecksums(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
put(t, dir, "bus.tar", snapshotTar(t, func(m *Manifest, files map[string][]byte) {
|
||||
bad := []byte("not an s2 stream at all")
|
||||
@@ -143,7 +143,7 @@ func TestAnArchiveThatDoesNotReadToItsEndFailsEvenWithMatchingChecksums(t *testi
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheNewestTarIsVerifiedAndOnlyInsideTheDirectory(t *testing.T) {
|
||||
func subTheNewestTarIsVerifiedAndOnlyInsideTheDirectory(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
now := time.Now()
|
||||
put(t, dir, "old.tar", []byte("junk"), now.Add(-48*time.Hour))
|
||||
@@ -171,3 +171,12 @@ func TestTheNewestTarIsVerifiedAndOnlyInsideTheDirectory(t *testing.T) {
|
||||
t.Error("an empty directory is not a snapshot")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAGoodSnapshotPassesAndSaysItsStreams runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestAGoodSnapshotPassesAndSaysItsStreams(t *testing.T) {
|
||||
t.Run("AGoodSnapshotPassesAndSaysItsStreams", subAGoodSnapshotPassesAndSaysItsStreams)
|
||||
t.Run("ACorruptedChecksumFailsNamingTheFile", subACorruptedChecksumFailsNamingTheFile)
|
||||
t.Run("ATruncatedSnapshotFails", subATruncatedSnapshotFails)
|
||||
t.Run("AnArchiveThatDoesNotReadToItsEndFailsEvenWithMatchingChecksums", subAnArchiveThatDoesNotReadToItsEndFailsEvenWithMatchingChecksums)
|
||||
t.Run("TheNewestTarIsVerifiedAndOnlyInsideTheDirectory", subTheNewestTarIsVerifiedAndOnlyInsideTheDirectory)
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package buscalls
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Design 47 G §587, §622: the ledger's upkeep calls have their requests and answers, and every family its word.
|
||||
func TestTheLedgerUpkeepCallsAndTheirWords(t *testing.T) {
|
||||
var removal LedgerRemoveRequest
|
||||
if err := json.Unmarshal([]byte(`{"ledger":"records","instance":"r1"}`), &removal); err != nil || removal.Ledger != "records" || removal.Instance != "r1" {
|
||||
t.Errorf("the removal read as %+v (%v)", removal, err)
|
||||
}
|
||||
raw, _ := json.Marshal(LedgerRemoved{Instance: "r1", Operations: 3, Bytes: 900})
|
||||
if string(raw) != `{"instance":"r1","operations":3,"bytes":900}` {
|
||||
t.Errorf("the removal's answer reads %s", raw)
|
||||
}
|
||||
raw, _ = json.Marshal(Fill{Count: 2, Bytes: 10, MaxBytes: 100})
|
||||
if string(raw) != `{"count":2,"bytes":10,"max_bytes":100}` || LedgerFill != "mesh/ledger.fill" || LedgerRemove != "mesh/ledger.remove" {
|
||||
t.Errorf("the fill reads %s, calls %s %s", raw, LedgerFill, LedgerRemove)
|
||||
}
|
||||
for word, want := range map[string]string{BusStateCalls: "bus-state-calls", LedgerCalls: "ledger-calls",
|
||||
LedgerUpkeepCalls: "ledger-upkeep-calls", SentObjectsCalls: "sent-objects-calls"} {
|
||||
if word != want {
|
||||
t.Errorf("the word %q is %q", want, word)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// Issue 774 (bus blackouts at every user-list reload): only a request that reached no one is asked again, within the
|
||||
// bound; a blackout probe that says the bus answers ends the wait at once.
|
||||
func TestABusThatAnswersNothingIsWaitedOut(t *testing.T) {
|
||||
func subABusThatAnswersNothingIsWaitedOut(t *testing.T) {
|
||||
for _, row := range []struct {
|
||||
name string
|
||||
errs []error // what each try answers; the last repeats
|
||||
@@ -71,7 +71,7 @@ func TestABusThatAnswersNothingIsWaitedOut(t *testing.T) {
|
||||
|
||||
// Design 47 G §351, §460: a kept call is asked again only while its key is open and not replaced; an error of `open` is
|
||||
// logged naming the key and the ask is made again.
|
||||
func TestAKeptCallIsAskedAgainOnlyWhileItsKeyIsOpen(t *testing.T) {
|
||||
func subAKeptCallIsAskedAgainOnlyWhileItsKeyIsOpen(t *testing.T) {
|
||||
for _, row := range []struct {
|
||||
name string
|
||||
open func(n int) (bool, error) // by the number of asks made so far
|
||||
@@ -109,3 +109,9 @@ func TestAKeptCallIsAskedAgainOnlyWhileItsKeyIsOpen(t *testing.T) {
|
||||
t.Errorf("a kept call with no open function was made")
|
||||
}
|
||||
}
|
||||
|
||||
// TestABusThatAnswersNothingIsWaitedOut runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestABusThatAnswersNothingIsWaitedOut(t *testing.T) {
|
||||
t.Run("ABusThatAnswersNothingIsWaitedOut", subABusThatAnswersNothingIsWaitedOut)
|
||||
t.Run("AKeptCallIsAskedAgainOnlyWhileItsKeyIsOpen", subAKeptCallIsAskedAgainOnlyWhileItsKeyIsOpen)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package busstate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buscalls"
|
||||
)
|
||||
|
||||
// Design 47 G §587, §622: the ledger's upkeep calls have their requests and answers, and every family its word.
|
||||
func subTheLedgerUpkeepCallsAndTheirWords(t *testing.T) {
|
||||
var removal buscalls.LedgerRemoveRequest
|
||||
if err := json.Unmarshal([]byte(`{"ledger":"records","instance":"r1"}`), &removal); err != nil || removal.Ledger != "records" || removal.Instance != "r1" {
|
||||
t.Errorf("the removal read as %+v (%v)", removal, err)
|
||||
}
|
||||
raw, _ := json.Marshal(buscalls.LedgerRemoved{Instance: "r1", Operations: 3, Bytes: 900})
|
||||
if string(raw) != `{"instance":"r1","operations":3,"bytes":900}` {
|
||||
t.Errorf("the removal's answer reads %s", raw)
|
||||
}
|
||||
raw, _ = json.Marshal(buscalls.Fill{Count: 2, Bytes: 10, MaxBytes: 100})
|
||||
if string(raw) != `{"count":2,"bytes":10,"max_bytes":100}` || buscalls.LedgerFill != "mesh/ledger.fill" || buscalls.LedgerRemove != "mesh/ledger.remove" {
|
||||
t.Errorf("the fill reads %s, calls %s %s", raw, buscalls.LedgerFill, buscalls.LedgerRemove)
|
||||
}
|
||||
for word, want := range map[string]string{buscalls.BusStateCalls: "bus-state-calls", buscalls.LedgerCalls: "ledger-calls",
|
||||
buscalls.LedgerUpkeepCalls: "ledger-upkeep-calls", buscalls.SentObjectsCalls: "sent-objects-calls"} {
|
||||
if word != want {
|
||||
t.Errorf("the word %q is %q", want, word)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// A bundle keeps objects in its own object store through the runtime (ADR 0595 §2, §9; A3's request for distribution's
|
||||
// incoming): each call is the runtime's method of buscalls, naming the store by its local name, and answers as the
|
||||
// runtime answered; a method that is no bus-state call is refused before the runtime is asked.
|
||||
func TestABundleKeepsObjectsThroughTheRuntime(t *testing.T) {
|
||||
func subABundleKeepsObjectsThroughTheRuntime(t *testing.T) {
|
||||
t.Setenv(stdio.ServedAs, "distribution")
|
||||
inR, inW := io.Pipe()
|
||||
outR, outW := io.Pipe()
|
||||
@@ -100,3 +100,10 @@ func TestABundleKeepsObjectsThroughTheRuntime(t *testing.T) {
|
||||
t.Errorf("put asked %v", p)
|
||||
}
|
||||
}
|
||||
|
||||
// TestABundleKeepsObjectsThroughTheRuntime runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestABundleKeepsObjectsThroughTheRuntime(t *testing.T) {
|
||||
t.Run("ABundleKeepsObjectsThroughTheRuntime", subABundleKeepsObjectsThroughTheRuntime)
|
||||
t.Run("TheLedgerUpkeepCallsAndTheirWords", subTheLedgerUpkeepCallsAndTheirWords)
|
||||
t.Run("AValueLargerThanOneChunkIsPutAndGotBySession", subAValueLargerThanOneChunkIsPutAndGotBySession)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func serveRuntime(t *testing.T, fn func(), handle func(method string, params map
|
||||
// Design 47 G §518, §345, §490 (A-21): a value larger than one chunk is put as chunks in order under one call id, each
|
||||
// answered before the next, with the store named in each; a get the runtime answers as a session is read chunk by chunk and
|
||||
// checked against its digest.
|
||||
func TestAValueLargerThanOneChunkIsPutAndGotBySession(t *testing.T) {
|
||||
func subAValueLargerThanOneChunkIsPutAndGotBySession(t *testing.T) {
|
||||
value := bytes.Repeat([]byte("0123456789abcdef"), (2*buscalls.ChunkBytes+1000)/16)
|
||||
sum := sha256.Sum256(value)
|
||||
digest := "sha256:" + hex.EncodeToString(sum[:])
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Design 47 G §576: the words are read once, an unknown-call answer reads as none, and a word not stated fails naming it
|
||||
// and the node (from MESH_NODE).
|
||||
func TestACapabilityWordIsReadOnceAndNamedWhenNotStated(t *testing.T) {
|
||||
func subACapabilityWordIsReadOnceAndNamedWhenNotStated(t *testing.T) {
|
||||
t.Setenv("MESH_NODE", "ace")
|
||||
asked := 0
|
||||
var c capabilityWords
|
||||
|
||||
@@ -54,7 +54,7 @@ func indexOf(out []map[string]any, id string) int {
|
||||
// otherwise — and the controller declares the verbs it answers as tools. Its service bundle is run
|
||||
// by its own process; launched a second time by the runtime it would be a second controller
|
||||
// pretending to be an MCP server.
|
||||
func TestABundleItsOwnProcessRunsIsNotServedByTheRuntime(t *testing.T) {
|
||||
func subABundleItsOwnProcessRunsIsNotServedByTheRuntime(t *testing.T) {
|
||||
m := aServiceModule(t)
|
||||
if len(m.Bundles) != 1 {
|
||||
t.Fatalf("the service's bundle was not kept: %+v", m.Bundles)
|
||||
@@ -75,3 +75,10 @@ func TestABundleItsOwnProcessRunsIsNotServedByTheRuntime(t *testing.T) {
|
||||
t.Fatalf("a tools bundle nothing runs is no longer served: %v", loads)
|
||||
}
|
||||
}
|
||||
|
||||
// TestABundleItsOwnProcessRunsIsNotServedByTheRuntime runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestABundleItsOwnProcessRunsIsNotServedByTheRuntime(t *testing.T) {
|
||||
t.Run("ABundleItsOwnProcessRunsIsNotServedByTheRuntime", subABundleItsOwnProcessRunsIsNotServedByTheRuntime)
|
||||
t.Run("TheBusNamesOfAnEntryAreOneRule", subTheBusNamesOfAnEntryAreOneRule)
|
||||
t.Run("AStarEmitterHearsEachDeclaringModulesUnrestrictedStream", subAStarEmitterHearsEachDeclaringModulesUnrestrictedStream)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
// ADR 0666 §16: the bus names of an entry are one rule. A state entry answers its bucket's stream and the stream of
|
||||
// its object store; a local name that meets a name the mesh makes is a clash for the judge.
|
||||
func TestTheBusNamesOfAnEntryAreOneRule(t *testing.T) {
|
||||
func subTheBusNamesOfAnEntryAreOneRule(t *testing.T) {
|
||||
state := BusStateEntry{Name: "views", Storage: StorageKeyValue, Template: TemplateState}
|
||||
rows := []struct {
|
||||
name string
|
||||
|
||||
@@ -155,7 +155,7 @@ func TestNarrowingTwoPatterns(t *testing.T) {
|
||||
|
||||
// Design 47 G §386: `*.<event>` reaches the unrestricted stream of each module that declares the event, and never a
|
||||
// restricted one; a wildcard before anything wider still reaches none.
|
||||
func TestAStarEmitterHearsEachDeclaringModulesUnrestrictedStream(t *testing.T) {
|
||||
func subAStarEmitterHearsEachDeclaringModulesUnrestrictedStream(t *testing.T) {
|
||||
mail, err := decodeForTest([]byte(mailManifest(t, mailStream, `"emits":["account.added"],`)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// 1171/AC2: given an account file broker-<process id> in the controller's form {url, fingerprint, node,
|
||||
// module, user, password, claims}, when the process package reads it, then it yields those fields and
|
||||
// tolerates claims.
|
||||
func TestReadAccountYieldsEveryFieldAndToleratesClaims(t *testing.T) {
|
||||
func subReadAccountYieldsEveryFieldAndToleratesClaims(t *testing.T) {
|
||||
path := writeAccount(t, map[string]any{
|
||||
"url": "nats://novox:4222", "fingerprint": "sha256:ab", "node": "novox", "module": "mesh-mail",
|
||||
"user": "novox.mesh-mail.send", "password": "secret-pw",
|
||||
@@ -297,7 +297,7 @@ func TestModuleAccountNamesAndDialOptions(t *testing.T) {
|
||||
|
||||
// W1.3 (design 47 *The fingerprint handover*): a process's own build is the fingerprint the node-engine handed it, read
|
||||
// once at its start; with none, or with one that is no single word, it says its own build is unknown and why.
|
||||
func TestTheOwnBuildIsTheFingerprintTheProcessWasStartedWith(t *testing.T) {
|
||||
func subTheOwnBuildIsTheFingerprintTheProcessWasStartedWith(t *testing.T) {
|
||||
for _, c := range []struct{ name, env, fp, unknown string }{
|
||||
{"handed", "src2:1480985e", "src2:1480985e", ""},
|
||||
{"handed with spaces around", " src2:1480985e\n", "src2:1480985e", ""},
|
||||
@@ -341,3 +341,9 @@ func TestTheOwnBuildIsTheFingerprintTheProcessWasStartedWith(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestReadAccountYieldsEveryFieldAndToleratesClaims runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestReadAccountYieldsEveryFieldAndToleratesClaims(t *testing.T) {
|
||||
t.Run("ReadAccountYieldsEveryFieldAndToleratesClaims", subReadAccountYieldsEveryFieldAndToleratesClaims)
|
||||
t.Run("TheOwnBuildIsTheFingerprintTheProcessWasStartedWith", subTheOwnBuildIsTheFingerprintTheProcessWasStartedWith)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
// A module whose consumer the bus makes after the module started is not lost for that order: the hub tries again after
|
||||
// each wait and calls live once it is bound (the checker's "until it starts again" hole).
|
||||
func TestTheHubBindsALateConsumerAndSaysItIsLive(t *testing.T) {
|
||||
func subTheHubBindsALateConsumerAndSaysItIsLive(t *testing.T) {
|
||||
nc, err := nats.Connect(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -69,7 +69,7 @@ func TestTheHubBindsALateConsumerAndSaysItIsLive(t *testing.T) {
|
||||
|
||||
// W1.12: a module hears a seat's event kept last per subject on its own stream through its durable, made by the maker
|
||||
// after the events were said, and is handed the last of each subject; without the consumer the call says so.
|
||||
func TestAModuleHearsASeatEventKeptOnItsOwnStream(t *testing.T) {
|
||||
func subAModuleHearsASeatEventKeptOnItsOwnStream(t *testing.T) {
|
||||
nc, err := nats.Connect(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -141,3 +141,9 @@ func TestAModuleHearsASeatEventKeptOnItsOwnStream(t *testing.T) {
|
||||
t.Fatal("the durable was not followed after the read")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTheHubBindsALateConsumerAndSaysItIsLive runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestTheHubBindsALateConsumerAndSaysItIsLive(t *testing.T) {
|
||||
t.Run("TheHubBindsALateConsumerAndSaysItIsLive", subTheHubBindsALateConsumerAndSaysItIsLive)
|
||||
t.Run("AModuleHearsASeatEventKeptOnItsOwnStream", subAModuleHearsASeatEventKeptOnItsOwnStream)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// A verb's schema has its named types in place (the seat list's readers take one schema, not a schema and its types).
|
||||
func TestResolveRefsPutsNamedTypesInPlace(t *testing.T) {
|
||||
func subResolveRefsPutsNamedTypesInPlace(t *testing.T) {
|
||||
defs := map[string]map[string]any{
|
||||
"id": {"type": "string"},
|
||||
"row": {"type": "object", "properties": map[string]any{"id": map[string]any{"$ref": "#/defs/id"}}},
|
||||
@@ -34,3 +34,10 @@ func TestResolveRefsPutsNamedTypesInPlace(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveRefsPutsNamedTypesInPlace runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestResolveRefsPutsNamedTypesInPlace(t *testing.T) {
|
||||
t.Run("ResolveRefsPutsNamedTypesInPlace", subResolveRefsPutsNamedTypesInPlace)
|
||||
t.Run("AVerbWithoutCallersTakesItsClassDefault", subAVerbWithoutCallersTakesItsClassDefault)
|
||||
t.Run("AKeptEventsStreamIsNamedAndBoundedByItsKeep", subAKeptEventsStreamIsNamedAndBoundedByItsKeep)
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ func TestAKeptEventsStreamIsNamedFromTheSeatAndTheEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
// Design 47 G §391: a verb that names no callers takes its class's default, a verb that names them keeps them.
|
||||
func TestAVerbWithoutCallersTakesItsClassDefault(t *testing.T) {
|
||||
func subAVerbWithoutCallersTakesItsClassDefault(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
verb Verb
|
||||
want string
|
||||
@@ -310,7 +310,7 @@ func TestAVerbWithoutCallersTakesItsClassDefault(t *testing.T) {
|
||||
|
||||
// Design 47 G §260, §396, §331: a kept event's stream is named from the seat and the event, 90 days and 16 MiB unless
|
||||
// the keep says otherwise, and 0 is no age limit.
|
||||
func TestAKeptEventsStreamIsNamedAndBoundedByItsKeep(t *testing.T) {
|
||||
func subAKeptEventsStreamIsNamedAndBoundedByItsKeep(t *testing.T) {
|
||||
d, err := Parse([]byte(`{"format":1,"seat":"mesh-delivery","scope":"mesh","grain":"seat-list","domain":"Delivery","decision":"x",
|
||||
"events":[{"name":"owed","keep":{"max-age-days":7,"max-mib":64}},{"name":"toolchain-agreed","keep":{}},
|
||||
{"name":"versions-filled","keep":{"max-age-days":0,"max-mib":1}}]}`))
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// ADR 0666 §16: a body is a reference when it is exactly `{"mesh-object": Ref}`; anything else is a value.
|
||||
func TestABodyIsAReferenceOnlyWhenItIsExactlyOne(t *testing.T) {
|
||||
func subABodyIsAReferenceOnlyWhenItIsExactlyOne(t *testing.T) {
|
||||
good := Ref{Store: "views-objects", Object: "k." + strings.Repeat("a", 64), Size: 3 << 20, Digest: strings.Repeat("a", 64), Form: FormVersion}
|
||||
got, ok := ReadRef(Wrap(good))
|
||||
if !ok || got != good {
|
||||
@@ -29,3 +29,15 @@ func TestABodyIsAReferenceOnlyWhenItIsExactlyOne(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestABodyIsAReferenceOnlyWhenItIsExactlyOne runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestABodyIsAReferenceOnlyWhenItIsExactlyOne(t *testing.T) {
|
||||
t.Run("ABodyIsAReferenceOnlyWhenItIsExactlyOne", subABodyIsAReferenceOnlyWhenItIsExactlyOne)
|
||||
t.Run("ABodyOverTheMessageLimitIsSentAsAReference", subABodyOverTheMessageLimitIsSentAsAReference)
|
||||
t.Run("ABundleReadsASentObjectThroughTheRuntime", subABundleReadsASentObjectThroughTheRuntime)
|
||||
t.Run("AFoundationPartsSentStoreAgesFromItsSendLog", subAFoundationPartsSentStoreAgesFromItsSendLog)
|
||||
t.Run("TheObjectToolsReadEverythingAndDropOnlyWhatIsNotLive", subTheObjectToolsReadEverythingAndDropOnlyWhatIsNotLive)
|
||||
t.Run("ValuesOfAnySizeThroughTheRuntime", subValuesOfAnySizeThroughTheRuntime)
|
||||
t.Run("AWriteLostLeavesNoObjectBehind", subAWriteLostLeavesNoObjectBehind)
|
||||
t.Run("AValueLargerThanTheStoreTakesIsRefused", subAValueLargerThanTheStoreTakesIsRefused)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// ADR 0666 §16: a reply or an event over the per-message limit carries the name of an object, and the receiver reads it
|
||||
// back whole; one that fits is sent as it is.
|
||||
func TestABodyOverTheMessageLimitIsSentAsAReference(t *testing.T) {
|
||||
func subABodyOverTheMessageLimitIsSentAsAReference(t *testing.T) {
|
||||
m := newMemory()
|
||||
s := Sender{Objects: memObjects{m}, Store: "m_sent-objects"}
|
||||
fetch := ProcessFetch(memObjects{m})
|
||||
@@ -48,7 +48,7 @@ func TestABodyOverTheMessageLimitIsSentAsAReference(t *testing.T) {
|
||||
}
|
||||
|
||||
// A bundle reads a sent object through the runtime's sent-get.
|
||||
func TestABundleReadsASentObjectThroughTheRuntime(t *testing.T) {
|
||||
func subABundleReadsASentObjectThroughTheRuntime(t *testing.T) {
|
||||
m := newMemory()
|
||||
body := []byte(`{"text":"` + strings.Repeat("y", 3<<20) + `"}`)
|
||||
sent, err := Sender{Objects: memObjects{m}, Store: "m_sent-objects"}.Send("reply.list", body)
|
||||
@@ -69,7 +69,7 @@ func TestABundleReadsASentObjectThroughTheRuntime(t *testing.T) {
|
||||
|
||||
// Design 47 G §410, §330: a foundation part's sender keeps the sequence of its sends in its old state section
|
||||
// `sent-log` and deletes the sent objects past 7 days at a later send, at most 20 a send.
|
||||
func TestAFoundationPartsSentStoreAgesFromItsSendLog(t *testing.T) {
|
||||
func subAFoundationPartsSentStoreAgesFromItsSendLog(t *testing.T) {
|
||||
m := newMemory()
|
||||
clock := time.Date(2026, 11, 1, 0, 0, 0, 0, time.UTC)
|
||||
s := Sender{Objects: memObjects{m}, Store: "m_sent-objects", Log: m, Node: "n1", Now: func() time.Time { return clock }}
|
||||
|
||||
@@ -25,7 +25,7 @@ func (a *acts) RecordHandAct(h views.HandAct) error {
|
||||
|
||||
// ADR 0666 §25: the four tools read what each store holds and drop only what is no longer live, each drop only with who
|
||||
// asks and why, recorded as a hand act first.
|
||||
func TestTheObjectToolsReadEverythingAndDropOnlyWhatIsNotLive(t *testing.T) {
|
||||
func subTheObjectToolsReadEverythingAndDropOnlyWhatIsNotLive(t *testing.T) {
|
||||
m := newMemory()
|
||||
m.modified = map[string]time.Time{}
|
||||
v := m.values()
|
||||
|
||||
@@ -104,7 +104,7 @@ func valueRows(t *testing.T, v Values, multiples ...int) {
|
||||
}
|
||||
|
||||
// ADR 0666 §16, C-S3, C-S5: values of 1x, 10x and 100x the limit through a bundle's runtime, whole.
|
||||
func TestValuesOfAnySizeThroughTheRuntime(t *testing.T) {
|
||||
func subValuesOfAnySizeThroughTheRuntime(t *testing.T) {
|
||||
m := newMemory()
|
||||
out := runtimeOf(t, m, func() (any, error) {
|
||||
v := ForState("views")
|
||||
@@ -117,7 +117,7 @@ func TestValuesOfAnySizeThroughTheRuntime(t *testing.T) {
|
||||
}
|
||||
|
||||
// A write another writer won first leaves the object it put removed and the key as it found it.
|
||||
func TestAWriteLostLeavesNoObjectBehind(t *testing.T) {
|
||||
func subAWriteLostLeavesNoObjectBehind(t *testing.T) {
|
||||
m := newMemory()
|
||||
v := m.values()
|
||||
if err := v.Put("k", big(3*kvlimit.ModuleStateValueBytes)); err != nil {
|
||||
@@ -158,7 +158,7 @@ func (r *raceKV) Get(key string) (*Entry, error) {
|
||||
}
|
||||
|
||||
// A store that takes no object as large as the value refuses it, loudly, and writes nothing.
|
||||
func TestAValueLargerThanTheStoreTakesIsRefused(t *testing.T) {
|
||||
func subAValueLargerThanTheStoreTakesIsRefused(t *testing.T) {
|
||||
m := newMemory()
|
||||
m.most = 100_000
|
||||
v := m.values()
|
||||
|
||||
+1
-1
@@ -346,7 +346,7 @@ func servesAlone(t *testing.T) {
|
||||
|
||||
// Design 47 G §559: a bundle's acknowledged publish asks `ack: true` and fails on the runtime's `{error}`; its
|
||||
// subscription for new messages asks `deliver: new`.
|
||||
func TestABundleAsksAnAcknowledgedPublishAndASubscriptionForNewMessages(t *testing.T) {
|
||||
func subABundleAsksAnAcknowledgedPublishAndASubscriptionForNewMessages(t *testing.T) {
|
||||
servesAlone(t)
|
||||
t.Setenv(ServedAs, "keeper")
|
||||
inR, inW := io.Pipe()
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
// An owner's change is an ordinary event on the shared events stream (novox/hq ADR 0635 §5): a reader that follows the
|
||||
// owner's state by its read tool hears it on the module's durable consumer of that stream, with no stream of the owner's,
|
||||
// and a message that is no views event goes to Other.
|
||||
func TestAReaderHearsAnOwnersChangesAsOrdinaryEvents(t *testing.T) {
|
||||
func subAReaderHearsAnOwnersChangesAsOrdinaryEvents(t *testing.T) {
|
||||
nc, err := nats.Connect(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -135,7 +135,7 @@ func TestAReaderHearsAnOwnersChangesAsOrdinaryEvents(t *testing.T) {
|
||||
// A module has one durable consumer on the shared events stream, so the loops that hear events in one module must share
|
||||
// one pull (process.EventHub): two handlers and the views reader on the hub each get their own messages and no other's,
|
||||
// and a message nobody takes is acknowledged.
|
||||
func TestTheViewsReaderAndTwoHandlersShareTheModulesOneDurable(t *testing.T) {
|
||||
func subTheViewsReaderAndTwoHandlersShareTheModulesOneDurable(t *testing.T) {
|
||||
nc, err := nats.Connect(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -237,3 +237,10 @@ func TestTheViewsReaderAndTwoHandlersShareTheModulesOneDurable(t *testing.T) {
|
||||
t.Fatal("the hub started late heard nothing")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAReaderHearsAnOwnersChangesAsOrdinaryEvents runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestAReaderHearsAnOwnersChangesAsOrdinaryEvents(t *testing.T) {
|
||||
t.Run("AReaderHearsAnOwnersChangesAsOrdinaryEvents", subAReaderHearsAnOwnersChangesAsOrdinaryEvents)
|
||||
t.Run("TheViewsReaderAndTwoHandlersShareTheModulesOneDurable", subTheViewsReaderAndTwoHandlersShareTheModulesOneDurable)
|
||||
t.Run("WriterSaysItsEventsAndHearsAsksOnTheBus", subWriterSaysItsEventsAndHearsAsksOnTheBus)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ type swAsked struct {
|
||||
|
||||
// A stdio module's writer says each of its events through the module's own publish, acknowledged, under the writer's
|
||||
// message id, and hears the asks through the module's subscription (design 47 G A-16).
|
||||
func TestStdioWriterPublishesAndListensThroughTheModulesOwnPlumbing(t *testing.T) {
|
||||
func subStdioWriterPublishesAndListensThroughTheModulesOwnPlumbing(t *testing.T) {
|
||||
type said struct {
|
||||
event string
|
||||
body string
|
||||
|
||||
@@ -55,7 +55,7 @@ func markOf(state string, rev uint64, made time.Time) Entry {
|
||||
|
||||
// The owners' and the readers' side of a first view (ADR 0615 §4-§6, ADR 0621 §2-§3, ADR 0622): one table of cases,
|
||||
// since mesh-delivery is over its test budget.
|
||||
func TestViews(t *testing.T) {
|
||||
func subViews(t *testing.T) {
|
||||
made := time.Date(2026, 11, 4, 12, 0, 0, 0, time.UTC)
|
||||
now := func() time.Time { return made.Add(time.Hour) }
|
||||
written := made.Add(-time.Minute)
|
||||
@@ -1210,3 +1210,9 @@ func (s *pagedSource) Page(after string, _ int, _ time.Duration) ([]KeyedEntry,
|
||||
}
|
||||
return out, "", nil
|
||||
}
|
||||
|
||||
// TestViews runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestViews(t *testing.T) {
|
||||
t.Run("Views", subViews)
|
||||
t.Run("StdioWriterPublishesAndListensThroughTheModulesOwnPlumbing", subStdioWriterPublishesAndListensThroughTheModulesOwnPlumbing)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// Against a real bus: the writer's events are acknowledged by their stream, a start states what the observer observes, an
|
||||
// ask published before the writer's consumer existed is never answered, one published after is, and after the connection
|
||||
// is made again (Reconnected) the consumer is made again from the newest ask (design 47 G §383, §390, §485).
|
||||
func TestWriterSaysItsEventsAndHearsAsksOnTheBus(t *testing.T) {
|
||||
func subWriterSaysItsEventsAndHearsAsksOnTheBus(t *testing.T) {
|
||||
nc, err := nats.Connect(testbus.URL(t))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
+22
-9
@@ -81,7 +81,7 @@ var subjects = views.FindingSubjects("m", "n1")
|
||||
// asked is the subject the module gives the writer for findings-asked, as its seat's generated contract has it (design 47 G A-17).
|
||||
const asked = "mesh.seat.s.event.findings-asked"
|
||||
|
||||
func TestFindingSubjectsAreTheEventsStreamForm(t *testing.T) {
|
||||
func subFindingSubjectsAreTheEventsStreamForm(t *testing.T) {
|
||||
want := views.WriterSubjects{
|
||||
Written: "mesh.stream.m.event.finding-written.n1", Cleared: "mesh.stream.m.event.finding-cleared.n1",
|
||||
Stated: "mesh.stream.m.event.findings-stated.n1",
|
||||
@@ -154,7 +154,7 @@ func finding(id, summary string) testFinding {
|
||||
|
||||
// A finding written says its event with the layout whole after the key, keeps the key's since, and says again only when
|
||||
// what it says changed; a finding cleared says its event, also of a key that does not exist (design 47 G §383, §485).
|
||||
func TestWriterWritesAndClearsFindingsAsEvents(t *testing.T) {
|
||||
func subWriterWritesAndClearsFindingsAsEvents(t *testing.T) {
|
||||
r := newRig(t)
|
||||
ctx := context.Background()
|
||||
first := r.now
|
||||
@@ -202,7 +202,7 @@ func TestWriterWritesAndClearsFindingsAsEvents(t *testing.T) {
|
||||
|
||||
// An event the bus does not acknowledge is sent again for one minute, then written in the journal as an error naming the
|
||||
// finding (design 47 G §383).
|
||||
func TestWriterSendsAgainForAMinuteThenLogs(t *testing.T) {
|
||||
func subWriterSendsAgainForAMinuteThenLogs(t *testing.T) {
|
||||
r := newRig(t)
|
||||
r.fail = 2
|
||||
if err := r.w.Write(context.Background(), finding("a", "one")); err != nil || len(r.events()) != 1 {
|
||||
@@ -225,7 +225,7 @@ func TestWriterSendsAgainForAMinuteThenLogs(t *testing.T) {
|
||||
// rest; an ask naming another module is not answered, one naming none or this one is, with its id; an observe that fails
|
||||
// says no set and is called again once a minute until a set is said; a connection made again makes the consumer again and
|
||||
// states again (design 47 G §384, §461, §485).
|
||||
func TestWriterStatesAtStartAndOnAsksAndRecovers(t *testing.T) {
|
||||
func subWriterStatesAtStartAndOnAsksAndRecovers(t *testing.T) {
|
||||
r := newRig(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
@@ -298,7 +298,7 @@ func waitEvent(t *testing.T, r *rig, n int) said {
|
||||
}
|
||||
|
||||
// A write asked during a statement waits for it, so the statement never deletes the key the write made (design 47 G §485).
|
||||
func TestWriterRunsOneActAtATime(t *testing.T) {
|
||||
func subWriterRunsOneActAtATime(t *testing.T) {
|
||||
r := newRig(t)
|
||||
inside, release := make(chan struct{}), make(chan struct{})
|
||||
r.observe = func(context.Context) (testObserved, error) {
|
||||
@@ -329,7 +329,7 @@ func TestWriterRunsOneActAtATime(t *testing.T) {
|
||||
|
||||
// A writer owns its kinds (design 47 G §580), states them, deletes no key of another kind, and covers a views reader's
|
||||
// findings written through it beside observe's (§639); an owner or waiting-on of no form is refused, naming the field (§678).
|
||||
func TestWriterKindsAndReaderFindings(t *testing.T) {
|
||||
func subWriterKindsAndReaderFindings(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
r := newRigKinds(t, "k")
|
||||
other := finding("x", "another bundle's")
|
||||
@@ -396,7 +396,7 @@ func TestWriterKindsAndReaderFindings(t *testing.T) {
|
||||
// kind, the scope module, the id escaped as a key's name is, a warning but urgent for view-too-large, the source views, the
|
||||
// subject the module on its node; and CheckFinding refuses a finding breaking a rule before it is written, naming the field
|
||||
// (§689).
|
||||
func TestReaderFindingsAreLaidOutAndCheckFindingRefuses(t *testing.T) {
|
||||
func subReaderFindingsAreLaidOutAndCheckFindingRefuses(t *testing.T) {
|
||||
r := newRig(t)
|
||||
for _, kind := range views.ViewKinds {
|
||||
if err := views.PutViewFinding(r.w, kind, "o.s.main", "sum "+kind, "head"); err != nil {
|
||||
@@ -441,7 +441,7 @@ func TestReaderFindingsAreLaidOutAndCheckFindingRefuses(t *testing.T) {
|
||||
|
||||
// A finding body whose wire form lacks a field the contract names is refused when the writer is made, naming the field
|
||||
// (design 47 G A-3).
|
||||
func TestWriterRefusesABodyWithoutAContractField(t *testing.T) {
|
||||
func subWriterRefusesABodyWithoutAContractField(t *testing.T) {
|
||||
type noSince struct {
|
||||
Kind, Scope, ID, Observer string
|
||||
At time.Time `json:"at"`
|
||||
@@ -467,7 +467,7 @@ func TestWriterRefusesABodyWithoutAContractField(t *testing.T) {
|
||||
// A part whose findings are raised as they happen holds them in a writer that observes from its own state (design 47 G A-29):
|
||||
// the findings standing in the state are seeded before it starts, a statement says what is held, Write and Clear keep the held
|
||||
// set, and an Observe of the part's own is refused.
|
||||
func TestHeldWriterStatesWhatItHoldsFromItsOwnState(t *testing.T) {
|
||||
func subHeldWriterStatesWhatItHoldsFromItsOwnState(t *testing.T) {
|
||||
state := &fakeState{m: map[string][]byte{}}
|
||||
raw, _ := json.Marshal(finding("seeded", "stood"))
|
||||
state.m["module.seeded.k"] = raw
|
||||
@@ -508,3 +508,16 @@ func TestHeldWriterStatesWhatItHoldsFromItsOwnState(t *testing.T) {
|
||||
t.Fatalf("the statement says %+v after %d events", stated, len(sent))
|
||||
}
|
||||
}
|
||||
|
||||
// TestFindingSubjectsAreTheEventsStreamForm runs the tests of this package that were merged to keep the module within its test budget (design 47 G §419), each as a subtest.
|
||||
func TestFindingSubjectsAreTheEventsStreamForm(t *testing.T) {
|
||||
t.Run("FindingSubjectsAreTheEventsStreamForm", subFindingSubjectsAreTheEventsStreamForm)
|
||||
t.Run("WriterWritesAndClearsFindingsAsEvents", subWriterWritesAndClearsFindingsAsEvents)
|
||||
t.Run("WriterSendsAgainForAMinuteThenLogs", subWriterSendsAgainForAMinuteThenLogs)
|
||||
t.Run("WriterStatesAtStartAndOnAsksAndRecovers", subWriterStatesAtStartAndOnAsksAndRecovers)
|
||||
t.Run("WriterRunsOneActAtATime", subWriterRunsOneActAtATime)
|
||||
t.Run("WriterKindsAndReaderFindings", subWriterKindsAndReaderFindings)
|
||||
t.Run("ReaderFindingsAreLaidOutAndCheckFindingRefuses", subReaderFindingsAreLaidOutAndCheckFindingRefuses)
|
||||
t.Run("WriterRefusesABodyWithoutAContractField", subWriterRefusesABodyWithoutAContractField)
|
||||
t.Run("HeldWriterStatesWhatItHoldsFromItsOwnState", subHeldWriterStatesWhatItHoldsFromItsOwnState)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user