81 lines
3.2 KiB
Go
81 lines
3.2 KiB
Go
package application
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"git.cin.su/m/Assistant/contracts/capability"
|
|
"git.cin.su/m/Assistant/contracts/work"
|
|
)
|
|
|
|
type callbackGraphStore struct {
|
|
graph work.WorkGraph
|
|
saves int
|
|
}
|
|
|
|
func (s *callbackGraphStore) SaveWorkGraph(_ context.Context, graph work.WorkGraph) error {
|
|
s.graph = graph
|
|
s.saves++
|
|
return nil
|
|
}
|
|
func (s *callbackGraphStore) LoadWorkGraph(context.Context, string, int64) (work.WorkGraph, error) {
|
|
return s.graph, nil
|
|
}
|
|
func (s *callbackGraphStore) SaveDispatchIntent(context.Context, work.AgentDispatchIntent) error {
|
|
return nil
|
|
}
|
|
func (s *callbackGraphStore) EnqueueDispatch(context.Context, work.AgentDispatchQueueItem) error {
|
|
return nil
|
|
}
|
|
|
|
type callbackSink struct {
|
|
evaluation capability.TaskEvaluation
|
|
err error
|
|
}
|
|
|
|
func (s *callbackSink) SaveTaskEvaluation(_ context.Context, value capability.TaskEvaluation) error {
|
|
s.evaluation = value
|
|
return s.err
|
|
}
|
|
|
|
func callbackRequest() (work.WorkGraph, work.TaskEvaluationEvidenceCallback) {
|
|
graph := work.WorkGraph{ID: "graph-1", Revision: 1, State: work.GraphRunning, Budget: work.GraphBudget{MaxParallel: 1, MaxAttempts: 1}, Nodes: []work.GraphNode{{ID: "node-1", DispatchID: "dispatch-1", IdempotencyKey: "idem-1", State: work.GraphNodeRunning, Purpose: work.GraphNodePurposeTaskEvaluation, TaskProfileID: "profile-1", BindingID: "binding-1"}}}
|
|
evaluation := capability.TaskEvaluation{ID: "evaluation-1", ProfileID: "profile-1", BindingID: "binding-1", BindingRevision: 2, CapabilityID: "capability-1", Result: capability.EvaluationSupported}
|
|
return graph, work.TaskEvaluationEvidenceCallback{GraphID: "graph-1", Revision: 1, NodeID: "node-1", Evaluation: evaluation, Succeeded: true}
|
|
}
|
|
|
|
func TestCompleteTaskEvaluationPersistsEvidenceBeforeGraphCompletion(t *testing.T) {
|
|
graph, callback := callbackRequest()
|
|
store := &callbackGraphStore{graph: graph}
|
|
sink := &callbackSink{}
|
|
completed, err := CompleteTaskEvaluationNode(context.Background(), store, sink, callback)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if sink.evaluation.ID != "evaluation-1" || store.saves != 1 || completed.State != work.GraphCompleted || completed.Nodes[0].State != work.GraphNodeSuccess {
|
|
t.Fatalf("callback did not complete in order: evaluation=%+v saves=%d graph=%+v", sink.evaluation, store.saves, completed)
|
|
}
|
|
}
|
|
|
|
func TestCompleteTaskEvaluationLeavesNodeOpenWhenCapabilityRejectsEvidence(t *testing.T) {
|
|
graph, callback := callbackRequest()
|
|
store := &callbackGraphStore{graph: graph}
|
|
sink := &callbackSink{err: errors.New("capability unavailable")}
|
|
if _, err := CompleteTaskEvaluationNode(context.Background(), store, sink, callback); err == nil {
|
|
t.Fatal("expected capability failure")
|
|
}
|
|
if store.saves != 0 {
|
|
t.Fatalf("graph was advanced after capability failure: saves=%d", store.saves)
|
|
}
|
|
}
|
|
|
|
func TestCompleteTaskEvaluationRejectsLineageMismatch(t *testing.T) {
|
|
graph, callback := callbackRequest()
|
|
callback.Evaluation.ProfileID = "profile-other"
|
|
store := &callbackGraphStore{graph: graph}
|
|
if _, err := CompleteTaskEvaluationNode(context.Background(), store, &callbackSink{}, callback); !errors.Is(err, ErrTaskEvaluationCallbackMismatch) {
|
|
t.Fatalf("expected lineage rejection, got %v", err)
|
|
}
|
|
}
|