faucet-common-redshift 1.2.3

Shared connection, credentials, and connection-pool types for the faucet-stream Amazon Redshift source and sink connectors
Documentation
use super::*;

fn conn(config: serde_json::Value) -> RedshiftConnection {
    serde_json::from_value(serde_json::json!({
        "host": "h", "database": "dev", "user": "fallback",
        "credentials": {"type": "iam", "config": config}
    }))
    .unwrap()
}

#[test]
fn a_cluster_takes_db_user_or_the_connection_user() {
    let p = IamCredentials::from_connection(&conn(serde_json::json!({"cluster_identifier": "c"})))
        .unwrap()
        .unwrap();
    assert!(matches!(&p.target, Target::Cluster { db_user, .. } if db_user == "fallback"));
    assert!(format!("{p:?}").contains("fallback"));
    let p = IamCredentials::from_connection(&conn(
            serde_json::json!({"cluster_identifier": "c", "db_user": "analyst", "auto_create": true, "db_groups": ["g"]}),
        ))
        .unwrap()
        .unwrap();
    assert!(
        matches!(&p.target, Target::Cluster { db_user, auto_create: true, db_groups, .. } if db_user == "analyst" && db_groups == &["g".to_string()])
    );
    let p = IamCredentials::from_connection(&conn(serde_json::json!({"workgroup_name": "wg"})))
        .unwrap()
        .unwrap();
    assert!(matches!(&p.target, Target::Workgroup { name } if name == "wg"));
    assert!(
        IamCredentials::from_connection(&RedshiftConnection::new("h", "d", "u", "p"))
            .unwrap()
            .is_none()
    );
}

#[test]
fn invalid_iam_settings_are_refused() {
    let mut no_user = conn(serde_json::json!({"cluster_identifier": "c"}));
    no_user.user = String::new();
    for (c, needle) in [
        (no_user, "db_user"),
        (conn(serde_json::json!({})), "exactly one"),
        (
            conn(serde_json::json!({"cluster_identifier": "c", "workgroup_name": "w"})),
            "exactly one",
        ),
        (
            conn(serde_json::json!({"workgroup_name": "w", "db_user": "u"})),
            "serverless",
        ),
        (
            conn(serde_json::json!({"cluster_identifier": "c", "duration_secs": 60})),
            "duration_secs",
        ),
    ] {
        let e = IamCredentials::from_connection(&c).unwrap_err().to_string();
        assert!(e.contains(needle), "{e}");
    }
}

#[test]
fn refresh_comes_a_margin_before_expiry_or_halfway() {
    let now = SystemTime::now();
    let fetched = Instant::now();
    assert_eq!(
        refresh_at(fetched, now, now + Duration::from_secs(900)),
        fetched + Duration::from_secs(840)
    );
    assert_eq!(
        refresh_at(fetched, now, now + Duration::from_secs(10)),
        fetched + Duration::from_secs(5)
    );
    assert_eq!(
        refresh_at(fetched, now, now - Duration::from_secs(1)),
        fetched + Duration::from_secs(1),
        "already expired by the local clock: not refetched on every use"
    );
    assert_eq!(
        refresh_at(fetched, now, now + Duration::from_secs(1)),
        fetched + Duration::from_secs(1)
    );
}

#[test]
fn a_failed_refresh_backs_off_within_the_remaining_lifetime() {
    let failed = Instant::now();
    let now = SystemTime::now();
    let at = |secs: u64| retry_at(failed, now, now + Duration::from_secs(secs)) - failed;
    assert_eq!(at(60), Duration::from_secs(15));
    assert_eq!(at(3600), Duration::from_secs(60));
    assert_eq!(at(2), Duration::from_secs(1));
    assert_eq!(
        retry_at(failed, now, now + Duration::from_millis(400)) - failed,
        Duration::from_millis(400)
    );
    assert_eq!(retry_at(failed, now, now - Duration::from_secs(1)), failed);
}

#[test]
fn a_response_needs_every_field() {
    let exp = aws_sdk_redshift::primitives::DateTime::from_secs(2_000_000_000);
    let ok = credentials_from(Some("IAM:u".into()), Some("pw".into()), Some(exp)).unwrap();
    assert_eq!(ok.user, "IAM:u");
    assert!(!format!("{ok:?}").contains("pw\""));
    for (u, p, e, needle) in [
        (None, Some("p"), Some(exp), "user"),
        (Some("u"), Some(""), Some(exp), "password"),
        (Some("u"), Some("p"), None, "expiration"),
    ] {
        let err = credentials_from(u.map(Into::into), p.map(Into::into), e)
            .unwrap_err()
            .to_string();
        assert!(err.contains(needle), "{err}");
    }
}