🏡 index : quine-global/quintodrome.git

package apiv1

import (
	"net/http"
	"net/http/httptest"

	"github.com/navidrome/navidrome/tests"
	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("Router", func() {
	var router *Router

	BeforeEach(func() {
		router = New(&tests.MockDataStore{})
	})

	It("returns a 404 problem for unknown paths", func() {
		w := serve(router, httptest.NewRequest(http.MethodGet, "/api/v1/nope", nil))
		Expect(w.Code).To(Equal(http.StatusNotFound))
		Expect(w.Header().Get("Content-Type")).To(Equal(problemContentType))
		Expect(decodeProblem(w).Code).To(Equal(ProblemCodeNotFound))
	})

	It("returns a 405 problem listing the allowed methods for a wrong method on a known path", func() {
		w := serve(router, httptest.NewRequest(http.MethodPost, "/api/v1/server", nil))
		Expect(w.Code).To(Equal(http.StatusMethodNotAllowed))
		Expect(w.Header().Get("Allow")).To(Equal("GET, HEAD"))
		Expect(decodeProblem(w).Code).To(Equal(ProblemCodeMethodNotAllowed))
	})

	DescribeTable("answers HEAD wherever GET is routed",
		func(path, contentType string) {
			w := serve(router, httptest.NewRequest(http.MethodHead, path, nil))
			Expect(w.Code).To(Equal(http.StatusOK))
			Expect(w.Header().Get("Content-Type")).To(Equal(contentType))
		},
		Entry("server info", "/api/v1/server", "application/json"),
		Entry("JSON spec", "/api/v1/openapi.json", "application/json"),
		Entry("YAML spec", "/api/v1/openapi.yaml", "application/yaml"),
	)

	It("revalidates HEAD requests with If-None-Match", func() {
		etag := serve(router, httptest.NewRequest(http.MethodHead, "/api/v1/openapi.json", nil)).Header().Get("ETag")
		req := httptest.NewRequest(http.MethodHead, "/api/v1/openapi.json", nil)
		req.Header.Set("If-None-Match", etag)
		Expect(serve(router, req).Code).To(Equal(http.StatusNotModified))
	})

	It("returns a 404 problem for HEAD on unknown paths", func() {
		w := serve(router, httptest.NewRequest(http.MethodHead, "/api/v1/nope", nil))
		Expect(w.Code).To(Equal(http.StatusNotFound))
		Expect(w.Header().Get("Allow")).To(BeEmpty())
	})

	panicking := func(v any) http.Handler {
		return problemRecoverer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { panic(v) }))
	}

	It("turns a handler panic into a 500 problem", func() {
		w := httptest.NewRecorder()
		panicking("kaboom").ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/boom", nil))
		Expect(w.Code).To(Equal(http.StatusInternalServerError))
		p := decodeProblem(w)
		Expect(p.Code).To(Equal(ProblemCodeInternal))
		Expect(p.Detail).To(BeNil())
	})

	It("re-panics http.ErrAbortHandler so the server can drop the connection", func() {
		Expect(func() {
			panicking(http.ErrAbortHandler).ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/abort", nil))
		}).To(PanicWith(http.ErrAbortHandler))
	})
})