236 lines
9.6 KiB
Go
236 lines
9.6 KiB
Go
package mesh
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSyntheticRuntimeTestServiceDirectRoute(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-direct", []string{"node-a", "node-b"})
|
|
transport := syntheticTestTransport{nodes: map[string]*SyntheticRuntime{}}
|
|
nodeA := testRuntime("node-a", transport, route)
|
|
nodeB := testRuntime("node-b", transport, route)
|
|
transport.nodes["node-a"] = nodeA
|
|
transport.nodes["node-b"] = nodeB
|
|
|
|
result, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, testServiceRequest("request-direct", "hello"))
|
|
if err != nil {
|
|
t.Fatalf("send test service: %v", err)
|
|
}
|
|
if result.Ack.MessageType != SyntheticMessageTestServiceAck {
|
|
t.Fatalf("MessageType = %q, want %q", result.Ack.MessageType, SyntheticMessageTestServiceAck)
|
|
}
|
|
if result.Response.EchoPayload != "hello" {
|
|
t.Fatalf("EchoPayload = %q, want hello", result.Response.EchoPayload)
|
|
}
|
|
if len(result.Response.Path) != 2 || result.Response.Path[0] != "node-a" || result.Response.Path[1] != "node-b" {
|
|
t.Fatalf("Path = %#v, want node-a -> node-b", result.Response.Path)
|
|
}
|
|
metrics := nodeA.SnapshotMetrics()
|
|
if metrics.TestServiceRequestsSent != 1 || metrics.TestServiceDeliveriesSucceeded != 1 {
|
|
t.Fatalf("metrics = %+v, want one test service success", metrics)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceSingleRelayRoute(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-relay", []string{"node-a", "node-r", "node-b"})
|
|
transport := syntheticTestTransport{nodes: map[string]*SyntheticRuntime{}}
|
|
nodeA := testRuntime("node-a", transport, route)
|
|
nodeR := testRuntime("node-r", transport, route)
|
|
nodeB := testRuntime("node-b", transport, route)
|
|
transport.nodes["node-a"] = nodeA
|
|
transport.nodes["node-r"] = nodeR
|
|
transport.nodes["node-b"] = nodeB
|
|
|
|
result, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, testServiceRequest("request-relay", "relay"))
|
|
if err != nil {
|
|
t.Fatalf("send test service: %v", err)
|
|
}
|
|
if len(result.Response.Path) != 3 || result.Response.Path[0] != "node-a" || result.Response.Path[1] != "node-r" || result.Response.Path[2] != "node-b" {
|
|
t.Fatalf("Path = %#v, want node-a -> node-r -> node-b", result.Response.Path)
|
|
}
|
|
if nodeR.SnapshotMetrics().ProbesForwarded != 1 {
|
|
t.Fatalf("ProbesForwarded = %d, want 1", nodeR.SnapshotMetrics().ProbesForwarded)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceUsesForcedFallback(t *testing.T) {
|
|
preferred := testServiceRoute("route-test-service-preferred", []string{"node-a", "node-r", "node-b"})
|
|
fallback := testServiceRoute("route-test-service-fallback", []string{"node-a", "node-b"})
|
|
transport := syntheticTestTransport{nodes: map[string]*SyntheticRuntime{}}
|
|
nodeA := testRuntime("node-a", transport, preferred, fallback)
|
|
nodeB := testRuntime("node-b", transport, preferred, fallback)
|
|
transport.nodes["node-a"] = nodeA
|
|
transport.nodes["node-b"] = nodeB
|
|
|
|
result, err := nodeA.SendTestServiceWithFallback(
|
|
context.Background(),
|
|
preferred.RouteID,
|
|
[]string{fallback.RouteID},
|
|
SyntheticChannelRouteControl,
|
|
testServiceRequest("request-fallback", "fallback"),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("send test service with fallback: %v", err)
|
|
}
|
|
if !result.FallbackUsed {
|
|
t.Fatal("FallbackUsed = false, want true")
|
|
}
|
|
if result.SelectedRouteID != fallback.RouteID {
|
|
t.Fatalf("SelectedRouteID = %q, want %q", result.SelectedRouteID, fallback.RouteID)
|
|
}
|
|
if result.Response.EchoPayload != "fallback" {
|
|
t.Fatalf("EchoPayload = %q, want fallback", result.Response.EchoPayload)
|
|
}
|
|
metrics := nodeA.SnapshotMetrics()
|
|
if metrics.TestServiceFallbacksUsed != 1 || metrics.TestServiceDeliveriesFailed != 1 || metrics.TestServiceDeliveriesSucceeded != 1 {
|
|
t.Fatalf("metrics = %+v, want fallback success with one preferred failure", metrics)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceRejectsWrongOrganization(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-wrong-org", []string{"node-a", "node-b"})
|
|
nodeA := testRuntime("node-a", syntheticTestTransport{}, route)
|
|
request := testServiceRequest("request-wrong-org", "hello")
|
|
request.OrganizationID = "org-other"
|
|
|
|
_, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, request)
|
|
if !errors.Is(err, ErrSyntheticOrganizationMismatch) {
|
|
t.Fatalf("err = %v, want ErrSyntheticOrganizationMismatch", err)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceRejectsUnsupportedService(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-unsupported", []string{"node-a", "node-b"})
|
|
nodeA := testRuntime("node-a", syntheticTestTransport{}, route)
|
|
request := testServiceRequest("request-unsupported", "hello")
|
|
request.ServiceType = "rdp"
|
|
|
|
_, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, request)
|
|
if !errors.Is(err, ErrUnsupportedSyntheticService) {
|
|
t.Fatalf("err = %v, want ErrUnsupportedSyntheticService", err)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceRejectsOversizedPayload(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-oversized", []string{"node-a", "node-b"})
|
|
nodeA := NewSyntheticRuntime(SyntheticRuntimeConfig{
|
|
Enabled: true,
|
|
Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"},
|
|
Routes: []SyntheticRoute{route},
|
|
MaxTestPayloadBytes: 4,
|
|
})
|
|
|
|
_, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, testServiceRequest("request-oversized", "12345"))
|
|
if !errors.Is(err, ErrSyntheticPayloadTooLarge) {
|
|
t.Fatalf("err = %v, want ErrSyntheticPayloadTooLarge", err)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceRejectsUnauthorizedChannel(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-channel", []string{"node-a", "node-b"})
|
|
nodeA := testRuntime("node-a", syntheticTestTransport{}, route)
|
|
|
|
_, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelFabricControl, testServiceRequest("request-channel", "hello"))
|
|
if !errors.Is(err, ErrUnauthorizedChannel) {
|
|
t.Fatalf("err = %v, want ErrUnauthorizedChannel", err)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceDisabledRejects(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-disabled", []string{"node-a", "node-b"})
|
|
nodeA := NewSyntheticRuntime(SyntheticRuntimeConfig{
|
|
Enabled: false,
|
|
Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"},
|
|
Routes: []SyntheticRoute{route},
|
|
})
|
|
|
|
_, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, testServiceRequest("request-disabled", "hello"))
|
|
if !errors.Is(err, ErrMeshRuntimeDisabled) {
|
|
t.Fatalf("err = %v, want ErrMeshRuntimeDisabled", err)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRelaySchedulerAcceptsTestServiceMessage(t *testing.T) {
|
|
scheduler := testRelayScheduler()
|
|
envelope := testRelayEnvelope(SyntheticChannelRouteControl, SyntheticMessageTestService, 42)
|
|
envelope.Payload = mustMarshalTestServiceRequest(testServiceRequest("request-relay-scheduler", "hello"))
|
|
|
|
if _, err := scheduler.Enqueue(envelope); err != nil {
|
|
t.Fatalf("enqueue test service: %v", err)
|
|
}
|
|
dequeued, err := scheduler.Dequeue()
|
|
if err != nil {
|
|
t.Fatalf("dequeue test service: %v", err)
|
|
}
|
|
if dequeued.MessageType != SyntheticMessageTestService {
|
|
t.Fatalf("MessageType = %q, want %q", dequeued.MessageType, SyntheticMessageTestService)
|
|
}
|
|
}
|
|
|
|
func testServiceRoute(routeID string, hops []string) SyntheticRoute {
|
|
route := testRoute(routeID, hops)
|
|
route.AllowedChannels = []string{SyntheticChannelRouteControl}
|
|
return route
|
|
}
|
|
|
|
func testServiceRequest(requestID string, payload string) SyntheticTestServiceRequest {
|
|
return SyntheticTestServiceRequest{
|
|
RequestID: requestID,
|
|
OrganizationID: SyntheticDefaultTestOrganizationID,
|
|
ServiceType: SyntheticTestServiceType,
|
|
Payload: payload,
|
|
}
|
|
}
|
|
|
|
func mustMarshalTestServiceRequest(request SyntheticTestServiceRequest) []byte {
|
|
payload, err := json.Marshal(request)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return payload
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceRejectsMissingRequestID(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-missing-request", []string{"node-a", "node-b"})
|
|
nodeA := testRuntime("node-a", syntheticTestTransport{}, route)
|
|
request := testServiceRequest("", "hello")
|
|
|
|
_, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, request)
|
|
if !errors.Is(err, ErrSyntheticRequestInvalid) {
|
|
t.Fatalf("err = %v, want ErrSyntheticRequestInvalid", err)
|
|
}
|
|
}
|
|
|
|
func TestSyntheticRuntimeTestServiceAllowsMaxPayloadBoundary(t *testing.T) {
|
|
route := testServiceRoute("route-test-service-max", []string{"node-a", "node-b"})
|
|
transport := syntheticTestTransport{nodes: map[string]*SyntheticRuntime{}}
|
|
nodeA := NewSyntheticRuntime(SyntheticRuntimeConfig{
|
|
Enabled: true,
|
|
Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-a"},
|
|
Routes: []SyntheticRoute{route},
|
|
Transport: transport,
|
|
MaxTestPayloadBytes: 8,
|
|
})
|
|
nodeB := NewSyntheticRuntime(SyntheticRuntimeConfig{
|
|
Enabled: true,
|
|
Local: PeerIdentity{ClusterID: "cluster-1", NodeID: "node-b"},
|
|
Routes: []SyntheticRoute{route},
|
|
Transport: transport,
|
|
MaxTestPayloadBytes: 8,
|
|
})
|
|
transport.nodes["node-a"] = nodeA
|
|
transport.nodes["node-b"] = nodeB
|
|
|
|
result, err := nodeA.SendTestService(context.Background(), route.RouteID, SyntheticChannelRouteControl, testServiceRequest("request-max", strings.Repeat("a", 8)))
|
|
if err != nil {
|
|
t.Fatalf("send test service: %v", err)
|
|
}
|
|
if result.Response.EchoPayload != strings.Repeat("a", 8) {
|
|
t.Fatalf("EchoPayload = %q", result.Response.EchoPayload)
|
|
}
|
|
}
|