#![cfg(feature = "cli")]
use assert_cmd::Command;
use predicates::prelude::*;
mod common {
pub fn fixture_envs(cmd: &mut assert_cmd::Command) {
cmd.env("TZ", "UTC")
.env("LC_ALL", "C.UTF-8")
.env_remove("RUSTY_TS_FORMAT")
.env_remove("RUSTY_TS_STRICT");
}
}
#[test]
fn utc_flag_renders_in_utc() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
let out = cmd
.args(["-u", "%H:%M"])
.write_stdin("x\n")
.assert()
.success();
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
let re = regex::Regex::new(r"^\d{2}:\d{2} x\n$").unwrap();
assert!(re.is_match(&stdout), "got {stdout:?}");
}
#[test]
fn utc_and_tz_mutex_rejected() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["-u", "--tz=Asia/Tokyo"])
.write_stdin("x\n")
.assert()
.failure()
.stderr(
predicate::str::contains("cannot be used").or(predicate::str::contains("conflicts")),
);
}
#[test]
fn unknown_iana_name_diagnosed() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["--tz=Atlantis/Atlantica"])
.write_stdin("x\n")
.assert()
.failure()
.stderr(predicate::str::contains("Atlantis/Atlantica"));
}
#[test]
fn strict_flag_rejects_utc() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["--strict", "-u"])
.write_stdin("x\n")
.assert()
.failure();
}
#[test]
fn strict_flag_rejects_tz() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["--strict", "--tz=Asia/Tokyo"])
.write_stdin("x\n")
.assert()
.failure();
}
#[test]
fn strict_env_var_enables_rejection() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_STRICT", "1")
.args(["-u"])
.write_stdin("x\n")
.assert()
.failure();
}
#[test]
fn no_strict_flag_overrides_env() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_STRICT", "1")
.args(["--no-strict", "-u"])
.write_stdin("x\n")
.assert()
.success();
}
#[test]
fn ts_alias_binary_auto_enables_strict() {
let mut cmd = Command::cargo_bin("ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["-u"]).write_stdin("x\n").assert().failure(); }
#[test]
fn rusty_ts_binary_name_does_not_trigger_strict() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["-u"]).write_stdin("x\n").assert().success();
}
#[test]
fn rusty_ts_format_env_var_default_path() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_FORMAT", "[%H:%M:%S]")
.args(["-u"])
.write_stdin("hi\n")
.assert()
.success()
.stdout(predicate::str::is_match(r"^\[\d{2}:\d{2}:\d{2}\] hi\n$").unwrap());
}
#[test]
fn positional_format_beats_env_var() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_FORMAT", "[%H:%M:%S]")
.args(["-u", "%H"]) .write_stdin("hi\n")
.assert()
.success()
.stdout(predicate::str::is_match(r"^\d{2} hi\n$").unwrap());
}
#[test]
fn rusty_ts_format_empty_treated_as_unset() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_FORMAT", "")
.args(["-u"])
.write_stdin("hi\n")
.assert()
.success()
.stdout(
predicate::str::is_match(r"^[A-Z][a-z]{2} [ 0-9]\d \d{2}:\d{2}:\d{2} hi\n$").unwrap(),
);
}
#[test]
fn rusty_ts_format_ignored_in_strict() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_FORMAT", "[%H:%M:%S]") .env("RUSTY_TS_STRICT", "1") .write_stdin("hi\n")
.assert()
.success()
.stdout(
predicate::str::is_match(r"^[A-Z][a-z]{2} [ 0-9]\d \d{2}:\d{2}:\d{2} hi\n$").unwrap(),
);
}
#[test]
fn completions_subcommand_emits_bash_script() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["completions", "bash"])
.assert()
.success()
.stdout(predicate::str::contains("rusty-ts"));
}
#[test]
fn elapsed_since_start_first_line_is_zero() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["-s", "%H:%M:%S"])
.write_stdin("first\n")
.assert()
.success()
.stdout(predicate::str::starts_with("00:00:00 first"));
}
#[test]
fn fixed_clock_pins_absolute_timestamp() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_TEST_FIXED_CLOCK", "2026-05-22T14:30:45Z")
.args(["-u", "%Y-%m-%d %H:%M:%S"])
.write_stdin("alpha\nbeta\n")
.assert()
.success()
.stdout("2026-05-22 14:30:45 alpha\n2026-05-22 14:30:45 beta\n");
}
#[test]
fn elapsed_i_with_fixed_clock_shows_zero_delta() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_TEST_FIXED_CLOCK", "2026-05-22T14:30:45Z")
.args(["-i", "%H:%M:%S"])
.write_stdin("a\nb\nc\n")
.assert()
.success()
.stdout("00:00:00 a\n00:00:00 b\n00:00:00 c\n");
}
#[test]
fn elapsed_s_with_fixed_clock_shows_zero_from_start() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.env("RUSTY_TS_TEST_FIXED_CLOCK", "2026-05-22T14:30:45Z")
.args(["-s", "%H:%M:%S"])
.write_stdin("a\nb\n")
.assert()
.success()
.stdout("00:00:00 a\n00:00:00 b\n");
}
#[test]
fn monotonic_flag_combined_with_elapsed_works() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
let out = cmd
.args(["-s", "-m", "%S"])
.write_stdin("a\nb\n")
.assert()
.success();
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
assert!(
stdout.starts_with("00 a\n"),
"expected first line to show 00 elapsed; got {stdout:?}",
);
let re = regex::Regex::new(r"^\d{2} a\n\d{2} b\n$").unwrap();
assert!(
re.is_match(&stdout),
"elapsed-mode output shape mismatch: {stdout:?}",
);
}
#[test]
fn flag_parse_error_returns_nonzero() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["--definitely-not-a-flag"])
.write_stdin("")
.assert()
.failure();
}
#[test]
fn version_flag_exits_clean() {
let mut cmd = Command::cargo_bin("rusty-ts").unwrap();
common::fixture_envs(&mut cmd);
cmd.args(["--version"])
.assert()
.success()
.stdout(predicate::str::contains("rusty-ts"));
}