technitium 0.4.0

Typed async Rust client for the Technitium DNS Server API
Documentation
mod common;

use technitium::{Client, Error};

#[tokio::test]
#[ignore = "requires running Technitium server"]
async fn login_with_valid_credentials() {
    let client = common::build_client();
    let result = client
        .login(&common::admin_username(), &common::admin_password())
        .await;
    assert!(result.is_ok(), "login should succeed: {result:?}");
}

#[tokio::test]
#[ignore = "requires running Technitium server"]
async fn login_with_bad_credentials() {
    let client = common::build_client();
    let result = client.login("admin", "wrong-password-12345").await;
    assert!(
        matches!(&result, Err(Error::Authentication { .. })),
        "expected Authentication error, got: {result:?}"
    );
}

#[tokio::test]
#[ignore = "requires running Technitium server"]
async fn create_api_token() {
    let client = common::authenticated_client().await;
    let token = client
        .create_api_token(
            &common::admin_username(),
            &common::admin_password(),
            "test-token",
        )
        .await
        .expect("create_api_token should succeed");
    assert!(!token.is_empty(), "token should not be empty");
}

#[tokio::test]
#[ignore = "requires running Technitium server"]
async fn logout() {
    let client = common::authenticated_client().await;
    let result = client.logout().await;
    assert!(result.is_ok(), "logout should succeed: {result:?}");
}

#[tokio::test]
#[ignore = "requires running Technitium server"]
async fn get_session_info() {
    let client = common::authenticated_client().await;
    let info = client
        .get_session_info()
        .await
        .expect("get_session_info should succeed");
    assert!(
        info.username.is_some(),
        "session info should include username"
    );
}

#[tokio::test]
#[ignore = "requires running Technitium server"]
async fn not_authenticated_error() {
    let client = Client::builder()
        .base_url(common::server_url())
        .build()
        .expect("build client");

    // No login — should get NotAuthenticated
    let result = client.list_zones().await;
    assert!(
        matches!(&result, Err(Error::NotAuthenticated)),
        "expected NotAuthenticated, got: {result:?}"
    );
}

#[test]
fn auto_reauth_without_credentials_fails() {
    let result = Client::builder()
        .base_url("http://localhost:5380")
        .auto_reauth(true)
        .build();
    assert!(
        matches!(&result, Err(Error::Config { .. })),
        "expected Config error, got: {result:?}"
    );
}

#[tokio::test]
#[ignore = "requires running Technitium server"]
async fn connect_convenience() {
    let client = Client::connect(
        &common::server_url(),
        &common::admin_username(),
        &common::admin_password(),
    )
    .await
    .expect("connect should succeed");

    let zones = client.list_zones().await;
    assert!(zones.is_ok(), "list_zones should work after connect");
}