test(projectworkflow): reject create-time active bypass

Creating with initial_workflow_key must stay in planning with an empty
active key while the catalog still mounts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-14 07:55:51 +00:00
co-authored by Cursor
parent 295ad89860
commit 2e586c99b8
2 changed files with 31 additions and 32 deletions
+18
View File
@@ -58,6 +58,24 @@ func TestProjectWorkflowService_CreateAndCreatorACL(t *testing.T) {
t.Errorf("expected initial version 1, got %d", proj.WorkflowSelectionVersion)
}
keyed, err := svc.CreateProject(ctx, creator, CreateProjectInput{
Name: "带初始工作流键的规划项目",
InitialWorkflowKey: "main",
})
if err != nil {
t.Fatalf("CreateProject with initial key: %v", err)
}
if keyed.LifecycleStatus != "planning" || keyed.ActiveWorkflowKey != "" {
t.Fatalf("initial_workflow_key must not activate, got lifecycle=%s key=%s", keyed.LifecycleStatus, keyed.ActiveWorkflowKey)
}
keyedWFs, err := svc.ListProjectWorkflows(ctx, keyed.ID)
if err != nil {
t.Fatalf("list keyed workflows: %v", err)
}
if len(keyedWFs) != 3 {
t.Fatalf("catalog must still mount after keyed create, got %d", len(keyedWFs))
}
// 3. 验证自动挂载的工作流清单
wfs, err := svc.ListProjectWorkflows(ctx, proj.ID)
if err != nil {
+13 -32
View File
@@ -98,7 +98,7 @@ func TestProjectWorkflowAPI_Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusConflict, recSwitchBlocked.Code)
assert.Contains(t, recSwitchBlocked.Body.String(), "LEGACY_SWITCH_BLOCKED")
// 6. 显式 initial_workflow_key 的 legacy 项目:ACL + 切换
// 6. 显式 initial_workflow_key 仍停留 planning,目录已挂,不可直切 active
bodyLegacyCreate, _ := json.Marshal(projectworkflow.CreateProjectInput{
Name: "Legacy 切换测试项目",
InitialWorkflowKey: "main",
@@ -110,47 +110,28 @@ func TestProjectWorkflowAPI_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, recLegacyCreate.Code)
var legacyProj projectworkflow.Project
require.NoError(t, json.Unmarshal(recLegacyCreate.Body.Bytes(), &legacyProj))
assert.Equal(t, "main", legacyProj.ActiveWorkflowKey)
assert.Equal(t, "planning", legacyProj.LifecycleStatus)
assert.Equal(t, "", legacyProj.ActiveWorkflowKey)
bodySwitchOther, _ := json.Marshal(projectworkflow.ActivateWorkflowInput{
WorkflowKey: "product_manager",
ExpectedVersion: 1,
Reason: "非法切换",
})
reqSwitchOther := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/projects/%d/active-workflow", legacyProj.ID), bytes.NewReader(bodySwitchOther))
withUserIDAuth(reqSwitchOther, loginTestUser(t, handler, "lizhuguan", "123456"))
recSwitchOther := httptest.NewRecorder()
handler.ServeHTTP(recSwitchOther, reqSwitchOther)
assert.Equal(t, http.StatusForbidden, recSwitchOther.Code)
reqKeyedWFs := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/workflows", legacyProj.ID), nil)
recKeyedWFs := httptest.NewRecorder()
handler.ServeHTTP(recKeyedWFs, reqKeyedWFs)
require.Equal(t, http.StatusOK, recKeyedWFs.Code)
var keyedWFs []*projectworkflow.ProjectWorkflow
require.NoError(t, json.Unmarshal(recKeyedWFs.Body.Bytes(), &keyedWFs))
assert.Equal(t, 3, len(keyedWFs))
bodySwitchCreator, _ := json.Marshal(projectworkflow.ActivateWorkflowInput{
WorkflowKey: "product_manager",
ExpectedVersion: 1,
Reason: "切换至智能产品经理编制需求",
Reason: "planning 项目不得靠创建旁路直切",
})
reqSwitchCreator := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/projects/%d/active-workflow", legacyProj.ID), bytes.NewReader(bodySwitchCreator))
withUserIDAuth(reqSwitchCreator, loginTestUser(t, handler, "zhanggong", "123456"))
recSwitchCreator := httptest.NewRecorder()
handler.ServeHTTP(recSwitchCreator, reqSwitchCreator)
require.Equal(t, http.StatusOK, recSwitchCreator.Code)
var switchResult projectworkflow.ActivateWorkflowResult
err = json.Unmarshal(recSwitchCreator.Body.Bytes(), &switchResult)
require.NoError(t, err)
assert.Equal(t, "product_manager", switchResult.ActiveWorkflowKey)
assert.Equal(t, 2, switchResult.Version)
// 7. 再次用旧版本 1 切换 -> 409 Conflict
bodySwitchConflict, _ := json.Marshal(projectworkflow.ActivateWorkflowInput{
WorkflowKey: "test_manager",
ExpectedVersion: 1,
Reason: "并发冲突测试",
})
reqSwitchConflict := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/projects/%d/active-workflow", legacyProj.ID), bytes.NewReader(bodySwitchConflict))
withUserIDAuth(reqSwitchConflict, loginTestUser(t, handler, "zhanggong", "123456"))
recSwitchConflict := httptest.NewRecorder()
handler.ServeHTTP(recSwitchConflict, reqSwitchConflict)
assert.Equal(t, http.StatusConflict, recSwitchConflict.Code)
assert.Equal(t, http.StatusConflict, recSwitchCreator.Code)
assert.Contains(t, recSwitchCreator.Body.String(), "LEGACY_SWITCH_BLOCKED")
// 8. 查询全局工作流规范目录 -> 200 OK
reqDefs := httptest.NewRequest(http.MethodGet, "/api/v1/workflows", nil)