- Reject --request payloads with a missing, empty, or non-string processCode; the backend answers a bad processCode with success:true and an empty list, so validate client-side like startTime - Add regression cases to keep changed-code coverage at 100%
292 lines
12 KiB
Go
292 lines
12 KiB
Go
package helpers
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func executeOACommand(t *testing.T, caller *scriptedToolCaller, args ...string) error {
|
|
t.Helper()
|
|
previous := deps
|
|
previousArgs := os.Args
|
|
os.Args = []string{"dws", "oa"}
|
|
InitDeps(caller)
|
|
deps.Out.w = io.Discard
|
|
deps.Out.errW = io.Discard
|
|
t.Cleanup(func() {
|
|
deps = previous
|
|
os.Args = previousArgs
|
|
})
|
|
|
|
cmd := newOaCommand()
|
|
cmd.PersistentFlags().Bool("yes", false, "跳过确认")
|
|
cmd.SilenceErrors = true
|
|
cmd.SilenceUsage = true
|
|
cmd.SetOut(io.Discard)
|
|
cmd.SetErr(io.Discard)
|
|
cmd.SetArgs(args)
|
|
return cmd.Execute()
|
|
}
|
|
|
|
func TestCrossPlatformCoverageOARemainingTimeAndRevertBranches(t *testing.T) {
|
|
installScriptedCaller(t, &scriptedToolCaller{dry: true})
|
|
for _, args := range [][]string{
|
|
{"approval", "list-pending", "--start", "bad", "--end", "2030-01-01T10:00:00+08:00"},
|
|
{"approval", "list-pending", "--start", "2030-01-01T09:00:00+08:00", "--end", "bad"},
|
|
{"approval", "list-pending", "--start", "2030-01-01T10:00:00+08:00", "--end", "2030-01-01T09:00:00+08:00"},
|
|
{"approval", "list-initiated", "--process-code", "code", "--start", "bad", "--end", "2030-01-01T10:00:00+08:00"},
|
|
{"approval", "list-initiated", "--process-code", "code", "--start", "2030-01-01T09:00:00+08:00", "--end", "bad"},
|
|
{"approval", "list-initiated", "--process-code", "code", "--start", "2030-01-01T10:00:00+08:00", "--end", "2030-01-01T09:00:00+08:00"},
|
|
} {
|
|
if err := executeFilterCoverage(t, newOaCommand(), args...); err == nil {
|
|
t.Fatalf("args=%v returned nil", args)
|
|
}
|
|
}
|
|
|
|
if err := executeFilterCoverage(t, newOaCommand(),
|
|
"approval", "list-pending",
|
|
"--start", "2030-01-01T09:00:00+08:00", "--end", "2030-01-01T10:00:00+08:00",
|
|
"--page", "2", "--size", "20", "--query", "travel",
|
|
); err != nil {
|
|
t.Fatalf("pending options: %v", err)
|
|
}
|
|
if err := executeFilterCoverage(t, newOaCommand(),
|
|
"approval", "revert-task", "--instance-id", "instance", "--task-id", "12",
|
|
"--target-activity-id", "activity", "--action", "REVERT_FOR_APPROVAL", "--remark", "retry",
|
|
); err != nil {
|
|
t.Fatalf("revert task: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageOAApprovalCreateInstanceMapsInternalSimpleOptions(t *testing.T) {
|
|
caller := &scriptedToolCaller{}
|
|
err := executeOACommand(t, caller,
|
|
"approval", "create-instance",
|
|
"--process-code", "PROC",
|
|
"--form-values", `{"事由":"测试"}`,
|
|
"--originator-user-id", "originator",
|
|
"--approvers", "approver-1,approver-2",
|
|
"--approvers-action-type", "AND",
|
|
"--cc-list", "cc-1,cc-2",
|
|
"--cc-position", "FINISH",
|
|
"--yes",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("create instance: %v", err)
|
|
}
|
|
if caller.server != "oa" || caller.tool != "start_process_instance" {
|
|
t.Fatalf("called %s/%s, want oa/start_process_instance", caller.server, caller.tool)
|
|
}
|
|
request, ok := caller.args["ProcessInstanceCreationPopRequest"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("request payload = %#v", caller.args)
|
|
}
|
|
if got := request["originatorUserId"]; got != "originator" {
|
|
t.Fatalf("originatorUserId = %#v", got)
|
|
}
|
|
approvers, ok := request["approvers"].([]map[string]any)
|
|
if !ok || len(approvers) != 1 || approvers[0]["actionType"] != "AND" {
|
|
t.Fatalf("approvers = %#v", request["approvers"])
|
|
}
|
|
if got := approvers[0]["userIds"]; len(got.([]string)) != 2 || got.([]string)[0] != "approver-1" || got.([]string)[1] != "approver-2" {
|
|
t.Fatalf("approver userIds = %#v", got)
|
|
}
|
|
if got := request["ccList"]; len(got.([]string)) != 2 || got.([]string)[0] != "cc-1" || got.([]string)[1] != "cc-2" {
|
|
t.Fatalf("ccList = %#v", got)
|
|
}
|
|
if got := request["ccPosition"]; got != "FINISH" {
|
|
t.Fatalf("ccPosition = %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageOAApprovalCreateInstanceRejectsMixedRequestModes(t *testing.T) {
|
|
caller := &scriptedToolCaller{}
|
|
err := executeOACommand(t, caller,
|
|
"approval", "create-instance",
|
|
"--request", `{"processCode":"PROC"}`,
|
|
"--process-code", "PROC",
|
|
"--yes",
|
|
)
|
|
if err == nil {
|
|
t.Fatal("mixed request modes returned nil")
|
|
}
|
|
if caller.calls != 0 {
|
|
t.Fatalf("unexpected MCP call count: %d", caller.calls)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageOAApprovalCreateInstanceRequiresExplicitYes(t *testing.T) {
|
|
caller := &scriptedToolCaller{}
|
|
err := executeOACommand(t, caller,
|
|
"approval", "create-instance",
|
|
"--request", `{"processCode":"PROC"}`,
|
|
)
|
|
if err == nil || !strings.Contains(err.Error(), "--yes") {
|
|
t.Fatalf("create instance without --yes error = %v, want explicit --yes requirement", err)
|
|
}
|
|
if caller.calls != 0 {
|
|
t.Fatalf("create instance without --yes made %d MCP calls", caller.calls)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageOAApprovalListByAdminMapsSimpleOptions(t *testing.T) {
|
|
caller := &scriptedToolCaller{}
|
|
err := executeOACommand(t, caller,
|
|
"approval", "list-by-admin",
|
|
"--process-code", "PROC",
|
|
"--start", "2030-01-01T09:00:00+08:00",
|
|
"--end", "2030-01-01T10:00:00+08:00",
|
|
"--cursor", "5",
|
|
"--limit", "20",
|
|
"--user-ids", "user-1,user-2",
|
|
"--statuses", "RUNNING,COMPLETED",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("list by admin: %v", err)
|
|
}
|
|
if caller.server != "oa" || caller.tool != "get_process_instances_by_admin" {
|
|
t.Fatalf("called %s/%s, want oa/get_process_instances_by_admin", caller.server, caller.tool)
|
|
}
|
|
request, ok := caller.args["ProcessInstanceListQueryRequest"].(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("request payload = %#v", caller.args)
|
|
}
|
|
if got := request["processCode"]; got != "PROC" {
|
|
t.Fatalf("processCode = %#v", got)
|
|
}
|
|
if got := request["cursor"]; got != float64(5) {
|
|
t.Fatalf("cursor = %#v", got)
|
|
}
|
|
if got := request["pageSize"]; got != float64(20) {
|
|
t.Fatalf("pageSize = %#v", got)
|
|
}
|
|
if got := request["startTime"]; got != "2030-01-01 09:00:00" {
|
|
t.Fatalf("startTime = %#v", got)
|
|
}
|
|
if got := request["endTime"]; got != "2030-01-01 10:00:00" {
|
|
t.Fatalf("endTime = %#v", got)
|
|
}
|
|
if got := request["userIds"]; len(got.([]string)) != 2 || got.([]string)[0] != "user-1" || got.([]string)[1] != "user-2" {
|
|
t.Fatalf("userIds = %#v", got)
|
|
}
|
|
if got := request["statuses"]; len(got.([]string)) != 2 || got.([]string)[0] != "RUNNING" || got.([]string)[1] != "COMPLETED" {
|
|
t.Fatalf("statuses = %#v", got)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageOAApprovalNewCommandValidationAndRequestModes(t *testing.T) {
|
|
validCases := []struct {
|
|
name string
|
|
args []string
|
|
tool string
|
|
}{
|
|
{
|
|
name: "form schema",
|
|
args: []string{"approval", "form-schema", "--process-code", "PROC"},
|
|
tool: "get_process_schema",
|
|
},
|
|
{
|
|
name: "forecast simple mode",
|
|
args: []string{"approval", "forecast-process", "--process-code", "PROC", "--dept-id", "-1", "--form-values", `{"金额":"100"}`},
|
|
tool: "forecast_process",
|
|
},
|
|
{
|
|
name: "forecast request mode",
|
|
args: []string{"approval", "forecast-process", "--request", `{"processCode":"PROC"}`},
|
|
tool: "forecast_process",
|
|
},
|
|
{
|
|
name: "create request mode",
|
|
args: []string{"approval", "create-instance", "--request", `{"processCode":"PROC"}`, "--yes"},
|
|
tool: "start_process_instance",
|
|
},
|
|
{
|
|
name: "list-by-admin simple mode",
|
|
args: []string{"approval", "list-by-admin", "--process-code", "PROC", "--start", "2030-01-01T09:00:00+08:00"},
|
|
tool: "get_process_instances_by_admin",
|
|
},
|
|
{
|
|
name: "list-by-admin request mode",
|
|
args: []string{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","cursor":0,"pageSize":20}`},
|
|
tool: "get_process_instances_by_admin",
|
|
},
|
|
{
|
|
name: "list-by-admin request mode without pageSize",
|
|
args: []string{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","cursor":0}`},
|
|
tool: "get_process_instances_by_admin",
|
|
},
|
|
{
|
|
name: "list-by-admin request mode with endTime",
|
|
args: []string{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","endTime":"2030-01-01 23:59:59","cursor":0,"pageSize":20}`},
|
|
tool: "get_process_instances_by_admin",
|
|
},
|
|
}
|
|
for _, tc := range validCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
caller := &scriptedToolCaller{}
|
|
if err := executeOACommand(t, caller, tc.args...); err != nil {
|
|
t.Fatalf("execute %v: %v", tc.args, err)
|
|
}
|
|
if caller.tool != tc.tool || caller.calls != 1 {
|
|
t.Fatalf("called tool=%q calls=%d, want %q once", caller.tool, caller.calls, tc.tool)
|
|
}
|
|
})
|
|
}
|
|
|
|
invalidCases := [][]string{
|
|
{"approval", "form-schema"},
|
|
{"approval", "forecast-process"},
|
|
{"approval", "forecast-process", "--request", `{"processCode":"PROC"}`, "--process-code", "PROC"},
|
|
{"approval", "forecast-process", "--request", "{"},
|
|
{"approval", "forecast-process", "--request", "null"},
|
|
{"approval", "forecast-process", "--request", "{} {}"},
|
|
{"approval", "forecast-process", "--process-code", "PROC", "--dept-id", "bad", "--form-values", `{"金额":"100"}`},
|
|
{"approval", "forecast-process", "--process-code", "PROC", "--dept-id", "-1", "--form-values", "["},
|
|
{"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`},
|
|
{"approval", "create-instance", "--yes"},
|
|
{"approval", "create-instance", "--request", "{", "--yes"},
|
|
{"approval", "create-instance", "--request", "null", "--yes"},
|
|
{"approval", "create-instance", "--request", "{} {}", "--yes"},
|
|
{"approval", "create-instance", "--process-code", "PROC", "--form-values", "[", "--yes"},
|
|
{"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`, "--dept-id", "bad", "--yes"},
|
|
{"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`, "--approvers", "u", "--approvers-action-type", "bad", "--yes"},
|
|
{"approval", "create-instance", "--process-code", "PROC", "--form-values", `{}`, "--cc-list", "u", "--cc-position", "bad", "--yes"},
|
|
{"approval", "list-by-admin"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", "bad"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", "2030-01-01T10:00:00+08:00", "--end", "bad"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", "2030-01-01T10:00:00+08:00", "--end", "2030-01-01T09:00:00+08:00"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", "2030-01-01T09:00:00+08:00", "--cursor", "bad"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", "2030-01-01T09:00:00+08:00", "--limit", "bad"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", "2030-01-01T09:00:00+08:00", "--limit", "21"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", "2030-01-01T09:00:00+08:00", "--limit", "0"},
|
|
{"approval", "list-by-admin", "--process-code", "PROC", "--start", ""},
|
|
{"approval", "list-by-admin", "--request", "{"},
|
|
{"approval", "list-by-admin", "--request", "null"},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC"}`, "--process-code", "PROC"},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","cursor":0,"pageSize":21}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","cursor":0,"pageSize":"20"}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":1893459600000,"cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01","cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 10:00:00","endTime":"2030-01-01 09:00:00","cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","endTime":1893463200000,"cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","endTime":"NOT-A-TIME","cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","endTime":"2030-01-01","cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"PROC","startTime":"2030-01-01 09:00:00","endTime":"2030-01-01 09:00:00","cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"startTime":"2030-01-01 09:00:00","cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":"","startTime":"2030-01-01 09:00:00","cursor":0,"pageSize":20}`},
|
|
{"approval", "list-by-admin", "--request", `{"processCode":123,"startTime":"2030-01-01 09:00:00","cursor":0,"pageSize":20}`},
|
|
}
|
|
for _, args := range invalidCases {
|
|
caller := &scriptedToolCaller{}
|
|
if err := executeOACommand(t, caller, args...); err == nil {
|
|
t.Fatalf("invalid args %v returned nil", args)
|
|
}
|
|
if caller.calls != 0 {
|
|
t.Fatalf("invalid args %v made %d MCP calls", args, caller.calls)
|
|
}
|
|
}
|
|
}
|