test(integration): rewrite broker integration tests for cTrader OpenAPI migration

Replace FIX-protocol-based integration tests with tests using real cTrader
types (ICMarketsConfig, TradingOrder, BrokerInterface). All 21 tests pass:
broker_failover (5), icmarkets_validation (10), order_lifecycle (6).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-22 16:32:49 +01:00
parent afee3489cd
commit 397ccb9f13
8 changed files with 1168 additions and 2047 deletions

View File

@@ -121,6 +121,59 @@ pub fn close_position(account_id: i64, position_id: i64, volume: i64) -> ProtoMe
}
}
// ── Execution event parsing ──────────────────────────────────────
/// Parsed execution event info (avoids exposing raw proto types to consumers).
#[derive(Debug, Clone)]
pub struct ExecutionEventInfo {
/// cTrader order ID.
pub order_id: i64,
/// Symbol ID.
pub symbol_id: i64,
/// Trade side as proto enum value (1=Buy, 2=Sell).
pub trade_side: i32,
/// Volume in cTrader units (cents of lot).
pub volume: i64,
/// Execution price (for filled orders).
pub execution_price: Option<f64>,
/// Executed volume in cents.
pub executed_volume: Option<i64>,
/// Linked position ID (if any).
pub position_id: Option<i64>,
/// Execution type as proto enum value.
pub execution_type: i32,
/// Open timestamp (Unix ms).
pub timestamp: Option<i64>,
}
/// Try to parse an execution event from a raw `ProtoMessage`.
///
/// Returns `None` if the message is not an execution event or cannot be decoded.
pub fn parse_execution_event(msg: &ProtoMessage) -> Option<ExecutionEventInfo> {
if msg.payload_type != proto::PT_EXECUTION_EVENT {
return None;
}
let payload = msg.payload.as_deref()?;
let event = proto::ProtoOaExecutionEvent::decode(payload).ok()?;
let order = event.order?;
Some(ExecutionEventInfo {
order_id: order.order_id,
symbol_id: order.trade_data.symbol_id,
trade_side: order.trade_data.trade_side,
volume: order.trade_data.volume,
execution_price: order.execution_price,
executed_volume: order.executed_volume,
position_id: event.position.map(|p| p.position_id),
execution_type: event.execution_type,
timestamp: order.trade_data.open_timestamp,
})
}
/// Extract the order ID from an execution response message.
pub fn extract_order_id(msg: &ProtoMessage) -> Option<i64> {
parse_execution_event(msg).map(|e| e.order_id)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -121,6 +121,14 @@ impl SymbolMapper {
pub fn is_empty(&self) -> bool {
self.by_name.is_empty()
}
/// Resolve a symbol ID to its name.
pub fn symbol_name(&self, id: i64) -> Result<String> {
self.by_id
.get(&id)
.map(|s| s.symbol_name.clone())
.ok_or_else(|| CTraderError::UnknownSymbol(format!("id={id}")))
}
}
fn symbol_info_from_light(sym: &ProtoOaLightSymbol) -> SymbolInfo {