#![cfg(all(feature = "cache-redis", feature = "resil"))]

use rs_zero::cache_redis::{
    RedisCacheError, RedisLuaScript, RedisScriptEvent, classify_redis_error,
};
use rs_zero::resil::{
    PeriodAlignment, RedisPeriodLimiterConfig, RedisTokenLimiterConfig, TokenLimiterRescueConfig,
};

#[test]
fn lua_script_sha_is_stable_and_matches_known_value() {
    let script = RedisLuaScript::new("return redis.call('PING')");

    assert_eq!(script.sha1(), "9e15db9d82a8a2f2029845f70d37494af8690c35");
    assert_eq!(script.text(), "return redis.call('PING')");
}

#[test]
fn noscript_is_classified_for_reload() {
    let error =
        RedisCacheError::Backend("NOSCRIPT No matching script. Please use EVAL.".to_string());

    assert!(classify_redis_error(&error).is_noscript());
    assert!(RedisLuaScript::is_noscript_error(&error));
}

#[test]
fn redis_server_noscript_error_is_classified_for_reload() {
    let error = RedisCacheError::Backend(
        "An error was signalled by the server - NoScriptError: No matching script. Please use EVAL."
            .to_string(),
    );

    assert!(classify_redis_error(&error).is_noscript());
    assert!(RedisLuaScript::is_noscript_error(&error));
}

#[test]
fn script_events_use_low_cardinality_outcomes() {
    assert_eq!(RedisScriptEvent::CacheHit.outcome(), "cache_hit");
    assert_eq!(RedisScriptEvent::Loaded.outcome(), "loaded");
    assert_eq!(RedisScriptEvent::NoScript.outcome(), "noscript");
}

#[test]
fn redis_limiters_keep_explicit_fail_open_config() {
    let period = RedisPeriodLimiterConfig {
        fail_open: true,
        ..RedisPeriodLimiterConfig::default()
    };
    let token = RedisTokenLimiterConfig {
        fail_open: true,
        ..RedisTokenLimiterConfig::default()
    };

    assert!(period.fail_open);
    assert!(token.fail_open);
    assert!(!token.rescue.enabled);
}

#[test]
fn redis_token_limiter_go_zero_defaults_enable_rescue() {
    let token = RedisTokenLimiterConfig::go_zero_defaults();

    assert!(token.rescue.enabled);
    assert_eq!(
        token.rescue.ping_interval,
        TokenLimiterRescueConfig::default().ping_interval
    );
    assert!(!token.fail_open);
}

#[test]
fn redis_period_limiter_supports_natural_alignment() {
    let period = RedisPeriodLimiterConfig::default().aligned_to_utc_offset(8 * 60 * 60);

    assert_eq!(
        period.alignment,
        PeriodAlignment::UtcOffset {
            seconds: 8 * 60 * 60
        }
    );
}
