Files
aego/asset/pipeline/source_auxiliary_test.go

137 lines
4.7 KiB
Go

package pipeline
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
)
func TestAuxiliarySourceCommitRollbackAndHistory(t *testing.T) {
for _, fail := range []bool{false, true} {
name := "commit-undo-redo"
if fail {
name = "import-failure"
}
t.Run(name, func(t *testing.T) {
p := newProject(t)
assetPath := write(t, p, "assets/animation.txt", "old")
companion := filepath.Join(p.Root, "assets/animation.frames.json")
imp := &rollbackConflictImporter{fakeImporter: fakeImporter{version: 1}}
if fail {
imp.mutate = func() error { return nil }
}
pl := openPipeline(t, p, imp)
if _, _, err := pl.Scan(); err != nil {
t.Fatal(err)
}
initial, err := pl.ImportAll()
if err != nil || initial == nil || len(initial.Failed) != 0 || len(initial.Retry) != 0 {
t.Fatalf("initial import: %+v, %v", initial, err)
}
edits := []SourceEdit{
{Path: "assets/animation.txt", Before: []byte("old"), After: []byte("candidate")},
{Path: "assets/animation.frames.json", After: []byte(`{"frames":[1,2]}`), Auxiliary: true},
}
batch, err := pl.ApplySources(edits, false)
if fail {
if err == nil {
t.Fatal("injected import failure was accepted")
}
if data, err := os.ReadFile(assetPath); err != nil || string(data) != "old" {
t.Fatalf("source did not roll back: %q, %v", data, err)
}
if _, err := os.Stat(companion); !os.IsNotExist(err) {
t.Fatalf("companion survived rollback: %v", err)
}
if status, err := SourceRecoveryStatus(p); err != nil || status != nil {
t.Fatalf("successful rollback left recovery pending: %+v, %v", status, err)
}
return
}
if err != nil || batch == nil || len(batch.Failed) != 0 || len(batch.Retry) != 0 {
t.Fatalf("commit: %+v, %v", batch, err)
}
if _, ok := pl.Lookup(edits[1].Path); ok {
t.Fatal("auxiliary companion unexpectedly obtained an AssetID")
}
if _, err := os.Stat(companion + MetaSuffix); !os.IsNotExist(err) {
t.Fatalf("auxiliary companion obtained metadata: %v", err)
}
for _, back := range []bool{true, false} {
batch, err := pl.ApplySources(edits, back)
if err != nil || batch == nil || len(batch.Failed) != 0 || len(batch.Retry) != 0 {
t.Fatalf("history back=%v: %+v, %v", back, batch, err)
}
data, err := os.ReadFile(companion)
if back && !os.IsNotExist(err) {
t.Fatalf("Undo retained companion: %v", err)
}
if !back && (err != nil || !bytes.Equal(data, edits[1].After)) {
t.Fatalf("Redo lost companion: %q, %v", data, err)
}
}
if err := os.WriteFile(companion, []byte("external"), 0600); err != nil {
t.Fatal(err)
}
if _, err := pl.ApplySources(edits, true); err == nil || !strings.Contains(err.Error(), "changed") {
t.Fatalf("external change was accepted: %v", err)
}
if data, _ := os.ReadFile(assetPath); string(data) != "candidate" {
t.Fatal("preflight conflict changed the imported source")
}
})
}
}
func TestAuxiliarySourceCannotBypassImporterOrMetadata(t *testing.T) {
p := newProject(t)
pl := openPipeline(t, p, &fakeImporter{version: 1})
for _, rel := range []string{"assets/image.txt", "assets/image.txt.meta", "assets/image.txt.META"} {
if _, err := pl.ApplySources([]SourceEdit{{Path: rel, After: []byte("invalid"), Auxiliary: true}}, false); err == nil {
t.Errorf("auxiliary bypass accepted %s", rel)
}
if _, err := os.Stat(filepath.Join(p.Root, filepath.FromSlash(rel))); !os.IsNotExist(err) {
t.Errorf("rejected edit wrote %s: %v", rel, err)
}
}
}
func TestVerifyCurrentRejectsPendingSourceJournal(t *testing.T) {
p := newProject(t)
write(t, p, "assets/a.txt", "old")
pl := openPipeline(t, p, &fakeImporter{version: 1})
if _, _, err := pl.Scan(); err != nil {
t.Fatal(err)
}
batch, err := pl.ImportAll()
if err != nil || batch == nil || len(batch.Failed) != 0 || len(batch.Retry) != 0 {
t.Fatalf("initial import: %+v, %v", batch, err)
}
id, ok := pl.Lookup("assets/a.txt")
if !ok || pl.VerifyCurrent(id) != nil {
t.Fatal("initial asset was not current")
}
root, err := os.OpenRoot(p.Root)
if err != nil {
t.Fatal(err)
}
defer root.Close()
journal, err := startSourceJournal(root, []SourceEdit{{Path: "assets/a.txt", Before: []byte("old"), After: []byte("candidate")}}, false)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := journal.finish("restored"); err != nil {
t.Errorf("fixture journal cleanup: %v", err)
}
}()
if err := pl.VerifyCurrent(id); err == nil {
t.Fatal("strict export accepted an asset while a source journal was pending")
}
if data, err := os.ReadFile(filepath.Join(p.Root, "assets", "a.txt")); err != nil || string(data) != "old" {
t.Fatalf("checking pending recovery modified the source: %q, %v", data, err)
}
}