Skip to content
go-libfossil Artifact [3304ac5045]
Login

Artifact [3304ac5045]

Artifact 3304ac504594121a90ad37bc2030bec1ef1609ef056dd27faf3dbf97e6bf1537:


package manifest

import (
	"reflect"
	"testing"

	"github.com/danmestas/go-libfossil/internal/cone"
	"github.com/danmestas/go-libfossil/internal/content"
	"github.com/danmestas/go-libfossil/internal/deck"
	libfossil "github.com/danmestas/go-libfossil/internal/fsltype"
	"github.com/danmestas/go-libfossil/internal/hash"
	"github.com/danmestas/go-libfossil/internal/repo"
)

func sparseReplacementParent(t *testing.T) (*repo.Repo, libfossil.FslID, *deck.Deck) {
	t.Helper()
	r := sha3Repo(t)
	setSettings(t, r, SettingEnableSubmanifests, "on", SettingSubmanifestK, "3")
	rid, parent := commit(t, r, CheckinOpts{Files: a1CheckinFiles()})
	c, err := cone.New([]string{"README.md"}, nil)
	if err != nil {
		t.Fatal(err)
	}
	if err := content.SetStoreCone(r.DB(), c); err != nil {
		t.Fatal(err)
	}
	elideWriterNode(t, r, parent.S[1].UUID)
	return r, rid, parent
}

// This is a refusal diagnostic, not a sparse-write conformance claim. The
// writer must not silently reinterpret a partial Checkin.Files as a full set.
func TestSparseWriterRefusesUnprovenPartialReplacement(t *testing.T) {
	r, parentRID, parent := sparseReplacementParent(t)
	var before int
	if err := r.DB().QueryRow("SELECT count(*) FROM blob").Scan(&before); err != nil {
		t.Fatal(err)
	}
	rid, uuid, err := Checkin(r, CheckinOpts{Parent: parentRID, User: "drh", Time: writeTime,
		Files: []File{{Name: "README.md", Content: []byte("changed\n"), Perm: "x"}}})
	if err == nil || rid != 0 || uuid != "" {
		t.Fatalf("partial replacement = %d, %q, %v; want atomic refusal", rid, uuid, err)
	}
	if missing, ok := MissingSubmanifest(err); !ok || missing != parent.S[1].UUID {
		t.Fatalf("partial replacement error = %v; want absent sibling S %s", err, parent.S[1].UUID)
	}
	t.Logf("same-name sparse replacement remains unsupported: %v", err)
	var after int
	if err := r.DB().QueryRow("SELECT count(*) FROM blob").Scan(&after); err != nil || after != before {
		t.Fatalf("blob rows after refusal = %d, %v; want unchanged %d", after, err, before)
	}
	if _, ok := content.AvailableByUUID(r.DB(), hash.SHA3([]byte("changed\n"))); ok {
		t.Fatal("failed sparse commit leaked new file content")
	}
	if _, ok := content.AvailableByUUID(r.DB(), parent.S[1].UUID); ok {
		t.Fatal("failed sparse commit filled untouched sibling S")
	}
	wantFiles := a1Files()
	wantFiles[0].UUID, wantFiles[0].Perm = hash.SHA3([]byte("changed\n")), "x"
	want := mustBuildTree(t, wantFiles, 3)
	if !reflect.DeepEqual(want.S[1:], parent.S[1:]) {
		t.Fatal("same-name edit at the original k unexpectedly changed an untouched sibling")
	}
}