refactor: replace all api_gateway/web-gateway references across codebase
- Rename test functions, variables, bench names (api_gateway → api) - Update e2e orchestrator executable path (target/debug/api) - Clean Grafana dashboards: remove dead web-gateway panels, fix trailing pipes/commas, update pod selectors - Update metrics_validation test metric prefixes (api_gateway_ → api_) - Fix .gitlab-ci.yml remaining path reference - Fix FXT CLI test flag and function names - Keep JWT issuer foxhunt-api-gateway (cross-service auth compat) - Keep serde alias api_gateway_url (config backwards compat) cargo check --workspace passes cleanly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -472,7 +472,7 @@ compile-trading-service:
|
||||
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
|
||||
changes:
|
||||
- services/trading_service/**
|
||||
- services/api_gateway/**
|
||||
- services/api/**
|
||||
- crates/common/**
|
||||
- crates/config/**
|
||||
- crates/data/**
|
||||
|
||||
@@ -21,7 +21,7 @@ fn test_fxt_help() {
|
||||
.stdout(predicate::str::contains(
|
||||
"Foxhunt Trading System Terminal Interface",
|
||||
))
|
||||
.stdout(predicate::str::contains("--api-gateway-url"))
|
||||
.stdout(predicate::str::contains("--api-url"))
|
||||
.stdout(predicate::str::contains("--log-level"));
|
||||
}
|
||||
|
||||
@@ -124,11 +124,11 @@ fn test_tune_requires_auth() {
|
||||
.stderr(predicate::str::contains("Not authenticated"));
|
||||
}
|
||||
|
||||
/// Test environment variable support for API Gateway URL
|
||||
/// Test environment variable support for API URL
|
||||
#[test]
|
||||
fn test_env_var_api_gateway_url() {
|
||||
fn test_env_var_api_url() {
|
||||
let mut cmd = Command::cargo_bin("fxt").unwrap();
|
||||
cmd.env("API_GATEWAY_URL", "http://test.example.com:50051")
|
||||
cmd.env("API_URL", "http://test.example.com:50051")
|
||||
.arg("--help")
|
||||
.assert()
|
||||
.success();
|
||||
@@ -149,7 +149,7 @@ fn test_env_var_log_level() {
|
||||
fn test_cli_flag_precedence() {
|
||||
let mut cmd = Command::cargo_bin("fxt").unwrap();
|
||||
cmd.env("API_GATEWAY_URL", "http://env.example.com")
|
||||
.arg("--api-gateway-url")
|
||||
.arg("--api-url")
|
||||
.arg("http://cli.example.com")
|
||||
.arg("--help")
|
||||
.assert()
|
||||
@@ -272,7 +272,7 @@ fn test_tune_stop_help() {
|
||||
#[test]
|
||||
fn test_multiple_cli_flags() {
|
||||
let mut cmd = Command::cargo_bin("fxt").unwrap();
|
||||
cmd.arg("--api-gateway-url")
|
||||
cmd.arg("--api-url")
|
||||
.arg("http://test.example.com")
|
||||
.arg("--log-level")
|
||||
.arg("debug")
|
||||
|
||||
@@ -38,7 +38,7 @@ struct JwtClaims {
|
||||
}
|
||||
|
||||
/// Check if API Gateway is reachable at localhost:50051
|
||||
async fn check_api_gateway_availability() -> Result<bool> {
|
||||
async fn check_api_availability() -> Result<bool> {
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
// Try to connect to port 50051 with 2-second timeout
|
||||
@@ -152,10 +152,10 @@ fn test_jwt_token_format_validation() {
|
||||
|
||||
/// Test: API Gateway connectivity check
|
||||
#[tokio::test]
|
||||
async fn test_api_gateway_connectivity() {
|
||||
println!("\n🧪 Testing API Gateway connectivity...");
|
||||
async fn test_api_connectivity() {
|
||||
println!("\n🧪 Testing API service connectivity...");
|
||||
|
||||
let is_available = check_api_gateway_availability()
|
||||
let is_available = check_api_availability()
|
||||
.await
|
||||
.expect("Connectivity check should not fail");
|
||||
|
||||
@@ -164,8 +164,8 @@ async fn test_api_gateway_connectivity() {
|
||||
} else {
|
||||
println!("⚠️ API Gateway not available (this is OK for CI/CD)");
|
||||
println!(" To test live connectivity:");
|
||||
println!(" 1. cargo run -p api_gateway &");
|
||||
println!(" 2. cargo test -p tli --test tune_integration_test");
|
||||
println!(" 1. cargo run -p api &");
|
||||
println!(" 2. cargo test -p fxt --test tune_integration_test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,8 +179,8 @@ async fn test_grpc_connection_mock() {
|
||||
use tonic::transport::Channel;
|
||||
|
||||
// Try to parse API Gateway URL (validates URL format)
|
||||
let api_gateway_url = "http://localhost:50051";
|
||||
let endpoint = Channel::from_shared(api_gateway_url.to_string());
|
||||
let api_url = "http://localhost:50051";
|
||||
let endpoint = Channel::from_shared(api_url.to_string());
|
||||
|
||||
assert!(endpoint.is_ok(), "API Gateway URL should be valid");
|
||||
println!("✅ gRPC endpoint URL parsing successful");
|
||||
@@ -226,27 +226,27 @@ async fn test_tuning_job_start_mock() {
|
||||
/// This test is ignored by default - run with `cargo test -- --ignored`
|
||||
#[tokio::test]
|
||||
#[ignore = "Requires API Gateway to be running (use --ignored to run)"]
|
||||
async fn test_api_gateway_connection_live() {
|
||||
println!("\n🧪 Testing live API Gateway connection...");
|
||||
async fn test_api_connection_live() {
|
||||
println!("\n🧪 Testing live API service connection...");
|
||||
|
||||
// Check if API Gateway is available
|
||||
let is_available = check_api_gateway_availability()
|
||||
let is_available = check_api_availability()
|
||||
.await
|
||||
.expect("Connectivity check should not fail");
|
||||
|
||||
if !is_available {
|
||||
println!("❌ SKIPPED: API Gateway not running");
|
||||
println!(" Start with: cargo run -p api_gateway");
|
||||
println!(" Start with: cargo run -p api");
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to establish gRPC connection
|
||||
use tonic::transport::Channel;
|
||||
|
||||
let api_gateway_url = "http://localhost:50051";
|
||||
let api_url = "http://localhost:50051";
|
||||
let channel_result = timeout(
|
||||
Duration::from_secs(5),
|
||||
Channel::from_shared(api_gateway_url.to_string())
|
||||
Channel::from_shared(api_url.to_string())
|
||||
.unwrap()
|
||||
.connect(),
|
||||
)
|
||||
@@ -332,7 +332,7 @@ fn test_integration_summary() {
|
||||
println!();
|
||||
println!("🚀 To start services:");
|
||||
println!(" docker-compose up -d");
|
||||
println!(" cargo run -p api_gateway &");
|
||||
println!(" cargo run -p api &");
|
||||
println!(" cargo run -p ml_training_service &");
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ use crate::error::CommonResult;
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `service_name` - Name of the service (e.g., "api_gateway", "trading_service")
|
||||
/// * `service_name` - Name of the service (e.g., "api", "trading_service")
|
||||
/// * `otlp_endpoint` - Optional OTLP collector endpoint (e.g., `Some("http://localhost:4317")`).
|
||||
/// When `None`, the OTLP tracing layer is disabled and only JSON logging is active.
|
||||
///
|
||||
|
||||
@@ -202,8 +202,8 @@ fn create_test_execution(order_id: u64) -> ExecutionResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// Simulate API Gateway processing (auth, rate limit, routing)
|
||||
async fn simulate_api_gateway(order: TradingOrder) -> Result<TradingOrder, String> {
|
||||
/// Simulate API service processing (auth, rate limit, routing)
|
||||
async fn simulate_api(order: TradingOrder) -> Result<TradingOrder, String> {
|
||||
// Simulate auth check (50-100μs)
|
||||
tokio::time::sleep(Duration::from_micros(75)).await;
|
||||
|
||||
@@ -254,12 +254,12 @@ async fn simulate_market_data_to_decision() -> Result<TradingOrder, String> {
|
||||
// ==================== BENCHMARK 1: API Gateway → Trading Service ====================
|
||||
//
|
||||
|
||||
/// Benchmark 1: API Gateway to Trading Service latency (target: <5ms)
|
||||
fn bench_api_gateway_to_trading(c: &mut Criterion) {
|
||||
/// Benchmark 1: API service to Trading Service latency (target: <5ms)
|
||||
fn bench_api_to_trading(c: &mut Criterion) {
|
||||
let rt = Runtime::new().unwrap();
|
||||
let trading_ops = Arc::new(TradingOperations::new());
|
||||
|
||||
c.bench_function("e2e_latency/api_gateway_to_trading", |b| {
|
||||
c.bench_function("e2e_latency/api_to_trading", |b| {
|
||||
b.iter_custom(|iters| {
|
||||
let mut metrics = LatencyMetrics::new();
|
||||
|
||||
@@ -269,7 +269,7 @@ fn bench_api_gateway_to_trading(c: &mut Criterion) {
|
||||
let start = Instant::now();
|
||||
|
||||
// Step 1: API Gateway processing
|
||||
let processed_order = simulate_api_gateway(order).await.unwrap();
|
||||
let processed_order = simulate_api(order).await.unwrap();
|
||||
|
||||
// Step 2: Trading service receives and processes order
|
||||
black_box(trading_ops.submit_order(processed_order).await).ok();
|
||||
@@ -303,7 +303,7 @@ fn bench_order_to_fill(c: &mut Criterion) {
|
||||
|
||||
// Step 1: Order placement
|
||||
let order = create_test_order(i);
|
||||
let processed_order = simulate_api_gateway(order).await.unwrap();
|
||||
let processed_order = simulate_api(order).await.unwrap();
|
||||
black_box(trading_ops.submit_order(processed_order).await).ok();
|
||||
|
||||
// Step 2: Risk check
|
||||
@@ -398,8 +398,8 @@ fn bench_component_breakdown(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("e2e_latency/components");
|
||||
let rt = Runtime::new().unwrap();
|
||||
|
||||
// Component 1: API Gateway Auth
|
||||
group.bench_function("api_gateway_auth", |b| {
|
||||
// Component 1: API Auth
|
||||
group.bench_function("api_auth", |b| {
|
||||
b.iter_custom(|iters| {
|
||||
let start = Instant::now();
|
||||
rt.block_on(async {
|
||||
@@ -507,7 +507,7 @@ fn bench_concurrent_latency(c: &mut Criterion) {
|
||||
|
||||
// Simulate full flow
|
||||
let order = create_test_order(i as u64);
|
||||
let processed = simulate_api_gateway(order).await.unwrap();
|
||||
let processed = simulate_api(order).await.unwrap();
|
||||
ops.submit_order(processed).await.ok();
|
||||
|
||||
tx.send(req_start.elapsed()).await.ok();
|
||||
@@ -554,7 +554,7 @@ fn bench_full_e2e_cycle(c: &mut Criterion) {
|
||||
let order = simulate_market_data_to_decision().await.unwrap();
|
||||
|
||||
// Step 2: API Gateway processing
|
||||
let processed_order = simulate_api_gateway(order).await.unwrap();
|
||||
let processed_order = simulate_api(order).await.unwrap();
|
||||
|
||||
// Step 3: Risk check
|
||||
simulate_risk_check(&processed_order).await.ok();
|
||||
@@ -602,7 +602,7 @@ fn bench_latency_by_load(c: &mut Criterion) {
|
||||
let order_start = Instant::now();
|
||||
|
||||
let order = create_test_order(i as u64);
|
||||
let processed = simulate_api_gateway(order).await.unwrap();
|
||||
let processed = simulate_api(order).await.unwrap();
|
||||
black_box(trading_ops.submit_order(processed).await).ok();
|
||||
|
||||
metrics.record_latency(order_start.elapsed());
|
||||
@@ -621,7 +621,7 @@ fn bench_latency_by_load(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
// Criterion benchmark groups
|
||||
criterion_group!(api_gateway_benches, bench_api_gateway_to_trading,);
|
||||
criterion_group!(api_benches, bench_api_to_trading,);
|
||||
|
||||
criterion_group!(order_flow_benches, bench_order_to_fill,);
|
||||
|
||||
@@ -640,7 +640,7 @@ criterion_group!(
|
||||
criterion_group!(e2e_benches, bench_full_e2e_cycle,);
|
||||
|
||||
criterion_main!(
|
||||
api_gateway_benches,
|
||||
api_benches,
|
||||
order_flow_benches,
|
||||
strategy_benches,
|
||||
risk_benches,
|
||||
|
||||
@@ -463,7 +463,7 @@
|
||||
{
|
||||
"id": 9,
|
||||
"type": "stat",
|
||||
"title": "api-gateway",
|
||||
"title": "api",
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 3,
|
||||
@@ -527,7 +527,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "Service Detail",
|
||||
"url": "/d/foxhunt-services?var-service=api-gateway",
|
||||
"url": "/d/foxhunt-services?var-service=api",
|
||||
"targetBlank": false
|
||||
}
|
||||
],
|
||||
@@ -538,7 +538,7 @@
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(kube_pod_status_phase{namespace=\"foxhunt\", pod=~\"api-gateway.*\", phase=\"Running\"}) > 0 or vector(0)",
|
||||
"expr": "sum(kube_pod_status_phase{namespace=\"foxhunt\", pod=~\"api.*\", phase=\"Running\"}) > 0 or vector(0)",
|
||||
"instant": true
|
||||
}
|
||||
]
|
||||
@@ -1041,89 +1041,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "stat",
|
||||
"title": "web-gateway",
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 3,
|
||||
"x": 21,
|
||||
"y": 5
|
||||
},
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "red",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"0": {
|
||||
"text": "DOWN",
|
||||
"color": "red"
|
||||
},
|
||||
"1": {
|
||||
"text": "UP",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"orientation": "auto",
|
||||
"textMode": "auto",
|
||||
"colorMode": "background",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "center"
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"title": "Service Detail",
|
||||
"url": "/d/foxhunt-services?var-service=web-gateway",
|
||||
"targetBlank": false
|
||||
}
|
||||
],
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(kube_pod_status_phase{namespace=\"foxhunt\", pod=~\"web-gateway.*\", phase=\"Running\"}) > 0 or vector(0)",
|
||||
"instant": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 100,
|
||||
"type": "row",
|
||||
@@ -2387,7 +2304,7 @@
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum by (pod) (container_memory_working_set_bytes{namespace=\"foxhunt\",container!=\"POD\",container!=\"\",pod=~\"(api-gateway|trading-service|ml-training|backtesting|trading-agent|broker-gateway|data-acquisition|web-gateway).*\"})",
|
||||
"expr": "sum by (pod) (container_memory_working_set_bytes{namespace=\"foxhunt\",container!=\"POD\",container!=\"\",pod=~\"(api|trading-service|ml-training|backtesting|trading-agent|broker-gateway|data-acquisition).*\"})",
|
||||
"legendFormat": "{{pod}}",
|
||||
"range": true
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
{
|
||||
"name": "service",
|
||||
"type": "custom",
|
||||
"query": "api-gateway,trading-service,ml-training-service,backtesting-service,trading-agent-service,broker-gateway,data-acquisition-service,web-gateway",
|
||||
"query": "api,trading-service,ml-training-service,backtesting-service,trading-agent-service,broker-gateway,data-acquisition-service",
|
||||
"current": {
|
||||
"selected": false,
|
||||
"text": "All",
|
||||
|
||||
@@ -460,7 +460,7 @@
|
||||
{
|
||||
"id": 11,
|
||||
"type": "stat",
|
||||
"title": "api-gateway",
|
||||
"title": "api",
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 3,
|
||||
@@ -528,7 +528,7 @@
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(kube_pod_status_phase{namespace=\"foxhunt\", pod=~\"api-gateway.*\", phase=\"Running\"}) > 0 or vector(0)",
|
||||
"expr": "sum(kube_pod_status_phase{namespace=\"foxhunt\", pod=~\"api.*\", phase=\"Running\"}) > 0 or vector(0)",
|
||||
"legendFormat": "",
|
||||
"instant": true
|
||||
}
|
||||
@@ -996,83 +996,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "stat",
|
||||
"title": "web-gateway",
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 3,
|
||||
"x": 21,
|
||||
"y": 6
|
||||
},
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "red",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "green",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"type": "value",
|
||||
"options": {
|
||||
"0": {
|
||||
"text": "DOWN",
|
||||
"color": "red"
|
||||
},
|
||||
"1": {
|
||||
"text": "UP",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"lastNotNull"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"orientation": "auto",
|
||||
"textMode": "auto",
|
||||
"colorMode": "background",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "center"
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum(kube_pod_status_phase{namespace=\"foxhunt\", pod=~\"web-gateway.*\", phase=\"Running\"}) > 0 or vector(0)",
|
||||
"legendFormat": "",
|
||||
"instant": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "row",
|
||||
@@ -1429,7 +1352,7 @@
|
||||
"type": "prometheus",
|
||||
"uid": "${DS_PROMETHEUS}"
|
||||
},
|
||||
"expr": "sum by (container) (rate(container_cpu_usage_seconds_total{namespace=\"foxhunt\", container=~\"api-gateway|trading-service|ml-training-service|backtesting-service|trading-agent-service|broker-gateway|data-acquisition-service|web-gateway\", image!=\"\"}[$interval]))",
|
||||
"expr": "sum by (container) (rate(container_cpu_usage_seconds_total{namespace=\"foxhunt\", container=~\"api|trading-service|ml-training-service|backtesting-service|trading-agent-service|broker-gateway|data-acquisition-service\", image!=\"\"}[$interval]))",
|
||||
"legendFormat": "{{ container }}"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -40,11 +40,11 @@
|
||||
{
|
||||
"name": "service",
|
||||
"type": "custom",
|
||||
"query": "api-gateway,trading-service,ml-training-service,backtesting-service,trading-agent-service,broker-gateway,data-acquisition-service,web-gateway",
|
||||
"query": "api,trading-service,ml-training-service,backtesting-service,trading-agent-service,broker-gateway,data-acquisition-service",
|
||||
"current": {
|
||||
"selected": true,
|
||||
"text": "api-gateway",
|
||||
"value": "api-gateway"
|
||||
"text": "api",
|
||||
"value": "api"
|
||||
},
|
||||
"multi": false,
|
||||
"includeAll": false,
|
||||
|
||||
@@ -103,7 +103,7 @@ impl JwtConfig {
|
||||
|
||||
Ok(Self {
|
||||
jwt_secret,
|
||||
jwt_issuer: "foxhunt-api".to_string(),
|
||||
jwt_issuer: "foxhunt-api-gateway".to_string(),
|
||||
jwt_audience: "foxhunt-services".to_string(),
|
||||
})
|
||||
}
|
||||
@@ -454,7 +454,7 @@ mod tests {
|
||||
.await
|
||||
.expect("Should create config with valid JWT_SECRET");
|
||||
|
||||
assert_eq!(config.jwt_issuer, "foxhunt-api");
|
||||
assert_eq!(config.jwt_issuer, "foxhunt-api-gateway");
|
||||
assert_eq!(config.jwt_audience, "foxhunt-services");
|
||||
assert!(config.jwt_secret.len() >= 64);
|
||||
|
||||
@@ -484,7 +484,7 @@ mod tests {
|
||||
match result {
|
||||
Ok(config) => {
|
||||
// Verify config was loaded successfully
|
||||
assert_eq!(config.jwt_issuer, "foxhunt-api");
|
||||
assert_eq!(config.jwt_issuer, "foxhunt-api-gateway");
|
||||
assert_eq!(config.jwt_audience, "foxhunt-services");
|
||||
assert!(config.jwt_secret.len() >= 64);
|
||||
|
||||
|
||||
@@ -842,7 +842,7 @@ impl MonitoringService for MonitoringServiceImpl {
|
||||
|
||||
// ========================================================================
|
||||
// Training metrics RPCs (not applicable to trading_service; served by
|
||||
// api_gateway which queries Prometheus directly). Return UNIMPLEMENTED.
|
||||
// api service which queries Prometheus directly). Return UNIMPLEMENTED.
|
||||
// ========================================================================
|
||||
|
||||
type StreamTrainingMetricsStream = std::pin::Pin<
|
||||
|
||||
@@ -197,7 +197,7 @@ fn bench_component_breakdown(c: &mut Criterion) {
|
||||
});
|
||||
|
||||
// Component 3: API Gateway auth (measured: 3.1μs P99)
|
||||
group.bench_function("api_gateway_auth", |b| {
|
||||
group.bench_function("api_auth", |b| {
|
||||
b.iter_custom(|iters| {
|
||||
let start = Instant::now();
|
||||
rt.block_on(async {
|
||||
|
||||
@@ -194,7 +194,7 @@ async fn start_services(matches: &ArgMatches) -> Result<()> {
|
||||
let background = matches.get_flag("background");
|
||||
|
||||
// API Gateway gets port 50051, backend services start at 50052
|
||||
let api_gateway_port: u16 = 50051;
|
||||
let api_port: u16 = 50051;
|
||||
let backend_base_port: u16 = 50052;
|
||||
|
||||
// Get JWT_SECRET from environment (required for API Gateway)
|
||||
@@ -209,7 +209,7 @@ async fn start_services(matches: &ArgMatches) -> Result<()> {
|
||||
let ml_training_service_url = format!("http://localhost:{}", backend_base_port + 2);
|
||||
|
||||
info!("Starting services: {}", services_arg);
|
||||
info!("API Gateway port: {}", api_gateway_port);
|
||||
info!("API service port: {}", api_port);
|
||||
info!("Backend services starting at port: {}", backend_base_port);
|
||||
|
||||
info!("Background mode: {}", background);
|
||||
@@ -245,41 +245,41 @@ async fn start_services(matches: &ArgMatches) -> Result<()> {
|
||||
|
||||
// Start API Gateway FIRST if requested
|
||||
if services_to_start.contains(&ServiceType::ApiGateway) || services_to_start.len() > 1 {
|
||||
info!("Starting API Gateway on port {}...", api_gateway_port);
|
||||
info!("Starting API service on port {}...", api_port);
|
||||
|
||||
let mut api_gateway_env = HashMap::new();
|
||||
api_gateway_env.insert("JWT_SECRET".to_string(), jwt_secret.clone());
|
||||
api_gateway_env.insert(
|
||||
let mut api_env = HashMap::new();
|
||||
api_env.insert("JWT_SECRET".to_string(), jwt_secret.clone());
|
||||
api_env.insert(
|
||||
"TRADING_SERVICE_URL".to_string(),
|
||||
trading_service_url.clone(),
|
||||
);
|
||||
api_gateway_env.insert(
|
||||
api_env.insert(
|
||||
"BACKTESTING_SERVICE_URL".to_string(),
|
||||
backtesting_service_url.clone(),
|
||||
);
|
||||
api_gateway_env.insert(
|
||||
api_env.insert(
|
||||
"ML_TRAINING_SERVICE_URL".to_string(),
|
||||
ml_training_service_url.clone(),
|
||||
);
|
||||
api_gateway_env.insert("GRPC_PORT".to_string(), api_gateway_port.to_string());
|
||||
api_gateway_env.insert("HTTP_PORT".to_string(), "8080".to_string());
|
||||
api_gateway_env.insert("METRICS_PORT".to_string(), "9091".to_string());
|
||||
api_gateway_env.insert("RUST_LOG".to_string(), "info".to_string());
|
||||
api_gateway_env.insert("FOXHUNT_TEST_MODE".to_string(), "true".to_string());
|
||||
api_env.insert("GRPC_PORT".to_string(), api_port.to_string());
|
||||
api_env.insert("HTTP_PORT".to_string(), "8080".to_string());
|
||||
api_env.insert("METRICS_PORT".to_string(), "9091".to_string());
|
||||
api_env.insert("RUST_LOG".to_string(), "info".to_string());
|
||||
api_env.insert("FOXHUNT_TEST_MODE".to_string(), "true".to_string());
|
||||
|
||||
let api_gateway_config = ServiceConfig {
|
||||
let api_config = ServiceConfig {
|
||||
service_type: ServiceType::ApiGateway,
|
||||
executable_path: "target/debug/api_gateway".to_string(),
|
||||
port: api_gateway_port,
|
||||
executable_path: "target/debug/api".to_string(),
|
||||
port: api_port,
|
||||
health_endpoint: "http://localhost:8080/health".to_string(),
|
||||
startup_timeout: Duration::from_secs(30),
|
||||
environment: api_gateway_env,
|
||||
environment: api_env,
|
||||
working_directory: std::env::current_dir()?,
|
||||
log_file: Some("/tmp/foxhunt_api_gateway_service.log".to_string()),
|
||||
log_file: Some("/tmp/foxhunt_api_service.log".to_string()),
|
||||
};
|
||||
|
||||
service_manager.start_service(api_gateway_config).await?;
|
||||
profiler.checkpoint("api_gateway_started");
|
||||
service_manager.start_service(api_config).await?;
|
||||
profiler.checkpoint("api_started");
|
||||
|
||||
// Wait for API Gateway to be ready
|
||||
if wait_ready {
|
||||
@@ -699,7 +699,7 @@ fn parse_service_list(services_str: &str) -> Result<Vec<ServiceType>> {
|
||||
];
|
||||
break;
|
||||
},
|
||||
"api_gateway" | "gateway" => services.push(ServiceType::ApiGateway),
|
||||
"api" | "api_gateway" | "gateway" => services.push(ServiceType::ApiGateway),
|
||||
"trading" => services.push(ServiceType::TradingService),
|
||||
"backtesting" => services.push(ServiceType::BacktestingService),
|
||||
"ml_training" | "ml" => services.push(ServiceType::MLTrainingService),
|
||||
@@ -715,7 +715,7 @@ fn create_service_config(service_type: &ServiceType, port: u16) -> Result<Servic
|
||||
let (health_endpoint, executable_path) = match service_type {
|
||||
ServiceType::ApiGateway => (
|
||||
"http://localhost:8080/health".to_string(),
|
||||
"api_gateway".to_string(),
|
||||
"api".to_string(),
|
||||
),
|
||||
ServiceType::TradingService => (
|
||||
"http://localhost:8081/health".to_string(),
|
||||
|
||||
@@ -24,7 +24,7 @@ pub enum ServiceType {
|
||||
impl ServiceType {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
ServiceType::ApiGateway => "api_gateway",
|
||||
ServiceType::ApiGateway => "api",
|
||||
ServiceType::TradingService => "trading",
|
||||
ServiceType::BacktestingService => "backtesting",
|
||||
ServiceType::MLTrainingService => "ml_training",
|
||||
|
||||
@@ -627,7 +627,7 @@ lazy_static::lazy_static! {
|
||||
pub static ref TEST_PORT_MANAGER: TestPortManager = TestPortManager::new();
|
||||
}
|
||||
|
||||
// TestEventPublisher removed — event streaming moved to web-gateway WebSocket infrastructure
|
||||
// TestEventPublisher removed — event streaming moved to gRPC server-streaming in api service
|
||||
|
||||
/// Performance metrics collector for tests
|
||||
#[derive(Debug, Default)]
|
||||
|
||||
@@ -28,7 +28,7 @@ pub struct MetricsValidationResult {
|
||||
|
||||
/// Required metrics per service
|
||||
pub struct RequiredMetrics {
|
||||
pub api_gateway: Vec<&'static str>,
|
||||
pub api: Vec<&'static str>,
|
||||
pub trading_service: Vec<&'static str>,
|
||||
pub backtesting_service: Vec<&'static str>,
|
||||
pub ml_training_service: Vec<&'static str>,
|
||||
@@ -43,15 +43,15 @@ impl Default for RequiredMetrics {
|
||||
impl RequiredMetrics {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
api_gateway: vec![
|
||||
"api_gateway_auth_attempts_total",
|
||||
"api_gateway_auth_duration_milliseconds",
|
||||
"api_gateway_backend_requests_total",
|
||||
"api_gateway_backend_request_duration_milliseconds",
|
||||
"api_gateway_circuit_breaker_state",
|
||||
"api_gateway_connection_pool_active",
|
||||
"api_gateway_health_status",
|
||||
"api_gateway_rate_limit_hits",
|
||||
api: vec![
|
||||
"api_auth_attempts_total",
|
||||
"api_auth_duration_milliseconds",
|
||||
"api_backend_requests_total",
|
||||
"api_backend_request_duration_milliseconds",
|
||||
"api_circuit_breaker_state",
|
||||
"api_connection_pool_active",
|
||||
"api_health_status",
|
||||
"api_rate_limit_hits",
|
||||
],
|
||||
trading_service: vec![
|
||||
"trading_orders_submitted_total",
|
||||
@@ -76,7 +76,7 @@ impl RequiredMetrics {
|
||||
|
||||
pub fn get_required(&self, service: &str) -> Vec<&'static str> {
|
||||
match service {
|
||||
"api_gateway" => self.api_gateway.clone(),
|
||||
"api" => self.api.clone(),
|
||||
"trading_service" => self.trading_service.clone(),
|
||||
"backtesting_service" => self.backtesting_service.clone(),
|
||||
"ml_training_service" => self.ml_training_service.clone(),
|
||||
@@ -294,15 +294,15 @@ mod tests {
|
||||
#[test]
|
||||
fn test_metrics_parser() {
|
||||
let content = r#"
|
||||
# HELP api_gateway_requests_total Total HTTP requests
|
||||
# TYPE api_gateway_requests_total counter
|
||||
api_gateway_requests_total{method="GET",status="200"} 1234
|
||||
api_gateway_requests_total{method="POST",status="201"} 567
|
||||
# HELP api_requests_total Total HTTP requests
|
||||
# TYPE api_requests_total counter
|
||||
api_requests_total{method="GET",status="200"} 1234
|
||||
api_requests_total{method="POST",status="201"} 567
|
||||
|
||||
# HELP api_gateway_latency_seconds Request latency
|
||||
# TYPE api_gateway_latency_seconds histogram
|
||||
api_gateway_latency_seconds{quantile="0.5"} 0.05
|
||||
api_gateway_latency_seconds{quantile="0.99"} 0.5
|
||||
# HELP api_latency_seconds Request latency
|
||||
# TYPE api_latency_seconds histogram
|
||||
api_latency_seconds{quantile="0.5"} 0.05
|
||||
api_latency_seconds{quantile="0.99"} 0.5
|
||||
"#;
|
||||
|
||||
let metrics = MetricsParser::parse(content);
|
||||
@@ -310,14 +310,14 @@ api_gateway_latency_seconds{quantile="0.99"} 0.5
|
||||
assert_eq!(metrics.len(), 4);
|
||||
|
||||
// Check first metric
|
||||
assert_eq!(metrics[0].name, "api_gateway_requests_total");
|
||||
assert_eq!(metrics[0].name, "api_requests_total");
|
||||
assert_eq!(metrics[0].value, 1234.0);
|
||||
assert_eq!(metrics[0].metric_type, Some("counter".to_owned()));
|
||||
assert_eq!(metrics[0].labels.get("method"), Some(&"GET".to_owned()));
|
||||
assert_eq!(metrics[0].labels.get("status"), Some(&"200".to_owned()));
|
||||
|
||||
// Check histogram metric
|
||||
assert_eq!(metrics[2].name, "api_gateway_latency_seconds");
|
||||
assert_eq!(metrics[2].name, "api_latency_seconds");
|
||||
assert_eq!(metrics[2].labels.get("quantile"), Some(&"0.5".to_owned()));
|
||||
assert_eq!(metrics[2].value, 0.05);
|
||||
}
|
||||
@@ -326,9 +326,9 @@ api_gateway_latency_seconds{quantile="0.99"} 0.5
|
||||
fn test_required_metrics() {
|
||||
let required = RequiredMetrics::new();
|
||||
|
||||
let api_metrics = required.get_required("api_gateway");
|
||||
assert!(api_metrics.contains(&"api_gateway_auth_attempts_total"));
|
||||
assert!(api_metrics.contains(&"api_gateway_circuit_breaker_state"));
|
||||
let api_metrics = required.get_required("api");
|
||||
assert!(api_metrics.contains(&"api_auth_attempts_total"));
|
||||
assert!(api_metrics.contains(&"api_circuit_breaker_state"));
|
||||
|
||||
let trading_metrics = required.get_required("trading_service");
|
||||
assert!(trading_metrics.contains(&"trading_orders_submitted_total"));
|
||||
@@ -363,14 +363,14 @@ api_gateway_latency_seconds{quantile="0.99"} 0.5
|
||||
async fn test_api_metrics() {
|
||||
let required = RequiredMetrics::new();
|
||||
let result = validate_service_metrics(
|
||||
"api_gateway",
|
||||
"api",
|
||||
"http://localhost:9091/metrics",
|
||||
&required.api_gateway,
|
||||
&required.api,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Ok(validation) = result {
|
||||
println!("API Gateway Metrics Validation:");
|
||||
println!("API Service Metrics Validation:");
|
||||
println!(" Total Metrics: {}", validation.total_metrics);
|
||||
println!(" Scrape Duration: {}ms", validation.scrape_duration_ms);
|
||||
println!(" Success: {}", validation.success);
|
||||
@@ -389,7 +389,7 @@ api_gateway_latency_seconds{quantile="0.99"} 0.5
|
||||
}
|
||||
}
|
||||
|
||||
assert!(validation.success, "API Gateway metrics validation failed");
|
||||
assert!(validation.success, "API service metrics validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,9 +424,9 @@ api_gateway_latency_seconds{quantile="0.99"} 0.5
|
||||
|
||||
let services = vec![
|
||||
(
|
||||
"api_gateway",
|
||||
"api",
|
||||
"http://localhost:9091/metrics",
|
||||
required.api_gateway,
|
||||
required.api,
|
||||
),
|
||||
(
|
||||
"trading_service",
|
||||
|
||||
Reference in New Issue
Block a user