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:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user