#![allow(unreachable_pub)]
#![allow(dead_code)]
use std::net::SocketAddr;
use std::time::Duration;
use brain_db_sdk::wire::types::RecallRequest;
use brain_db_sdk::{new_id, Auth, BrainClient, RecallAnswer};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
pub const VISIBILITY_TIMEOUT: Duration = Duration::from_secs(5);
pub struct It {
pub data: SocketAddr,
admin: SocketAddr,
admin_secret: String,
pub namespace: String,
}
impl It {
pub fn from_env() -> Option<It> {
let data: SocketAddr = std::env::var("BRAIN_SDK_IT_DATA").ok()?.parse().ok()?;
let admin: SocketAddr = std::env::var("BRAIN_SDK_IT_ADMIN")
.unwrap_or_else(|_| "127.0.0.1:19092".to_string())
.parse()
.ok()?;
let admin_secret = std::env::var("BRAIN_SDK_IT_ADMIN_SECRET")
.unwrap_or_else(|_| "sdk-it-admin-secret".to_string());
let namespace =
std::env::var("BRAIN_SDK_IT_NAMESPACE").unwrap_or_else(|_| "sdk-it".to_string());
Some(It {
data,
admin,
admin_secret,
namespace,
})
}
pub async fn mint(&self, space_id: [u8; 16]) -> String {
let body = format!(
r#"{{"space_id_hex":"{}","namespace":"{}","permissions":["FULL"]}}"#,
hex16(&space_id),
self.namespace,
);
let req = format!(
"POST /v1/api-keys HTTP/1.1\r\nHost: {host}\r\nAuthorization: Bearer {secret}\r\n\
Content-Type: application/json\r\nContent-Length: {len}\r\nConnection: close\r\n\r\n{body}",
host = self.admin,
secret = self.admin_secret,
len = body.len(),
body = body,
);
let mut sock = TcpStream::connect(self.admin)
.await
.expect("connect admin plane");
sock.write_all(req.as_bytes())
.await
.expect("write mint request");
let mut raw = Vec::new();
sock.read_to_end(&mut raw)
.await
.expect("read mint response");
let text = String::from_utf8_lossy(&raw);
let status = text
.lines()
.next()
.and_then(|l| l.split_whitespace().nth(1))
.unwrap_or("?");
assert_eq!(status, "201", "admin mint failed: {text}");
let payload = text
.split_once("\r\n\r\n")
.map(|(_, b)| b)
.unwrap_or_default();
let json: serde_json::Value =
serde_json::from_str(payload.trim()).expect("mint response is JSON");
json.get("secret")
.and_then(serde_json::Value::as_str)
.filter(|s| !s.is_empty())
.expect("mint response carries a non-empty secret")
.to_string()
}
pub async fn connect_as(&self, space_id: [u8; 16]) -> BrainClient {
let token = self.mint(space_id).await;
BrainClient::connect(self.data, Auth::Token(token.into_bytes()))
.await
.expect("connect + handshake")
}
pub async fn connect_fresh(&self) -> (BrainClient, [u8; 16]) {
let space = new_id();
(self.connect_as(space).await, space)
}
}
pub async fn recall_until<F>(client: &BrainClient, req: &RecallRequest, pred: F) -> RecallAnswer
where
F: Fn(&RecallAnswer) -> bool,
{
let deadline = tokio::time::Instant::now() + VISIBILITY_TIMEOUT;
loop {
let answer = client.recall(req).await.expect("recall");
if pred(&answer) || tokio::time::Instant::now() >= deadline {
return answer;
}
tokio::time::sleep(Duration::from_millis(150)).await;
}
}
pub fn hex16(id: &[u8; 16]) -> String {
use std::fmt::Write;
let mut s = String::with_capacity(32);
for b in id {
let _ = write!(s, "{b:02x}");
}
s
}
pub fn skip(what: &str) {
if std::env::var("BRAIN_SDK_IT_REQUIRED").as_deref() == Ok("1") {
panic!(
"{what}: BRAIN_SDK_IT_REQUIRED=1 but BRAIN_SDK_IT_DATA is unset — the \
integration server was expected to be reachable. Unset the flag to \
allow skipping, or boot one with scripts/it-server.sh."
);
}
eprintln!(
"SKIP {what}: integration server not configured \
(set BRAIN_SDK_IT_DATA; see scripts/it-server.sh)"
);
}