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{}, } { name := protoSocket.TypeNameOf(m) if _, ok := pm[name]; !ok { t.Errorf("API parser map missing %q", name) } } }