package xfer
import (
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
// A repository the forge serves but has not registered yet is starting,
// not missing. Telling a sync client 404 during a deploy makes it
// conclude the repository is gone; 503 with Retry-After makes it wait.
func TestSyncSaysStartingRatherThanMissing(t *testing.T) {
f := &Fronter{repos: map[string]*backend{}}
f.SetKnown(func(name string) bool { return name == "quarry" })
for _, tc := range []struct {
name string
repo string
want int
retry bool
reason string
}{
{"served but not yet registered", "quarry", http.StatusServiceUnavailable, true,
"a store the forge serves is starting, not gone"},
{"genuinely absent", "nosuchrepo", http.StatusNotFound, false,
"a name the forge does not serve is still not found"},
} {
w := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/r/"+tc.repo+"/xfer", strings.NewReader(""))
req.SetPathValue("repo", tc.repo)
f.ServeHTTP(w, req)
if w.Code != tc.want {
t.Errorf("FAIL: %s: status %d, want %d — %s", tc.name, w.Code, tc.want, tc.reason)
continue
}
if got := w.Header().Get("Retry-After"); (got != "") != tc.retry {
t.Errorf("FAIL: %s: Retry-After %q, want present=%v", tc.name, got, tc.retry)
continue
}
t.Logf("ok: %s answers %d", tc.name, w.Code)
}
}
// Without the seam wired, behaviour is exactly as before: a missing
// backend is a missing repository.
func TestSyncWithoutTheKnownSeamStillSaysNotFound(t *testing.T) {
f := &Fronter{repos: map[string]*backend{}}
w := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/r/quarry/xfer", strings.NewReader(""))
req.SetPathValue("repo", "quarry")
f.ServeHTTP(w, req)
if w.Code != http.StatusNotFound {
t.Fatalf("FAIL: status %d, want 404 when nothing can answer whether the name is served", w.Code)
}
t.Log("ok: an unwired fronter is unchanged")
}
// The log line is a diagnostic surface and was wrong in the same way
// the status was: it said "unknown repository" while the server
// answered "starting". Asserting the reason string keeps the two from
// drifting apart again.
func TestStartingAndMissingAreDistinctReasons(t *testing.T) {
f := &Fronter{repos: map[string]*backend{}}
f.SetKnown(func(name string) bool { return name == "quarry" })
var lines []string
restore := log.Writer()
log.SetOutput(writerFunc(func(b []byte) (int, error) {
lines = append(lines, string(b))
return len(b), nil
}))
defer log.SetOutput(restore)
for _, repo := range []string{"quarry", "nosuchrepo"} {
w := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/r/"+repo+"/xfer", strings.NewReader(""))
req.SetPathValue("repo", repo)
f.ServeHTTP(w, req)
}
all := strings.Join(lines, "")
if !strings.Contains(all, "still starting") {
t.Errorf("FAIL: the starting case is not named in the log: %s", all)
} else {
t.Log("ok: the starting case says it is starting")
}
if !strings.Contains(all, "unknown repository") {
t.Errorf("FAIL: the genuinely missing case lost its reason: %s", all)
} else {
t.Log("ok: the missing case still says unknown repository")
}
}
type writerFunc func([]byte) (int, error)
func (f writerFunc) Write(b []byte) (int, error) { return f(b) }