package httpapi_test import ( "fmt" "net/http" "strings" "testing" ) // 「小项不存在」在组别 / 通道这一族接口上必须只有一个答案:404「小项不存在」。 // // 改前是四个:组别整表替换(非空表)撞外键报 500;组别整表替换(空表)走到末尾 // bumpEvent 才是 404;通道整表替换报 400「profileId 不存在」(profileId 本身没问题, // 缺的是小项,照着提示去查积分模板只会白费功夫);两个列表接口给 200 空数组。 func TestNonexistentEventIsNotFoundOnCategoryAndChannelEndpoints(t *testing.T) { env := newTestEnv(t) ids := env.seed(t) client := recorderClient(t, env) const missing = 99999 cats := fmt.Sprintf("/api/events/%d/categories", missing) chans := fmt.Sprintf("/api/events/%d/channels", missing) for _, tc := range []struct { label string method string path string body any }{ {"组别列表", http.MethodGet, cats, nil}, {"通道列表", http.MethodGet, chans, nil}, {"组别整表替换(非空表)", http.MethodPut, cats, map[string]any{"categories": []map[string]any{{"name": "男子", "multiplier": 1}}}}, {"组别整表替换(空表)", http.MethodPut, cats, map[string]any{"categories": []map[string]any{}}}, {"通道整表替换(非空表)", http.MethodPut, chans, map[string]any{"channels": []map[string]any{{"scope": "athlete", "basis": "result", "profileId": 1}}}}, {"通道整表替换(空表)", http.MethodPut, chans, map[string]any{"channels": []map[string]any{}}}, } { status, resp := env.do(client, tc.method, tc.path, tc.body, true) if status != http.StatusNotFound || resp.Error == nil || resp.Error.Code != "VALIDATION_FAILED" { t.Fatalf("%s 对不存在的小项应 404,得到 %d %+v", tc.label, status, resp.Error) } if resp.Error.Message != "小项不存在" { t.Fatalf("%s 的 404 文案应是「小项不存在」,得到 %q", tc.label, resp.Error.Message) } } // 对照:真小项上照常可用,免得这条校验把合法请求一起挡掉。 status, resp := env.do(client, http.MethodGet, fmt.Sprintf("/api/events/%d/categories", ids.event), nil, false) if status != http.StatusOK || !resp.OK { t.Fatalf("真小项的组别列表应 200,得到 %d %+v", status, resp.Error) } status, resp = env.do(client, http.MethodPost, "/api/events", map[string]any{ "sportId": ids.sport, "code": "P2A-CTRL", "name": "对照小项", "eventType": "individual", "resultMode": "auto", "resultType": "time", "compare": "asc", "scored": true, }, true) if status != http.StatusOK || !resp.OK { t.Fatalf("建对照小项应 200,得到 %d %+v", status, resp.Error) } ctrl := int64(decodeData[map[string]any](t, resp)["id"].(float64)) status, resp = env.do(client, http.MethodPut, fmt.Sprintf("/api/events/%d/categories", ctrl), map[string]any{ "categories": []map[string]any{{"name": "男子", "multiplier": 1}}, }, true) if status != http.StatusOK || !resp.OK { t.Fatalf("真小项的组别整表替换应 200,得到 %d %+v", status, resp.Error) } status, resp = env.do(client, http.MethodPut, fmt.Sprintf("/api/events/%d/channels", ctrl), map[string]any{ "channels": []map[string]any{{"scope": "athlete", "basis": "result", "profileId": 1}}, }, true) if status != http.StatusOK || !resp.OK { t.Fatalf("真小项的通道整表替换应 200,得到 %d %+v", status, resp.Error) } // 通道整表替换的「profileId 不存在」仍要指对字段:小项存在、模板不存在才是 400 指向 profileId。 status, resp = env.do(client, http.MethodPut, fmt.Sprintf("/api/events/%d/channels", ctrl), map[string]any{ "channels": []map[string]any{{"scope": "athlete", "basis": "result", "profileId": 99999}}, }, true) if status != http.StatusBadRequest || resp.Error == nil || !strings.Contains(resp.Error.Message, "profileId") { t.Fatalf("模板不存在应 400 并点明 profileId,得到 %d %+v", status, resp.Error) } }