9099c4af99
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
278 lines
9.2 KiB
Go
278 lines
9.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/wangjia/jiu/backend/testutil"
|
|
)
|
|
|
|
func TestPartnerHandler_CRUD(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT001")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
// 1. Create supplier
|
|
w := makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "Test Supplier",
|
|
"type": "supplier",
|
|
"code": "SUP001",
|
|
})
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
partnerID := extractID(w)
|
|
assert.NotZero(t, partnerID)
|
|
data := parseResponse(w)["data"].(map[string]interface{})
|
|
assert.Equal(t, "Test Supplier", data["name"])
|
|
assert.Equal(t, "supplier", data["type"])
|
|
|
|
// 2. List
|
|
w = makeRequest(r, "GET", "/api/v1/partners", token, nil)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
resp := parseResponse(w)
|
|
assert.Equal(t, float64(1), resp["total"].(float64))
|
|
|
|
// 3. Update
|
|
w = makeRequest(r, "PUT", fmt.Sprintf("/api/v1/partners/%d", partnerID), token, map[string]interface{}{
|
|
"name": "Updated Supplier",
|
|
"type": "supplier",
|
|
})
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
updatedData := parseResponse(w)["data"].(map[string]interface{})
|
|
assert.Equal(t, "Updated Supplier", updatedData["name"])
|
|
|
|
// 4. Delete
|
|
w = makeRequest(r, "DELETE", fmt.Sprintf("/api/v1/partners/%d", partnerID), token, nil)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
// 5. List after delete - should be 0
|
|
w = makeRequest(r, "GET", "/api/v1/partners", token, nil)
|
|
resp = parseResponse(w)
|
|
assert.Equal(t, float64(0), resp["total"].(float64))
|
|
}
|
|
|
|
func TestPartnerHandler_NoAuth(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
r := setupProtectedRouter(db)
|
|
|
|
w := makeRequest(r, "GET", "/api/v1/partners", "", nil)
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
}
|
|
|
|
func TestPartnerHandler_HotelIsolation(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
|
|
shopA := testutil.CreateTestShop(db, "PT_A")
|
|
userA := testutil.CreateTestUser(db, shopA.ID, "adminA", "pass", "admin")
|
|
tokenA := getAuthToken(userA.ID, shopA.ID, "admin")
|
|
|
|
shopB := testutil.CreateTestShop(db, "PT_B")
|
|
userB := testutil.CreateTestUser(db, shopB.ID, "adminB", "pass", "admin")
|
|
tokenB := getAuthToken(userB.ID, shopB.ID, "admin")
|
|
|
|
r := setupProtectedRouter(db)
|
|
|
|
// 门店 A 创建往来单位
|
|
w := makeRequest(r, "POST", "/api/v1/partners", tokenA, map[string]interface{}{
|
|
"name": "A Supplier",
|
|
"type": "supplier",
|
|
})
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
partnerAID := extractID(w)
|
|
|
|
// 门店 B 创建往来单位
|
|
makeRequest(r, "POST", "/api/v1/partners", tokenB, map[string]interface{}{
|
|
"name": "B Customer",
|
|
"type": "customer",
|
|
})
|
|
|
|
// 门店 A 只能看到自己的数据
|
|
w = makeRequest(r, "GET", "/api/v1/partners", tokenA, nil)
|
|
resp := parseResponse(w)
|
|
assert.Equal(t, float64(1), resp["total"].(float64))
|
|
listData := resp["data"].([]interface{})
|
|
assert.Equal(t, "A Supplier", listData[0].(map[string]interface{})["name"])
|
|
|
|
// 门店 B 只能看到自己的数据
|
|
w = makeRequest(r, "GET", "/api/v1/partners", tokenB, nil)
|
|
respB := parseResponse(w)
|
|
assert.Equal(t, float64(1), respB["total"].(float64))
|
|
|
|
// 门店 B 不能修改门店 A 的往来单位
|
|
w = makeRequest(r, "PUT", fmt.Sprintf("/api/v1/partners/%d", partnerAID), tokenB, map[string]interface{}{
|
|
"name": "Hacked",
|
|
"type": "supplier",
|
|
})
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
|
|
// 门店 B 不能删除门店 A 的往来单位
|
|
w = makeRequest(r, "DELETE", fmt.Sprintf("/api/v1/partners/%d", partnerAID), tokenB, nil)
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
func TestPartnerHandler_UpdateNotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT002")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
w := makeRequest(r, "PUT", "/api/v1/partners/99999", token, map[string]interface{}{
|
|
"name": "Nonexistent",
|
|
"type": "supplier",
|
|
})
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
func TestPartnerHandler_DeleteNotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT003")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
w := makeRequest(r, "DELETE", "/api/v1/partners/99999", token, nil)
|
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
|
}
|
|
|
|
func TestPartnerHandler_Create_MissingName(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT004")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
// 缺少 name 字段(必填)
|
|
w := makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"type": "supplier",
|
|
})
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestPartnerHandler_List_FilterByType(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT005")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
// 创建供应商和客户
|
|
makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "Supplier One",
|
|
"type": "supplier",
|
|
})
|
|
makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "Customer One",
|
|
"type": "customer",
|
|
})
|
|
|
|
// 列出全部
|
|
w := makeRequest(r, "GET", "/api/v1/partners", token, nil)
|
|
resp := parseResponse(w)
|
|
assert.Equal(t, float64(2), resp["total"].(float64))
|
|
}
|
|
|
|
func TestPartnerHandler_List_KeywordSearch(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT006")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "Beijing Beer Co",
|
|
"type": "supplier",
|
|
"phone": "13800001111",
|
|
})
|
|
makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "Shanghai Wine Ltd",
|
|
"type": "supplier",
|
|
})
|
|
|
|
// 按名称搜索
|
|
w := makeRequest(r, "GET", "/api/v1/partners?keyword=Beijing", token, nil)
|
|
resp := parseResponse(w)
|
|
assert.Equal(t, float64(1), resp["total"].(float64))
|
|
|
|
// 搜索不存在的关键词
|
|
w = makeRequest(r, "GET", "/api/v1/partners?keyword=Nonexistent", token, nil)
|
|
resp = parseResponse(w)
|
|
assert.Equal(t, float64(0), resp["total"].(float64))
|
|
}
|
|
|
|
// 拼音/联系人搜索(原型口径:名称 / 拼音 / 联系人):Create 时自动生成拼音列
|
|
func TestPartnerHandler_List_PinyinAndContactSearch(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT006B")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "茅台华东总代", "type": "supplier", "contact": "张伟",
|
|
})
|
|
makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "鼎丰超市", "type": "customer", "contact": "王芳",
|
|
})
|
|
|
|
for kw, want := range map[string]float64{
|
|
"maotai": 1, // 全拼
|
|
"mthdzd": 1, // 首字母
|
|
"张伟": 1, // 联系人
|
|
"dingfeng": 1,
|
|
"buxiang": 0,
|
|
} {
|
|
w := makeRequest(r, "GET", "/api/v1/partners?keyword="+kw, token, nil)
|
|
assert.Equal(t, want, parseResponse(w)["total"].(float64), "keyword=%s", kw)
|
|
}
|
|
}
|
|
|
|
// 下拉需一次取全部:请求大页(>默认 20)应返回全部,而非被 ValidatePageSize
|
|
// 回退到默认 20(超上限即返回默认值的历史坑)。
|
|
func TestPartnerHandler_List_LargePageSizeReturnsAll(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT007")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
for i := 0; i < 25; i++ {
|
|
makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": fmt.Sprintf("Partner %02d", i),
|
|
"type": "supplier",
|
|
})
|
|
}
|
|
|
|
w := makeRequest(r, "GET", "/api/v1/partners?page_size=1000", token, nil)
|
|
resp := parseResponse(w)
|
|
assert.Equal(t, float64(25), resp["total"].(float64))
|
|
assert.Equal(t, float64(1000), resp["page_size"].(float64))
|
|
assert.Len(t, resp["data"].([]interface{}), 25)
|
|
}
|
|
|
|
func TestPartnerHandler_ShopIDFromToken(t *testing.T) {
|
|
db := testutil.SetupTestDB()
|
|
shop := testutil.CreateTestShop(db, "PT007")
|
|
user := testutil.CreateTestUser(db, shop.ID, "admin", "pass", "admin")
|
|
token := getAuthToken(user.ID, shop.ID, "admin")
|
|
r := setupProtectedRouter(db)
|
|
|
|
// 在请求体中尝试传入不同的 shop_id
|
|
w := makeRequest(r, "POST", "/api/v1/partners", token, map[string]interface{}{
|
|
"name": "Test Partner",
|
|
"type": "supplier",
|
|
"shop_id": 9999,
|
|
})
|
|
require.Equal(t, http.StatusCreated, w.Code)
|
|
data := parseResponse(w)["data"].(map[string]interface{})
|
|
|
|
// shop_id 应该来自 token
|
|
createdShopID := uint64(data["shop_id"].(float64))
|
|
assert.Equal(t, shop.ID, createdShopID)
|
|
}
|