use super::{WORKSPACE_FALLBACK_ENV, WorkspaceSwitch};
use crate::test_tracing_capture::with_test_subscriber;
use rstest::rstest;
use std::env::VarError;
use std::ffi::OsString;
use tracing::level_filters::LevelFilter;
#[rstest]
#[case("0")]
#[case("false")]
#[case("off")]
#[case("FALSE")]
#[case("Off")]
#[case("OFF")]
fn disabling_values_switch_the_fallback_off(#[case] value: &str) {
assert!(
!WorkspaceSwitch::Value(value.to_owned()).enabled(),
"{value:?} should disable the workspace fallback"
);
}
#[rstest]
#[case("1")]
#[case("true")]
#[case("on")]
#[case("")]
#[case("offbeat")]
#[case("no")]
fn other_values_leave_the_fallback_on(#[case] value: &str) {
assert!(
WorkspaceSwitch::Value(value.to_owned()).enabled(),
"{value:?} should leave the workspace fallback enabled"
);
}
#[test]
fn absent_variable_leaves_the_fallback_on() {
assert!(
WorkspaceSwitch::Absent.enabled(),
"the fallback is opt-out, so an unset variable must leave it enabled"
);
}
#[test]
fn non_utf8_value_switches_the_fallback_off() {
assert!(
!WorkspaceSwitch::NotUnicode.enabled(),
"a non-UTF-8 value must disable the fallback rather than be treated as absent"
);
}
#[rstest]
#[case(Ok(String::from("off")), WorkspaceSwitch::Value(String::from("off")))]
#[case(Err(VarError::NotPresent), WorkspaceSwitch::Absent)]
#[case(
Err(VarError::NotUnicode(OsString::from("ignored"))),
WorkspaceSwitch::NotUnicode
)]
fn readings_translate_to_switch_states(
#[case] raw: Result<String, VarError>,
#[case] expected: WorkspaceSwitch,
) {
assert_eq!(WorkspaceSwitch::from(raw), expected);
}
#[test]
fn the_documented_variable_name_is_used() {
assert_eq!(WORKSPACE_FALLBACK_ENV, "NETSUKE_WHICH_WORKSPACE");
}
#[test]
fn non_utf8_classification_is_silent() {
let (enabled, events) = with_test_subscriber(LevelFilter::WARN, |captured| {
let enabled = WorkspaceSwitch::NotUnicode.enabled();
(enabled, captured.snapshot())
});
assert!(!enabled, "a non-UTF-8 value must disable the fallback");
assert!(
events.is_empty(),
"consulting the switch must emit no events, got {events:?}"
);
}
mod properties {
use super::WorkspaceSwitch;
use proptest::prelude::*;
fn model_says_enabled(value: &str) -> bool {
const DISABLING: [&str; 3] = ["0", "false", "off"];
!DISABLING.contains(&value.to_ascii_lowercase().as_str())
}
fn case_variant() -> impl Strategy<Value = String> {
prop_oneof![
1 => Just(String::from("0")),
4 => mixed_case("false"),
3 => mixed_case("off"),
]
}
fn mixed_case(word: &'static str) -> impl Strategy<Value = String> {
proptest::collection::vec(any::<bool>(), word.len()).prop_map(move |uppers| {
word.chars()
.zip(uppers)
.map(|(ch, upper)| if upper { ch.to_ascii_uppercase() } else { ch })
.collect()
})
}
proptest! {
#[test]
fn classification_matches_the_model(value in "\\PC{0,12}") {
let expected = model_says_enabled(&value);
let diagnostic = format!("value {value:?}");
let enabled = WorkspaceSwitch::Value(value).enabled();
prop_assert_eq!(enabled, expected, "{}", diagnostic);
}
#[test]
fn case_variants_disable(value in case_variant()) {
let diagnostic = format!("{value:?} should disable the fallback");
prop_assert!(!WorkspaceSwitch::Value(value).enabled(), "{}", diagnostic);
}
#[test]
fn other_values_enable(value in "\\PC{0,12}".prop_filter(
"must not be a disabling spelling",
|v: &String| !matches!(v.to_ascii_lowercase().as_str(), "0" | "false" | "off"),
)) {
prop_assert!(WorkspaceSwitch::Value(value).enabled());
}
}
#[test]
fn error_states_are_classified() {
assert!(WorkspaceSwitch::Absent.enabled());
assert!(!WorkspaceSwitch::NotUnicode.enabled());
}
}