Skip to content
quarry Artifact [1462eb1045]
Login

Artifact [1462eb1045]

Artifact 1462eb10453ded2701be52312955729cfa1119fe50ed3d13b7a36e04d0b86fc6:


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()))
}