95 lines
3.2 KiB
Go
95 lines
3.2 KiB
Go
package link
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/declaration"
|
|
"github.com/nats-io/nats.go"
|
|
)
|
|
|
|
// 1695/AC7: (untagged on purpose: the merge check of the trunk runs the tagless suite against its throwaway bus, and the
|
|
// test skips without MESH_TEST_NATS) against a real bus with the bucket declaration-files, over the bus's default maximum message size, a
|
|
// node-engine's link reads three objects that together are over 3 MiB by name, verifies each with File.Verify, and
|
|
// refuses an object that is absent, one larger than the declaration says, and one that differs by a byte.
|
|
//
|
|
// MESH_TEST_NATS=nats://127.0.0.1:4222 go test ./internal/link/ -run TestFilesAreFetchedByDigestFromTheBucket
|
|
//
|
|
// The bucket is made here: in the mesh the controller makes and fills it, and the node-engine only reads.
|
|
func TestFilesAreFetchedByDigestFromTheBucket(t *testing.T) {
|
|
conn, js := aBus(t)
|
|
store, err := js.ObjectStore(declaration.FileBucket)
|
|
if err != nil {
|
|
if store, err = js.CreateObjectStore(&nats.ObjectStoreConfig{Bucket: declaration.FileBucket}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
var files []*declaration.File
|
|
bodies := map[string][]byte{}
|
|
total := 0
|
|
for i, b := range []byte{'a', 'b', 'c'} {
|
|
body := append(bytes.Repeat([]byte{b}, 1100*1024), byte(i))
|
|
total += len(body)
|
|
f := &declaration.File{ID: "m.f" + string(b), Type: declaration.TypeFile, Path: "/x/" + string(b),
|
|
Digest: declaration.DigestOf(body), Size: int64(len(body))}
|
|
name, err := declaration.ObjectName(f.Digest)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := store.PutBytes(name, body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files, bodies[f.ID] = append(files, f), body
|
|
}
|
|
if total <= 3<<20 {
|
|
t.Fatalf("the objects add up to %d bytes, not over 3 MiB", total)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
bus := OverNATS{Conn: conn, JS: js}
|
|
fetch := FilesFrom(WithFiles(ctx, bus))
|
|
if fetch == nil {
|
|
t.Fatal("a link over the bus carries no fetch")
|
|
}
|
|
for _, f := range files {
|
|
name, _ := declaration.ObjectName(f.Digest)
|
|
got, err := fetch(ctx, name, f.Size)
|
|
if err != nil {
|
|
t.Fatalf("fetching %s: %v", f.ID, err)
|
|
}
|
|
if err := f.Verify(got); err != nil {
|
|
t.Errorf("%s fetched and does not verify: %v", f.ID, err)
|
|
}
|
|
if !bytes.Equal(got, bodies[f.ID]) {
|
|
t.Errorf("%s fetched with other content", f.ID)
|
|
}
|
|
}
|
|
|
|
name, _ := declaration.ObjectName(files[0].Digest)
|
|
if _, err := fetch(ctx, name, files[0].Size-1); err == nil || !strings.Contains(err.Error(), "larger") {
|
|
t.Errorf("an object larger than the declaration says was read: %v", err)
|
|
}
|
|
absent := strings.Repeat("0", 64)
|
|
if _, err := fetch(ctx, absent, 10); err == nil {
|
|
t.Error("an absent object was read")
|
|
}
|
|
// A changed byte is stored under the digest of the original: what the controller never does and an attacker might.
|
|
bad := append([]byte(nil), bodies["m.fa"]...)
|
|
bad[5] ^= 1
|
|
swapped := strings.Repeat("1", 64)
|
|
if _, err := store.PutBytes(swapped, bad); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := fetch(ctx, swapped, files[0].Size)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := files[0].Verify(got); err == nil {
|
|
t.Error("an object that differs by a byte verified")
|
|
}
|
|
}
|