69 lines
2.9 KiB
Go
69 lines
2.9 KiB
Go
package dispatcher
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
|
|
"git.ipao.vip/rogee/go-sip/internal/session"
|
|
"git.ipao.vip/rogee/go-sip/internal/store"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
type inboundSessionTestClient struct {
|
|
agentpb.AgentControlServiceClient
|
|
}
|
|
|
|
func TestInboundAgentFactRequiresCurrentUnexpiredActivatedSession(t *testing.T) {
|
|
now := time.Date(2026, 9, 21, 2, 0, 0, 0, time.UTC)
|
|
coordinator := NewAgentCoordinator(func() time.Time { return now })
|
|
if err := coordinator.Register("agent-a", &inboundSessionTestClient{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
coordinator.sessions["agent-a"] = AgentSession{
|
|
AgentID: "agent-a", CellID: "cell-a", BootID: "boot-a", DispatcherEpoch: "epoch-a",
|
|
SessionGeneration: 3, ExpiresAtUnixMs: now.Add(time.Minute).UnixMilli(),
|
|
}
|
|
meta := &agentpb.RequestMeta{
|
|
ProtocolVersion: "agent.v1", AgentId: "agent-a", CellId: "cell-a", BootId: "boot-a",
|
|
DispatcherEpoch: "epoch-a", SessionGeneration: 3,
|
|
}
|
|
if err := coordinator.AuthorizeInboundMeta(meta); err != nil {
|
|
t.Fatalf("active Agent was refused: %v", err)
|
|
}
|
|
for _, tc := range []struct {
|
|
name string
|
|
change func(*agentpb.RequestMeta)
|
|
transition bool
|
|
}{
|
|
{"old boot", func(m *agentpb.RequestMeta) { m.BootId = "boot-old" }, false},
|
|
{"old generation", func(m *agentpb.RequestMeta) { m.SessionGeneration = 2 }, true},
|
|
{"next generation before activation response", func(m *agentpb.RequestMeta) { m.SessionGeneration = 4 }, true},
|
|
{"distant generation", func(m *agentpb.RequestMeta) { m.SessionGeneration = 7 }, false},
|
|
{"missing generation", func(m *agentpb.RequestMeta) { m.SessionGeneration = 0 }, false},
|
|
{"other cell", func(m *agentpb.RequestMeta) { m.CellId = "cell-other" }, false},
|
|
{"other epoch", func(m *agentpb.RequestMeta) { m.DispatcherEpoch = "epoch-other" }, false},
|
|
{"unregistered Agent", func(m *agentpb.RequestMeta) { m.AgentId = "agent-other" }, false},
|
|
{"old boot and generation", func(m *agentpb.RequestMeta) { m.BootId = "boot-old"; m.SessionGeneration = 2 }, false},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
changed := proto.Clone(meta).(*agentpb.RequestMeta)
|
|
tc.change(changed)
|
|
err := coordinator.AuthorizeInboundMeta(changed)
|
|
if !errors.Is(err, store.ErrCommandConflict) || errors.Is(err, session.ErrGenerationTransition) != tc.transition {
|
|
t.Fatalf("Agent report refusal classification: got %v, transition=%t", err, tc.transition)
|
|
}
|
|
})
|
|
}
|
|
now = now.Add(time.Minute)
|
|
if err := coordinator.AuthorizeInboundMeta(meta); !errors.Is(err, store.ErrCommandConflict) {
|
|
t.Fatalf("expired Agent report passed: %v", err)
|
|
}
|
|
stale := proto.Clone(meta).(*agentpb.RequestMeta)
|
|
stale.SessionGeneration = 2
|
|
if err := coordinator.AuthorizeInboundMeta(stale); errors.Is(err, session.ErrGenerationTransition) {
|
|
t.Fatalf("expired Agent session misclassified as a transient cutover: %v", err)
|
|
}
|
|
}
|