Files

268 lines
9.2 KiB
Go

package webingress
import (
"context"
"encoding/json"
"errors"
"net/http"
"testing"
"github.com/example/remote-access-platform/agents/rap-node-agent/internal/mesh"
)
func TestMeshEnvelopeSenderSendsSignedEnvelopeOverReliableFabricRuntime(t *testing.T) {
runtime := &recordingReliableRuntime{
result: mesh.FabricChannelRuntimeResult{
Channel: mesh.FabricChannel{RouteID: "route-fast", TargetNode: "node-runtime"},
BytesSent: 123,
FramesSent: 1,
AcksReceived: 1,
},
}
sender := MeshEnvelopeSender{
Runtime: runtime,
RouteSet: testWebIngressRouteSet(),
ClusterID: "cluster-1",
SourceNodeID: "node-ingress",
TargetKind: mesh.FabricChannelTargetPool,
TargetID: "pool-admin-runtime",
ChannelID: "channel-web-1",
Now: fixedEnvelopeNow,
}
envelope := SignedFabricServiceChannelEnvelope{
SchemaVersion: "rap.web_ingress.signed_fabric_service_channel_envelope.v1",
Envelope: FabricServiceChannelEnvelope{
SchemaVersion: FabricServiceChannelEnvelopeSchema,
Scope: "platform",
ServiceClass: "platform_admin",
},
Signature: FabricEnvelopeSignature{KeyID: "node-key", Alg: "ed25519", Signature: "sig"},
}
response, err := sender.Send(context.Background(), envelope)
if err != nil {
t.Fatalf("send: %v", err)
}
if response.StatusCode != http.StatusAccepted || response.Headers.Get("Content-Type") != "application/json" {
t.Fatalf("response = %+v", response)
}
if runtime.spec.ChannelID != "channel-web-1" ||
runtime.spec.ClusterID != "cluster-1" ||
runtime.spec.SourceNodeID != "node-ingress" ||
runtime.spec.TargetID != "pool-admin-runtime" ||
runtime.spec.TargetKind != mesh.FabricChannelTargetPool ||
runtime.spec.TrafficClass != "control" ||
runtime.spec.StickyKey != "platform:platform_admin" {
t.Fatalf("spec = %+v", runtime.spec)
}
if runtime.routeSet.TargetID != "pool-admin-runtime" || len(runtime.payloads) != 1 {
t.Fatalf("route/payload = %+v payloads=%d", runtime.routeSet, len(runtime.payloads))
}
var delivered SignedFabricServiceChannelEnvelope
if err := json.Unmarshal(runtime.payloads[0], &delivered); err != nil {
t.Fatalf("decode delivered envelope: %v", err)
}
if delivered.Signature.Signature != "sig" || delivered.Envelope.ServiceClass != "platform_admin" {
t.Fatalf("delivered = %+v", delivered)
}
var body MeshEnvelopeDeliveryResponse
if err := json.Unmarshal(response.Body, &body); err != nil {
t.Fatalf("decode response: %v", err)
}
if body.SchemaVersion != "rap.web_ingress.mesh_envelope_delivery_response.v1" ||
body.Status != "accepted" ||
body.RouteID != "route-fast" ||
body.AcksReceived != 1 {
t.Fatalf("body = %+v", body)
}
}
func TestMeshEnvelopeSenderReturnsRuntimeHTTPResponse(t *testing.T) {
runtime := &recordingRequestResponseRuntime{
result: mesh.FabricChannelRequestResponseResult{
FabricChannelRuntimeResult: mesh.FabricChannelRuntimeResult{
Channel: mesh.FabricChannel{RouteID: "route-runtime", TargetNode: "node-runtime"},
BytesSent: 123,
BytesRecv: 16,
FramesSent: 1,
FramesRecv: 1,
AcksReceived: 1,
},
ResponsePayload: []byte(`{"payload":{"schema_version":"rap.web_ingress.fabric_runtime_response.v1","status_code":201,"headers":{"X-RAP-Runtime":["ok"],"Set-Cookie":["blocked"]},"body_b64":"eyJvayI6dHJ1ZX0="}}`),
},
}
sender := MeshEnvelopeSender{
ResponseRuntime: runtime,
RouteSet: testWebIngressRouteSet(),
ClusterID: "cluster-1",
SourceNodeID: "node-ingress",
TargetKind: mesh.FabricChannelTargetPool,
TargetID: "pool-admin-runtime",
ChannelID: "channel-web-1",
Now: fixedEnvelopeNow,
}
response, err := sender.Send(context.Background(), SignedFabricServiceChannelEnvelope{
SchemaVersion: "rap.web_ingress.signed_fabric_service_channel_envelope.v1",
Envelope: FabricServiceChannelEnvelope{SchemaVersion: FabricServiceChannelEnvelopeSchema, Scope: "platform", ServiceClass: "platform_admin"},
Signature: FabricEnvelopeSignature{KeyID: "node-key", Alg: "ed25519", Signature: "sig"},
})
if err != nil {
t.Fatalf("send: %v", err)
}
if response.StatusCode != http.StatusCreated || response.Headers.Get("X-RAP-Runtime") != "ok" || response.Headers.Get("Set-Cookie") != "" || string(response.Body) != `{"ok":true}` {
t.Fatalf("response = %+v body=%s", response, string(response.Body))
}
if runtime.spec.ChannelID != "channel-web-1" || len(runtime.payload) == 0 {
t.Fatalf("runtime spec=%+v payload=%s", runtime.spec, string(runtime.payload))
}
}
func TestMeshEnvelopeSenderReportsWrappedRuntimeError(t *testing.T) {
sender := MeshEnvelopeSender{
ResponseRuntime: &recordingRequestResponseRuntime{
result: mesh.FabricChannelRequestResponseResult{ResponsePayload: []byte(`{"error":"runtime unavailable"}`)},
},
RouteSet: testWebIngressRouteSet(),
ClusterID: "cluster-1",
SourceNodeID: "node-ingress",
TargetID: "pool-admin-runtime",
ChannelID: "channel-web-1",
}
_, err := sender.Send(context.Background(), SignedFabricServiceChannelEnvelope{
Envelope: FabricServiceChannelEnvelope{Scope: "platform", ServiceClass: "platform_admin"},
})
if !errors.Is(err, ErrMeshEnvelopeRuntimeRequired) {
t.Fatalf("err = %v", err)
}
}
func TestMeshEnvelopeSenderFallsBackToDeliveryAckForNonHTTPRuntimePayload(t *testing.T) {
runtime := &recordingRequestResponseRuntime{
result: mesh.FabricChannelRequestResponseResult{
FabricChannelRuntimeResult: mesh.FabricChannelRuntimeResult{
Channel: mesh.FabricChannel{RouteID: "route-runtime", TargetNode: "node-runtime"},
BytesSent: 123,
FramesSent: 1,
AcksReceived: 1,
},
ResponsePayload: []byte(`{"not":"http"}`),
},
}
sender := MeshEnvelopeSender{
ResponseRuntime: runtime,
RouteSet: testWebIngressRouteSet(),
ClusterID: "cluster-1",
SourceNodeID: "node-ingress",
TargetID: "pool-admin-runtime",
ChannelID: "channel-web-1",
}
response, err := sender.Send(context.Background(), SignedFabricServiceChannelEnvelope{
Envelope: FabricServiceChannelEnvelope{Scope: "platform", ServiceClass: "platform_admin"},
})
if err != nil {
t.Fatalf("send: %v", err)
}
if response.StatusCode != http.StatusAccepted {
t.Fatalf("response = %+v", response)
}
var body MeshEnvelopeDeliveryResponse
if err := json.Unmarshal(response.Body, &body); err != nil {
t.Fatalf("decode response: %v", err)
}
if body.Status != "accepted" || body.RouteID != "route-runtime" {
t.Fatalf("body = %+v", body)
}
}
func TestMeshEnvelopeSenderReportsRuntimeRouteAndIdentityErrors(t *testing.T) {
_, err := (MeshEnvelopeSender{}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
if !errors.Is(err, ErrMeshEnvelopeRuntimeRequired) {
t.Fatalf("runtime error = %v", err)
}
_, err = (MeshEnvelopeSender{
Runtime: &recordingReliableRuntime{},
ClusterID: "cluster-1",
SourceNodeID: "node-ingress",
TargetID: "pool-admin-runtime",
}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
if !errors.Is(err, ErrMeshEnvelopeRouteRequired) {
t.Fatalf("route error = %v", err)
}
_, err = (MeshEnvelopeSender{
Runtime: &recordingReliableRuntime{},
RouteSet: testWebIngressRouteSet(),
}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
if !errors.Is(err, ErrMeshEnvelopeIdentityInvalid) {
t.Fatalf("identity error = %v", err)
}
}
func TestMeshEnvelopeSenderPropagatesReliableRuntimeFailure(t *testing.T) {
sendErr := errors.New("send failed")
_, err := (MeshEnvelopeSender{
Runtime: &recordingReliableRuntime{err: sendErr},
RouteSet: testWebIngressRouteSet(),
ClusterID: "cluster-1",
SourceNodeID: "node-ingress",
TargetID: "pool-admin-runtime",
}).Send(context.Background(), SignedFabricServiceChannelEnvelope{})
if !errors.Is(err, sendErr) {
t.Fatalf("send error = %v", err)
}
}
type recordingReliableRuntime struct {
spec mesh.FabricChannelSpec
routeSet mesh.FabricRouteSet
payloads [][]byte
result mesh.FabricChannelRuntimeResult
err error
}
type recordingRequestResponseRuntime struct {
spec mesh.FabricChannelSpec
routeSet mesh.FabricRouteSet
payload []byte
result mesh.FabricChannelRequestResponseResult
err error
}
func (r *recordingRequestResponseRuntime) SendRequestResponse(_ context.Context, spec mesh.FabricChannelSpec, routeSet mesh.FabricRouteSet, payload []byte) (mesh.FabricChannelRequestResponseResult, error) {
r.spec = spec
r.routeSet = routeSet
r.payload = payload
if r.err != nil {
return mesh.FabricChannelRequestResponseResult{}, r.err
}
return r.result, nil
}
func (r *recordingReliableRuntime) SendReliable(_ context.Context, spec mesh.FabricChannelSpec, routeSet mesh.FabricRouteSet, payloads [][]byte) (mesh.FabricChannelRuntimeResult, error) {
r.spec = spec
r.routeSet = routeSet
r.payloads = payloads
if r.err != nil {
return mesh.FabricChannelRuntimeResult{}, r.err
}
return r.result, nil
}
func testWebIngressRouteSet() mesh.FabricRouteSet {
return mesh.FabricRouteSet{
Primary: mesh.FabricRoute{
RouteID: "route-fast",
ClusterID: "cluster-1",
SourceNodeID: "node-ingress",
DestinationNodeID: "node-runtime",
PoolID: "pool-admin-runtime",
Healthy: true,
Capacity: 100,
},
}
}