1use crate::Endpoint;
17
18pub fn mint_token() -> String {
21 let nanos = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_nanos()).unwrap_or(0);
22 format!("ucat_mock_{}_{nanos}", std::process::id())
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct Heard {
28 pub requests: u64,
29 pub account_calls: u64,
30}
31
32pub fn heard(endpoint: &Endpoint, token: &str) -> Result<Heard, String> {
35 let Endpoint::Mock(base) = endpoint else {
36 return Err("REFUSED [heard-asked-of-the-account] /mock/heard is a door of the mock; the account has none".into());
37 };
38 let root = base.as_str().trim_end_matches("/1.3");
39 let digest = nornir_hash::sha256_hex(token.as_bytes());
40 let url = format!("{root}/mock/heard?token_sha256={digest}");
41 let cfg = ureq::Agent::config_builder().http_status_as_error(false).timeout_global(Some(std::time::Duration::from_secs(10))).build();
42 let mut res = ureq::Agent::new_with_config(cfg).get(&url).call().map_err(|e| format!("GET /mock/heard: {e}"))?;
43 let status = res.status().as_u16();
44 let text = res.body_mut().read_to_string().map_err(|e| format!("GET /mock/heard: {e}"))?;
45 if status != 200 {
46 return Err(format!("GET /mock/heard answered {status} — {} (a mock without behaviour 63 cannot vouch for a run)", text.trim()));
47 }
48 let v: serde_json::Value = serde_json::from_str(&text).map_err(|e| format!("GET /mock/heard: not JSON ({e}): {text}"))?;
49 Ok(Heard {
50 requests: v["requests"].as_u64().unwrap_or(0),
51 account_calls: v["account_calls"].as_u64().unwrap_or(0),
52 })
53}
54
55pub fn require_heard(endpoint: &Endpoint, token: &str, what: &str) -> Result<Heard, String> {
58 let h = heard(endpoint, token)?;
59 if h.account_calls == 0 {
60 return Err(format!(
61 "REFUSED [mock-never-heard-this-run] before {what}: the mock at {} heard {} request(s) and NO `GET \
62 /1.3/account` from this run's token. The client that was supposed to be aimed at it spoke to somebody \
63 else — the likeliest is a terraform provider that no longer honours UPCLOUD_DEBUG_API_BASE_URL, which \
64 would send this {what} to THE ACCOUNT. Nothing was applied.",
65 endpoint.base_for_display(),
66 h.requests
67 ));
68 }
69 Ok(h)
70}