117 lines
4.1 KiB
Go
117 lines
4.1 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"yingce/backend/internal/model"
|
|
)
|
|
|
|
type taskRouteExecutionPortStub struct {
|
|
processCalls []string
|
|
dispatchCalls []string
|
|
finished []string
|
|
logs []string
|
|
nextAttempts map[string]*model.RouteAttempt
|
|
nextErrors map[string]error
|
|
processResults map[string]taskRouteExecutionStubResult
|
|
}
|
|
|
|
type taskRouteExecutionStubResult struct {
|
|
result map[string]interface{}
|
|
canvasOps []map[string]interface{}
|
|
err error
|
|
}
|
|
|
|
func (p *taskRouteExecutionPortStub) markRouteAttemptDispatching(attempt *model.RouteAttempt) error {
|
|
p.dispatchCalls = append(p.dispatchCalls, attempt.RouteID)
|
|
return nil
|
|
}
|
|
|
|
func (p *taskRouteExecutionPortStub) processTask(_ context.Context, task model.Task) (map[string]interface{}, []map[string]interface{}, error) {
|
|
routeID := task.RouteID
|
|
p.processCalls = append(p.processCalls, routeID)
|
|
result := p.processResults[routeID]
|
|
return result.result, result.canvasOps, result.err
|
|
}
|
|
|
|
func (p *taskRouteExecutionPortStub) refreshTaskProviderState(*model.Task) error {
|
|
return nil
|
|
}
|
|
|
|
func (p *taskRouteExecutionPortStub) finishTaskRouteAttempt(attempt *model.RouteAttempt, _ *model.Task, taskErr error) {
|
|
if taskErr == nil {
|
|
p.finished = append(p.finished, attempt.RouteID+":succeeded")
|
|
return
|
|
}
|
|
p.finished = append(p.finished, attempt.RouteID+":failed")
|
|
}
|
|
|
|
func (p *taskRouteExecutionPortStub) nextRouteAttemptAfterFailure(_ *model.Task, attempt *model.RouteAttempt, _ error) (*model.RouteAttempt, error) {
|
|
if err := p.nextErrors[attempt.RouteID]; err != nil {
|
|
return nil, err
|
|
}
|
|
return p.nextAttempts[attempt.RouteID], nil
|
|
}
|
|
|
|
func (p *taskRouteExecutionPortStub) log(_ string, _ string, _ string, message string, payload string) error {
|
|
p.logs = append(p.logs, message+":"+payload)
|
|
return nil
|
|
}
|
|
|
|
func TestTaskRouteExecutorSwitchesOnlyWhenNextRouteIsAvailable(t *testing.T) {
|
|
first := &model.RouteAttempt{RouteID: "route-a"}
|
|
second := &model.RouteAttempt{RouteID: "route-b"}
|
|
p := &taskRouteExecutionPortStub{
|
|
nextAttempts: map[string]*model.RouteAttempt{"route-a": second},
|
|
nextErrors: map[string]error{},
|
|
processResults: map[string]taskRouteExecutionStubResult{
|
|
"route-a": {err: errors.New("rejected before job")},
|
|
"route-b": {result: map[string]interface{}{"ok": true}, canvasOps: []map[string]interface{}{{"type": "add_node"}}},
|
|
},
|
|
}
|
|
task := &model.Task{ID: "task-1", UserID: "user-1", RouteID: "route-a"}
|
|
|
|
result, err := (&taskRouteExecutor{port: p}).execute(context.Background(), task, first)
|
|
if err != nil {
|
|
t.Fatalf("execute() error = %v", err)
|
|
}
|
|
if !result.providerSucceeded || result.err != nil {
|
|
t.Fatalf("unexpected execution result: %+v", result)
|
|
}
|
|
if len(p.processCalls) != 2 || p.processCalls[0] != "route-a" || p.processCalls[1] != "route-b" {
|
|
t.Fatalf("process calls = %v, want route-a then route-b", p.processCalls)
|
|
}
|
|
if len(p.finished) != 2 || p.finished[0] != "route-a:failed" || p.finished[1] != "route-b:succeeded" {
|
|
t.Fatalf("finished attempts = %v", p.finished)
|
|
}
|
|
if len(p.logs) != 1 || p.logs[0] != "上游未创建任务,切换备用能力路由:route-b" {
|
|
t.Fatalf("route switch logs = %v", p.logs)
|
|
}
|
|
}
|
|
|
|
func TestTaskRouteExecutorStopsWhenRouteSwitchFails(t *testing.T) {
|
|
first := &model.RouteAttempt{RouteID: "route-a"}
|
|
routeError := errors.New("no usable fallback")
|
|
p := &taskRouteExecutionPortStub{
|
|
nextAttempts: map[string]*model.RouteAttempt{},
|
|
nextErrors: map[string]error{"route-a": routeError},
|
|
processResults: map[string]taskRouteExecutionStubResult{
|
|
"route-a": {err: errors.New("rejected before job")},
|
|
},
|
|
}
|
|
task := &model.Task{ID: "task-1", UserID: "user-1", RouteID: "route-a"}
|
|
|
|
result, err := (&taskRouteExecutor{port: p}).execute(context.Background(), task, first)
|
|
if err != nil {
|
|
t.Fatalf("execute() error = %v", err)
|
|
}
|
|
if result.providerSucceeded || !errors.Is(result.err, p.processResults["route-a"].err) {
|
|
t.Fatalf("unexpected execution result: %+v", result)
|
|
}
|
|
if len(p.processCalls) != 1 || len(p.finished) != 1 || len(p.logs) != 1 {
|
|
t.Fatalf("expected one failed route and one warning, calls=%v finished=%v logs=%v", p.processCalls, p.finished, p.logs)
|
|
}
|
|
}
|