migrations: - 001_trading_events.sql, 003_audit_system.sql: the hard-coded node_id literals (`trading-node-01`, `audit-node-01`, `ml-node-01`, `system-node-01`, `change-tracker-01`) are overridden per-deployment by later migrations rather than read from the environment. Describe that in the inline comment. - 004_compliance_views.sql: `generate_compliance_report` is a log stub — actual report generation is performed by the compliance service. Say so explicitly. services: - ml_training_service/tests/orchestrator_225_features_test.rs: the empty `#[ignore]`d placeholder for the 225-feature orchestrator loader has been removed; it held no assertions and only tracked a TODO (feedback_no_stubs.md). - trading_agent_service/src/service.rs: portfolio volatility uses the diagonal-only approximation because cross-asset return correlations are not maintained in this service. Document that. - trading_service/src/services/risk.rs: `get_risk_metrics` uses `calculate_marginal_var` + asset-class fallback; describe why `calculate_comprehensive_var` is not wired at this boundary. - trading_service/tests/auth_comprehensive.rs: delete the entire commented-out legacy BackupCodeValidator test block — the old `generate_backup_codes` / `store_backup_code` / `verify_backup_code` surface no longer exists, and MFA integration tests already cover the new API. testing: - harness/grpc_clients.rs: no BacktestingServiceClient proto exists; reword the stale TODO import line. - chaos/*: reword the family of "TODO: Implement ..." stubs as "Currently a no-op / synthetic result" descriptions so readers know exactly how much of the chaos framework is live. - compliance_automation_tests.rs: delete the file; it was a giant /* ... */ block referencing a nonexistent compliance module and was not wired into any Cargo target. - framework.rs: describe why `setup()` uses `println!` instead of `tracing_subscriber` (tracing_subscriber is not a dep of this integration crate). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
277 lines
8.6 KiB
Rust
277 lines
8.6 KiB
Rust
//! gRPC Client Utilities for Integration Testing
|
|
//!
|
|
//! Provides test clients for all Foxhunt services:
|
|
//! - TLI (Terminal Interface)
|
|
//! - MLTrainingService
|
|
//! - MLService (Model Inference)
|
|
//! - Trading Service
|
|
|
|
// Proto module imports
|
|
use crate::proto;
|
|
use super::TestConfig;
|
|
|
|
use anyhow::Result;
|
|
use std::time::Duration;
|
|
use tokio::time::timeout;
|
|
use tonic::transport::{Channel, Endpoint};
|
|
use super::TestConfig;
|
|
|
|
// REMOVED: All pub use statements eliminated per cleanup requirements
|
|
// Tests must import from canonical sources:
|
|
// // Import from proto modules
|
|
use crate::proto::ml_training::ml_training_service_client::MlTrainingServiceClient;
|
|
use crate::proto::trading::trading_service_client::TradingServiceClient as ProtoTradingServiceClient;
|
|
// The harness does not currently expose a Backtesting gRPC client — the
|
|
// backtesting crate is driven directly in tests rather than over the
|
|
// wire. Re-add an import here once a BacktestingServiceClient proto
|
|
// surface exists.
|
|
|
|
/// Container for all gRPC service clients
|
|
#[derive(Clone)]
|
|
pub struct GrpcClients {
|
|
pub tli_client: TliClient,
|
|
pub ml_training_client: MlTrainingServiceClient<Channel>,
|
|
pub ml_service_client: MlTrainingServiceClient<Channel>, // Using same client for now
|
|
pub trading_client: TradingServiceClient,
|
|
config: TestConfig,
|
|
}
|
|
|
|
impl GrpcClients {
|
|
/// Initialize all gRPC clients with connection pooling
|
|
pub async fn new() -> Result<Self> {
|
|
let config = super::load_test_config()?;
|
|
|
|
// Create channels with connection pooling and keepalive
|
|
let tli_channel = Self::create_channel(&config.tli_endpoint).await?;
|
|
let ml_training_channel = Self::create_channel(&config.ml_training_endpoint).await?;
|
|
let trading_channel = Self::create_channel(&config.trading_service_endpoint).await?;
|
|
|
|
let tli_client = TliClient::new(tli_channel)?;
|
|
let ml_training_client = MlTrainingServiceClient::new(ml_training_channel);
|
|
let ml_service_client = MlTrainingServiceClient::new(ml_training_channel.clone());
|
|
let trading_client = TradingServiceClient::new(trading_channel)?;
|
|
|
|
Ok(Self {
|
|
tli_client,
|
|
ml_training_client,
|
|
ml_service_client,
|
|
trading_client,
|
|
config,
|
|
})
|
|
}
|
|
|
|
/// Create optimized gRPC channel with connection pooling
|
|
async fn create_channel(endpoint: &str) -> Result<Channel> {
|
|
let channel = Endpoint::from_shared(endpoint.to_string())?
|
|
.connect_timeout(Duration::from_secs(10))
|
|
.timeout(Duration::from_secs(30))
|
|
.tcp_keepalive(Some(Duration::from_secs(30)))
|
|
.http2_keep_alive_interval(Duration::from_secs(30))
|
|
.keep_alive_timeout(Duration::from_secs(5))
|
|
.connect()
|
|
.await?;
|
|
|
|
Ok(channel)
|
|
}
|
|
|
|
/// Check if all services are healthy and responsive
|
|
pub async fn are_all_healthy(&self) -> Result<bool> {
|
|
let timeout_duration = Duration::from_secs(5);
|
|
|
|
let tli_healthy = timeout(timeout_duration, self.tli_client.health_check()).await.is_ok();
|
|
let ml_training_healthy = timeout(timeout_duration, self.ml_training_client.clone().health_check(
|
|
crate::proto::ml_training::HealthCheckRequest {}
|
|
)).await.is_ok();
|
|
let trading_healthy = timeout(timeout_duration, self.trading_client.health_check()).await.is_ok();
|
|
|
|
Ok(tli_healthy && ml_training_healthy && trading_healthy)
|
|
}
|
|
|
|
/// Get service endpoints for debugging
|
|
pub fn get_endpoints(&self) -> Vec<(String, String)> {
|
|
vec![
|
|
("TLI".to_string(), self.config.tli_endpoint.clone()),
|
|
("MLTraining".to_string(), self.config.ml_training_endpoint.clone()),
|
|
("Trading".to_string(), self.config.trading_service_endpoint.clone()),
|
|
]
|
|
}
|
|
}
|
|
|
|
/// TLI Service client wrapper
|
|
#[derive(Clone)]
|
|
pub struct TliClient {
|
|
// This would use the actual TLI gRPC client
|
|
endpoint: String,
|
|
}
|
|
|
|
impl TliClient {
|
|
pub fn new(channel: Channel) -> Result<Self> {
|
|
// In practice, this would initialize the actual TLI gRPC client
|
|
Ok(Self {
|
|
endpoint: "localhost:50051".to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn health_check(&self) -> Result<()> {
|
|
// Implement actual health check
|
|
Ok(())
|
|
}
|
|
|
|
/// Send ML training command via TLI
|
|
pub async fn start_ml_training(&mut self, request: StartMLTrainingRequest) -> Result<StartMLTrainingResponse> {
|
|
// This would call the actual TLI gRPC method
|
|
Ok(StartMLTrainingResponse {
|
|
success: true,
|
|
job_id: "test-job-123".to_string(),
|
|
message: "Training started successfully".to_string(),
|
|
})
|
|
}
|
|
|
|
/// Get ML training status via TLI
|
|
pub async fn get_ml_training_status(&mut self, job_id: String) -> Result<MLTrainingStatusResponse> {
|
|
Ok(MLTrainingStatusResponse {
|
|
job_id,
|
|
status: "RUNNING".to_string(),
|
|
progress_percentage: 25.0,
|
|
current_epoch: 10,
|
|
total_epochs: 40,
|
|
})
|
|
}
|
|
|
|
/// Stop ML training via TLI
|
|
pub async fn stop_ml_training(&mut self, job_id: String) -> Result<StopMLTrainingResponse> {
|
|
Ok(StopMLTrainingResponse {
|
|
success: true,
|
|
job_id,
|
|
message: "Training stopped successfully".to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Trading Service client wrapper
|
|
#[derive(Clone)]
|
|
pub struct TradingServiceClient {
|
|
inner: ProtoTradingServiceClient<Channel>,
|
|
endpoint: String,
|
|
}
|
|
|
|
impl TradingServiceClient {
|
|
pub fn new(channel: Channel) -> Result<Self> {
|
|
Ok(Self {
|
|
inner: ProtoTradingServiceClient::new(channel),
|
|
endpoint: "localhost:50053".to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn health_check(&self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
/// Deploy trained model to trading service
|
|
pub async fn deploy_model(&mut self, request: DeployModelRequest) -> Result<DeployModelResponse> {
|
|
Ok(DeployModelResponse {
|
|
success: true,
|
|
model_id: request.model_id,
|
|
version: "v1.0.0".to_string(),
|
|
deployment_id: "deploy-123".to_string(),
|
|
})
|
|
}
|
|
|
|
/// Get model inference results from trading service
|
|
pub async fn get_model_predictions(&mut self, request: PredictionRequest) -> Result<PredictionResponse> {
|
|
Ok(PredictionResponse {
|
|
model_id: request.model_id,
|
|
symbol: request.symbol,
|
|
prediction: "BUY".to_string(),
|
|
confidence: 0.85,
|
|
signal_strength: 0.72,
|
|
})
|
|
}
|
|
|
|
/// Update model in trading service
|
|
pub async fn update_model(&mut self, request: UpdateModelRequest) -> Result<UpdateModelResponse> {
|
|
Ok(UpdateModelResponse {
|
|
success: true,
|
|
model_id: request.model_id,
|
|
previous_version: "v1.0.0".to_string(),
|
|
new_version: "v1.1.0".to_string(),
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test request/response types (these would normally be generated from proto files)
|
|
#[derive(Debug, Clone)]
|
|
pub struct StartMLTrainingRequest {
|
|
pub model_name: String,
|
|
pub dataset_id: String,
|
|
pub hyperparameters: std::collections::HashMap<String, String>,
|
|
pub auto_deploy: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct StartMLTrainingResponse {
|
|
pub success: bool,
|
|
pub job_id: String,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MLTrainingStatusResponse {
|
|
pub job_id: String,
|
|
pub status: String,
|
|
pub progress_percentage: f64,
|
|
pub current_epoch: i32,
|
|
pub total_epochs: i32,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct StopMLTrainingResponse {
|
|
pub success: bool,
|
|
pub job_id: String,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DeployModelRequest {
|
|
pub model_id: String,
|
|
pub model_path: String,
|
|
pub target_symbols: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DeployModelResponse {
|
|
pub success: bool,
|
|
pub model_id: String,
|
|
pub version: String,
|
|
pub deployment_id: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct PredictionRequest {
|
|
pub model_id: String,
|
|
pub symbol: String,
|
|
pub features: Vec<f64>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct PredictionResponse {
|
|
pub model_id: String,
|
|
pub symbol: String,
|
|
pub prediction: String,
|
|
pub confidence: f64,
|
|
pub signal_strength: f64,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct UpdateModelRequest {
|
|
pub model_id: String,
|
|
pub new_model_path: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct UpdateModelResponse {
|
|
pub success: bool,
|
|
pub model_id: String,
|
|
pub previous_version: String,
|
|
pub new_version: String,
|
|
} |