package api
import (
"context"
"errors"
"strings"
"testing"
"craftdesign.group/quarry/internal/artifact"
"craftdesign.group/quarry/internal/index"
"craftdesign.group/quarry/internal/ops"
"craftdesign.group/quarry/internal/timing"
)
// A mirror can carry an A card whose blob its upstream never served:
// all six attachments on tcl's ticket 5d170b5c are like that. The head
// answer "not in this store" was never remembered, so every render of
// that page asked the store six times, warm or cold. It is remembered
// until the next fold — the only way the blob can arrive — and after a
// fold the store is asked again.
func TestAMissingAttachmentIsNotAskedForAgainUntilAFold(t *testing.T) {
ix, z, event := attachStore(t)
ghost := strings.Repeat("d", 40)
if err := ix.FoldNow([]index.Parsed{{Hash: strings.Repeat("a", 40), Art: &artifact.Artifact{
Type: artifact.Attachment, Date: "2026-01-02T00:00:00", User: "dan",
Attachment: &artifact.Attach{Name: "gone.txt", Target: event, Source: ghost},
}}}); err != nil {
t.Fatal(err)
}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: ix, Zit: z}})}); err != nil {
t.Fatal(err)
}
// head answers whether the ask reached the store.
head := func(what string) bool {
ctx, tm := timing.With(context.Background())
_, err := r.Invoke(ctx, "attachment.head", attachmentHeadIn{Repo: "test", Hash: ghost})
if !errors.Is(err, ops.ErrNotFound) {
t.Fatalf("FAIL: %s: a blob the store lacks answered %v, want not found", what, err)
}
return tm.Category(timing.Zit) > 0
}
if !head("first ask") {
t.Fatal("FAIL: the first ask never reached the store")
}
if head("second ask") {
t.Fatal("FAIL: the second ask went back to the store for a blob it already knew was missing")
}
if err := ix.FoldNow([]index.Parsed{{Hash: strings.Repeat("b", 40), Art: &artifact.Artifact{
Type: artifact.Wiki, WikiTitle: "Later", Date: "2026-01-03T00:00:00", User: "dan", Body: []byte("x\n")}}}); err != nil {
t.Fatal(err)
}
if !head("after a fold") {
t.Fatal("FAIL: after a fold the missing verdict was still served; the blob may have arrived")
}
t.Log("ok: a missing blob is remembered until the next fold, then asked for again")
}