feat(03-03): add typed path params and per-plugin rollback
Compile regex and enum constraints at route registration so malformed and unknown IDs share a 404, and named rollback errors isolate one plugin's history. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -109,28 +109,17 @@ func TestCORSPreflightBypassesNamedAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntParamMalformedIsFalse(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/items/abc", nil)
|
||||
req.SetPathValue("id", "abc")
|
||||
if _, ok := IntParam(req, "id"); ok {
|
||||
t.Fatal("malformed id must be false")
|
||||
}
|
||||
req.SetPathValue("id", "12")
|
||||
n, ok := IntParam(req, "id")
|
||||
if !ok || n != 12 {
|
||||
t.Fatalf("got %d %t", n, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypedIDRouteReturns404(t *testing.T) {
|
||||
r := New(nil)
|
||||
r.Get("/items/{id}", func(w http.ResponseWriter, req *http.Request) {
|
||||
if _, ok := IntParam(req, "id"); !ok {
|
||||
id, ok := IntParam(req, "id")
|
||||
if !ok || id != 1 {
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
r.Where("id", `[0-9]+`)
|
||||
h, err := r.compile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -138,6 +127,56 @@ func TestTypedIDRouteReturns404(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/items/nope", nil))
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("malformed status = %d", rec.Code)
|
||||
}
|
||||
rec = httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/items/99", nil))
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("unknown status = %d", rec.Code)
|
||||
}
|
||||
rec = httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/items/1", nil))
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("known status = %d", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhereInRejectsOutsideEnum(t *testing.T) {
|
||||
r := New(nil)
|
||||
r.Get("/kinds/{kind}", func(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
r.WhereIn("kind", "widget", "gadget")
|
||||
h, err := r.compile()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/kinds/other", nil))
|
||||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("status = %d", rec.Code)
|
||||
}
|
||||
rec = httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/kinds/widget", nil))
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhereInvalidRegexFailsCompile(t *testing.T) {
|
||||
r := New(nil)
|
||||
r.Get("/items/{id}", func(http.ResponseWriter, *http.Request) {})
|
||||
r.Where("id", "[")
|
||||
if _, err := r.compile(); err == nil {
|
||||
t.Fatal("want compile error for invalid regex")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhereUnknownParamFailsCompile(t *testing.T) {
|
||||
r := New(nil)
|
||||
r.Get("/items/{id}", func(http.ResponseWriter, *http.Request) {})
|
||||
r.Where("slug", `[a-z]+`)
|
||||
if _, err := r.compile(); err == nil || !strings.Contains(err.Error(), "slug") {
|
||||
t.Fatal("want unknown param error")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user