98 lines
3.8 KiB
Go
98 lines
3.8 KiB
Go
package dispatcher
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.ipao.vip/rogee/go-sip/contracts"
|
|
agentv1 "git.ipao.vip/rogee/go-sip/gen/agent/v1"
|
|
"git.ipao.vip/rogee/go-sip/internal/contract"
|
|
rpcserver "git.ipao.vip/rogee/go-sip/internal/rpc"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
"google.golang.org/grpc/test/bufconn"
|
|
)
|
|
|
|
func TestTwoMockCellsKeepAgentSessionsAndPermitsSeparate(t *testing.T) {
|
|
coordinator := NewAgentCoordinator(func() time.Time { return time.Date(2026, 9, 18, 1, 0, 0, 0, time.UTC) })
|
|
clientA := startMockAgent(t, &agentv1.AgentStatus{AgentId: "agent-a", CellId: "cell-a"})
|
|
clientB := startMockAgent(t, &agentv1.AgentStatus{AgentId: "agent-b", CellId: "cell-b"})
|
|
if err := coordinator.Register("agent-a", clientA); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := coordinator.Register("agent-b", clientB); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := coordinator.Activate(context.Background(), "agent-a", "cell-a", "boot-a", "epoch-1", 1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := coordinator.Activate(context.Background(), "agent-b", "cell-b", "boot-b", "epoch-1", 1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
raw, err := contracts.Read("examples/call.execute.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rawA := mutateExecution(t, raw, "execution-a", "task-a", "item-a")
|
|
rawB := mutateExecution(t, raw, "execution-b", "task-b", "item-b")
|
|
bindingA := executionBinding(t, rawA)
|
|
bindingB := executionBinding(t, rawB)
|
|
resultA, err := coordinator.ExecuteRaw(context.Background(), "agent-a", bindingA, rawA, "reservation-a", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
resultB, err := coordinator.ExecuteRaw(context.Background(), "agent-b", bindingB, rawB, "reservation-b", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resultA.Permit == nil || resultB.Permit == nil || resultA.Permit.PermitId == resultB.Permit.PermitId {
|
|
t.Fatalf("permits are not cell-scoped: A=%v B=%v", resultA.Permit, resultB.Permit)
|
|
}
|
|
}
|
|
|
|
func startMockAgent(t *testing.T, status *agentv1.AgentStatus) agentv1.AgentControlServiceClient {
|
|
t.Helper()
|
|
listener := bufconn.Listen(1024 * 1024)
|
|
server := rpcserver.NewServer(rpcserver.ServerOptions{Now: func() time.Time { return time.Date(2026, 9, 18, 1, 0, 0, 0, time.UTC) }, Status: status})
|
|
grpcServer := grpc.NewServer()
|
|
agentv1.RegisterAgentControlServiceServer(grpcServer, server)
|
|
go func() { _ = grpcServer.Serve(listener) }()
|
|
t.Cleanup(func() { grpcServer.Stop() })
|
|
conn, err := grpc.NewClient("passthrough:///mock-agent", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return listener.Dial() }), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = conn.Close() })
|
|
return agentv1.NewAgentControlServiceClient(conn)
|
|
}
|
|
|
|
func mutateExecution(t *testing.T, raw []byte, executionID, taskID, itemID string) []byte {
|
|
t.Helper()
|
|
var document map[string]any
|
|
if err := json.Unmarshal(raw, &document); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
payload := document["payload"].(map[string]any)
|
|
payload["execution_id"] = executionID
|
|
payload["task_id"] = taskID
|
|
payload["task_item_id"] = itemID
|
|
encoded, err := json.Marshal(document)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return encoded
|
|
}
|
|
|
|
func executionBinding(t *testing.T, raw []byte) *agentv1.ExecutionBinding {
|
|
t.Helper()
|
|
envelope, payload, err := contract.DecodeExecute(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return &agentv1.ExecutionBinding{TenantId: envelope.TenantID, TenantKey: envelope.TenantKey, ExecutionId: payload.ExecutionID, TaskId: payload.TaskID, TaskItemId: payload.TaskItemID, TaskRevision: payload.TaskRevision, AgentVersionId: payload.AgentVersionID, RoutePolicyId: payload.RoutePolicyID, CallerProfileId: payload.CallerProfileID}
|
|
}
|