Files
wx_service/internal/expiry/handler_test.go

206 lines
5.8 KiB
Go

package expiry
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"wx_service/internal/middleware"
"wx_service/internal/model"
)
type apiResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data"`
}
func newTestRouter(t *testing.T) *gin.Engine {
t.Helper()
gin.SetMode(gin.TestMode)
db := newTestDB(t)
repo := NewRepository(db)
service := NewService(repo)
handler := NewHandler(service)
r := gin.New()
r.Use(func(c *gin.Context) {
c.Set(middleware.ContextCurrentUserKey, &model.User{ID: 1, MiniProgramID: 1})
c.Next()
})
api := r.Group("/api/expiry")
{
api.GET("/summary", handler.GetSummary)
api.GET("/items", handler.GetItems)
api.POST("/items", handler.CreateItem)
api.PUT("/items/:id", handler.UpdateItem)
api.DELETE("/items/:id", handler.DeleteItem)
api.POST("/items/:id/status", handler.UpdateStatus)
api.GET("/settings", handler.GetSettings)
api.POST("/settings", handler.UpdateSettings)
}
return r
}
func requestJSON(t *testing.T, r http.Handler, method, path string, body interface{}) *httptest.ResponseRecorder {
t.Helper()
var payload []byte
if body != nil {
var err error
payload, err = json.Marshal(body)
if err != nil {
t.Fatalf("marshal body: %v", err)
}
}
req := httptest.NewRequest(method, path, bytes.NewReader(payload))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
return w
}
func decodeResponse(t *testing.T, w *httptest.ResponseRecorder) apiResponse {
t.Helper()
var resp apiResponse
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode response: %v, body=%s", err, w.Body.String())
}
return resp
}
func TestHandler_ItemsFlow(t *testing.T) {
r := newTestRouter(t)
createResp := requestJSON(t, r, http.MethodPost, "/api/expiry/items", map[string]interface{}{
"name": "酸奶",
"category": "food",
"expiry_date": "2030-06-01",
"quantity": 2,
"location": "冰箱",
})
if createResp.Code != http.StatusOK {
t.Fatalf("create status code = %d, body=%s", createResp.Code, createResp.Body.String())
}
createBody := decodeResponse(t, createResp)
if createBody.Code != 0 {
t.Fatalf("create business code = %d", createBody.Code)
}
var created struct {
ID uint `json:"id"`
}
if err := json.Unmarshal(createBody.Data, &created); err != nil {
t.Fatalf("decode create data: %v", err)
}
if created.ID == 0 {
t.Fatalf("expected created id > 0")
}
updateResp := requestJSON(t, r, http.MethodPut, "/api/expiry/items/1", map[string]interface{}{
"name": "酸奶(更新)",
"category": "food",
"expiry_date": "2030-06-02",
"quantity": 1,
})
if updateResp.Code != http.StatusOK {
t.Fatalf("update status code = %d, body=%s", updateResp.Code, updateResp.Body.String())
}
notFoundUpdateResp := requestJSON(t, r, http.MethodPut, "/api/expiry/items/999", map[string]interface{}{
"name": "不存在",
"category": "food",
"expiry_date": "2030-06-02",
"quantity": 1,
})
if notFoundUpdateResp.Code != http.StatusNotFound {
t.Fatalf("update not found code = %d, body=%s", notFoundUpdateResp.Code, notFoundUpdateResp.Body.String())
}
listResp := requestJSON(t, r, http.MethodGet, "/api/expiry/items?status=all&page=1&page_size=20", nil)
if listResp.Code != http.StatusOK {
t.Fatalf("list status code = %d, body=%s", listResp.Code, listResp.Body.String())
}
listBody := decodeResponse(t, listResp)
if listBody.Code != 0 {
t.Fatalf("list business code = %d", listBody.Code)
}
statusResp := requestJSON(t, r, http.MethodPost, "/api/expiry/items/1/status", map[string]string{
"status": "used",
})
if statusResp.Code != http.StatusOK {
t.Fatalf("status update code = %d, body=%s", statusResp.Code, statusResp.Body.String())
}
delResp := requestJSON(t, r, http.MethodDelete, "/api/expiry/items/1", nil)
if delResp.Code != http.StatusOK {
t.Fatalf("delete code = %d, body=%s", delResp.Code, delResp.Body.String())
}
}
func TestHandler_SettingsAndSummary(t *testing.T) {
r := newTestRouter(t)
settingsResp := requestJSON(t, r, http.MethodGet, "/api/expiry/settings", nil)
if settingsResp.Code != http.StatusOK {
t.Fatalf("get settings code=%d body=%s", settingsResp.Code, settingsResp.Body.String())
}
settingsBody := decodeResponse(t, settingsResp)
if settingsBody.Code != 0 {
t.Fatalf("get settings business code=%d", settingsBody.Code)
}
updateSettingsResp := requestJSON(t, r, http.MethodPost, "/api/expiry/settings", map[string]interface{}{
"remind_days": []int{10, 5, 1},
})
if updateSettingsResp.Code != http.StatusOK {
t.Fatalf("update settings code=%d body=%s", updateSettingsResp.Code, updateSettingsResp.Body.String())
}
updateSettingsBody := decodeResponse(t, updateSettingsResp)
if updateSettingsBody.Code != 0 {
t.Fatalf("update settings business code=%d", updateSettingsBody.Code)
}
summaryResp := requestJSON(t, r, http.MethodGet, "/api/expiry/summary", nil)
if summaryResp.Code != http.StatusOK {
t.Fatalf("summary code=%d body=%s", summaryResp.Code, summaryResp.Body.String())
}
summaryBody := decodeResponse(t, summaryResp)
if summaryBody.Code != 0 {
t.Fatalf("summary business code=%d", summaryBody.Code)
}
}
func TestHandler_BadRequest(t *testing.T) {
r := newTestRouter(t)
badCreate := requestJSON(t, r, http.MethodPost, "/api/expiry/items", map[string]interface{}{
"name": "",
"category": "food",
})
if badCreate.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d body=%s", badCreate.Code, badCreate.Body.String())
}
badSettings := requestJSON(t, r, http.MethodPost, "/api/expiry/settings", map[string]interface{}{
"remind_days": []int{0},
})
if badSettings.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d body=%s", badSettings.Code, badSettings.Body.String())
}
}