package socket import ( "testing" "google.golang.org/protobuf/proto" altv1 "git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1" apiContracts "git.toki-labs.com/toki/alt/services/api/internal/contracts" "git.toki-labs.com/toki/alt/services/api/internal/workerclient" protoSocket "git.toki-labs.com/toki/proto-socket/go" ) func TestHandleGetLiveBrokerCapabilitiesWorkerForward(t *testing.T) { expectedCap := &altv1.LiveBrokerCapability{Broker: "kis"} worker := &fakeWorkerClient{ liveCapRes: &altv1.GetLiveBrokerCapabilitiesResponse{BrokerCapabilities: expectedCap}, isConnected: true, } resp, err := handleGetLiveBrokerCapabilities(worker, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected error: %+v", resp.GetError()) } if resp.GetBrokerCapabilities() == nil { t.Fatal("expected broker capabilities, got nil") } if worker.GetLiveBrokerCapabilitiesCallCount() != 1 { t.Errorf("expected worker call count 1, got %d", worker.GetLiveBrokerCapabilitiesCallCount()) } if resp.GetBrokerCapabilities().GetBroker() != "kis" { t.Errorf("broker mismatch: %q", resp.GetBrokerCapabilities().GetBroker()) } } func TestHandleGetLiveBrokerCapabilitiesNilWorker(t *testing.T) { resp, err := handleGetLiveBrokerCapabilities(nil, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"}) if err != nil { t.Fatalf("expected typed unavailable response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorUnavailable { t.Errorf("expected unavailable error code, got %q", resp.GetError().GetCode()) } } func TestHandleGetLiveBrokerCapabilitiesEmptyAccountID(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} resp, err := handleGetLiveBrokerCapabilities(worker, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: ""}) if err != nil { t.Fatalf("expected typed invalid_request response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorInvalidRequest { t.Errorf("expected invalid_request error code, got %q", resp.GetError().GetCode()) } if resp.GetError().GetMessage() != "invalid live trading request: account_id is required" { t.Errorf("unexpected error message: %q", resp.GetError().GetMessage()) } if worker.GetLiveBrokerCapabilitiesCallCount() != 0 { t.Errorf("worker should not be called when account_id is empty, got %d calls", worker.GetLiveBrokerCapabilitiesCallCount()) } } func TestHandleGetLiveBrokerCapabilitiesWorkerUnavailable(t *testing.T) { worker := &fakeWorkerClient{connectErr: workerclient.ErrUnavailable, isConnected: false} resp, err := handleGetLiveBrokerCapabilities(worker, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"}) if err != nil { t.Fatalf("expected typed unavailable response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorUnavailable { t.Errorf("expected unavailable error code, got %q", resp.GetError().GetCode()) } } func TestHandleGetLiveBrokerCapabilitiesWorkerTimeout(t *testing.T) { worker := &fakeWorkerClient{connectErr: workerclient.ErrTimeout, isConnected: false} resp, err := handleGetLiveBrokerCapabilities(worker, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"}) if err != nil { t.Fatalf("expected typed timeout response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorTimeout { t.Errorf("expected timeout error code, got %q", resp.GetError().GetCode()) } } // TestHandleGetLiveBrokerCapabilitiesWorkerCallTimeout verifies the live handler // maps a worker-level call timeout (GetLiveBrokerCapabilities returning // workerclient.ErrTimeout) to backtestErrorTimeout. This is separate from the // Connect failure test above because Connect failure path is tested with // connectErr: workerclient.ErrTimeout above. func TestHandleGetLiveBrokerCapabilitiesWorkerCallTimeout(t *testing.T) { worker := &fakeWorkerClient{err: workerclient.ErrTimeout, isConnected: true} resp, err := handleGetLiveBrokerCapabilities(worker, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"}) if err != nil { t.Fatalf("expected typed timeout response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorTimeout { t.Errorf("expected timeout error code, got %q", resp.GetError().GetCode()) } if worker.GetLiveBrokerCapabilitiesCallCount() != 1 { t.Errorf("expected worker call count 1, got %d", worker.GetLiveBrokerCapabilitiesCallCount()) } } func TestHandleGetLiveBrokerCapabilitiesNilWorkerResponse(t *testing.T) { worker := &fakeWorkerClient{liveCapRes: nil, isConnected: true} resp, err := handleGetLiveBrokerCapabilities(worker, &altv1.GetLiveBrokerCapabilitiesRequest{AccountId: "test"}) if err != nil { t.Fatalf("expected typed internal response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorInternal { t.Errorf("expected internal error code, got %q", resp.GetError().GetCode()) } } func TestSessionHandlersIncludeLive(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} handlers := sessionHandlers(worker) seenLive := false for _, h := range handlers { if h.requestType == "alt.v1.GetLiveBrokerCapabilitiesRequest" { seenLive = true break } } if !seenLive { t.Error("sessionHandlers should include live capability handler") } } func TestLiveParserMapContainsLiveMessages(t *testing.T) { pm := apiContracts.ParserMap() for _, m := range []proto.Message{ &altv1.GetLiveBrokerCapabilitiesRequest{}, &altv1.GetLiveBrokerCapabilitiesResponse{}, &altv1.LiveBrokerCapability{}, &altv1.SubmitLiveOrderRequest{}, &altv1.SubmitLiveOrderResponse{}, &altv1.CancelLiveOrderRequest{}, &altv1.CancelLiveOrderResponse{}, &altv1.GetLiveOrderRequest{}, &altv1.GetLiveOrderResponse{}, &altv1.GetLiveRiskPolicyRequest{}, &altv1.GetLiveRiskPolicyResponse{}, &altv1.LiveRiskPolicy{}, &altv1.GetLiveKillSwitchRequest{}, &altv1.GetLiveKillSwitchResponse{}, &altv1.SetLiveKillSwitchRequest{}, &altv1.SetLiveKillSwitchResponse{}, &altv1.LiveKillSwitchState{}, } { name := protoSocket.TypeNameOf(m) if _, ok := pm[name]; !ok { t.Errorf("API parser map missing %q", name) } } } func TestHandleGetLiveRiskPolicyForward(t *testing.T) { worker := &fakeWorkerClient{ liveRiskPolicyRes: &altv1.GetLiveRiskPolicyResponse{ Policy: &altv1.LiveRiskPolicy{MaxDailyOrders: 5, MaxOpenOrders: 3, AllowShortSelling: false}, }, isConnected: true, } resp, err := handleGetLiveRiskPolicy(worker, &altv1.GetLiveRiskPolicyRequest{AccountId: "acct-1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if resp.GetPolicy() == nil { t.Fatal("expected policy, got nil") } if resp.GetPolicy().GetMaxDailyOrders() != 5 { t.Errorf("expected max_daily_orders 5, got %d", resp.GetPolicy().GetMaxDailyOrders()) } if worker.liveRiskPolicyReq == nil { t.Error("expected worker to be called") } } func TestHandleGetLiveRiskPolicyMissingAccountID(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} resp, err := handleGetLiveRiskPolicy(worker, &altv1.GetLiveRiskPolicyRequest{}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() == nil { t.Fatal("expected typed error, got nil") } if resp.GetError().GetCode() != backtestErrorInvalidRequest { t.Errorf("expected invalid_request, got %q", resp.GetError().GetCode()) } if worker.liveRiskPolicyReq != nil { t.Error("worker should not be called when account_id is missing") } } func TestHandleGetLiveKillSwitchForward(t *testing.T) { worker := &fakeWorkerClient{ liveGetKillSwitchRes: &altv1.GetLiveKillSwitchResponse{ State: &altv1.LiveKillSwitchState{Halted: true, Reason: "kill switch active by default"}, }, isConnected: true, } resp, err := handleGetLiveKillSwitch(worker, &altv1.GetLiveKillSwitchRequest{AccountId: "acct-1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if resp.GetState() == nil { t.Fatal("expected state, got nil") } if !resp.GetState().GetHalted() { t.Error("expected halted=true") } if worker.liveGetKillSwitchReq == nil { t.Error("expected worker to be called") } } func TestHandleSetLiveKillSwitchForward(t *testing.T) { worker := &fakeWorkerClient{ liveSetKillSwitchRes: &altv1.SetLiveKillSwitchResponse{ State: &altv1.LiveKillSwitchState{Halted: false, Reason: "operator cleared"}, }, isConnected: true, } resp, err := handleSetLiveKillSwitch(worker, &altv1.SetLiveKillSwitchRequest{ AccountId: "acct-1", Halted: false, Reason: "operator cleared", }) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if resp.GetState() == nil { t.Fatal("expected state, got nil") } if resp.GetState().GetHalted() { t.Error("expected halted=false after operator clear") } if resp.GetState().GetReason() != "operator cleared" { t.Errorf("unexpected reason: %q", resp.GetState().GetReason()) } if worker.liveSetKillSwitchReq == nil { t.Error("expected worker to be called") } } func TestHandleSetLiveKillSwitchMissingAccountID(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} resp, err := handleSetLiveKillSwitch(worker, &altv1.SetLiveKillSwitchRequest{Halted: false}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() == nil { t.Fatal("expected typed error, got nil") } if resp.GetError().GetCode() != backtestErrorInvalidRequest { t.Errorf("expected invalid_request, got %q", resp.GetError().GetCode()) } } func TestHandleSubmitLiveOrderForward(t *testing.T) { worker := &fakeWorkerClient{ liveSubmitRes: &altv1.SubmitLiveOrderResponse{Order: &altv1.LiveOrder{Id: "lo-1", Status: "submitted"}}, isConnected: true, } resp, err := handleSubmitLiveOrder(worker, &altv1.SubmitLiveOrderRequest{ AccountId: "acct-1", Intent: &altv1.LiveOrderIntent{InstrumentId: "KRX:005930", Side: "buy", Type: "market"}, OperatorConfirmation: &altv1.OperatorConfirmation{Confirmed: true}, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if resp.GetOrder().GetId() != "lo-1" { t.Errorf("unexpected order id: %q", resp.GetOrder().GetId()) } if worker.liveSubmitReq == nil { t.Error("expected worker to be called") } } func TestHandleSubmitLiveOrderMissingAccountID(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} resp, err := handleSubmitLiveOrder(worker, &altv1.SubmitLiveOrderRequest{ Intent: &altv1.LiveOrderIntent{InstrumentId: "KRX:005930"}, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() == nil { t.Fatal("expected typed error, got nil") } if resp.GetError().GetCode() != backtestErrorInvalidRequest { t.Errorf("expected invalid_request, got %q", resp.GetError().GetCode()) } if worker.liveSubmitReq != nil { t.Error("worker should not be called when account_id is missing") } } func TestHandleSubmitLiveOrderNilWorker(t *testing.T) { resp, err := handleSubmitLiveOrder(nil, &altv1.SubmitLiveOrderRequest{ AccountId: "acct-1", Intent: &altv1.LiveOrderIntent{InstrumentId: "KRX:005930"}, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() == nil { t.Fatal("expected typed error, got nil") } if resp.GetError().GetCode() != backtestErrorUnavailable { t.Errorf("expected unavailable, got %q", resp.GetError().GetCode()) } } func TestHandleCancelLiveOrderForward(t *testing.T) { worker := &fakeWorkerClient{ liveCancelRes: &altv1.CancelLiveOrderResponse{Order: &altv1.LiveOrder{Id: "lo-1", Status: "canceled"}}, isConnected: true, } resp, err := handleCancelLiveOrder(worker, &altv1.CancelLiveOrderRequest{ AccountId: "acct-1", LiveOrderId: "lo-1", }) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if resp.GetOrder().GetStatus() != "canceled" { t.Errorf("unexpected order status: %q", resp.GetOrder().GetStatus()) } } func TestHandleGetLiveOrderForward(t *testing.T) { worker := &fakeWorkerClient{ liveGetRes: &altv1.GetLiveOrderResponse{Order: &altv1.LiveOrder{Id: "lo-1", Status: "filled", BrokerStatus: "FILLED"}}, isConnected: true, } resp, err := handleGetLiveOrder(worker, &altv1.GetLiveOrderRequest{ AccountId: "acct-1", LiveOrderId: "lo-1", }) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if resp.GetOrder().GetBrokerStatus() != "FILLED" { t.Errorf("expected broker_status FILLED, got %q", resp.GetOrder().GetBrokerStatus()) } } func TestHandleSyncLiveAccountForward(t *testing.T) { snap := &altv1.LiveAccountSnapshot{AccountId: "op-alias-001", Broker: "kis"} worker := &fakeWorkerClient{ liveSyncAccRes: &altv1.SyncLiveAccountResponse{Snapshot: snap}, isConnected: true, } resp, err := handleSyncLiveAccount(worker, &altv1.SyncLiveAccountRequest{AccountId: "op-alias-001"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if worker.liveSyncAccReq == nil { t.Fatal("expected request to be forwarded to worker") } if resp.GetSnapshot().GetAccountId() != "op-alias-001" { t.Errorf("account_id mismatch: %q", resp.GetSnapshot().GetAccountId()) } } func TestHandleSyncLiveAccountEmptyAccountID(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} resp, err := handleSyncLiveAccount(worker, &altv1.SyncLiveAccountRequest{AccountId: ""}) if err != nil { t.Fatalf("expected typed response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorInvalidRequest { t.Errorf("expected invalid_request, got %q", resp.GetError().GetCode()) } if worker.liveSyncAccReq != nil { t.Error("worker should not be called when account_id is empty") } } func TestHandleSyncLiveAccountNilWorker(t *testing.T) { resp, err := handleSyncLiveAccount(nil, &altv1.SyncLiveAccountRequest{AccountId: "op-alias-001"}) if err != nil { t.Fatalf("expected typed response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorUnavailable { t.Errorf("expected unavailable, got %q", resp.GetError().GetCode()) } } func TestHandleGetLiveAccountSnapshotForward(t *testing.T) { snap := &altv1.LiveAccountSnapshot{AccountId: "op-alias-001", Broker: "kis", Stale: false} worker := &fakeWorkerClient{ liveGetAccSnapRes: &altv1.GetLiveAccountSnapshotResponse{Snapshot: snap}, isConnected: true, } resp, err := handleGetLiveAccountSnapshot(worker, &altv1.GetLiveAccountSnapshotRequest{AccountId: "op-alias-001"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if worker.liveGetAccSnapReq == nil { t.Fatal("expected request to be forwarded to worker") } if resp.GetSnapshot().GetAccountId() != "op-alias-001" { t.Errorf("account_id mismatch: %q", resp.GetSnapshot().GetAccountId()) } } func TestHandleGetLiveAccountSnapshotEmptyAccountID(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} resp, err := handleGetLiveAccountSnapshot(worker, &altv1.GetLiveAccountSnapshotRequest{AccountId: ""}) if err != nil { t.Fatalf("expected typed response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorInvalidRequest { t.Errorf("expected invalid_request, got %q", resp.GetError().GetCode()) } } func TestHandleGetLiveAccountSnapshotNilWorker(t *testing.T) { resp, err := handleGetLiveAccountSnapshot(nil, &altv1.GetLiveAccountSnapshotRequest{AccountId: "op-alias-001"}) if err != nil { t.Fatalf("expected typed response, got error: %v", err) } if resp.GetError() == nil { t.Fatal("expected error, got nil") } if resp.GetError().GetCode() != backtestErrorUnavailable { t.Errorf("expected unavailable, got %q", resp.GetError().GetCode()) } } func TestApiLiveHandlersRegistrationIncludesAccountSync(t *testing.T) { required := map[string]bool{ "alt.v1.SyncLiveAccountRequest": false, "alt.v1.GetLiveAccountSnapshotRequest": false, } for _, h := range apiLiveHandlers(nil) { if _, ok := required[h.requestType]; ok { required[h.requestType] = true } } for name, seen := range required { if !seen { t.Errorf("missing API live handler registration: %s", name) } } } func TestApiLiveHandlersRegistrationIncludesAuditQuery(t *testing.T) { found := false for _, h := range apiLiveHandlers(nil) { if h.requestType == "alt.v1.ListLiveAuditEventsRequest" { found = true break } } if !found { t.Error("missing API live handler registration: alt.v1.ListLiveAuditEventsRequest") } } func TestHandleListLiveAuditEventsForward(t *testing.T) { auditEvents := []*altv1.LiveAuditEvent{ {EventId: "aud-1", Type: "submit_confirmed", AccountId: "acct-1", OrderId: "lo-1"}, } worker := &fakeWorkerClient{ liveListAuditRes: &altv1.ListLiveAuditEventsResponse{Events: auditEvents}, isConnected: true, } resp, err := handleListLiveAuditEvents(worker, &altv1.ListLiveAuditEventsRequest{AccountId: "acct-1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() != nil { t.Fatalf("unexpected typed error: %+v", resp.GetError()) } if worker.liveListAuditReq == nil { t.Fatal("expected request to be forwarded to worker") } if len(resp.GetEvents()) != 1 { t.Fatalf("expected 1 event, got %d", len(resp.GetEvents())) } if resp.GetEvents()[0].GetEventId() != "aud-1" { t.Errorf("event_id mismatch: %q", resp.GetEvents()[0].GetEventId()) } } func TestHandleListLiveAuditEventsEmptyAccountID(t *testing.T) { worker := &fakeWorkerClient{isConnected: true} resp, err := handleListLiveAuditEvents(worker, &altv1.ListLiveAuditEventsRequest{}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() == nil { t.Fatal("expected typed error, got nil") } if resp.GetError().GetCode() != backtestErrorInvalidRequest { t.Errorf("expected invalid_request, got %q", resp.GetError().GetCode()) } if worker.liveListAuditReq != nil { t.Error("worker should not be called when account_id is empty") } } func TestHandleListLiveAuditEventsNilWorker(t *testing.T) { resp, err := handleListLiveAuditEvents(nil, &altv1.ListLiveAuditEventsRequest{AccountId: "acct-1"}) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.GetError() == nil { t.Fatal("expected typed error, got nil") } if resp.GetError().GetCode() != backtestErrorUnavailable { t.Errorf("expected unavailable, got %q", resp.GetError().GetCode()) } }