RR2-2: the manifest reader reads max-mib on an old state section
G's indexer, builder and runner cap their buckets; the running reader called the word an unknown field and refused the whole manifest. A value out of the objects section's bounds is refused naming them. Builds: design 47 G §494, §469, §621 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package catalogue
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A manifest carrying the words design 47 G writes is read whole by the reader release (design 47 G §340, §409):
|
||||
// `version` as MAJOR.MINOR (§368), a `*` emitter in `consumes` (§386), an observer's finding stream (§387) and an
|
||||
@@ -21,3 +24,27 @@ func TestTheReaderReleaseReadsTheManifestWordsGWrites(t *testing.T) {
|
||||
t.Fatalf("read as %+v", m)
|
||||
}
|
||||
}
|
||||
|
||||
// The second reader release reads the words G's manifests write beyond the first's (design 47 G §494, §621): each row a
|
||||
// manifest read whole, or refused naming what is wrong, and what the reader gives of it.
|
||||
func TestTheSecondReaderReleaseReadsGsManifestWords(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name, raw, refused string
|
||||
check func(Manifest) bool
|
||||
}{
|
||||
{name: "a state's cap (§469)", raw: `{"module":"m","version":"1.0","state":["plain",{"name":"pipelines","max-mib":256}]}`,
|
||||
check: func(m Manifest) bool { return m.State[0].MaxMiB == 0 && m.State[1].MaxMiB == 256 }},
|
||||
{name: "a state's cap out of the objects section's bounds (§469)",
|
||||
raw: `{"module":"m","version":"1.0","state":[{"name":"pipelines","max-mib":0}]}`, refused: "max-mib is between 1 and 8192"},
|
||||
} {
|
||||
m, err := decodeForTest([]byte(c.raw))
|
||||
switch {
|
||||
case c.refused != "" && (err == nil || !strings.Contains(err.Error(), c.refused)):
|
||||
t.Errorf("%s: read as %v, want refused naming %q", c.name, err, c.refused)
|
||||
case c.refused == "" && err != nil:
|
||||
t.Errorf("%s: refused: %v", c.name, err)
|
||||
case c.refused == "" && (len(m.UnknownField()) != 0 || !c.check(m)):
|
||||
t.Errorf("%s: read as %+v (set aside %v)", c.name, m, m.UnknownField())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -35,11 +35,14 @@ type StateDeclaration struct {
|
||||
// the tool runner's account, read it and never write it (the lead's ruling, 2026-11-04: nats' warrant spend record
|
||||
// `spent`). The judge's rule and the grants follow from it.
|
||||
ReadOnly bool `json:"read-only,omitempty"`
|
||||
// MaxMiB is the bucket's cap in MiB, between ObjectsLeastMiB and ObjectsMostMiB, as an old objects section's
|
||||
// is (design 47 G §469, §494); zero is no cap, as before.
|
||||
MaxMiB int `json:"max-mib,omitempty"`
|
||||
// KeptLayout is the layout its holder says it writes: its form and its catch-up tool (design 47).
|
||||
KeptLayout
|
||||
}
|
||||
|
||||
// UnmarshalJSON reads a bucket as its bare name, or as {name, history, ttl-seconds, per-machine, key-ttl, read-only, form, catch-up}.
|
||||
// UnmarshalJSON reads a bucket as its bare name, or as {name, history, ttl-seconds, per-machine, key-ttl, read-only, max-mib, form, catch-up}.
|
||||
func (s *StateDeclaration) UnmarshalJSON(raw []byte) error {
|
||||
trimmed := bytes.TrimSpace(raw)
|
||||
if len(trimmed) > 0 && trimmed[0] == '"' {
|
||||
@@ -47,21 +50,28 @@ func (s *StateDeclaration) UnmarshalJSON(raw []byte) error {
|
||||
}
|
||||
type plain StateDeclaration
|
||||
var full plain
|
||||
var said struct {
|
||||
MaxMiB *int `json:"max-mib"`
|
||||
}
|
||||
_ = json.Unmarshal(trimmed, &said)
|
||||
dec := json.NewDecoder(bytes.NewReader(trimmed))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&full); err != nil {
|
||||
return fmt.Errorf("a state is either a name or {name, history, ttl-seconds, per-machine, key-ttl, read-only, form, catch-up}: %w", typedUnknown(err))
|
||||
return fmt.Errorf("a state is either a name or {name, history, ttl-seconds, per-machine, key-ttl, read-only, max-mib, form, catch-up}: %w", typedUnknown(err))
|
||||
}
|
||||
if err := full.KeptLayout.refuse(fmt.Sprintf("state %q", full.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
if said.MaxMiB != nil && (*said.MaxMiB < ObjectsLeastMiB || *said.MaxMiB > ObjectsMostMiB) {
|
||||
return fmt.Errorf("state %q: max-mib is between %d and %d, not %d", full.Name, ObjectsLeastMiB, ObjectsMostMiB, *said.MaxMiB)
|
||||
}
|
||||
*s = StateDeclaration(full)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON writes back the short form when there is nothing else to say.
|
||||
func (s StateDeclaration) MarshalJSON() ([]byte, error) {
|
||||
if s.History == 0 && s.TTLSeconds == 0 && !s.PerMachine && !s.KeyTTL && !s.ReadOnly && s.KeptLayout == (KeptLayout{}) {
|
||||
if s.History == 0 && s.TTLSeconds == 0 && !s.PerMachine && !s.KeyTTL && !s.ReadOnly && s.MaxMiB == 0 && s.KeptLayout == (KeptLayout{}) {
|
||||
return json.Marshal(s.Name)
|
||||
}
|
||||
type plain StateDeclaration
|
||||
|
||||
Reference in New Issue
Block a user