116 lines
4.1 KiB
Go
116 lines
4.1 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// Defends novox/hq issue 1585: an archive's files were refused as "not put there by the mesh" when the state held
|
|
// no record of them, though the host had unpacked them itself.
|
|
|
|
// An archive the host unpacked, whose record is gone, is its own: the next version replaces its files, drops the
|
|
// ones it no longer has, and leaves a file somebody added; a file the host never unpacked, or an unrecorded file
|
|
// with other bytes where no unpack was journaled, still refuses the archive.
|
|
func TestAnArchiveWhoseRecordIsGoneIsStillTheHosts(t *testing.T) {
|
|
rows := []struct {
|
|
name string
|
|
journaled bool // the first version was unpacked by the host
|
|
foreign string
|
|
wantRefuse bool
|
|
}{
|
|
{"unpacked by the host, record lost", true, "", false},
|
|
{"unpacked by the host, somebody's file beside", true, "mine.txt", false},
|
|
{"never unpacked by the host", false, "", true},
|
|
}
|
|
for _, row := range rows {
|
|
t.Run(row.name, func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
target := filepath.Join(dir, "tools")
|
|
first, _ := archiveDecl(t, "m.tools", target, map[string]string{"bin": "one", "old": "o"}, "")
|
|
if row.journaled {
|
|
if _, _, err := Apply(context.Background(), archHost(t), declare(t, first), store.State{},
|
|
store.OriginDeclared, noServices, nil, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
} else {
|
|
os.MkdirAll(target, 0o755)
|
|
os.WriteFile(filepath.Join(target, "bin"), []byte("one"), 0o644)
|
|
}
|
|
if row.foreign != "" {
|
|
os.WriteFile(filepath.Join(target, row.foreign), []byte("x"), 0o644)
|
|
}
|
|
second, _ := archiveDecl(t, "m.tools", target, map[string]string{"bin": "two"}, "")
|
|
// The state is empty: the record of the first unpack was lost.
|
|
_, _, err := Apply(context.Background(), archHost(t), declare(t, second), store.State{},
|
|
store.OriginDeclared, noServices, nil, nil)
|
|
if row.wantRefuse {
|
|
if err == nil || !strings.Contains(err.Error(), "did not put there") {
|
|
t.Fatalf("refused? %v", err)
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, _ := os.ReadFile(filepath.Join(target, "bin")); string(got) != "two" {
|
|
t.Fatalf("bin is %q", got)
|
|
}
|
|
if exists(filepath.Join(target, "old")) {
|
|
t.Fatal("a file the new version no longer has is still there")
|
|
}
|
|
if row.foreign != "" && !exists(filepath.Join(target, row.foreign)) {
|
|
t.Fatal("somebody's file went")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// An unrecorded file already holding exactly the archive's bytes is not a collision; one with other bytes is.
|
|
func TestAnUnrecordedFileWithTheArchivesBytesIsNotACollision(t *testing.T) {
|
|
for _, row := range []struct {
|
|
name, has string
|
|
refused bool
|
|
}{{"same bytes", "theme", false}, {"other bytes", "somebody's", true}} {
|
|
t.Run(row.name, func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
target := filepath.Join(dir, "theme")
|
|
os.MkdirAll(target, 0o755)
|
|
os.WriteFile(filepath.Join(target, "p10k.zsh"), []byte(row.has), 0o644)
|
|
archive, _ := archiveDecl(t, "shell.theme", target, map[string]string{"p10k.zsh": "theme"}, "")
|
|
_, _, err := Apply(context.Background(), archHost(t), declare(t, archive), store.State{},
|
|
store.OriginDeclared, noServices, nil, nil)
|
|
if (err != nil) != row.refused {
|
|
t.Fatalf("err = %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Undeclaring an archive takes its journal with it, and a journal alone never makes a file the host's.
|
|
func TestTheJournalGoesWithTheArchive(t *testing.T) {
|
|
dir := t.TempDir()
|
|
target := filepath.Join(dir, "theme")
|
|
archive, _ := archiveDecl(t, "shell.theme", target, map[string]string{"a": "a"}, "")
|
|
_, state, err := Apply(context.Background(), archHost(t), declare(t, archive), store.State{},
|
|
store.OriginDeclared, noServices, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !exists(journalPath(target)) {
|
|
t.Fatal("no journal after the unpack")
|
|
}
|
|
if _, _, err := Apply(context.Background(), archHost(t),
|
|
declare(t, `{"id":"other","type":"file","path":"`+dir+`/other","content":"o"}`), state,
|
|
store.OriginDeclared, noServices, nil, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if exists(journalPath(target)) {
|
|
t.Fatal("the journal outlived the archive")
|
|
}
|
|
}
|