use super::{
DEFAULT_COMMAND_MAX_OUTPUT_BYTES, DEFAULT_COMMAND_MAX_STREAM_BYTES,
DEFAULT_WHICH_CACHE_CAPACITY, StdlibConfig,
};
use crate::localization::{self, keys};
use anyhow::{Context, Result, ensure};
use camino::Utf8Path;
use rstest::{fixture, rstest};
#[fixture]
fn base_config() -> Result<StdlibConfig> {
StdlibConfig::from_current_dir()
}
#[rstest]
#[case(Utf8Path::new(""), keys::STDLIB_FETCH_CACHE_EMPTY)]
#[case(Utf8Path::new("/cache"), keys::STDLIB_FETCH_CACHE_NOT_RELATIVE)]
#[case(Utf8Path::new("../escape"), keys::STDLIB_FETCH_CACHE_ESCAPES)]
fn validate_cache_relative_rejects_invalid_inputs(
#[case] path: &Utf8Path,
#[case] message_key: &'static str,
) {
let err = StdlibConfig::validate_cache_relative(path).expect_err("invalid paths should fail");
let expected = match message_key {
keys::STDLIB_FETCH_CACHE_EMPTY => localization::message(message_key).to_string(),
keys::STDLIB_FETCH_CACHE_NOT_RELATIVE | keys::STDLIB_FETCH_CACHE_ESCAPES => {
localization::message(message_key)
.with_arg("path", path.as_str())
.to_string()
}
_ => panic!("unexpected message key {message_key}"),
};
assert_eq!(err.to_string(), expected);
}
#[rstest]
fn validate_cache_relative_accepts_workspace_relative_paths() {
StdlibConfig::validate_cache_relative(Utf8Path::new("nested/cache"))
.expect("relative path should be accepted");
}
#[rstest]
#[case::output(CommandLimitCase {
builder: StdlibConfig::with_command_max_output_bytes,
accessor: |cfg: &StdlibConfig| cfg.command_max_output_bytes,
default_value: DEFAULT_COMMAND_MAX_OUTPUT_BYTES,
updated: 2_048,
zero_err_key: keys::STDLIB_COMMAND_OUTPUT_LIMIT_POSITIVE,
})]
#[case::stream(CommandLimitCase {
builder: StdlibConfig::with_command_max_stream_bytes,
accessor: |cfg: &StdlibConfig| cfg.command_max_stream_bytes,
default_value: DEFAULT_COMMAND_MAX_STREAM_BYTES,
updated: 65_536,
zero_err_key: keys::STDLIB_COMMAND_STREAM_LIMIT_POSITIVE,
})]
fn command_limit_builders_validate_and_update(
#[from(base_config)] base_config_res: Result<StdlibConfig>,
#[case] case: CommandLimitCase,
) -> Result<()> {
let base_config = base_config_res?;
let default_value = (case.accessor)(&base_config);
ensure!(
default_value == case.default_value,
"default limit {default_value} did not match {}",
case.default_value
);
let updated_config =
(case.builder)(base_config.clone(), case.updated).context("positive limit")?;
let updated_value = (case.accessor)(&updated_config);
ensure!(
updated_value == case.updated,
"updated limit {updated_value} did not match {}",
case.updated
);
let err = (case.builder)(base_config, 0).expect_err("zero-byte limits must be rejected");
let expected = localization::message(case.zero_err_key).to_string();
ensure!(
err.to_string() == expected,
"error '{err}' did not match '{expected}'"
);
Ok(())
}
struct CommandLimitCase {
builder: fn(StdlibConfig, u64) -> anyhow::Result<StdlibConfig>,
accessor: fn(&StdlibConfig) -> u64,
default_value: u64,
updated: u64,
zero_err_key: &'static str,
}
#[rstest]
fn command_limits_propagate_into_components(base_config: Result<StdlibConfig>) -> Result<()> {
let config = base_config?
.with_command_max_output_bytes(4_096)
.context("set capture limit")?
.with_command_max_stream_bytes(131_072)
.context("set streaming limit")?;
let (_network, command) = config.into_components();
ensure!(
command.max_capture_bytes == 4_096,
"capture limit {} did not match 4096",
command.max_capture_bytes
);
ensure!(
command.max_stream_bytes == 131_072,
"stream limit {} did not match 131072",
command.max_stream_bytes
);
Ok(())
}
#[rstest]
fn which_cache_capacity_validates_and_updates(
#[from(base_config)] base_config_res: Result<StdlibConfig>,
) -> Result<()> {
let base_config = base_config_res?;
let default_capacity = base_config.which_cache_capacity().get();
ensure!(
default_capacity == DEFAULT_WHICH_CACHE_CAPACITY,
"default capacity {default_capacity} did not match {DEFAULT_WHICH_CACHE_CAPACITY}"
);
let updated = base_config
.clone()
.with_which_cache_capacity(5)
.context("positive capacity should be accepted")?;
let updated_capacity = updated.which_cache_capacity().get();
ensure!(
updated_capacity == 5,
"updated capacity {updated_capacity} did not match 5"
);
let err = base_config
.with_which_cache_capacity(0)
.expect_err("zero capacity must be rejected");
let expected = localization::message(keys::STDLIB_WHICH_CACHE_CAPACITY_POSITIVE).to_string();
ensure!(
err.to_string() == expected,
"error '{err}' did not match '{expected}'"
);
Ok(())
}
#[rstest]
#[case(vec![""], keys::STDLIB_SKIP_DIR_EMPTY)]
#[case(vec!["."], keys::STDLIB_SKIP_DIR_NAVIGATION)]
#[case(vec![".."], keys::STDLIB_SKIP_DIR_NAVIGATION)]
#[case(vec!["dir/name"], keys::STDLIB_SKIP_DIR_SEPARATOR)]
#[case(vec!["dir\\name"], keys::STDLIB_SKIP_DIR_SEPARATOR)]
fn workspace_skip_dirs_validate_inputs(
base_config: Result<StdlibConfig>,
#[case] entries: Vec<&str>,
#[case] message_key: &'static str,
) -> Result<()> {
let err = base_config?
.with_workspace_skip_dirs(entries)
.expect_err("invalid skip entries should error");
let expected = localization::message(message_key).to_string();
ensure!(
err.to_string() == expected,
"error '{err}' did not match '{expected}'"
);
Ok(())
}
#[rstest]
fn workspace_skip_dirs_override_defaults(base_config: Result<StdlibConfig>) -> Result<()> {
let config = base_config?
.with_workspace_skip_dirs(["build", ".cache"])
.context("configure skip dirs")?;
let skip_dirs = config.workspace_skip_dirs();
ensure!(
skip_dirs == ["build".to_owned(), ".cache".to_owned()],
"skip dirs {skip_dirs:?} did not match [\"build\", \".cache\"]"
);
Ok(())
}