168 lines
8.4 KiB
Go
168 lines
8.4 KiB
Go
package companion
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type configPortFixture struct {
|
|
fields map[string]string
|
|
patches []ConfigFieldPatch
|
|
}
|
|
|
|
func (fixture *configPortFixture) ReadConfig(context.Context) (map[string]string, error) {
|
|
return fixture.fields, nil
|
|
}
|
|
func (fixture *configPortFixture) ApplyConfigPatch(_ context.Context, _ string, fields []ConfigFieldPatch) (map[string]string, error) {
|
|
fixture.patches = fields
|
|
return map[string]string{"ServerName": "Moon", "hostPath": "C:/secret"}, nil
|
|
}
|
|
|
|
type notificationPortFixture struct {
|
|
deliveries []ue4SSPlayerNotification
|
|
accepted bool
|
|
}
|
|
|
|
// nonProductionVehicleSpawnPortFixture is an isolated test double. It has no
|
|
// network, socket, credential, or raw-command entry point; it can observe only
|
|
// the Companion's private typed request and return a bounded receipt.
|
|
type nonProductionVehicleSpawnPortFixture struct {
|
|
requests []ue4SSVehicleSpawn
|
|
receipt UE4SSVehicleSpawnReceipt
|
|
err error
|
|
}
|
|
|
|
func (fixture *nonProductionVehicleSpawnPortFixture) SpawnVehicle(_ context.Context, request ue4SSVehicleSpawn) (UE4SSVehicleSpawnReceipt, error) {
|
|
fixture.requests = append(fixture.requests, request)
|
|
return fixture.receipt, fixture.err
|
|
}
|
|
|
|
func (fixture *notificationPortFixture) SendPlayerNotification(_ context.Context, notification ue4SSPlayerNotification) (UE4SSNotificationReceipt, error) {
|
|
fixture.deliveries = append(fixture.deliveries, notification)
|
|
return UE4SSNotificationReceipt{Accepted: fixture.accepted}, nil
|
|
}
|
|
|
|
func TestRuntimeAdapterUsesOnlyLogicalConfigValuesAndRedactsDiagnostics(t *testing.T) {
|
|
port := &configPortFixture{fields: map[string]string{"ServerName": "Moon", "hostPath": "C:/secret", "Password": "nope"}}
|
|
adapter := RuntimeAdapter{Config: port, DiagnosticsState: map[string]string{"status": "healthy", "hostPath": "C:/secret"}}
|
|
read, err := adapter.ReadConfiguration(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("read config: %v", err)
|
|
}
|
|
fields := read["fields"].(map[string]string)
|
|
if fields["ServerName"] != "Moon" || len(fields) != 1 {
|
|
t.Fatalf("unsafe config was exposed: %+v", fields)
|
|
}
|
|
patched, err := adapter.PatchConfiguration(context.Background(), map[string]any{"revision": "r1", "fields": []any{map[string]any{"key": "ServerName", "value": "Moon"}}})
|
|
if err != nil {
|
|
t.Fatalf("patch config: %v", err)
|
|
}
|
|
if len(port.patches) != 1 || patched["appliedFields"].(map[string]string)["hostPath"] != "" {
|
|
t.Fatalf("patch leaked unsafe details: %+v", patched)
|
|
}
|
|
diagnostics, _ := adapter.Diagnostics(context.Background())
|
|
if diagnostics["hostPath"] != nil || diagnostics["status"] != "healthy" {
|
|
t.Fatalf("diagnostics leaked unsafe details: %+v", diagnostics)
|
|
}
|
|
}
|
|
|
|
func TestUE4SSNotificationIsTypedAndRedacted(t *testing.T) {
|
|
port := ¬ificationPortFixture{accepted: true}
|
|
adapter := RuntimeAdapter{BoundServerID: "server-1", Notification: port}
|
|
result, err := adapter.NotifyPlayer(context.Background(), map[string]any{"playerId": "76561198000000001", "message": "Moon \"gift\""})
|
|
if err != nil || !result["accepted"].(bool) || len(port.deliveries) != 1 {
|
|
t.Fatalf("typed notification was not delivered: result=%+v err=%v deliveries=%+v", result, err, port.deliveries)
|
|
}
|
|
delivery := port.deliveries[0]
|
|
if delivery.ServerID != "server-1" || delivery.chatType != fixedNotificationType || delivery.protectedAuditCommand != "SendChat 4 \"Moon \\\"gift\\\"\" 76561198000000001" {
|
|
t.Fatalf("notification did not use the fixed UE4SS contract: %+v", delivery)
|
|
}
|
|
if result["message"] == delivery.protectedAuditCommand || result["command"] != nil || result["rcon"] != nil {
|
|
t.Fatalf("notification leaked protected transport details: %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestRuntimeNotificationRejectsInvalidRecipient(t *testing.T) {
|
|
port := ¬ificationPortFixture{accepted: true}
|
|
adapter := RuntimeAdapter{BoundServerID: "server-1", Notification: port}
|
|
if _, err := adapter.NotifyPlayer(context.Background(), map[string]any{"playerId": "not-a-steam-id", "message": "Moonlight"}); err == nil {
|
|
t.Fatal("unverified recipient identity must be rejected")
|
|
}
|
|
}
|
|
|
|
func TestNotificationFailureIsCachedWithoutInvokingRewardDelivery(t *testing.T) {
|
|
stamp := time.Now().UTC()
|
|
port := ¬ificationPortFixture{accepted: false}
|
|
adapter := RuntimeAdapter{BoundServerID: "server-1", Notification: port}
|
|
registry := NewHandlerRegistry(HandlerAvailability{BoundServerID: "server-1", Approved: true, Capabilities: map[string]bool{"player.notify": true}}, adapter)
|
|
command := ClaimedCommand{ID: "notification-1", ProfileKey: ProfileKey, CommandType: "player.notify", Payload: map[string]any{"playerId": "76561198000000001", "message": "Moonlight"}, FencingToken: 1, LeaseExpiresAt: stamp.Add(time.Minute), ExpiresAt: stamp.Add(time.Minute)}
|
|
for range 2 {
|
|
result, err := registry.Execute(context.Background(), command)
|
|
if err != nil || result.Payload["accepted"] != false {
|
|
t.Fatalf("notification failure was not typed: result=%+v err=%v", result, err)
|
|
}
|
|
}
|
|
if len(port.deliveries) != 1 {
|
|
t.Fatalf("duplicate notification attempted transport %d times", len(port.deliveries))
|
|
}
|
|
}
|
|
|
|
func TestVersionedVehicleSpawnUsesFixedTemplateAndPrivateAuditOnly(t *testing.T) {
|
|
port := &nonProductionVehicleSpawnPortFixture{receipt: UE4SSVehicleSpawnReceipt{Outcome: UE4SSVehicleSpawnAccepted}}
|
|
adapter := RuntimeAdapter{BoundServerID: "server-1", VehicleSpawn: port}
|
|
result, err := adapter.SpawnVehicle(context.Background(), map[string]any{"vehicleCode": "BPC_Laika_C"})
|
|
if err != nil || result["outcome"] != "succeeded" || len(port.requests) != 1 {
|
|
t.Fatalf("fixed vehicle spawn was not delivered: result=%+v err=%v requests=%+v", result, err, port.requests)
|
|
}
|
|
request := port.requests[0]
|
|
if request.ServerID != "server-1" || request.VehicleCode != "BPC_Laika_C" || request.protectedAuditCommand != "#spawnvehicle BPC_Laika_C" {
|
|
t.Fatalf("vehicle spawn did not use the fixed template: %+v", request)
|
|
}
|
|
if result["command"] != nil || result["rcon"] != nil || result["audit"] != nil || result["outcome"] == request.protectedAuditCommand {
|
|
t.Fatalf("vehicle spawn leaked protected transport details: %+v", result)
|
|
}
|
|
}
|
|
|
|
func TestVersionedVehicleSpawnFailsClosedAndClassifiesBoundedReceipts(t *testing.T) {
|
|
port := &nonProductionVehicleSpawnPortFixture{receipt: UE4SSVehicleSpawnReceipt{Outcome: UE4SSVehicleSpawnRejected}}
|
|
adapter := RuntimeAdapter{BoundServerID: "server-1", VehicleSpawn: port}
|
|
for name, testCase := range map[string]struct {
|
|
outcome string
|
|
receipt UE4SSVehicleSpawnReceipt
|
|
err error
|
|
}{
|
|
"failed": {outcome: "failed", receipt: UE4SSVehicleSpawnReceipt{Outcome: UE4SSVehicleSpawnRejected}},
|
|
"unknown": {outcome: "unknown", receipt: UE4SSVehicleSpawnReceipt{Outcome: UE4SSVehicleSpawnUnknown}},
|
|
"unknown-transport": {outcome: "unknown", receipt: UE4SSVehicleSpawnReceipt{}, err: errors.New("isolated transport timeout")},
|
|
} {
|
|
port.receipt, port.err = testCase.receipt, testCase.err
|
|
result, err := adapter.SpawnVehicle(context.Background(), map[string]any{"vehicleCode": "BPC_WolfsWagen_C"})
|
|
if err != nil || result["outcome"] != testCase.outcome {
|
|
t.Fatalf("receipt %s was not safely classified: result=%+v err=%v", name, result, err)
|
|
}
|
|
}
|
|
before := len(port.requests)
|
|
if _, err := adapter.SpawnVehicle(context.Background(), map[string]any{"vehicleCode": "#spawnvehicle BPC_Laika_C"}); err == nil || len(port.requests) != before {
|
|
t.Fatal("raw command text must not reach the vehicle transport")
|
|
}
|
|
}
|
|
|
|
func TestVehicleSpawnUnknownOutcomeIsCachedWithoutRetry(t *testing.T) {
|
|
stamp := time.Now().UTC()
|
|
port := &nonProductionVehicleSpawnPortFixture{receipt: UE4SSVehicleSpawnReceipt{Outcome: UE4SSVehicleSpawnUnknown}}
|
|
adapter := RuntimeAdapter{BoundServerID: "server-1", VehicleSpawn: port}
|
|
registry := NewHandlerRegistry(HandlerAvailability{BoundServerID: "server-1", Approved: true, Capabilities: map[string]bool{"vehicle.spawn": true}}, adapter)
|
|
command := ClaimedCommand{ID: "vehicle-unknown-1", ProfileKey: ProfileKey, CommandType: "vehicle.spawn", Payload: map[string]any{"vehicleCode": "BPC_Laika_C"}, FencingToken: 1, LeaseExpiresAt: stamp.Add(time.Minute), ExpiresAt: stamp.Add(time.Minute)}
|
|
for range 2 {
|
|
result, err := registry.Execute(context.Background(), command)
|
|
if err != nil || result.Payload["outcome"] != "unknown" {
|
|
t.Fatalf("unknown vehicle outcome was not retained: result=%+v err=%v", result, err)
|
|
}
|
|
}
|
|
if len(port.requests) != 1 {
|
|
t.Fatalf("unknown vehicle outcome retried transport %d times", len(port.requests))
|
|
}
|
|
}
|