You've already forked agentic-coding-workflow
feat(project): add workspace_id to issue/requirement with tri-state update
This commit is contained in:
@@ -114,3 +114,102 @@ func TestHandler_SlugTaken_Returns409(t *testing.T) {
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
|
||||
require.Equal(t, "project_slug_taken", body["code"])
|
||||
}
|
||||
|
||||
// decodeBodyMap 解析 200 响应里的 JSON 对象。
|
||||
func decodeBodyMap(t *testing.T, resp *http.Response) map[string]any {
|
||||
t.Helper()
|
||||
t.Cleanup(func() { _ = resp.Body.Close() })
|
||||
var body map[string]any
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
|
||||
return body
|
||||
}
|
||||
|
||||
// TestHandler_Issue_PATCH_WorkspaceID_TriState 验证 PATCH /issues/{n} 的
|
||||
// workspace_id 三态:缺失=不变 / null=解绑 / UUID 字符串=关联 / 非法=400。
|
||||
func TestHandler_Issue_PATCH_WorkspaceID_TriState(t *testing.T) {
|
||||
owner := uuid.New()
|
||||
tok := "tok"
|
||||
_, h := mountFullHandler(t, tok, owner)
|
||||
checkStatus(t, h, "POST", "/api/v1/projects/", tok, map[string]any{"slug": "demo", "name": "Demo"}, http.StatusCreated)
|
||||
wsID := uuid.New()
|
||||
resp := doJSON(t, h, "POST", "/api/v1/projects/demo/issues", tok, map[string]any{"title": "I", "workspace_id": wsID.String()})
|
||||
t.Cleanup(func() { _ = resp.Body.Close() })
|
||||
require.Equal(t, http.StatusCreated, resp.StatusCode)
|
||||
createBody := decodeBodyMap(t, resp)
|
||||
require.Equal(t, wsID.String(), createBody["workspace_id"])
|
||||
|
||||
// 1) 缺失字段:不变
|
||||
resp2 := doJSON(t, h, "PATCH", "/api/v1/projects/demo/issues/1", tok, map[string]any{"title": "I-new"})
|
||||
require.Equal(t, http.StatusOK, resp2.StatusCode)
|
||||
body2 := decodeBodyMap(t, resp2)
|
||||
require.Equal(t, wsID.String(), body2["workspace_id"])
|
||||
|
||||
// 2) UUID 字符串:关联到新 workspace
|
||||
wsB := uuid.New()
|
||||
resp3 := doJSON(t, h, "PATCH", "/api/v1/projects/demo/issues/1", tok, map[string]any{"workspace_id": wsB.String()})
|
||||
require.Equal(t, http.StatusOK, resp3.StatusCode)
|
||||
body3 := decodeBodyMap(t, resp3)
|
||||
require.Equal(t, wsB.String(), body3["workspace_id"])
|
||||
|
||||
// 3) 显式 null:解绑(用 raw JSON 才能传 null,不能用 map[string]any{"workspace_id": nil},
|
||||
// 因为 map nil 也会被序列化成 null,这里直接拼字符串保证准确)
|
||||
rawNull := bytes.NewBufferString(`{"workspace_id": null}`)
|
||||
req := httptest.NewRequest("PATCH", "/api/v1/projects/demo/issues/1", rawNull)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
resp4 := rec.Result()
|
||||
require.Equal(t, http.StatusOK, resp4.StatusCode)
|
||||
body4 := decodeBodyMap(t, resp4)
|
||||
require.Nil(t, body4["workspace_id"])
|
||||
|
||||
// 4) 非法字符串:400
|
||||
resp5 := doJSON(t, h, "PATCH", "/api/v1/projects/demo/issues/1", tok, map[string]any{"workspace_id": "not-a-uuid"})
|
||||
t.Cleanup(func() { _ = resp5.Body.Close() })
|
||||
require.Equal(t, http.StatusBadRequest, resp5.StatusCode)
|
||||
}
|
||||
|
||||
// TestHandler_Requirement_PATCH_WorkspaceID_TriState 验证 PATCH /requirements/{n}
|
||||
// 的 workspace_id 三态。
|
||||
func TestHandler_Requirement_PATCH_WorkspaceID_TriState(t *testing.T) {
|
||||
owner := uuid.New()
|
||||
tok := "tok"
|
||||
_, h := mountFullHandler(t, tok, owner)
|
||||
checkStatus(t, h, "POST", "/api/v1/projects/", tok, map[string]any{"slug": "demo", "name": "Demo"}, http.StatusCreated)
|
||||
wsID := uuid.New()
|
||||
resp := doJSON(t, h, "POST", "/api/v1/projects/demo/requirements", tok, map[string]any{"title": "R", "workspace_id": wsID.String()})
|
||||
require.Equal(t, http.StatusCreated, resp.StatusCode)
|
||||
createBody := decodeBodyMap(t, resp)
|
||||
require.Equal(t, wsID.String(), createBody["workspace_id"])
|
||||
|
||||
// 1) 缺失字段:不变
|
||||
resp2 := doJSON(t, h, "PATCH", "/api/v1/projects/demo/requirements/1", tok, map[string]any{"title": "R-new"})
|
||||
require.Equal(t, http.StatusOK, resp2.StatusCode)
|
||||
body2 := decodeBodyMap(t, resp2)
|
||||
require.Equal(t, wsID.String(), body2["workspace_id"])
|
||||
|
||||
// 2) UUID 字符串:关联到新 workspace
|
||||
wsB := uuid.New()
|
||||
resp3 := doJSON(t, h, "PATCH", "/api/v1/projects/demo/requirements/1", tok, map[string]any{"workspace_id": wsB.String()})
|
||||
require.Equal(t, http.StatusOK, resp3.StatusCode)
|
||||
body3 := decodeBodyMap(t, resp3)
|
||||
require.Equal(t, wsB.String(), body3["workspace_id"])
|
||||
|
||||
// 3) 显式 null:解绑
|
||||
rawNull := bytes.NewBufferString(`{"workspace_id": null}`)
|
||||
req := httptest.NewRequest("PATCH", "/api/v1/projects/demo/requirements/1", rawNull)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, req)
|
||||
resp4 := rec.Result()
|
||||
require.Equal(t, http.StatusOK, resp4.StatusCode)
|
||||
body4 := decodeBodyMap(t, resp4)
|
||||
require.Nil(t, body4["workspace_id"])
|
||||
|
||||
// 4) 非法字符串:400
|
||||
resp5 := doJSON(t, h, "PATCH", "/api/v1/projects/demo/requirements/1", tok, map[string]any{"workspace_id": "not-a-uuid"})
|
||||
t.Cleanup(func() { _ = resp5.Body.Close() })
|
||||
require.Equal(t, http.StatusBadRequest, resp5.StatusCode)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user