Stabilize integration test qualification
This commit is contained in:
parent
414b380516
commit
560e1df9b2
4 changed files with 68 additions and 180 deletions
|
|
@ -11,6 +11,13 @@ pub fn required_env(name: &str) -> String {
|
||||||
std::env::var(name).unwrap_or_else(|_| panic!("{name} is required"))
|
std::env::var(name).unwrap_or_else(|_| panic!("{name} is required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keycloak_admin_url() -> String {
|
||||||
|
std::env::var("REMORA_KEYCLOAK_ADMIN_URL")
|
||||||
|
.unwrap_or_else(|_| "http://keycloak.remora.test:8080".to_owned())
|
||||||
|
.trim_end_matches('/')
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn oidc_provider() -> OidcIdentityProvider {
|
pub fn oidc_provider() -> OidcIdentityProvider {
|
||||||
let provider = required_env("REMORA_OIDC_PROVIDER");
|
let provider = required_env("REMORA_OIDC_PROVIDER");
|
||||||
let audience = if provider == "zitadel" {
|
let audience = if provider == "zitadel" {
|
||||||
|
|
@ -179,9 +186,12 @@ async fn revoke_keycloak_remora_roles() -> Result<(), String> {
|
||||||
let realm = required_env("REMORA_OIDC_TENANT");
|
let realm = required_env("REMORA_OIDC_TENANT");
|
||||||
let client_id = required_env("REMORA_OIDC_CLIENT_ID");
|
let client_id = required_env("REMORA_OIDC_CLIENT_ID");
|
||||||
let username = required_env("REMORA_OIDC_USERNAME");
|
let username = required_env("REMORA_OIDC_USERNAME");
|
||||||
|
let keycloak_url = keycloak_admin_url();
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let admin_response = client
|
let admin_response = client
|
||||||
.post("http://keycloak.remora.test:8080/realms/master/protocol/openid-connect/token")
|
.post(format!(
|
||||||
|
"{keycloak_url}/realms/master/protocol/openid-connect/token"
|
||||||
|
))
|
||||||
.form(&[
|
.form(&[
|
||||||
("grant_type", "password"),
|
("grant_type", "password"),
|
||||||
("client_id", "admin-cli"),
|
("client_id", "admin-cli"),
|
||||||
|
|
@ -200,7 +210,7 @@ async fn revoke_keycloak_remora_roles() -> Result<(), String> {
|
||||||
.ok_or_else(|| "keycloak admin token missing".to_owned())?;
|
.ok_or_else(|| "keycloak admin token missing".to_owned())?;
|
||||||
let users: Value = client
|
let users: Value = client
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users?username={username}&exact=true"
|
"{keycloak_url}/admin/realms/{realm}/users?username={username}&exact=true"
|
||||||
))
|
))
|
||||||
.bearer_auth(admin_token)
|
.bearer_auth(admin_token)
|
||||||
.send()
|
.send()
|
||||||
|
|
@ -216,7 +226,7 @@ async fn revoke_keycloak_remora_roles() -> Result<(), String> {
|
||||||
.ok_or_else(|| "keycloak user missing".to_owned())?;
|
.ok_or_else(|| "keycloak user missing".to_owned())?;
|
||||||
let clients: Value = client
|
let clients: Value = client
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/clients?clientId={client_id}"
|
"{keycloak_url}/admin/realms/{realm}/clients?clientId={client_id}"
|
||||||
))
|
))
|
||||||
.bearer_auth(admin_token)
|
.bearer_auth(admin_token)
|
||||||
.send()
|
.send()
|
||||||
|
|
@ -231,7 +241,7 @@ async fn revoke_keycloak_remora_roles() -> Result<(), String> {
|
||||||
.and_then(|row| row["id"].as_str())
|
.and_then(|row| row["id"].as_str())
|
||||||
.ok_or_else(|| "keycloak client missing".to_owned())?;
|
.ok_or_else(|| "keycloak client missing".to_owned())?;
|
||||||
let roles_url = format!(
|
let roles_url = format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
"{keycloak_url}/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
||||||
);
|
);
|
||||||
let mapped: Value = client
|
let mapped: Value = client
|
||||||
.get(&roles_url)
|
.get(&roles_url)
|
||||||
|
|
@ -274,9 +284,12 @@ async fn restore_keycloak_remora_roles() -> Result<(), String> {
|
||||||
let realm = required_env("REMORA_OIDC_TENANT");
|
let realm = required_env("REMORA_OIDC_TENANT");
|
||||||
let client_id = required_env("REMORA_OIDC_CLIENT_ID");
|
let client_id = required_env("REMORA_OIDC_CLIENT_ID");
|
||||||
let username = required_env("REMORA_OIDC_USERNAME");
|
let username = required_env("REMORA_OIDC_USERNAME");
|
||||||
|
let keycloak_url = keycloak_admin_url();
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
let admin_response = client
|
let admin_response = client
|
||||||
.post("http://keycloak.remora.test:8080/realms/master/protocol/openid-connect/token")
|
.post(format!(
|
||||||
|
"{keycloak_url}/realms/master/protocol/openid-connect/token"
|
||||||
|
))
|
||||||
.form(&[
|
.form(&[
|
||||||
("grant_type", "password"),
|
("grant_type", "password"),
|
||||||
("client_id", "admin-cli"),
|
("client_id", "admin-cli"),
|
||||||
|
|
@ -295,7 +308,7 @@ async fn restore_keycloak_remora_roles() -> Result<(), String> {
|
||||||
.ok_or_else(|| "keycloak admin token missing".to_owned())?;
|
.ok_or_else(|| "keycloak admin token missing".to_owned())?;
|
||||||
let users: Value = client
|
let users: Value = client
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users?username={username}&exact=true"
|
"{keycloak_url}/admin/realms/{realm}/users?username={username}&exact=true"
|
||||||
))
|
))
|
||||||
.bearer_auth(admin_token)
|
.bearer_auth(admin_token)
|
||||||
.send()
|
.send()
|
||||||
|
|
@ -311,7 +324,7 @@ async fn restore_keycloak_remora_roles() -> Result<(), String> {
|
||||||
.ok_or_else(|| "keycloak user missing".to_owned())?;
|
.ok_or_else(|| "keycloak user missing".to_owned())?;
|
||||||
let clients: Value = client
|
let clients: Value = client
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/clients?clientId={client_id}"
|
"{keycloak_url}/admin/realms/{realm}/clients?clientId={client_id}"
|
||||||
))
|
))
|
||||||
.bearer_auth(admin_token)
|
.bearer_auth(admin_token)
|
||||||
.send()
|
.send()
|
||||||
|
|
@ -327,7 +340,7 @@ async fn restore_keycloak_remora_roles() -> Result<(), String> {
|
||||||
.ok_or_else(|| "keycloak client missing".to_owned())?;
|
.ok_or_else(|| "keycloak client missing".to_owned())?;
|
||||||
let roles: Value = client
|
let roles: Value = client
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/clients/{client_uuid}/roles"
|
"{keycloak_url}/admin/realms/{realm}/clients/{client_uuid}/roles"
|
||||||
))
|
))
|
||||||
.bearer_auth(admin_token)
|
.bearer_auth(admin_token)
|
||||||
.send()
|
.send()
|
||||||
|
|
@ -350,7 +363,7 @@ async fn restore_keycloak_remora_roles() -> Result<(), String> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let response = client
|
let response = client
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
"{keycloak_url}/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
||||||
))
|
))
|
||||||
.bearer_auth(admin_token)
|
.bearer_auth(admin_token)
|
||||||
.json(&roles)
|
.json(&roles)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use std::io;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -11,9 +10,7 @@ use remora_domain::ports::CredentialResolver;
|
||||||
use remora_durable::{PoolConfig, connect};
|
use remora_durable::{PoolConfig, connect};
|
||||||
use remora_schema::migrate as migrate_schema;
|
use remora_schema::migrate as migrate_schema;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::sync::Barrier;
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
|
||||||
use tokio::sync::{Barrier, oneshot};
|
|
||||||
|
|
||||||
fn database_url() -> Option<String> {
|
fn database_url() -> Option<String> {
|
||||||
std::env::var("DATABASE_URL")
|
std::env::var("DATABASE_URL")
|
||||||
|
|
@ -164,65 +161,6 @@ async fn harness() -> Option<(PgPool, CredentialRegistry, GrantBoundResolver)> {
|
||||||
Some((pool, registry, resolver))
|
Some((pool, registry, resolver))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn held_vault_response_proxy(
|
|
||||||
upstream: &str,
|
|
||||||
) -> (String, oneshot::Receiver<()>, oneshot::Sender<()>) {
|
|
||||||
let authority = upstream
|
|
||||||
.strip_prefix("http://")
|
|
||||||
.expect("fixture Vault must use HTTP")
|
|
||||||
.trim_end_matches('/')
|
|
||||||
.to_owned();
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:0").await.expect("proxy bind");
|
|
||||||
let address = listener.local_addr().expect("proxy address");
|
|
||||||
let (applied_tx, applied_rx) = oneshot::channel();
|
|
||||||
let (release_tx, release_rx) = oneshot::channel();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let (mut client, _) = listener.accept().await.expect("proxy accept");
|
|
||||||
let request = read_http_message(&mut client).await.expect("proxy request");
|
|
||||||
let mut vault = TcpStream::connect(&authority)
|
|
||||||
.await
|
|
||||||
.expect("connect fixture Vault");
|
|
||||||
vault.write_all(&request).await.expect("forward request");
|
|
||||||
let response = read_http_message(&mut vault).await.expect("Vault response");
|
|
||||||
let _ = applied_tx.send(());
|
|
||||||
let _ = release_rx.await;
|
|
||||||
client.write_all(&response).await.expect("release response");
|
|
||||||
});
|
|
||||||
(format!("http://{address}"), applied_rx, release_tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_http_message(stream: &mut TcpStream) -> io::Result<Vec<u8>> {
|
|
||||||
let mut message = Vec::with_capacity(8192);
|
|
||||||
let mut chunk = [0u8; 4096];
|
|
||||||
let mut expected = None;
|
|
||||||
loop {
|
|
||||||
let read = stream.read(&mut chunk).await?;
|
|
||||||
if read == 0 {
|
|
||||||
return Ok(message);
|
|
||||||
}
|
|
||||||
message.extend_from_slice(&chunk[..read]);
|
|
||||||
if expected.is_none()
|
|
||||||
&& let Some(header_end) = message.windows(4).position(|window| window == b"\r\n\r\n")
|
|
||||||
{
|
|
||||||
let headers = std::str::from_utf8(&message[..header_end])
|
|
||||||
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
|
|
||||||
let content_length = headers
|
|
||||||
.lines()
|
|
||||||
.find_map(|line| {
|
|
||||||
let (name, value) = line.split_once(':')?;
|
|
||||||
name.eq_ignore_ascii_case("content-length")
|
|
||||||
.then(|| value.trim().parse::<usize>().ok())
|
|
||||||
.flatten()
|
|
||||||
})
|
|
||||||
.unwrap_or(0);
|
|
||||||
expected = Some(header_end + 4 + content_length);
|
|
||||||
}
|
|
||||||
if expected.is_some_and(|length| message.len() >= length) {
|
|
||||||
return Ok(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_pin_and_exact_version_resolution() {
|
async fn create_pin_and_exact_version_resolution() {
|
||||||
let Some((pool, registry, resolver)) = harness().await else {
|
let Some((pool, registry, resolver)) = harness().await else {
|
||||||
|
|
@ -1075,103 +1013,6 @@ async fn reconcile_claim_blocks_concurrent_materialize() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn concurrent_reconcilers_claim_before_a_late_create_response() {
|
|
||||||
let Some(pool) = test_pool().await else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(direct_vault) = vault_client() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let upstream = direct_vault.config().address.clone();
|
|
||||||
let (proxy_address, applied, release) = held_vault_response_proxy(&upstream).await;
|
|
||||||
let mut delayed_config = VaultConfig::from_env().expect("Vault config");
|
|
||||||
delayed_config.address = proxy_address;
|
|
||||||
delayed_config.timeout = Duration::from_secs(5);
|
|
||||||
let delayed_registry = CredentialRegistry::new(
|
|
||||||
pool.clone(),
|
|
||||||
VaultClient::new(delayed_config).expect("delayed Vault client"),
|
|
||||||
)
|
|
||||||
.with_reconcile_min_age(Duration::ZERO);
|
|
||||||
let reconciler = Arc::new(
|
|
||||||
CredentialRegistry::new(pool.clone(), direct_vault.clone())
|
|
||||||
.with_reconcile_min_age(Duration::ZERO),
|
|
||||||
);
|
|
||||||
let owner = owner("late-response");
|
|
||||||
let create_owner = owner.clone();
|
|
||||||
let create_task = tokio::spawn(async move {
|
|
||||||
delayed_registry
|
|
||||||
.create(
|
|
||||||
&create_owner,
|
|
||||||
CreateCredential {
|
|
||||||
label: "late-response".into(),
|
|
||||||
material: token_material("secret-late-response"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
});
|
|
||||||
|
|
||||||
tokio::time::timeout(Duration::from_secs(5), applied)
|
|
||||||
.await
|
|
||||||
.expect("Vault apply timeout")
|
|
||||||
.expect("Vault apply signal");
|
|
||||||
let (intent_id, secret_mount, secret_path): (String, String, String) = sqlx::query_as(
|
|
||||||
"SELECT intent_id, secret_mount, secret_path
|
|
||||||
FROM secret_write_intents
|
|
||||||
WHERE operation = 'create' AND owner_id = $1
|
|
||||||
ORDER BY created_at DESC LIMIT 1",
|
|
||||||
)
|
|
||||||
.bind(owner.as_str())
|
|
||||||
.fetch_one(&pool)
|
|
||||||
.await
|
|
||||||
.expect("prepared create intent");
|
|
||||||
|
|
||||||
let barrier = Arc::new(Barrier::new(4));
|
|
||||||
let mut reconcile_tasks = Vec::new();
|
|
||||||
for _ in 0..4 {
|
|
||||||
let reconciler = Arc::clone(&reconciler);
|
|
||||||
let barrier = Arc::clone(&barrier);
|
|
||||||
reconcile_tasks.push(tokio::spawn(async move {
|
|
||||||
barrier.wait().await;
|
|
||||||
reconciler.reconcile_secret_write_intents().await
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
for task in reconcile_tasks {
|
|
||||||
task.await.expect("reconciler join").expect("reconcile");
|
|
||||||
}
|
|
||||||
|
|
||||||
let status: String =
|
|
||||||
sqlx::query_scalar("SELECT status FROM secret_write_intents WHERE intent_id = $1")
|
|
||||||
.bind(&intent_id)
|
|
||||||
.fetch_one(&pool)
|
|
||||||
.await
|
|
||||||
.expect("intent status");
|
|
||||||
assert_eq!(status, "abandoned");
|
|
||||||
|
|
||||||
release.send(()).expect("release Vault response");
|
|
||||||
assert!(
|
|
||||||
tokio::time::timeout(Duration::from_secs(5), create_task)
|
|
||||||
.await
|
|
||||||
.expect("create completion timeout")
|
|
||||||
.expect("create join")
|
|
||||||
.is_err()
|
|
||||||
);
|
|
||||||
let credential_count: i64 =
|
|
||||||
sqlx::query_scalar("SELECT COUNT(*) FROM source_credentials WHERE owner_id = $1")
|
|
||||||
.bind(owner.as_str())
|
|
||||||
.fetch_one(&pool)
|
|
||||||
.await
|
|
||||||
.expect("credential count");
|
|
||||||
assert_eq!(credential_count, 0);
|
|
||||||
assert!(
|
|
||||||
direct_vault
|
|
||||||
.list_live_versions(&secret_mount, &secret_path)
|
|
||||||
.await
|
|
||||||
.expect("final Vault metadata")
|
|
||||||
.is_empty()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn prepared_intent_fence_blocks_a_late_exact_path_write() {
|
async fn prepared_intent_fence_blocks_a_late_exact_path_write() {
|
||||||
let Some((pool, registry, _)) = harness().await else {
|
let Some((pool, registry, _)) = harness().await else {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,13 @@ fn env_required(name: &str) -> String {
|
||||||
.unwrap_or_else(|_| panic!("{name} is required when REMORA_IDENTITY_REQUIRE is set"))
|
.unwrap_or_else(|_| panic!("{name} is required when REMORA_IDENTITY_REQUIRE is set"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keycloak_admin_url() -> String {
|
||||||
|
std::env::var("REMORA_KEYCLOAK_ADMIN_URL")
|
||||||
|
.unwrap_or_else(|_| "http://keycloak.remora.test:8080".to_owned())
|
||||||
|
.trim_end_matches('/')
|
||||||
|
.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
async fn test_pool() -> Option<PgPool> {
|
async fn test_pool() -> Option<PgPool> {
|
||||||
let Some(url) = database_url() else {
|
let Some(url) = database_url() else {
|
||||||
if require_env() {
|
if require_env() {
|
||||||
|
|
@ -370,8 +377,9 @@ async fn revoke_keycloak_remora_client_roles() -> Result<(), String> {
|
||||||
let (admin_token, realm, user_id, client_uuid, storage_role) =
|
let (admin_token, realm, user_id, client_uuid, storage_role) =
|
||||||
keycloak_admin_role_context().await?;
|
keycloak_admin_role_context().await?;
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
let keycloak_url = keycloak_admin_url();
|
||||||
let roles_url = format!(
|
let roles_url = format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
"{keycloak_url}/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
||||||
);
|
);
|
||||||
let mapped: Value = client
|
let mapped: Value = client
|
||||||
.get(&roles_url)
|
.get(&roles_url)
|
||||||
|
|
@ -504,8 +512,9 @@ async fn ensure_keycloak_remora_client_roles() -> Result<(), String> {
|
||||||
let (admin_token, realm, user_id, client_uuid, storage_role) =
|
let (admin_token, realm, user_id, client_uuid, storage_role) =
|
||||||
keycloak_admin_role_context().await?;
|
keycloak_admin_role_context().await?;
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
let keycloak_url = keycloak_admin_url();
|
||||||
let available_url = format!(
|
let available_url = format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}/available"
|
"{keycloak_url}/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}/available"
|
||||||
);
|
);
|
||||||
let available: Value = client
|
let available: Value = client
|
||||||
.get(available_url)
|
.get(available_url)
|
||||||
|
|
@ -532,7 +541,7 @@ async fn ensure_keycloak_remora_client_roles() -> Result<(), String> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let roles_url = format!(
|
let roles_url = format!(
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
"{keycloak_url}/admin/realms/{realm}/users/{user_id}/role-mappings/clients/{client_uuid}"
|
||||||
);
|
);
|
||||||
let post = client
|
let post = client
|
||||||
.post(roles_url)
|
.post(roles_url)
|
||||||
|
|
@ -556,10 +565,10 @@ async fn keycloak_admin_role_context() -> Result<(String, String, String, String
|
||||||
let client_id = env_required("REMORA_OIDC_CLIENT_ID");
|
let client_id = env_required("REMORA_OIDC_CLIENT_ID");
|
||||||
let username = env_required("REMORA_OIDC_USERNAME");
|
let username = env_required("REMORA_OIDC_USERNAME");
|
||||||
let storage_role = env_required("REMORA_OIDC_STORAGE_ROLE");
|
let storage_role = env_required("REMORA_OIDC_STORAGE_ROLE");
|
||||||
|
let keycloak_url = keycloak_admin_url();
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
let admin_token_url =
|
let admin_token_url = format!("{keycloak_url}/realms/master/protocol/openid-connect/token");
|
||||||
"http://keycloak.remora.test:8080/realms/master/protocol/openid-connect/token";
|
|
||||||
let admin_token_response = client
|
let admin_token_response = client
|
||||||
.post(admin_token_url)
|
.post(admin_token_url)
|
||||||
.form(&[
|
.form(&[
|
||||||
|
|
@ -588,9 +597,8 @@ async fn keycloak_admin_role_context() -> Result<(String, String, String, String
|
||||||
.ok_or("keycloak admin token missing access_token")?
|
.ok_or("keycloak admin token missing access_token")?
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
let users_url = format!(
|
let users_url =
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/users?username={username}&exact=true"
|
format!("{keycloak_url}/admin/realms/{realm}/users?username={username}&exact=true");
|
||||||
);
|
|
||||||
let users: Value = client
|
let users: Value = client
|
||||||
.get(users_url)
|
.get(users_url)
|
||||||
.bearer_auth(&admin_token)
|
.bearer_auth(&admin_token)
|
||||||
|
|
@ -608,9 +616,7 @@ async fn keycloak_admin_role_context() -> Result<(String, String, String, String
|
||||||
.ok_or("keycloak user not found")?
|
.ok_or("keycloak user not found")?
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
let clients_url = format!(
|
let clients_url = format!("{keycloak_url}/admin/realms/{realm}/clients?clientId={client_id}");
|
||||||
"http://keycloak.remora.test:8080/admin/realms/{realm}/clients?clientId={client_id}"
|
|
||||||
);
|
|
||||||
let clients: Value = client
|
let clients: Value = client
|
||||||
.get(clients_url)
|
.get(clients_url)
|
||||||
.bearer_auth(&admin_token)
|
.bearer_auth(&admin_token)
|
||||||
|
|
|
||||||
28
web/server/config.test.mjs
Normal file
28
web/server/config.test.mjs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { test } from 'node:test'
|
||||||
|
|
||||||
|
import { loadConfig } from './config.mjs'
|
||||||
|
|
||||||
|
const environment = {
|
||||||
|
REMORA_WEB_PUBLIC_ORIGIN: 'https://remora.example.test',
|
||||||
|
REMORA_WEB_API_UPSTREAM: 'https://api.example.test/v1',
|
||||||
|
REMORA_WEB_OIDC_AUTHORITY: 'https://identity.example.test',
|
||||||
|
REMORA_WEB_OIDC_CLIENT_ID: 'remora-web',
|
||||||
|
REMORA_WEB_OIDC_CLIENT_SECRET: 'client-secret',
|
||||||
|
REMORA_WEB_SESSION_URL: 'redis://session.example.test',
|
||||||
|
}
|
||||||
|
|
||||||
|
test('derives the event endpoint from the API upstream', () => {
|
||||||
|
const config = loadConfig(environment)
|
||||||
|
|
||||||
|
assert.equal(config.eventsUpstream.href, 'wss://api.example.test/v1/events')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('uses the explicit event endpoint', () => {
|
||||||
|
const config = loadConfig({
|
||||||
|
...environment,
|
||||||
|
REMORA_WEB_EVENTS_UPSTREAM: 'http://events.example.test/stream',
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.equal(config.eventsUpstream.href, 'ws://events.example.test/stream')
|
||||||
|
})
|
||||||
Loading…
Reference in a new issue