package api
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// The web's /r/{owner}/{repo} spelling, carried into the API, answers a
// 404 that names the bare-name path to try. The catch-all used to say
// only "no such endpoint": the hint repoFor gives fired for %2F, which
// routes, and never for the slash, which does not (G-1, jva #1068).
type ownerIn struct {
Repo string `json:"repo" validate:"required,max=64"`
}
// ownerRoutes is a registry with the two route shapes the test walks,
// so the hint has something real to point at — it fires only when the
// path minus the owner is a route this API has.
func ownerRoutes(t *testing.T) *ops.Registry {
t.Helper()
r := ops.NewRegistry()
for _, route := range []string{"GET /api/v1/r/{repo}/info", "GET /api/v1/r/{repo}/file/{version}/{path...}"} {
if err := ops.Register(r, ops.Op[ownerIn, struct{}]{Name: "owner." + strings.Fields(route)[1][len("/api/v1/r/{repo}/"):], Kind: ops.Query, Auth: ops.Anonymous,
Route: route, Doc: "test double",
Run: func(context.Context, ownerIn) (struct{}, error) { return struct{}{}, nil }}); err != nil {
t.Fatal(err)
}
}
return r
}
func TestAnOwnerPrefixedAPIPathIsToldTheBarePath(t *testing.T) {
r := ownerRoutes(t)
h, err := RouterWithCaps(r, "test", func(_, _ string) CapSet { return oltp.CapSet("s") }, nil)
if err != nil {
t.Fatal(err)
}
for _, c := range []struct{ path, want string }{
{"/api/v1/r/jva/dantest-sandbox/info", "/api/v1/r/dantest-sandbox/info"},
{"/api/v1/r/jva/dantest-sandbox/file/trunk/README.md", "/api/v1/r/dantest-sandbox/file/trunk/README.md"},
} {
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest("GET", c.path, nil))
if w.Code != http.StatusNotFound || !strings.Contains(w.Body.String(), c.want) || !strings.Contains(w.Body.String(), "jva") {
t.Fatalf("FAIL: %s -> %d %s", c.path, w.Code, w.Body.String())
}
t.Logf("ok: %s -> %s", c.path, strings.TrimSpace(w.Body.String()))
}
// Two segments is an unknown op, not an owner prefix: no hint.
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest("GET", "/api/v1/r/quarry/nosuchop", nil))
if w.Code != 404 || strings.Contains(w.Body.String(), "bare name") {
t.Fatalf("FAIL: an unknown op got the owner hint: %s", w.Body.String())
}
t.Log("ok: an unknown op is a plain 404")
}
// The hint fires only when the path minus its first segment is a real
// route. A three-segment path to an op that does not exist is a plain
// 404: the first version read the repository as an owner and pointed
// at /api/v1/r/ci/run/<id>/cancel, which is nonsense.
func TestAnUnknownOpWithExtraSegmentsIsNotMistakenForAnOwnerPrefix(t *testing.T) {
r := ownerRoutes(t) // real routes exist; this path is still not one of them
h, err := RouterWithCaps(r, "test", func(_, _ string) CapSet { return oltp.CapSet("s") }, nil)
if err != nil {
t.Fatal(err)
}
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest("POST", "/api/v1/r/quarry/ci/run/abc/cancel", nil))
if w.Code != 404 || strings.Contains(w.Body.String(), "bare name") {
t.Fatalf("FAIL: a non-existent op got the owner hint: %s", w.Body.String())
}
t.Logf("ok: %s", strings.TrimSpace(w.Body.String()))
}