Skip to content
quarry Artifact [b8b9671476]
Login

Artifact [b8b9671476]

Artifact b8b96714762340f4c926b3411e4278b6746da6ad9c5413416cab744c31fca171:


package api

import (
	"errors"
	"os"
	"strings"
	"testing"

	"craftdesign.group/quarry/internal/ingest"
	"craftdesign.group/quarry/internal/ops"
)

// A settle folds the index first, on purpose: "a settle is a
// deliberate, rare act and can afford one ingest round to be sure what
// it is settling". synced() then swallowed ErrStalled and answered nil,
// so a stalled ingest read as a successful fold and the accept went on
// to merge onto whatever tip the stale index still held.
//
// Ticket b7c678a5: the merge's first parent was four hours old, trunk
// grew a second leaf, and thirteen check-ins' files left the tree. The
// landing reported behind:0 the whole time, because behind is counted
// from the same index that had not moved.
func TestASettleWillNotProceedOnAStalledIndex(t *testing.T) {
	stalled := &Repo{Sync: func() (int, error) { return 0, ingest.ErrStalled }}
	err := stalled.settleSynced()
	if err == nil {
		t.Error("FAIL: a settle proceeded while the ingest was stalled")
	} else if !errors.Is(err, ops.ErrUnavailable) {
		t.Errorf("FAIL: refused with %v, want ErrUnavailable so a caller can retry", err)
	} else {
		t.Logf("ok: refused — %v", err)
	}

	// A READ is unchanged: a page may lag, it may only never lie about
	// it, and refusing every page during a backoff would be worse than
	// the lag it guards.
	if err := stalled.synced(); err != nil {
		t.Errorf("FAIL: a read path now refuses during a backoff: %v", err)
	} else {
		t.Log("ok: reads still tolerate a stall")
	}

	// A healthy fold settles as before, and a real failure still
	// surfaces rather than being turned into a stall message.
	healthy := &Repo{Sync: func() (int, error) { return 3, nil }}
	if err := healthy.settleSynced(); err != nil {
		t.Errorf("FAIL: a healthy fold refused: %v", err)
	} else {
		t.Log("ok: a healthy fold settles")
	}
	boom := errors.New("disk on fire")
	broken := &Repo{Sync: func() (int, error) { return 0, boom }}
	if err := broken.settleSynced(); !errors.Is(err, boom) {
		t.Errorf("FAIL: a real ingest error became %v", err)
	} else {
		t.Log("ok: a real error is still itself")
	}

	// A forge with no ingest at all has nothing to be stale.
	if err := (&Repo{}).settleSynced(); err != nil {
		t.Errorf("FAIL: a forge with no Sync refused: %v", err)
	} else {
		t.Log("ok: no ingest, nothing to wait for")
	}
}

// And the settle path must actually CALL the strict one. Testing the
// helper alone would pass forever while somebody put synced() back at
// the call site, which is the exact line that caused b7c678a5.
func TestTheSettlePathCallsTheStrictFold(t *testing.T) {
	src, err := os.ReadFile("register.go")
	if err != nil {
		t.Fatal(err)
	}
	i := strings.Index(string(src), `if verb != "comment" {`)
	if i < 0 {
		t.Fatal("FAIL: the settle's fold is not where this guard looks any more")
	}
	block := string(src)[i : i+400]
	if !strings.Contains(block, "repo.settleSynced()") {
		t.Errorf("FAIL: the settle folds with the lenient synced(); a stalled index reads as folded:\n%s", block[:200])
	} else {
		t.Log("ok: the settle folds strictly")
	}
}