package rpc import ( "context" "crypto/tls" "crypto/x509" "net" "testing" "time" agentpb "git.ipao.vip/rogee/go-sip/gen/agent" "git.ipao.vip/rogee/go-sip/internal/contract" "git.ipao.vip/rogee/go-sip/internal/testfixture" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/peer" "google.golang.org/grpc/status" "google.golang.org/grpc/test/bufconn" "google.golang.org/protobuf/proto" "reflect" ) func TestPeerCertificateAllowlist(t *testing.T) { _, allowedCert, _ := testCertificate(t, nil, nil, false, []string{"dispatcher.local"}, nil) fingerprint := CertificateFingerprint(allowedCert) server := NewServer(ServerOptions{RequirePeerCertificate: true, PeerCertificateFingerprints: map[string]struct{}{fingerprint: {}}}) allowedContext := peer.NewContext(context.Background(), &peer.Peer{AuthInfo: credentials.TLSInfo{State: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{{allowedCert}}}}}) if err := server.checkPeer(allowedContext, "agent-1"); err != nil { t.Fatal(err) } _, rejectedCert, _ := testCertificate(t, nil, nil, false, []string{"other-dispatcher.local"}, nil) rejectedContext := peer.NewContext(context.Background(), &peer.Peer{AuthInfo: credentials.TLSInfo{State: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{{rejectedCert}}}}}) if err := server.checkPeer(rejectedContext, "agent-1"); status.Code(err) != codes.PermissionDenied { t.Fatalf("got %v, want PermissionDenied", err) } } func TestGetAgentStatusSupportsPreActivationProbe(t *testing.T) { now := time.Unix(100, 0) server := NewServer(ServerOptions{ Now: func() time.Time { return now }, Status: &agentpb.AgentStatus{ AgentId: "agent-1", CellId: "cell-1", BootId: "boot-current", SoftwareVersion: "test", ProtocolVersion: "agent.v1", AdmissionState: agentpb.AdmissionState_ADMISSION_STATE_CLOSED, }, }) response, err := server.GetAgentStatus(context.Background(), &agentpb.GetAgentStatusRequest{ Meta: &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: "probe-request", TraceId: "probe-trace", OperationId: "probe-operation", AgentId: "agent-1", CellId: "cell-1"}, Target: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1"}, }) require.NoError(t, err) require.Equal(t, "boot-current", response.Status.BootId) require.Equal(t, false, response.Status.SessionActive) require.Equal(t, false, response.Status.MtlsAuthenticated) active := activatedServer(now, t) activeResponse, err := active.GetAgentStatus(context.Background(), &agentpb.GetAgentStatusRequest{Meta: testMeta("status-active", "status-active-key", 1), Target: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1"}}) require.NoError(t, err) require.Equal(t, true, activeResponse.Status.SessionActive) } func TestAgentIdentityIsBoundToConfiguredEndpoint(t *testing.T) { server := NewServer(ServerOptions{Status: &agentpb.AgentStatus{AgentId: "agent-1", CellId: "cell-1"}}) _, err := server.GetAgentStatus(context.Background(), &agentpb.GetAgentStatusRequest{ Meta: &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: "wrong-probe", TraceId: "wrong-probe", OperationId: "wrong-probe", AgentId: "agent-2", CellId: "cell-2"}, Target: &agentpb.AgentBinding{AgentId: "agent-2", CellId: "cell-2"}, }) require.Equal(t, codes.PermissionDenied, status.Code(err)) _, err = server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{ Meta: &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: "wrong-activate", TraceId: "wrong-activate", OperationId: "wrong-activate", AgentId: "agent-2", CellId: "cell-2", BootId: "boot-2", DispatcherEpoch: "epoch-2"}, Binding: &agentpb.AgentBinding{AgentId: "agent-2", CellId: "cell-2", ExpectedBootId: "boot-2", DispatcherEpoch: "epoch-2", SessionGeneration: 1}, ActivationOperationId: "wrong-activate", }) require.Equal(t, codes.PermissionDenied, status.Code(err)) } func TestSessionGenerationFencesOlderRequests(t *testing.T) { now := time.Unix(100, 0) server := NewServer(ServerOptions{Now: func() time.Time { return now }}) firstMeta := testMeta("activate-1", "", 0) firstMeta.OperationId = "activate-1" _, err := server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{ Meta: firstMeta, Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-1", DispatcherEpoch: "epoch-1", SessionGeneration: 1}, ActivationOperationId: "activate-1", }) require.NoError(t, err) secondMeta := testMeta("activate-2", "", 0) secondMeta.BootId = "boot-2" secondMeta.OperationId = "activate-2" _, err = server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{ Meta: secondMeta, Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-2", DispatcherEpoch: "epoch-2", SessionGeneration: 2}, ActivationOperationId: "activate-2", }) require.NoError(t, err) _, err = server.GetBootstrap(context.Background(), &agentpb.GetBootstrapRequest{Meta: testMeta("old", "read-old", 1)}) require.Error(t, err) require.Equal(t, codes.Aborted, status.Code(err)) fresh := testMeta("fresh", "read-fresh", 2) fresh.BootId = "boot-2" fresh.DispatcherEpoch = "epoch-2" _, err = server.GetBootstrap(context.Background(), &agentpb.GetBootstrapRequest{Meta: fresh}) require.NoError(t, err) } func TestExecuteIdempotencyAndBinding(t *testing.T) { now := time.Date(2026, 9, 18, 1, 0, 0, 0, time.UTC) server := activatedServer(now, t) raw, err := testfixture.Execute() require.NoError(t, err) envelope, payload, err := contract.DecodeExecute(raw) require.NoError(t, err) meta := testMeta("execute-1", "execute-key", 1) req := &agentpb.ExecuteRequest{Meta: meta, Binding: &agentpb.ExecutionBinding{TenantId: envelope.TenantID, TenantKey: envelope.TenantKey, ExecutionId: payload.ExecutionID, TaskId: payload.TaskID, TaskItemId: payload.TaskItemID, TaskRevision: payload.TaskRevision, AgentVersionId: payload.AgentVersionID}, CallExecuteJson: raw, ConfigSha256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} first, err := server.Execute(context.Background(), req) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_ACCEPTED, first.Receipt.Result) replay, err := server.Execute(context.Background(), req) require.NoError(t, err) require.Equal(t, first.Receipt.Meta.OperationId, replay.Receipt.Meta.OperationId) conflictReq := proto.Clone(req).(*agentpb.ExecuteRequest) conflictReq.ConfigSha256 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" conflict, err := server.Execute(context.Background(), conflictReq) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_CONFLICT, conflict.Receipt.Result) } func TestRealModeRejectsExecutionOutsideCallWindow(t *testing.T) { server := NewServer(ServerOptions{ Mode: "mixed", Now: func() time.Time { return time.Date(2026, 9, 18, 12, 0, 0, 0, time.UTC) }, }) activateTestServer(t, server) response, err := server.GetExecutionPermit(context.Background(), &agentpb.GetExecutionPermitRequest{ Meta: testMeta("permit-window", "permit-window-key", 1), Binding: &agentpb.ExecutionBinding{ExecutionId: "execution-window"}, ResourceReservationId: "reservation-window", }) if response != nil || status.Code(err) != codes.FailedPrecondition { t.Fatalf("response=%+v err=%v code=%s", response, err, status.Code(err)) } } func TestRealModeRejectsMockUploadDataPlane(t *testing.T) { server := NewServer(ServerOptions{Mode: "real"}) response, err := server.RequestUpload(context.Background(), &agentpb.RequestUploadRequest{}) if response != nil || status.Code(err) != codes.Unimplemented { t.Fatalf("response=%+v err=%v code=%s", response, err, status.Code(err)) } } func TestAdmissionAndControlCAS(t *testing.T) { server := activatedServer(time.Date(2026, 9, 18, 1, 0, 0, 0, time.UTC), t) meta := testMeta("admission-1", "admission-key", 1) admission, err := server.SetAdmissionState(context.Background(), &agentpb.SetAdmissionStateRequest{Meta: meta, Target: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1"}, State: agentpb.AdmissionState_ADMISSION_STATE_OPEN}) require.NoError(t, err) require.Equal(t, uint64(1), admission.AppliedAdmissionGeneration) conflict, err := server.SetAdmissionState(context.Background(), &agentpb.SetAdmissionStateRequest{Meta: testMeta("admission-2", "admission-key-2", 1), Target: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1"}, State: agentpb.AdmissionState_ADMISSION_STATE_CLOSED}) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_CONFLICT, conflict.Receipt.Result) raw, err := testfixture.Execute() require.NoError(t, err) envelope, payload, err := contract.DecodeExecute(raw) require.NoError(t, err) executeMeta := testMeta("execute-control", "execute-control-key", 1) _, err = server.Execute(context.Background(), &agentpb.ExecuteRequest{Meta: executeMeta, Binding: &agentpb.ExecutionBinding{TenantId: envelope.TenantID, TenantKey: envelope.TenantKey, ExecutionId: payload.ExecutionID, TaskId: payload.TaskID, TaskItemId: payload.TaskItemID, TaskRevision: payload.TaskRevision, AgentVersionId: payload.AgentVersionID}, CallExecuteJson: raw, ConfigSha256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}) require.NoError(t, err) controlMeta := testMeta("control-1", "control-key", 1) binding := &agentpb.ExecutionBinding{TenantId: envelope.TenantID, TenantKey: envelope.TenantKey, ExecutionId: payload.ExecutionID, TaskId: payload.TaskID, TaskItemId: payload.TaskItemID, TaskRevision: payload.TaskRevision, AgentVersionId: payload.AgentVersionID} pauseRequest := &agentpb.ApplyTaskControlRequest{Meta: controlMeta, Binding: proto.Clone(binding).(*agentpb.ExecutionBinding), Action: agentpb.ControlAction_CONTROL_ACTION_PAUSE, ActiveCallPolicy: agentpb.ActiveCallPolicy_ACTIVE_CALL_POLICY_DRAIN, ExpectedTaskRevision: payload.TaskRevision} foreign := proto.Clone(pauseRequest).(*agentpb.ApplyTaskControlRequest) foreign.Meta = testMeta("foreign-control", "foreign-control-key", 1) foreign.Binding.TenantKey = "different-tenant" foreignResponse, err := server.ApplyTaskControl(context.Background(), foreign) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_CONFLICT, foreignResponse.Receipt.Result) paused, err := server.ApplyTaskControl(context.Background(), pauseRequest) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_APPLIED, paused.Receipt.Result) duplicate, err := server.ApplyTaskControl(context.Background(), pauseRequest) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_APPLIED, duplicate.Receipt.Result) require.Equal(t, paused.AppliedTaskRevision, duplicate.AppliedTaskRevision) blockedPermit, err := server.GetExecutionPermit(context.Background(), &agentpb.GetExecutionPermitRequest{Meta: testMeta("permit-paused", "permit-paused-key", 1), Binding: &agentpb.ExecutionBinding{ExecutionId: payload.ExecutionID, TaskRevision: paused.AppliedTaskRevision}, ResourceReservationId: "paused-reservation"}) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_REJECTED, blockedPermit.Receipt.Result) if blockedPermit.Permit != nil { t.Fatal("paused execution received a permit") } resetAttempt, err := server.Execute(context.Background(), &agentpb.ExecuteRequest{Meta: testMeta("execute-after-pause", "execute-after-pause-key", 1), Binding: &agentpb.ExecutionBinding{TenantId: envelope.TenantID, TenantKey: envelope.TenantKey, ExecutionId: payload.ExecutionID, TaskId: payload.TaskID, TaskItemId: payload.TaskItemID, TaskRevision: payload.TaskRevision, AgentVersionId: payload.AgentVersionID}, CallExecuteJson: raw, ConfigSha256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_REJECTED, resetAttempt.Receipt.Result) retraced := proto.Clone(pauseRequest).(*agentpb.ApplyTaskControlRequest) retraced.Meta.RequestId = "recovered-control-request" retraced.Meta.TraceId = "recovered-control-trace" replayed, err := server.ApplyTaskControl(context.Background(), retraced) require.NoError(t, err) if !proto.Equal(paused, replayed) { t.Fatal("transport metadata changed a control's business identity") } changed := proto.Clone(pauseRequest).(*agentpb.ApplyTaskControlRequest) changed.Reason = "different control content" conflicted, err := server.ApplyTaskControl(context.Background(), changed) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_CONFLICT, conflicted.Receipt.Result) binding.TaskRevision = paused.AppliedTaskRevision stopped, err := server.ApplyTaskControl(context.Background(), &agentpb.ApplyTaskControlRequest{Meta: testMeta("control-2", "control-key-2", 1), Binding: proto.Clone(binding).(*agentpb.ExecutionBinding), Action: agentpb.ControlAction_CONTROL_ACTION_STOP, ActiveCallPolicy: agentpb.ActiveCallPolicy_ACTIVE_CALL_POLICY_HANGUP, ExpectedTaskRevision: paused.AppliedTaskRevision}) require.NoError(t, err) require.Equal(t, agentpb.ExecutionState_EXECUTION_STATE_TERMINAL, stopped.State) original, err := server.ApplyTaskControl(context.Background(), pauseRequest) require.NoError(t, err) require.Equal(t, paused.AppliedTaskRevision, original.AppliedTaskRevision) snapshot, err := server.QueryExecution(context.Background(), &agentpb.QueryExecutionRequest{Meta: testMeta("query-after-stop", "", 1), Binding: pauseRequest.Binding}) require.NoError(t, err) require.Equal(t, stopped.AppliedTaskRevision, snapshot.Snapshot.Binding.TaskRevision) binding.TaskRevision = stopped.AppliedTaskRevision resumed, err := server.ApplyTaskControl(context.Background(), &agentpb.ApplyTaskControlRequest{Meta: testMeta("control-3", "control-key-3", 1), Binding: proto.Clone(binding).(*agentpb.ExecutionBinding), Action: agentpb.ControlAction_CONTROL_ACTION_RESUME, ActiveCallPolicy: agentpb.ActiveCallPolicy_ACTIVE_CALL_POLICY_DRAIN, ExpectedTaskRevision: stopped.AppliedTaskRevision}) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_REJECTED, resumed.Receipt.Result) } func TestFactDeduplication(t *testing.T) { server := activatedServer(time.Unix(100, 0), t) fact := &agentpb.ExecutionFact{FactId: "fact-1", ContentSha256: "digest-a", Binding: &agentpb.ExecutionBinding{ExecutionId: "execution-1"}, Kind: agentpb.FactKind_FACT_KIND_CALL_STATUS} first, err := server.ReportExecutionEvent(context.Background(), &agentpb.ReportExecutionEventRequest{Meta: testMeta("fact-1", "fact-key-1", 1), Fact: fact}) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_ACCEPTED, first.Receipt.Result) replay, err := server.ReportExecutionEvent(context.Background(), &agentpb.ReportExecutionEventRequest{Meta: testMeta("fact-2", "fact-key-2", 1), Fact: fact}) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_ACCEPTED, replay.Receipt.Result) fact.ContentSha256 = "digest-b" conflict, err := server.ReportExecutionEvent(context.Background(), &agentpb.ReportExecutionEventRequest{Meta: testMeta("fact-3", "fact-key-3", 1), Fact: fact}) require.NoError(t, err) require.Equal(t, agentpb.ResultCode_RESULT_CODE_CONFLICT, conflict.Receipt.Result) } func TestGeneratedUnaryServiceWiring(t *testing.T) { server := NewServer(ServerOptions{Now: func() time.Time { return time.Unix(100, 0) }}) listener := bufconn.Listen(1024 * 1024) grpcServer := grpc.NewServer() agentpb.RegisterAgentControlServiceServer(grpcServer, server) go func() { _ = grpcServer.Serve(listener) }() defer grpcServer.Stop() conn, err := grpc.NewClient("passthrough:///bufnet", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return listener.Dial() }), grpc.WithTransportCredentials(insecure.NewCredentials())) require.NoError(t, err) defer conn.Close() client := agentpb.NewAgentControlServiceClient(conn) meta := testMeta("activate-rpc", "", 0) response, err := client.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{Meta: meta, Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-1", DispatcherEpoch: "epoch-1", SessionGeneration: 1}, ActivationOperationId: "activate-rpc"}) require.NoError(t, err) require.Equal(t, agentpb.ActivationState_ACTIVATION_STATE_ACTIVE, response.State) } func activatedServer(now time.Time, t *testing.T) *Server { t.Helper() server := NewServer(ServerOptions{Now: func() time.Time { return now }, UploadPolicy: &agentpb.UploadPolicy{Enabled: true, MaxAssetBytes: 16 << 20}}) meta := testMeta("activate", "", 0) _, err := server.ActivateAgent(context.Background(), &agentpb.ActivateAgentRequest{Meta: meta, Binding: &agentpb.AgentBinding{AgentId: "agent-1", CellId: "cell-1", ExpectedBootId: "boot-1", DispatcherEpoch: "epoch-1", SessionGeneration: 1}, ActivationOperationId: "activate"}) require.NoError(t, err) return server } type testAssertions struct{} var require testAssertions func (testAssertions) NoError(t *testing.T, err error, _ ...any) { t.Helper() if err != nil { t.Fatalf("unexpected error: %v", err) } } func (testAssertions) Error(t *testing.T, err error, _ ...any) { t.Helper() if err == nil { t.Fatal("expected error") } } func (testAssertions) Equal(t *testing.T, expected, actual any, _ ...any) { t.Helper() if !reflect.DeepEqual(expected, actual) { t.Fatalf("expected %#v, got %#v", expected, actual) } } func testMeta(operationID, idempotencyKey string, generation uint64) *agentpb.RequestMeta { return &agentpb.RequestMeta{ProtocolVersion: "agent.v1", RequestId: operationID + "-request", TraceId: "trace-1", OperationId: operationID, IdempotencyKey: idempotencyKey, DispatcherEpoch: "epoch-1", AgentId: "agent-1", CellId: "cell-1", BootId: "boot-1", SessionGeneration: generation} }