Files
rdp-proxy/agents/rap-node-agent/internal/mesh/synthetic_relay_test.go
T
2026-04-28 22:29:50 +03:00

214 lines
6.8 KiB
Go

package mesh
import (
"errors"
"testing"
)
func TestSyntheticRelaySchedulerDequeuesByQoSPriority(t *testing.T) {
scheduler := testRelayScheduler()
telemetry := testRelayEnvelope(SyntheticChannelTelemetry, SyntheticMessageTelemetry, 1)
routeControl := testRelayEnvelope(SyntheticChannelRouteControl, SyntheticMessageRouteHealth, 2)
fabricControl := testRelayEnvelope(SyntheticChannelFabricControl, SyntheticMessageProbe, 3)
if _, err := scheduler.Enqueue(telemetry); err != nil {
t.Fatalf("enqueue telemetry: %v", err)
}
if _, err := scheduler.Enqueue(routeControl); err != nil {
t.Fatalf("enqueue route control: %v", err)
}
if _, err := scheduler.Enqueue(fabricControl); err != nil {
t.Fatalf("enqueue fabric control: %v", err)
}
first, err := scheduler.Dequeue()
if err != nil {
t.Fatalf("dequeue first: %v", err)
}
second, err := scheduler.Dequeue()
if err != nil {
t.Fatalf("dequeue second: %v", err)
}
third, err := scheduler.Dequeue()
if err != nil {
t.Fatalf("dequeue third: %v", err)
}
if first.Channel != SyntheticChannelFabricControl {
t.Fatalf("first channel = %q, want fabric_control", first.Channel)
}
if second.Channel != SyntheticChannelRouteControl {
t.Fatalf("second channel = %q, want route_control", second.Channel)
}
if third.Channel != SyntheticChannelTelemetry {
t.Fatalf("third channel = %q, want telemetry", third.Channel)
}
}
func TestSyntheticRelaySchedulerDropsOldestTelemetryOnly(t *testing.T) {
scheduler := testRelayScheduler()
first := testRelayEnvelope(SyntheticChannelTelemetry, SyntheticMessageTelemetry, 1)
second := testRelayEnvelope(SyntheticChannelTelemetry, SyntheticMessageTelemetry, 2)
if result, err := scheduler.Enqueue(first); err != nil || result.Dropped {
t.Fatalf("enqueue first result=%+v err=%v", result, err)
}
result, err := scheduler.Enqueue(second)
if err != nil {
t.Fatalf("enqueue second: %v", err)
}
if !result.Dropped || result.DroppedSequence != 1 {
t.Fatalf("result = %+v, want dropped sequence 1", result)
}
dequeued, err := scheduler.Dequeue()
if err != nil {
t.Fatalf("dequeue: %v", err)
}
if dequeued.Sequence != 2 {
t.Fatalf("dequeued sequence = %d, want 2", dequeued.Sequence)
}
metrics := scheduler.SnapshotQueueMetrics()
if metrics.Dropped != 1 || metrics.Enqueued != 2 {
t.Fatalf("metrics = %+v, want one drop and two enqueues", metrics)
}
}
func TestSyntheticRelaySchedulerRejectsFullReliableQueue(t *testing.T) {
scheduler := testRelayScheduler()
first := testRelayEnvelope(SyntheticChannelFabricControl, SyntheticMessageProbe, 1)
second := testRelayEnvelope(SyntheticChannelFabricControl, SyntheticMessageProbe, 2)
if _, err := scheduler.Enqueue(first); err != nil {
t.Fatalf("enqueue first: %v", err)
}
_, err := scheduler.Enqueue(second)
if !errors.Is(err, ErrSyntheticRelayQueueFull) {
t.Fatalf("err = %v, want ErrSyntheticRelayQueueFull", err)
}
dequeued, err := scheduler.Dequeue()
if err != nil {
t.Fatalf("dequeue: %v", err)
}
if dequeued.Sequence != 1 {
t.Fatalf("dequeued sequence = %d, want 1", dequeued.Sequence)
}
metrics := scheduler.SnapshotQueueMetrics()
if metrics.Dropped != 0 || metrics.Rejected != 1 {
t.Fatalf("metrics = %+v, want no drop and one rejection", metrics)
}
}
func TestSyntheticRelaySchedulerRejectsInvalidEnvelopes(t *testing.T) {
tests := []struct {
name string
mutate func(*SyntheticEnvelope)
want error
}{
{
name: "wrong cluster",
mutate: func(envelope *SyntheticEnvelope) {
envelope.ClusterID = "cluster-2"
},
want: ErrClusterMismatch,
},
{
name: "wrong node",
mutate: func(envelope *SyntheticEnvelope) {
envelope.To.NodeID = "node-x"
},
want: ErrNodeMismatch,
},
{
name: "unauthorized channel",
mutate: func(envelope *SyntheticEnvelope) {
envelope.Channel = "rdp_render"
},
want: ErrUnauthorizedChannel,
},
{
name: "unsupported message",
mutate: func(envelope *SyntheticEnvelope) {
envelope.MessageType = "rdp.input"
},
want: ErrUnsupportedSyntheticMessage,
},
{
name: "ttl exhausted",
mutate: func(envelope *SyntheticEnvelope) {
envelope.TTL = 0
},
want: ErrTTLExhausted,
},
{
name: "loop detected",
mutate: func(envelope *SyntheticEnvelope) {
envelope.Visited = append(envelope.Visited, "node-r")
},
want: ErrLoopDetected,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
scheduler := testRelayScheduler()
envelope := testRelayEnvelope(SyntheticChannelFabricControl, SyntheticMessageProbe, 1)
tt.mutate(&envelope)
_, err := scheduler.Enqueue(envelope)
if !errors.Is(err, tt.want) {
t.Fatalf("err = %v, want %v", err, tt.want)
}
})
}
}
func TestSyntheticRelaySchedulerDisabledRejects(t *testing.T) {
scheduler := NewSyntheticRelayScheduler(SyntheticRelaySchedulerConfig{
Enabled: false,
Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-r"},
})
_, err := scheduler.Enqueue(testRelayEnvelope(SyntheticChannelFabricControl, SyntheticMessageProbe, 1))
if !errors.Is(err, ErrMeshRuntimeDisabled) {
t.Fatalf("err = %v, want ErrMeshRuntimeDisabled", err)
}
if _, err := scheduler.Dequeue(); !errors.Is(err, ErrMeshRuntimeDisabled) {
t.Fatalf("dequeue err = %v, want ErrMeshRuntimeDisabled", err)
}
}
func TestSyntheticRelaySchedulerQueueDepthSnapshot(t *testing.T) {
scheduler := testRelayScheduler()
if _, err := scheduler.Enqueue(testRelayEnvelope(SyntheticChannelFabricControl, SyntheticMessageProbe, 1)); err != nil {
t.Fatalf("enqueue fabric control: %v", err)
}
if _, err := scheduler.Enqueue(testRelayEnvelope(SyntheticChannelRouteControl, SyntheticMessageRouteHealth, 2)); err != nil {
t.Fatalf("enqueue route control: %v", err)
}
metrics := scheduler.SnapshotQueueMetrics()
if metrics.QueueDepths[SyntheticChannelFabricControl] != 1 {
t.Fatalf("fabric_control depth = %d, want 1", metrics.QueueDepths[SyntheticChannelFabricControl])
}
if metrics.QueueDepths[SyntheticChannelRouteControl] != 1 {
t.Fatalf("route_control depth = %d, want 1", metrics.QueueDepths[SyntheticChannelRouteControl])
}
}
func testRelayScheduler() *SyntheticRelayScheduler {
return NewSyntheticRelayScheduler(SyntheticRelaySchedulerConfig{
Enabled: true,
Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-r"},
QueuePolicies: []SyntheticRelayQueuePolicy{
{Channel: SyntheticChannelFabricControl, Capacity: 1, Droppable: false},
{Channel: SyntheticChannelRouteControl, Capacity: 1, Droppable: false},
{Channel: SyntheticChannelTelemetry, Capacity: 1, Droppable: true},
},
})
}
func testRelayEnvelope(channel string, messageType string, sequence uint64) SyntheticEnvelope {
route := testRoute("route-relay-scheduler", []string{"node-a", "node-r", "node-b"})
envelope := testEnvelope(route, "node-a", "node-r")
envelope.Channel = channel
envelope.MessageType = messageType
envelope.Sequence = sequence
return envelope
}