133 lines
5.2 KiB
Go
133 lines
5.2 KiB
Go
package dispatcher
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
|
|
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 TestAgentCoordinatorRenewsTenMinuteSessionWithoutAdoptingAnotherBoot(t *testing.T) {
|
|
clock := time.Date(2026, 9, 18, 1, 0, 0, 0, time.UTC)
|
|
listener := bufconn.Listen(1024 * 1024)
|
|
agent := rpcserver.NewServer(rpcserver.ServerOptions{
|
|
ApprovedDispatcherID: approvedTestDispatcherID,
|
|
Now: func() time.Time { return clock },
|
|
Status: &agentpb.AgentStatus{
|
|
AgentId: "agent-1", CellId: "cell-1", BootId: "boot-1", ProtocolVersion: "agent.v1",
|
|
},
|
|
})
|
|
server := grpc.NewServer()
|
|
agentpb.RegisterAgentControlServiceServer(server, agent)
|
|
go func() { _ = server.Serve(listener) }()
|
|
defer server.Stop()
|
|
conn, err := grpc.NewClient("passthrough:///bufnet", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return listener.Dial() }), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
coordinator := NewAgentCoordinator(func() time.Time { return clock })
|
|
client := agentpb.NewAgentControlServiceClient(conn)
|
|
if err := coordinator.Register("agent-1", client); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
status, err := coordinator.Probe(context.Background(), "agent-1", "cell-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
initial, err := coordinator.Activate(context.Background(), approvedTestDispatcherID, "agent-1", "cell-1", status.BootId, "epoch-1", 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
oldMeta, err := coordinator.ApprovedMeta(context.Background(), "agent-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
clock = clock.Add(6 * time.Minute)
|
|
renewed, err := coordinator.Renew(context.Background(), approvedTestDispatcherID, initial)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if renewed.SessionGeneration != initial.SessionGeneration+1 || renewed.ExpiresAtUnixMs <= initial.ExpiresAtUnixMs || renewed.BootID != initial.BootID || renewed.DispatcherEpoch != initial.DispatcherEpoch {
|
|
t.Fatalf("renewal did not advance only the existing Agent session: first=%+v renewed=%+v", initial, renewed)
|
|
}
|
|
if err := coordinator.AuthorizeInboundMeta(oldMeta); err == nil {
|
|
t.Fatal("fenced pre-renewal metadata authorized an Agent report")
|
|
}
|
|
if _, err := agent.ActiveSessionMeta(); err != nil {
|
|
t.Fatalf("Agent lost its renewed session: %v", err)
|
|
}
|
|
|
|
clock = clock.Add(5 * time.Minute) // Beyond the original ten-minute expiry.
|
|
if _, err := coordinator.ApprovedMeta(context.Background(), "agent-1"); err != nil {
|
|
t.Fatalf("Agent admission expired despite timely renewal: %v", err)
|
|
}
|
|
if _, err := agent.ActiveSessionMeta(); err != nil {
|
|
t.Fatalf("Agent reports expired despite timely renewal: %v", err)
|
|
}
|
|
|
|
changed := &changedBootClient{AgentControlServiceClient: client}
|
|
if err := coordinator.Register("agent-1", changed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := coordinator.Renew(context.Background(), approvedTestDispatcherID, renewed); err == nil {
|
|
t.Fatal("a new Agent boot was silently adopted during renewal")
|
|
}
|
|
if changed.activated {
|
|
t.Fatal("a changed Agent boot received an activation")
|
|
}
|
|
advanced := &clockAdvancingClient{AgentControlServiceClient: client, advance: func() { clock = clock.Add(6 * time.Minute) }}
|
|
if err := coordinator.Register("agent-1", advanced); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := coordinator.Renew(context.Background(), approvedTestDispatcherID, renewed); err == nil {
|
|
t.Fatal("a session that expired during status probing was renewed")
|
|
}
|
|
if advanced.activated {
|
|
t.Fatal("an expired session issued a fresh activation after status probing")
|
|
}
|
|
if _, err := coordinator.Renew(context.Background(), approvedTestDispatcherID, renewed); err == nil {
|
|
t.Fatal("an already expired session was renewed without explicit restart")
|
|
}
|
|
}
|
|
|
|
type changedBootClient struct {
|
|
agentpb.AgentControlServiceClient
|
|
activated bool
|
|
}
|
|
|
|
func (client *changedBootClient) GetAgentStatus(context.Context, *agentpb.GetAgentStatusRequest, ...grpc.CallOption) (*agentpb.GetAgentStatusResponse, error) {
|
|
return &agentpb.GetAgentStatusResponse{Status: &agentpb.AgentStatus{
|
|
AgentId: "agent-1", CellId: "cell-1", BootId: "different-boot", ProtocolVersion: "agent.v1",
|
|
}}, nil
|
|
}
|
|
|
|
func (client *changedBootClient) ActivateAgent(ctx context.Context, request *agentpb.ActivateAgentRequest, options ...grpc.CallOption) (*agentpb.ActivateAgentResponse, error) {
|
|
client.activated = true
|
|
return client.AgentControlServiceClient.ActivateAgent(ctx, request, options...)
|
|
}
|
|
|
|
type clockAdvancingClient struct {
|
|
agentpb.AgentControlServiceClient
|
|
advance func()
|
|
activated bool
|
|
}
|
|
|
|
func (client *clockAdvancingClient) GetAgentStatus(ctx context.Context, request *agentpb.GetAgentStatusRequest, options ...grpc.CallOption) (*agentpb.GetAgentStatusResponse, error) {
|
|
client.advance()
|
|
return client.AgentControlServiceClient.GetAgentStatus(ctx, request, options...)
|
|
}
|
|
|
|
func (client *clockAdvancingClient) ActivateAgent(ctx context.Context, request *agentpb.ActivateAgentRequest, options ...grpc.CallOption) (*agentpb.ActivateAgentResponse, error) {
|
|
client.activated = true
|
|
return client.AgentControlServiceClient.ActivateAgent(ctx, request, options...)
|
|
}
|