Private
Public Access
CI / rust (push) Successful in 3m6s
CI / go (push) Successful in 33s
CI / js (push) Successful in 34s
Standalone clients that resolve a machine unique ID (OS machine ID first, falling back to a generated UUID persisted to disk), detect hostname/IP/ container/orchestrator, and register+re-ping origin.warky.dev's public service-checkin endpoint on a daily interval. Ported from icy2's internal origincheckin package but extended to the full check-in contract and made dependency-light for external reuse. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
125 lines
3.8 KiB
Rust
125 lines
3.8 KiB
Rust
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
use origin_client::{Client, Config, FnLogger};
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
fn test_config(url: String) -> Config {
|
|
Config {
|
|
url: Some(url),
|
|
service_key: "test-key".to_string(),
|
|
type_: "icy2".to_string(),
|
|
version: "v1.0.0".to_string(),
|
|
name: "icy2-test".to_string(),
|
|
site: Some("Test Site".to_string()),
|
|
environment: Some("test".to_string()),
|
|
install_id: Some("test-install-id".to_string()),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn new_requires_fields() {
|
|
assert!(Client::new(Config::default()).is_err());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn checkin_once_success() {
|
|
let mut server = mockito::Server::new_async().await;
|
|
let mock = server
|
|
.mock("POST", "/")
|
|
.match_header("x-origin-service-key", "test-key")
|
|
.with_status(200)
|
|
.with_header("content-type", "application/json")
|
|
.with_body(
|
|
r#"{"success":true,"service_instance_id":42,"unique_install_id":"test-install-id","status":"provisioning","pinged_at":"2026-08-01T00:00:00Z"}"#,
|
|
)
|
|
.create_async()
|
|
.await;
|
|
|
|
let client = Client::new(test_config(server.url())).unwrap();
|
|
let resp = client.checkin_once().await.unwrap();
|
|
|
|
mock.assert_async().await;
|
|
assert!(resp.success);
|
|
assert_eq!(resp.service_instance_id, 42);
|
|
assert_eq!(resp.unique_install_id, "test-install-id");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn checkin_once_server_error_returns_error() {
|
|
let mut server = mockito::Server::new_async().await;
|
|
server
|
|
.mock("POST", "/")
|
|
.with_status(500)
|
|
.create_async()
|
|
.await;
|
|
|
|
let client = Client::new(test_config(server.url())).unwrap();
|
|
assert!(client.checkin_once().await.is_err());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn checkin_once_unreachable_does_not_panic() {
|
|
let client = Client::new(test_config("http://127.0.0.1:1".to_string())).unwrap();
|
|
let result = client.checkin_once().await;
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn checkin_once_logs_on_failure() {
|
|
let mut server = mockito::Server::new_async().await;
|
|
server
|
|
.mock("POST", "/")
|
|
.with_status(500)
|
|
.create_async()
|
|
.await;
|
|
|
|
let warned = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
|
let warned_clone = warned.clone();
|
|
|
|
let mut cfg = test_config(server.url());
|
|
cfg.logger = Arc::new(FnLogger(
|
|
move |_msg: &str, _err: Option<&(dyn std::error::Error + Send + Sync)>| {
|
|
warned_clone.store(true, std::sync::atomic::Ordering::SeqCst);
|
|
},
|
|
));
|
|
|
|
let client = Client::new(cfg).unwrap();
|
|
let _ = client.checkin_once().await;
|
|
|
|
assert!(warned.load(std::sync::atomic::Ordering::SeqCst));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn run_checks_in_immediately_then_stops_on_cancel() {
|
|
let mut server = mockito::Server::new_async().await;
|
|
let mock = server
|
|
.mock("POST", "/")
|
|
.with_status(200)
|
|
.with_header("content-type", "application/json")
|
|
.with_body(
|
|
r#"{"success":true,"service_instance_id":1,"unique_install_id":"x","status":"provisioning","pinged_at":"2026-08-01T00:00:00Z"}"#,
|
|
)
|
|
.expect(1)
|
|
.create_async()
|
|
.await;
|
|
|
|
let client = Client::new(test_config(server.url())).unwrap();
|
|
let cancel = CancellationToken::new();
|
|
let cancel_clone = cancel.clone();
|
|
|
|
let handle = tokio::spawn(async move {
|
|
client.run(cancel_clone).await;
|
|
});
|
|
|
|
// The immediate check-in happens synchronously at the start of run();
|
|
// give the spawned task a moment to execute it, then cancel before the
|
|
// (24h) interval could ever fire again.
|
|
tokio::time::sleep(Duration::from_millis(50)).await;
|
|
cancel.cancel();
|
|
handle.await.unwrap();
|
|
|
|
mock.assert_async().await;
|
|
}
|