Files
mesh-sdk/go/declaration/filereference_test.go
T
jschoubben 3218da4ee4 J3: mesh-sdk within its test budget, each file of declaration and the provider harness one test of subtests
The unit test functions fall from 587 to 476 (the budget is 500): the cases of each of twenty test files in
declaration and provider/harness become subtests of one test named for the file, under their own names, their bodies
unchanged. Integration tests stay at 30 (budget 50).

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 14:28:23 +01:00

169 lines
6.3 KiB
Go

package declaration
import (
"bytes"
"encoding/base64"
"encoding/json"
"reflect"
"strings"
"testing"
)
func refFile(extra string) string {
return `{"declaration":1,"resources":[{"id":"m.f","type":"file","path":"/etc/m","digest":"sha256:` +
strings.Repeat("a", 64) + `","size":70000` + extra + `}]}`
}
// 1694/AC1: a reference round trips, and an invalid one is refused naming the field.
func testAFileReferenceRoundTripsAndABadOneNamesItsField(t *testing.T) {
d, err := Parse([]byte(refFile("")))
if err != nil {
t.Fatal(err)
}
f := d.Resources[0].(*File)
if !f.IsReference() || f.Size != 70000 || f.Digest != "sha256:"+strings.Repeat("a", 64) {
t.Fatalf("read: %+v", f)
}
raw, _ := json.Marshal(d.Resources[0])
d2, err := Parse([]byte(`{"declaration":1,"resources":[` + string(raw) + `]}`))
if err != nil || !reflect.DeepEqual(d2.Resources[0], d.Resources[0]) {
t.Fatalf("round trip: %v %s", err, raw)
}
for want, raw := range map[string]string{
"digest": strings.Replace(refFile(""), strings.Repeat("a", 64), "abc", 1),
"size": strings.Replace(refFile(""), `"size":70000`, `"size":0`, 1),
"lower-case": strings.Replace(refFile(""), strings.Repeat("a", 64), strings.Repeat("A", 64), 1),
"content": refFile(`,"content":"x"`),
"bytes": refFile(`,"bytes":"eA=="`),
"whole": refFile(`,"into":"block"`),
} {
_, err := Parse([]byte(raw))
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("want a refusal naming %q, got %v", want, err)
}
}
// a size alone is not a reference
if _, err := Parse([]byte(`{"declaration":1,"resources":[{"id":"m.f","type":"file","path":"/e","size":5}]}`)); err == nil {
t.Error("a size without a digest was accepted")
}
}
// 1694/AC2: a declaration as today's SDK writes it reads unchanged and its bytes carry no new key.
func testAnInlineFileIsReadAndWrittenAsBefore(t *testing.T) {
const in = `{"id":"m.f","type":"file","path":"/e","content":"hello"}`
d, err := Parse([]byte(`{"declaration":1,"resources":[` + in + `]}`))
if err != nil {
t.Fatal(err)
}
out, _ := json.Marshal(d.Resources[0])
if string(out) != in {
t.Fatalf("signed bytes changed: %s", out)
}
if d.Resources[0].(*File).IsReference() {
t.Fatal("inline read as a reference")
}
}
// 1694/AC3: the controller's helper makes a reference of a big file and the engine's check refuses a changed byte.
func testABigFileBecomesAReferenceAndAChangedByteFailsTheCheck(t *testing.T) {
big := strings.Repeat("x", FileReferenceThreshold+1)
f := &File{ID: "m.f", Type: TypeFile, Path: "/e", Content: big}
if !f.TravelsByDigest() {
t.Fatal("a file over the threshold stays inline")
}
ref, body, err := f.AsReference()
if err != nil || ref.Content != "" || ref.Size != int64(len(big)) || string(body) != big {
t.Fatalf("%v %+v", err, ref)
}
if p := ref.validate("f", false); len(p) > 0 {
t.Fatalf("the reference is refused: %v", p)
}
if name, err := ObjectName(ref.Digest); err != nil || len(name) != 64 {
t.Fatalf("object name %q %v", name, err)
}
if err := ref.Verify(body); err != nil {
t.Fatal(err)
}
bad := bytes.Clone(body)
bad[7] ^= 1
if err := ref.Verify(bad); err == nil || !strings.Contains(err.Error(), "hashes to") {
t.Fatalf("a changed byte: %v", err)
}
if err := ref.Verify(body[1:]); err == nil || !strings.Contains(err.Error(), "bytes") {
t.Fatalf("a short object: %v", err)
}
}
// 1694/AC4: what stays inline, and bytes files travel decoded.
func testWhichFilesTravelByDigest(t *testing.T) {
small := strings.Repeat("x", FileReferenceThreshold)
big := strings.Repeat("x", FileReferenceThreshold+1)
for name, f := range map[string]File{
"at the threshold": {Content: small},
"sealed": {Sealed: "s"},
"secrets": {Content: big, Secrets: map[string]string{"a": "b"}},
"into": {Content: big, Into: IntoBlock},
} {
if f.TravelsByDigest() {
t.Errorf("%s travels by digest", name)
}
if _, _, err := f.AsReference(); err == nil {
t.Errorf("%s was made a reference", name)
}
}
exact := File{ID: "m.e", Bytes: base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{7}, FileReferenceThreshold))}
if exact.TravelsByDigest() {
t.Error("a bytes file of exactly the threshold travels by digest")
}
if _, _, err := exact.AsReference(); err == nil {
t.Error("a bytes file of exactly the threshold was made a reference")
}
raw := bytes.Repeat([]byte{0, 1, 2}, FileReferenceThreshold)
f := File{ID: "m.b", Bytes: base64.StdEncoding.EncodeToString(raw)}
ref, body, err := f.AsReference()
if err != nil || !bytes.Equal(body, raw) || ref.Size != int64(len(raw)) || ref.Digest != DigestOf(raw) {
t.Fatalf("bytes file: %v", err)
}
}
func testTheSharedNames(t *testing.T) {
if FileBucketPrefix != "declaration-files-" || FileReferencesCapability != "file-references" || FileReferenceThreshold != 65536 {
t.Fatal("a name changed")
}
if _, err := ObjectName("sha256:zz"); err == nil {
t.Fatal("bad digest named an object")
}
}
// 1694/AC6: each node has a bucket of its own, so a grant names it and never needs a `*` (ADR 0486 §2).
func testEachNodeHasABucketOfItsOwn(t *testing.T) {
for _, n := range []string{"ace", "g14", "novox", "shanks"} {
b, err := FileBucketFor(n)
if err != nil || b != "declaration-files-"+n {
t.Errorf("bucket for %q: %q %v", n, b, err)
}
s, err := FileStreamFor(n)
if err != nil || s != "OBJ_declaration-files-"+n {
t.Errorf("stream for %q: %q %v", n, s, err)
}
}
for _, bad := range []string{"", "a.b", "a*", "*", ">", "a b", "a>", "a/b", "ä"} {
if _, err := FileBucketFor(bad); err == nil {
t.Errorf("bucket for %q accepted", bad)
}
if _, err := FileStreamFor(bad); err == nil {
t.Errorf("stream for %q accepted", bad)
}
}
}
// TestFilereference holds this file's cases, each a subtest of its own name (mesh-sdk's test budget: at most 500 unit tests).
func TestFilereference(t *testing.T) {
t.Run("AFileReferenceRoundTripsAndABadOneNamesItsField", testAFileReferenceRoundTripsAndABadOneNamesItsField)
t.Run("AnInlineFileIsReadAndWrittenAsBefore", testAnInlineFileIsReadAndWrittenAsBefore)
t.Run("ABigFileBecomesAReferenceAndAChangedByteFailsTheCheck", testABigFileBecomesAReferenceAndAChangedByteFailsTheCheck)
t.Run("WhichFilesTravelByDigest", testWhichFilesTravelByDigest)
t.Run("TheSharedNames", testTheSharedNames)
t.Run("EachNodeHasABucketOfItsOwn", testEachNodeHasABucketOfItsOwn)
}