package api
import (
"net/http"
"net/http/httptest"
"path/filepath"
"strings"
"testing"
"time"
"craftdesign.group/quarry/internal/artifact"
"craftdesign.group/quarry/internal/index"
"craftdesign.group/quarry/internal/ops"
)
// #228: a read with ?indexed=HASH answers once the repository's index
// holds that artifact, and 425 with Retry-After when it does not in
// time; the parameter never reaches the op's own decoding.
func TestAReadWaitsForItsOwnWrite(t *testing.T) {
ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { ix.Close() })
tip := "c0ffee" + strings.Repeat("0", 34)
if err := ix.FoldNow([]index.Parsed{{Hash: tip, Art: &artifact.Artifact{Type: artifact.Manifest,
Date: "2026-10-29T12:00:00", User: "dan", Comment: "pushed"}}}); err != nil {
t.Fatal(err)
}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: ix}})}); err != nil {
t.Fatal(err)
}
h, err := Router(r, "v")
if err != nil {
t.Fatal(err)
}
defer func(b time.Duration) { indexedBound = b }(indexedBound)
indexedBound = 300 * time.Millisecond
get := func(q string) *httptest.ResponseRecorder {
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest("GET", "/api/v1/r/test/timeline"+q, nil))
return w
}
if w := get("?indexed=c0ffee"); w.Code != http.StatusOK {
t.Errorf("FAIL: a read after an indexed write answered %d: %s", w.Code, w.Body)
}
w := get("?indexed=deadbeef")
if w.Code != http.StatusTooEarly || w.Header().Get("Retry-After") == "" {
t.Errorf("FAIL: a read ahead of the index answered %d (Retry-After %q), want 425 with Retry-After", w.Code, w.Header().Get("Retry-After"))
}
if w := get("?indexed=not-a-hash"); w.Code != http.StatusBadRequest {
t.Errorf("FAIL: a malformed hash answered %d, want 400", w.Code)
}
}