autumn-plugin-clickhouse 0.3.0

Autumn plugin: a ClickHouse OLAP client with batch inserts, typed queries, and migrations.
Documentation
use super::*;

fn database(kind: ErrorKind) -> ClickHouseError {
    ClickHouseError::Database {
        kind,
        detail: "secret detail".to_owned(),
    }
}

#[test]
fn status_codes() {
    use http::StatusCode;
    assert_eq!(
        ClickHouseError::Timeout {
            timeout: Duration::from_secs(1)
        }
        .status(),
        StatusCode::GATEWAY_TIMEOUT
    );
    assert_eq!(
        database(ErrorKind::TimedOut).status(),
        StatusCode::GATEWAY_TIMEOUT
    );
    assert_eq!(
        database(ErrorKind::Network).status(),
        StatusCode::SERVICE_UNAVAILABLE
    );
    assert_eq!(
        database(ErrorKind::BadResponse).status(),
        StatusCode::BAD_GATEWAY
    );
    assert_eq!(
        database(ErrorKind::NotFound).status(),
        StatusCode::NOT_FOUND
    );
    assert_eq!(
        database(ErrorKind::InvalidParams).status(),
        StatusCode::BAD_REQUEST
    );
    assert_eq!(
        database(ErrorKind::SchemaMismatch).status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
    assert_eq!(
        database(ErrorKind::Decode).status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
    assert_eq!(
        database(ErrorKind::Unsupported).status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
    assert_eq!(
        database(ErrorKind::Custom).status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
    assert_eq!(
        database(ErrorKind::Unknown).status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
    assert_eq!(
        ClickHouseError::Migration {
            name: "m".to_owned(),
            detail: "d".to_owned(),
        }
        .status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
    assert_eq!(
        ClickHouseError::NotInstalled.status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
    assert_eq!(
        ClickHouseError::from(ConfigError("bad".to_owned())).status(),
        StatusCode::INTERNAL_SERVER_ERROR
    );
}

#[test]
fn retryable_kinds() {
    assert!(
        ClickHouseError::Timeout {
            timeout: Duration::from_secs(1)
        }
        .is_retryable()
    );
    assert!(database(ErrorKind::TimedOut).is_retryable());
    assert!(database(ErrorKind::Network).is_retryable());
    assert!(!database(ErrorKind::BadResponse).is_retryable());
    assert!(!database(ErrorKind::NotFound).is_retryable());
    assert!(!database(ErrorKind::InvalidParams).is_retryable());
    assert!(!ClickHouseError::NotInstalled.is_retryable());
}

#[test]
fn text_and_debug_hide_the_detail() {
    let err = database(ErrorKind::BadResponse);
    assert_eq!(err.to_string(), "ClickHouse refused the call: bad response");
    assert_eq!(
        format!("{err:?}"),
        "ClickHouseError(\"ClickHouse refused the call: bad response\")"
    );
    assert_eq!(err.detail(), Some("secret detail"));
    assert_eq!(err.kind(), Some(ErrorKind::BadResponse));

    let migration = ClickHouseError::Migration {
        name: "create table events".to_owned(),
        detail: "secret detail".to_owned(),
    };
    assert_eq!(
        migration.to_string(),
        "the migration `create table events` failed"
    );
    assert!(!format!("{migration:?}").contains("secret detail"));
    assert_eq!(migration.detail(), Some("secret detail"));
    assert_eq!(migration.kind(), None);

    assert_eq!(ClickHouseError::NotInstalled.detail(), None);
    assert_eq!(
        ClickHouseError::Timeout {
            timeout: Duration::from_secs(1)
        }
        .detail(),
        None
    );
}

#[test]
fn maps_clickhouse_errors() {
    use clickhouse::error::Error as Ch;
    let err = ClickHouseError::from(Ch::TimedOut);
    assert_eq!(err.kind(), Some(ErrorKind::TimedOut));
    assert!(err.is_retryable());

    let err = ClickHouseError::from(Ch::RowNotFound);
    assert_eq!(err.kind(), Some(ErrorKind::NotFound));
    assert_eq!(err.status(), StatusCode::NOT_FOUND);

    let err = ClickHouseError::from(Ch::BadResponse("Code: 209".to_owned()));
    assert_eq!(err.kind(), Some(ErrorKind::BadResponse));
    assert!(err.detail().is_some());

    let err = ClickHouseError::from(Ch::SchemaMismatch("no such column".to_owned()));
    assert_eq!(err.kind(), Some(ErrorKind::SchemaMismatch));

    let err = ClickHouseError::from(Ch::NotEnoughData);
    assert_eq!(err.kind(), Some(ErrorKind::Decode));
}

#[test]
fn or_http_converts() {
    use http::StatusCode;
    let result: Result<(), ClickHouseError> = Err(database(ErrorKind::Network));
    let err = result.or_http().unwrap_err();
    assert_eq!(err.status(), StatusCode::SERVICE_UNAVAILABLE);
    let result: Result<(), ClickHouseError> = Err(database(ErrorKind::NotFound));
    let err = result.or_http().unwrap_err();
    assert_eq!(err.status(), StatusCode::NOT_FOUND);
}