PSA/ee/runner/tests/api_execution_integration.rs
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

118 lines
4.3 KiB
Rust

use alga_ext_runner::models::{ExecuteContext, ExecuteRequest, HttpPayload, Limits};
use reqwest::Client;
use std::collections::HashMap;
use std::time::Duration;
use tokio::net::TcpListener;
use tokio::task::JoinHandle;
/// Helper to start the runner server in a background task.
/// Returns the base URL of the runner and a shutdown handle.
async fn start_runner_server() -> anyhow::Result<(String, JoinHandle<()>)> {
// Pick a random port for the runner
let port = {
let listener = TcpListener::bind("127.0.0.1:0").await?;
let addr = listener.local_addr()?;
addr.port()
}; // Listener is dropped here, freeing the port
// Set necessary environment variables for the runner configuration
// In a real test, we would likely mock the registry and bundle store too.
unsafe {
std::env::set_var("PORT", port.to_string());
std::env::set_var("REGISTRY_BASE_URL", "http://127.0.0.1:9999"); // Dummy
std::env::set_var("BUNDLE_STORE_BASE", "http://127.0.0.1:9998"); // Dummy
std::env::set_var("ALGA_AUTH_KEY", "test-key");
std::env::set_var("EXT_CACHE_ROOT", std::env::temp_dir().join("alga-runner-test"));
// Disable strict validation if needed, or just let it fail later
}
// Spawn the runner
let handle = tokio::spawn(async move {
if let Err(e) = alga_ext_runner::http::server::run().await {
eprintln!("Runner server exited with error: {}", e);
}
});
// Give it a moment to start listening
let client = Client::builder()
.timeout(Duration::from_secs(1))
.build()?;
let base_url = format!("http://127.0.0.1:{}", port);
let start = std::time::Instant::now();
loop {
if handle.is_finished() {
anyhow::bail!("Runner server task exited prematurely");
}
if start.elapsed() > Duration::from_secs(10) {
anyhow::bail!("Runner failed to start within 10 seconds");
}
// We check healthz. It might return 503 if dependencies (registry) are unreachable,
// but that proves the HTTP server is UP.
match client.get(format!("{}/healthz", base_url)).send().await {
Ok(resp) => {
// 503 is expected since we have dummy dependencies
if resp.status().is_success() || resp.status() == reqwest::StatusCode::SERVICE_UNAVAILABLE {
break;
}
}
Err(_) => tokio::time::sleep(Duration::from_millis(100)).await,
}
}
Ok((base_url, handle))
}
#[tokio::test]
async fn test_execute_endpoint_structure() -> anyhow::Result<()> {
let (base_url, _server_handle) = start_runner_server().await?;
let client = Client::new();
let req_body = ExecuteRequest {
context: ExecuteContext {
request_id: Some("api-test".to_string()),
tenant_id: "tenant-1".to_string(),
extension_id: "ext-1".to_string(),
install_id: Some("inst-1".to_string()),
content_hash: "sha256:dummy".to_string(),
version_id: Some("v1".to_string()),
config: HashMap::new(),
},
http: HttpPayload {
method: "POST".to_string(),
path: "/foo".to_string(),
query: HashMap::new(),
headers: HashMap::new(),
body_b64: None,
},
limits: Limits::default(),
secret_envelope: None,
providers: vec![],
user: None,
};
let resp = client.post(format!("{}/v1/execute", base_url))
.header("x-request-id", "req-123")
.header("x-alga-tenant", "tenant-1")
.header("x-alga-extension", "ext-1")
.json(&req_body)
.send()
.await?;
let status = resp.status();
println!("Runner response status: {}", status);
// Since our "registry" is 127.0.0.1:9999 (which likely doesn't exist),
// the runner should fail inside `execute` when trying to fetch the bundle or init the engine.
// But the *HTTP endpoint* should be reachable.
// It will likely return 500 or 502.
// Crucially, it should NOT be 404.
assert!(status != reqwest::StatusCode::NOT_FOUND);
if let Ok(body_text) = resp.text().await {
println!("Runner response body: {}", body_text);
}
Ok(())
}