package api import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/go-chi/chi/v5" ) // newItemRouter creates a chi router with item routes for testing. func newItemRouter(s *Server) http.Handler { r := chi.NewRouter() r.Route("/api/items", func(r chi.Router) { r.Get("/", s.HandleListItems) r.Post("/", s.HandleCreateItem) r.Route("/{partNumber}", func(r chi.Router) { r.Get("/", s.HandleGetItem) r.Put("/", s.HandleUpdateItem) r.Delete("/", s.HandleDeleteItem) }) }) return r } func TestHandleCreateItem(t *testing.T) { s := newTestServer(t) router := newItemRouter(s) body := `{"part_number":"NEW-001","item_type":"part","description":"new item"}` req := authRequest(httptest.NewRequest("POST", "/api/items", strings.NewReader(body))) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusCreated { t.Fatalf("status: got %d, want %d; body: %s", w.Code, http.StatusCreated, w.Body.String()) } var resp map[string]any if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil { t.Fatalf("decoding response: %v", err) } if resp["part_number"] != "NEW-001" { t.Errorf("part_number: got %v, want %q", resp["part_number"], "NEW-001") } } func TestHandleGetItem(t *testing.T) { s := newTestServer(t) router := newItemRouter(s) createItemDirect(t, s, "GET-001", "get test", nil) req := httptest.NewRequest("GET", "/api/items/GET-001", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) } var resp map[string]any json.Unmarshal(w.Body.Bytes(), &resp) if resp["part_number"] != "GET-001" { t.Errorf("part_number: got %v, want %q", resp["part_number"], "GET-001") } } func TestHandleGetItemNotFound(t *testing.T) { s := newTestServer(t) router := newItemRouter(s) req := httptest.NewRequest("GET", "/api/items/NOPE-999", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Errorf("status: got %d, want %d", w.Code, http.StatusNotFound) } } func TestHandleListItems(t *testing.T) { s := newTestServer(t) router := newItemRouter(s) createItemDirect(t, s, "LST-001", "list item 1", nil) createItemDirect(t, s, "LST-002", "list item 2", nil) req := httptest.NewRequest("GET", "/api/items", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status: got %d, want %d; body: %s", w.Code, http.StatusOK, w.Body.String()) } var resp map[string]any json.Unmarshal(w.Body.Bytes(), &resp) items, ok := resp["items"].([]any) if !ok { t.Fatalf("expected items array in response, got: %s", w.Body.String()) } if len(items) < 2 { t.Errorf("expected at least 2 items, got %d", len(items)) } } func TestHandleDeleteItem(t *testing.T) { s := newTestServer(t) router := newItemRouter(s) createItemDirect(t, s, "DEL-ITEM-001", "deletable", nil) req := authRequest(httptest.NewRequest("DELETE", "/api/items/DEL-ITEM-001", nil)) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusNoContent && w.Code != http.StatusOK { t.Fatalf("status: got %d, want 200 or 204; body: %s", w.Code, w.Body.String()) } // Should be gone (archived) req2 := httptest.NewRequest("GET", "/api/items/DEL-ITEM-001", nil) w2 := httptest.NewRecorder() router.ServeHTTP(w2, req2) if w2.Code != http.StatusNotFound { t.Errorf("after delete, expected 404, got %d", w2.Code) } }