feat(billing): implement tenant subscription entitlements system (milestones 0-6)
This commit is contained in:
157
control/api/tests/control_api_smoke_env_gated.rs
Normal file
157
control/api/tests/control_api_smoke_env_gated.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use jsonwebtoken::{EncodingKey, Header, encode};
|
||||
use reqwest::StatusCode;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TestClaims {
|
||||
sub: String,
|
||||
session_id: String,
|
||||
permissions: Vec<String>,
|
||||
exp: usize,
|
||||
}
|
||||
|
||||
fn make_token(secret: &[u8], perms: &[&str]) -> String {
|
||||
let exp = (std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
+ 300) as usize;
|
||||
encode(
|
||||
&Header::default(),
|
||||
&TestClaims {
|
||||
sub: "smoke".to_string(),
|
||||
session_id: "smoke".to_string(),
|
||||
permissions: perms.iter().map(|p| (*p).to_string()).collect(),
|
||||
exp,
|
||||
},
|
||||
&EncodingKey::from_secret(secret),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn control_api_docs_smoke_is_env_gated() {
|
||||
let enabled = std::env::var("CONTROL_TEST_SMOKE").ok();
|
||||
if enabled.as_deref() != Some("1") {
|
||||
eprintln!("skipping: set CONTROL_TEST_SMOKE=1 to enable env smoke tests");
|
||||
return;
|
||||
}
|
||||
|
||||
let base_url =
|
||||
std::env::var("CONTROL_TEST_BASE_URL").expect("CONTROL_TEST_BASE_URL is required");
|
||||
let base_url = base_url.trim_end_matches('/').to_string();
|
||||
|
||||
// Either provide a token directly, or provide secret+perms to mint one.
|
||||
let token = if let Ok(t) = std::env::var("CONTROL_TEST_TOKEN") {
|
||||
t
|
||||
} else {
|
||||
let secret = std::env::var("CONTROL_TEST_JWT_SECRET")
|
||||
.expect("CONTROL_TEST_TOKEN or CONTROL_TEST_JWT_SECRET is required");
|
||||
make_token(secret.as_bytes(), &["control:read", "control:write"])
|
||||
};
|
||||
|
||||
let tenant_id = std::env::var("CONTROL_TEST_TENANT_ID")
|
||||
.ok()
|
||||
.unwrap_or_else(|| Uuid::new_v4().to_string());
|
||||
|
||||
let http = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(15))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Health.
|
||||
let health = http
|
||||
.get(format!("{base_url}/health"))
|
||||
.send()
|
||||
.await
|
||||
.expect("health request failed");
|
||||
assert!(health.status().is_success(), "health not ok");
|
||||
|
||||
// Presign upload.
|
||||
let doc_id = Uuid::new_v4().to_string();
|
||||
let filename = "smoke.txt";
|
||||
let presign_up = http
|
||||
.post(format!(
|
||||
"{base_url}/admin/v1/tenants/{tenant_id}/docs/presign/upload"
|
||||
))
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("x-tenant-id", &tenant_id)
|
||||
.json(&json!({
|
||||
"doc_type": "deployments",
|
||||
"doc_id": doc_id,
|
||||
"filename": filename,
|
||||
"content_type": "text/plain",
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.expect("presign upload failed");
|
||||
assert!(
|
||||
presign_up.status().is_success(),
|
||||
"presign upload not ok: {}",
|
||||
presign_up.status()
|
||||
);
|
||||
let up_json: serde_json::Value = presign_up.json().await.unwrap();
|
||||
let put_url = up_json.get("url").and_then(|v| v.as_str()).unwrap();
|
||||
let key = up_json
|
||||
.get("key")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
// PUT bytes to S3 directly.
|
||||
let payload = b"hello-smoke".to_vec();
|
||||
let put = http
|
||||
.put(put_url)
|
||||
.header("content-type", "text/plain")
|
||||
.body(payload.clone())
|
||||
.send()
|
||||
.await
|
||||
.expect("s3 put failed");
|
||||
assert!(put.status().is_success(), "s3 put not ok: {}", put.status());
|
||||
|
||||
// List should include key.
|
||||
let list = http
|
||||
.get(format!(
|
||||
"{base_url}/admin/v1/tenants/{tenant_id}/docs?prefix=deployments/"
|
||||
))
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("x-tenant-id", &tenant_id)
|
||||
.send()
|
||||
.await
|
||||
.expect("list failed");
|
||||
assert!(list.status().is_success(), "list not ok");
|
||||
let list_json: serde_json::Value = list.json().await.unwrap();
|
||||
let objects = list_json.get("objects").and_then(|v| v.as_array()).unwrap();
|
||||
assert!(
|
||||
objects
|
||||
.iter()
|
||||
.any(|o| o.get("key").and_then(|k| k.as_str()) == Some(key.as_str())),
|
||||
"expected list to include presigned upload key"
|
||||
);
|
||||
|
||||
// Presign download and fetch bytes.
|
||||
let presign_down = http
|
||||
.post(format!(
|
||||
"{base_url}/admin/v1/tenants/{tenant_id}/docs/presign/download"
|
||||
))
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("x-tenant-id", &tenant_id)
|
||||
.json(&json!({ "key": key }))
|
||||
.send()
|
||||
.await
|
||||
.expect("presign download failed");
|
||||
assert!(
|
||||
presign_down.status().is_success(),
|
||||
"presign download not ok"
|
||||
);
|
||||
let down_json: serde_json::Value = presign_down.json().await.unwrap();
|
||||
let get_url = down_json.get("url").and_then(|v| v.as_str()).unwrap();
|
||||
|
||||
let got = http.get(get_url).send().await.expect("s3 get failed");
|
||||
assert_eq!(got.status(), StatusCode::OK);
|
||||
let got_bytes = got.bytes().await.unwrap().to_vec();
|
||||
assert_eq!(got_bytes, payload);
|
||||
}
|
||||
Reference in New Issue
Block a user