rs-zero 0.2.8

Rust-first microservice framework inspired by go-zero engineering practices
Documentation
#![cfg(feature = "discovery-etcd")]

use rs_zero::discovery::{Discovery, InstanceEndpoint, ServiceInstance};
use rs_zero::discovery_etcd::{
    BackoffConfig, EtcdClientFactory, EtcdDiscoveryConfig, EtcdDiscoveryError, EtcdRegistry,
    decode_instance, encode_instance, instance_key, service_prefix, split_instance_key,
};

#[test]
fn etcd_codec_keys_and_config_validate_without_external_service() {
    let instance = ServiceInstance::new(
        "api",
        "api-1",
        InstanceEndpoint::new("127.0.0.1", 8080).expect("endpoint"),
    );
    let encoded = encode_instance(&instance).expect("encode");
    assert_eq!(decode_instance(&encoded).expect("decode"), instance);

    let config = EtcdDiscoveryConfig::default();
    assert_eq!(instance_key(&config, "api", "api-1"), "/rs-zero/api/api-1");
    assert_eq!(service_prefix(&config, "api"), "/rs-zero/api/");
    let parsed = split_instance_key(&config, "/rs-zero/api/api-1")
        .expect("split")
        .expect("key parts");
    assert_eq!(parsed.service, "api");
    assert_eq!(parsed.id, "api-1");

    let error = EtcdClientFactory::new(EtcdDiscoveryConfig {
        endpoints: Vec::new(),
        ..EtcdDiscoveryConfig::default()
    })
    .expect_err("missing endpoint");
    assert!(matches!(error, EtcdDiscoveryError::MissingEndpoint));
}

#[tokio::test]
async fn disconnected_registry_fails_explicitly_without_memory_fallback() {
    let registry = EtcdRegistry::new(EtcdDiscoveryConfig::default());
    let error = registry.discover("api").await.expect_err("not connected");

    assert!(error.to_string().contains("etcd"));
    assert!(error.to_string().contains("connect"));
}

#[test]
fn backoff_is_capped_and_uses_first_attempt_as_initial_delay() {
    let config = BackoffConfig::default();

    assert_eq!(config.delay_for_attempt(0), config.initial);
    assert!(config.delay_for_attempt(10) <= config.max);
}