Files
go-sip/internal/rpc/authorized_execution_test.go
T

202 lines
8.7 KiB
Go

package rpc
import (
"context"
"errors"
"path/filepath"
"strings"
"testing"
"time"
agentv1 "git.ipao.vip/rogee/go-sip/gen/agent/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
func authorizedMockRequest(now time.Time) *agentv1.ExecuteAuthorizedRequest {
return &agentv1.ExecuteAuthorizedRequest{
SchemaVersion: "agent-authorized-origination.v0.1",
Meta: testMeta("authorized-1", "authorized-key-1", 1),
Binding: &agentv1.ExecutionBinding{
TenantId: "tenant-1", TenantKey: "tenant-original", ExecutionId: "execution-authorized-1",
TaskId: "task-1", TaskItemId: "command-1", TaskRevision: 2,
AgentVersionId: "agent-version-1", RoutePolicyId: "route-1", CallerProfileId: "caller-1",
},
SelectedTrunkId: "trunk-1",
CallerId: "BD93205882",
Callee: "15003164745",
RingTimeoutMs: 12000,
MaxCallDurationMs: 30000,
DialBeforeUnixMs: now.Add(time.Minute).UnixMilli(),
BoundSnapshotSha256: strings.Repeat("a", 64),
}
}
func TestExecuteAuthorizedMockOneShotAndIdentity(t *testing.T) {
now := time.Date(2026, 9, 21, 1, 30, 0, 0, time.UTC)
attempts := 0
server := NewServer(ServerOptions{Mode: "mock", Now: func() time.Time { return now }, MockAuthorizedOriginate: func(_ context.Context, req *agentv1.ExecuteAuthorizedRequest) error {
attempts++
if req.SelectedTrunkId != "trunk-1" || req.CallerId != "BD93205882" || req.MaxCallDurationMs != 30000 {
t.Fatalf("Agent received modified Dispatcher decision: %+v", req)
}
return nil
}})
activateTestServer(t, server)
req := authorizedMockRequest(now)
first, err := server.ExecuteAuthorized(context.Background(), req)
if err != nil || first.GetReceipt().GetResult() != agentv1.ResultCode_RESULT_CODE_APPLIED || first.GetState() != agentv1.ExecutionState_EXECUTION_STATE_TERMINAL || attempts != 1 {
t.Fatalf("first mock execution=%+v attempts=%d err=%v", first, attempts, err)
}
replay, err := server.ExecuteAuthorized(context.Background(), req)
if err != nil || replay.GetReceipt().GetMeta().GetOperationId() != first.GetReceipt().GetMeta().GetOperationId() || attempts != 1 {
t.Fatalf("replay caused duplicate mock dial: %+v attempts=%d err=%v", replay, attempts, err)
}
changed := proto.Clone(req).(*agentv1.ExecuteAuthorizedRequest)
changed.Callee = "15830461047"
conflict, err := server.ExecuteAuthorized(context.Background(), changed)
if err != nil || conflict.GetReceipt().GetResult() != agentv1.ResultCode_RESULT_CODE_CONFLICT || attempts != 1 {
t.Fatalf("same key/different body: %+v attempts=%d err=%v", conflict, attempts, err)
}
newKey := proto.Clone(req).(*agentv1.ExecuteAuthorizedRequest)
newKey.Meta = testMeta("authorized-2", "authorized-key-2", 1)
conflict, err = server.ExecuteAuthorized(context.Background(), newKey)
if err != nil || conflict.GetReceipt().GetResult() != agentv1.ResultCode_RESULT_CODE_CONFLICT || attempts != 1 {
t.Fatalf("same execution/new operation: %+v attempts=%d err=%v", conflict, attempts, err)
}
}
func TestExecuteAuthorizedRejectsExpiredAndInvalidMock(t *testing.T) {
now := time.Date(2026, 9, 21, 1, 30, 0, 0, time.UTC)
attempts := 0
server := NewServer(ServerOptions{Mode: "mock", Now: func() time.Time { return now }, MockAuthorizedOriginate: func(context.Context, *agentv1.ExecuteAuthorizedRequest) error {
attempts++
return nil
}})
activateTestServer(t, server)
for _, tc := range []struct {
name string
change func(*agentv1.ExecuteAuthorizedRequest)
}{
{"expired at Agent", func(r *agentv1.ExecuteAuthorizedRequest) { r.DialBeforeUnixMs = now.UnixMilli() }},
{"missing selected trunk", func(r *agentv1.ExecuteAuthorizedRequest) { r.SelectedTrunkId = "" }},
{"invalid snapshot digest", func(r *agentv1.ExecuteAuthorizedRequest) { r.BoundSnapshotSha256 = "bad" }},
{"unknown contract version", func(r *agentv1.ExecuteAuthorizedRequest) { r.SchemaVersion = "other" }},
} {
t.Run(tc.name, func(t *testing.T) {
req := authorizedMockRequest(now)
tc.change(req)
response, err := server.ExecuteAuthorized(context.Background(), req)
if response != nil || status.Code(err) != codes.InvalidArgument && status.Code(err) != codes.FailedPrecondition {
t.Fatalf("invalid decision response=%+v err=%v", response, err)
}
if attempts != 0 {
t.Fatalf("invalid decision reached mock dial %d times", attempts)
}
})
}
}
func TestExecuteAuthorizedMockFailureIsUnknownAndNeverRetried(t *testing.T) {
now := time.Date(2026, 9, 21, 1, 30, 0, 0, time.UTC)
attempts := 0
server := NewServer(ServerOptions{Mode: "mock", Now: func() time.Time { return now }, MockAuthorizedOriginate: func(context.Context, *agentv1.ExecuteAuthorizedRequest) error {
attempts++
return errors.New("mock transport failed")
}})
activateTestServer(t, server)
req := authorizedMockRequest(now)
first, err := server.ExecuteAuthorized(context.Background(), req)
if err != nil || first.GetReceipt().GetResult() != agentv1.ResultCode_RESULT_CODE_UNKNOWN || attempts != 1 {
t.Fatalf("uncertain mock execution=%+v attempts=%d err=%v", first, attempts, err)
}
replay, err := server.ExecuteAuthorized(context.Background(), req)
if err != nil || replay.GetReceipt().GetResult() != agentv1.ResultCode_RESULT_CODE_UNKNOWN || attempts != 1 {
t.Fatalf("uncertain execution redialed: %+v attempts=%d err=%v", replay, attempts, err)
}
}
func TestExecuteAuthorizedJournalPreventsRedialAfterAgentRestart(t *testing.T) {
for _, tc := range []struct {
name string
fail bool
}{
{"mock completed", false},
{"mock outcome unknown", true},
} {
t.Run(tc.name, func(t *testing.T) {
now := time.Date(2026, 9, 21, 1, 30, 0, 0, time.UTC)
path := filepath.Join(t.TempDir(), "agent-session.json")
attempts := 0
start := func(generation uint64, bootID string) *Server {
server := NewServer(ServerOptions{Mode: "mock", Now: func() time.Time { return now }, StatePath: path,
Status: &agentv1.AgentStatus{AgentId: "agent-1", CellId: "cell-1", BootId: bootID},
MockAuthorizedOriginate: func(context.Context, *agentv1.ExecuteAuthorizedRequest) error {
attempts++
if tc.fail {
return errors.New("mock adapter failed")
}
return nil
},
})
meta := testMeta("activate", "", 0)
meta.BootId = bootID
if _, err := server.ActivateAgent(context.Background(), &agentv1.ActivateAgentRequest{
Meta: meta, Binding: &agentv1.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: bootID,
DispatcherEpoch: "epoch-1", SessionGeneration: generation}, ActivationOperationId: "activate",
}); err != nil {
t.Fatal(err)
}
return server
}
first := start(1, "boot-1")
req := authorizedMockRequest(now)
response, err := first.ExecuteAuthorized(context.Background(), req)
if err != nil || response == nil || attempts != 1 {
t.Fatalf("first mock call: response=%+v attempts=%d err=%v", response, attempts, err)
}
terminalObservedAt := now.UnixMilli()
now = now.Add(time.Hour)
recovered := start(2, "boot-2")
replay := proto.Clone(req).(*agentv1.ExecuteAuthorizedRequest)
replay.Meta = testMeta("authorized-new-boot", "authorized-key-new-boot", 2)
replay.Meta.BootId = "boot-2"
retry, err := recovered.ExecuteAuthorized(context.Background(), replay)
if err != nil || retry.GetReceipt().GetResult() == agentv1.ResultCode_RESULT_CODE_APPLIED || attempts != 1 {
t.Fatalf("restart redialed execution: response=%+v attempts=%d err=%v", retry, attempts, err)
}
queryMeta := testMeta("query-recovered", "query-recovered-key", 2)
queryMeta.BootId = "boot-2"
query, err := recovered.QueryExecution(context.Background(), &agentv1.QueryExecutionRequest{Meta: queryMeta, Binding: req.Binding})
if err != nil || query.Snapshot == nil || query.Snapshot.Unknown != tc.fail {
t.Fatalf("recovered state failed to retain uncertainty: %+v err=%v", query, err)
}
if !tc.fail && query.Snapshot.ObservedAtUnixMs != terminalObservedAt {
t.Fatalf("terminal observation drifted after restart: got=%d want=%d", query.Snapshot.ObservedAtUnixMs, terminalObservedAt)
}
})
}
}
func TestExecuteAuthorizedCannotDialOutsideMockOrWithoutAdapter(t *testing.T) {
now := time.Date(2026, 9, 21, 1, 30, 0, 0, time.UTC)
for _, tc := range []struct {
name string
mode string
}{
{"mixed disabled", "mixed"},
{"real disabled", "real"},
{"mock adapter missing", "mock"},
} {
t.Run(tc.name, func(t *testing.T) {
server := NewServer(ServerOptions{Mode: tc.mode, Now: func() time.Time { return now }})
activateTestServer(t, server)
response, err := server.ExecuteAuthorized(context.Background(), authorizedMockRequest(now))
if response != nil || status.Code(err) != codes.FailedPrecondition {
t.Fatalf("unavailable mode/adapter response=%+v err=%v", response, err)
}
})
}
}