Fetch a declaration's files given by digest from the bus's object bucket, verify them and report file-references (issue 1695, ADR 0486)
mesh-sdk is pinned to fix/1694-file-references 6c857e1c025f until it merges.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/declaration"
|
||||
)
|
||||
|
||||
func referenceTo(t *testing.T, path string, body []byte) *declaration.File {
|
||||
t.Helper()
|
||||
return &declaration.File{ID: "m.big", Type: declaration.TypeFile, Path: path,
|
||||
Digest: declaration.DigestOf(body), Size: int64(len(body))}
|
||||
}
|
||||
|
||||
func objects(m map[string][]byte, asked *[]string) FetchObject {
|
||||
return func(_ context.Context, name string, limit int64) ([]byte, error) {
|
||||
*asked = append(*asked, name)
|
||||
b, ok := m[name]
|
||||
if !ok {
|
||||
return nil, errors.New("object not found")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
func objectOf(body []byte) string {
|
||||
name, _ := declaration.ObjectName(declaration.DigestOf(body))
|
||||
return name
|
||||
}
|
||||
|
||||
// 1695/AC1: both forms apply: a file by reference becomes the inline file, an inline one is left as it is.
|
||||
func TestAFileByReferenceBecomesTheInlineFile(t *testing.T) {
|
||||
body := []byte(strings.Repeat("a line\n", 20000))
|
||||
inline := &declaration.File{ID: "m.small", Type: declaration.TypeFile, Path: "/etc/small", Content: "x"}
|
||||
d := &declaration.Declaration{Resources: []declaration.Resource{inline, referenceTo(t, "/nowhere/big", body)}}
|
||||
var asked []string
|
||||
if err := ResolveFileReferences(context.Background(), d, objects(map[string][]byte{objectOf(body): body}, &asked)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := d.Resources[1].(*declaration.File)
|
||||
if got.IsReference() || got.Content != string(body) || got.Path != "/nowhere/big" || d.Resources[0] != declaration.Resource(inline) {
|
||||
t.Errorf("1695/AC1: resolved to %+v", got)
|
||||
}
|
||||
if len(asked) != 1 || asked[0] != objectOf(body) {
|
||||
t.Errorf("1695/AC1: asked %v", asked)
|
||||
}
|
||||
}
|
||||
|
||||
// 1695/AC2: an object that differs by one byte, or in size, fails the whole resolve and gives no content.
|
||||
func TestAnObjectThatDiffersFailsTheApply(t *testing.T) {
|
||||
body := []byte(strings.Repeat("x", 70000))
|
||||
bad := append([]byte(nil), body...)
|
||||
bad[3] ^= 1
|
||||
for name, served := range map[string][]byte{"one byte": bad, "shorter": body[1:]} {
|
||||
d := &declaration.Declaration{Resources: []declaration.Resource{referenceTo(t, "/nowhere/big", body)}}
|
||||
var asked []string
|
||||
err := ResolveFileReferences(context.Background(), d, objects(map[string][]byte{objectOf(body): served}, &asked))
|
||||
if err == nil || !strings.Contains(err.Error(), "nothing was applied") {
|
||||
t.Errorf("1695/AC2: %s: %v", name, err)
|
||||
}
|
||||
if !d.Resources[0].(*declaration.File).IsReference() {
|
||||
t.Errorf("1695/AC2: %s: the reference was replaced by unverified content", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1695/AC3: a missing object, or a node with no bus, fails the apply naming the file.
|
||||
func TestAMissingObjectFailsTheApply(t *testing.T) {
|
||||
body := []byte(strings.Repeat("y", 70000))
|
||||
d := &declaration.Declaration{Resources: []declaration.Resource{referenceTo(t, "/nowhere/big", body)}}
|
||||
var asked []string
|
||||
if err := ResolveFileReferences(context.Background(), d, objects(nil, &asked)); err == nil ||
|
||||
!strings.Contains(err.Error(), "m.big") || !strings.Contains(err.Error(), "declaration-files") {
|
||||
t.Errorf("1695/AC3: missing object: %v", err)
|
||||
}
|
||||
if err := ResolveFileReferences(context.Background(), d, nil); err == nil || !strings.Contains(err.Error(), "no link to the bus") {
|
||||
t.Errorf("1695/AC3: no bus: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 1695/AC4: a file already on the machine that verifies is used and nothing is fetched; one that does not is fetched.
|
||||
func TestAVerifiedFileOnTheMachineFetchesNothing(t *testing.T) {
|
||||
body := []byte(strings.Repeat("z", 70000))
|
||||
path := filepath.Join(t.TempDir(), "big")
|
||||
if err := os.WriteFile(path, body, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var asked []string
|
||||
d := &declaration.Declaration{Resources: []declaration.Resource{referenceTo(t, path, body)}}
|
||||
if err := ResolveFileReferences(context.Background(), d, objects(nil, &asked)); err != nil || len(asked) != 0 {
|
||||
t.Errorf("1695/AC4: err %v, asked %v", err, asked)
|
||||
}
|
||||
if err := os.WriteFile(path, body[:100], 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d = &declaration.Declaration{Resources: []declaration.Resource{referenceTo(t, path, body)}}
|
||||
if err := ResolveFileReferences(context.Background(), d, objects(map[string][]byte{objectOf(body): body}, &asked)); err != nil || len(asked) != 1 {
|
||||
t.Errorf("1695/AC4: a differing file: err %v, asked %v", err, asked)
|
||||
}
|
||||
}
|
||||
|
||||
// 1695/AC5: binary content comes back as bytes, and two files naming one digest fetch it once.
|
||||
func TestBinaryContentAndOneFetchPerDigest(t *testing.T) {
|
||||
body := append([]byte{0xff, 0xfe, 0x00}, []byte(strings.Repeat("b", 70000))...)
|
||||
d := &declaration.Declaration{Resources: []declaration.Resource{referenceTo(t, "/n/a", body), referenceTo(t, "/n/b", body)}}
|
||||
var asked []string
|
||||
if err := ResolveFileReferences(context.Background(), d, objects(map[string][]byte{objectOf(body): body}, &asked)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a, _ := d.Resources[0].(*declaration.File).Body()
|
||||
if d.Resources[0].(*declaration.File).Bytes == "" || string(a) != string(body) || len(asked) != 1 {
|
||||
t.Errorf("1695/AC5: bytes %q asked %v", d.Resources[0].(*declaration.File).Bytes[:4], asked)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user