Skip to content

Commit

Permalink
Add --request subcommand for testing (#2498)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored Dec 2, 2024
1 parent 7d56d52 commit cdf104b
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 16 deletions.
18 changes: 18 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ mod cmd {
pub(crate) const INIT: &str = "INIT";
pub(crate) const LIST: &str = "LIST";
pub(crate) const MAN: &str = "MAN";
pub(crate) const REQUEST: &str = "REQUEST";
pub(crate) const SHOW: &str = "SHOW";
pub(crate) const SUMMARY: &str = "SUMMARY";
pub(crate) const VARIABLES: &str = "VARIABLES";
Expand All @@ -69,6 +70,7 @@ mod cmd {
INIT,
LIST,
MAN,
REQUEST,
SHOW,
SUMMARY,
VARIABLES,
Expand Down Expand Up @@ -517,6 +519,17 @@ impl Config {
.help("Print man page")
.help_heading(cmd::HEADING),
)
.arg(
Arg::new(cmd::REQUEST)
.long("request")
.action(ArgAction::Set)
.hide(true)
.help(
"Execute <REQUEST>. For internal testing purposes only. May be changed or removed at \
any time.",
)
.help_heading(cmd::REQUEST),
)
.arg(
Arg::new(cmd::SHOW)
.short('s')
Expand Down Expand Up @@ -696,6 +709,11 @@ impl Config {
}
} else if matches.get_flag(cmd::MAN) {
Subcommand::Man
} else if let Some(request) = matches.get_one::<String>(cmd::REQUEST) {
Subcommand::Request {
request: serde_json::from_str(request)
.map_err(|source| ConfigError::RequestParse { source })?,
}
} else if let Some(path) = matches.get_many::<String>(cmd::SHOW) {
Subcommand::Show {
path: Self::parse_module_path(path)?,
Expand Down
2 changes: 2 additions & 0 deletions src/config_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub(crate) enum ConfigError {
Internal { message: String },
#[snafu(display("Invalid module path `{}`", path.join(" ")))]
ModulePath { path: Vec<String> },
#[snafu(display("Failed to parse request: {source}"))]
RequestParse { source: serde_json::Error },
#[snafu(display(
"Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
))]
Expand Down
6 changes: 3 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub(crate) enum Error<'src> {
},
DotenvRequired,
DumpJson {
serde_json_error: serde_json::Error,
source: serde_json::Error,
},
EditorInvoke {
editor: OsString,
Expand Down Expand Up @@ -359,8 +359,8 @@ impl ColorDisplay for Error<'_> {
DotenvRequired => {
write!(f, "Dotenv file not found")?;
}
DumpJson { serde_json_error } => {
write!(f, "Failed to dump JSON to stdout: {serde_json_error}")?;
DumpJson { source } => {
write!(f, "Failed to dump JSON to stdout: {source}")?;
}
EditorInvoke { editor, io_error } => {
let editor = editor.to_string_lossy();
Expand Down
11 changes: 9 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub(crate) use {
regex::Regex,
serde::{
ser::{SerializeMap, SerializeSeq},
Serialize, Serializer,
Deserialize, Serialize, Serializer,
},
snafu::{ResultExt, Snafu},
std::{
Expand Down Expand Up @@ -76,9 +76,12 @@ pub(crate) use crate::{node::Node, tree::Tree};

pub use crate::run::run;

#[doc(hidden)]
use request::Request;

// Used in integration tests.
#[doc(hidden)]
pub use unindent::unindent;
pub use {request::Response, unindent::unindent};

type CompileResult<'a, T = ()> = Result<T, CompileError<'a>>;
type ConfigResult<T> = Result<T, ConfigError>;
Expand Down Expand Up @@ -106,6 +109,10 @@ pub mod fuzzing;
#[doc(hidden)]
pub mod summary;

// Used for testing with the `--request` subcommand.
#[doc(hidden)]
pub mod request;

mod alias;
mod analyzer;
mod argument_parser;
Expand Down
13 changes: 13 additions & 0 deletions src/request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::*;

#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum Request {
EnvironmentVariable(String),
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Response {
EnvironmentVariable(Option<OsString>),
}
24 changes: 19 additions & 5 deletions src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub(crate) enum Subcommand {
path: ModulePath,
},
Man,
Request {
request: Request,
},
Run {
arguments: Vec<String>,
overrides: BTreeMap<String, String>,
Expand Down Expand Up @@ -71,10 +74,6 @@ impl Subcommand {
let justfile = &compilation.justfile;

match self {
Run {
arguments,
overrides,
} => Self::run(config, loader, search, compilation, arguments, overrides)?,
Choose { overrides, chooser } => {
Self::choose(config, justfile, &search, overrides, chooser.as_deref())?;
}
Expand All @@ -85,6 +84,11 @@ impl Subcommand {
Format => Self::format(config, &search, compilation)?,
Groups => Self::groups(config, justfile),
List { path } => Self::list(config, justfile, path)?,
Request { request } => Self::request(request)?,
Run {
arguments,
overrides,
} => Self::run(config, loader, search, compilation, arguments, overrides)?,
Show { path } => Self::show(config, justfile, path)?,
Summary => Self::summary(config, justfile),
Variables => Self::variables(justfile),
Expand Down Expand Up @@ -280,7 +284,7 @@ impl Subcommand {
match config.dump_format {
DumpFormat::Json => {
serde_json::to_writer(io::stdout(), &compilation.justfile)
.map_err(|serde_json_error| Error::DumpJson { serde_json_error })?;
.map_err(|source| Error::DumpJson { source })?;
println!();
}
DumpFormat::Just => print!("{}", compilation.root_ast()),
Expand Down Expand Up @@ -402,6 +406,16 @@ impl Subcommand {
Ok(())
}

fn request(request: &Request) -> RunResult<'static> {
let response = match request {
Request::EnvironmentVariable(key) => Response::EnvironmentVariable(env::var_os(key)),
};

serde_json::to_writer(io::stdout(), &response).map_err(|source| Error::DumpJson { source })?;

Ok(())
}

fn list(config: &Config, mut module: &Justfile, path: &ModulePath) -> RunResult<'static> {
for name in &path.path {
module = module
Expand Down
9 changes: 4 additions & 5 deletions tests/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,13 @@ fn constants_can_be_redefined() {
fn constants_are_not_exported() {
Test::new()
.justfile(
"
r#"
set export
foo:
echo $HEXUPPER
",
@'{{just_executable()}}' --request '{"environment-variable": "HEXUPPER"}'
"#,
)
.stderr_regex(".*HEXUPPER: unbound variable.*")
.status(127)
.response(Response::EnvironmentVariable(None))
.run();
}
3 changes: 2 additions & 1 deletion tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub(crate) use {
test::{assert_eval_eq, Output, Test},
},
executable_path::executable_path,
just::unindent,
just::{unindent, Response},
libc::{EXIT_FAILURE, EXIT_SUCCESS},
pretty_assertions::Comparison,
regex::Regex,
Expand Down Expand Up @@ -99,6 +99,7 @@ mod quote;
mod readme;
mod recursion_limit;
mod regexes;
mod request;
mod run;
mod script;
mod search;
Expand Down
29 changes: 29 additions & 0 deletions tests/request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use super::*;

#[test]
fn environment_variable_set() {
Test::new()
.justfile(
r#"
export BAR := 'baz'
@foo:
'{{just_executable()}}' --request '{"environment-variable": "BAR"}'
"#,
)
.response(Response::EnvironmentVariable(Some("baz".into())))
.run();
}

#[test]
fn environment_variable_missing() {
Test::new()
.justfile(
r#"
@foo:
'{{just_executable()}}' --request '{"environment-variable": "FOO_BAR_BAZ"}'
"#,
)
.response(Response::EnvironmentVariable(None))
.run();
}
16 changes: 16 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub(crate) struct Test {
pub(crate) env: BTreeMap<String, String>,
pub(crate) expected_files: BTreeMap<PathBuf, Vec<u8>>,
pub(crate) justfile: Option<String>,
pub(crate) response: Option<Response>,
pub(crate) shell: bool,
pub(crate) status: i32,
pub(crate) stderr: String,
Expand All @@ -74,6 +75,7 @@ impl Test {
env: BTreeMap::new(),
expected_files: BTreeMap::new(),
justfile: Some(String::new()),
response: None,
shell: true,
status: EXIT_SUCCESS,
stderr: String::new(),
Expand Down Expand Up @@ -139,6 +141,11 @@ impl Test {
self
}

pub(crate) fn response(mut self, response: Response) -> Self {
self.response = Some(response);
self.stdout_regex(".*")
}

pub(crate) fn shell(mut self, shell: bool) -> Self {
self.shell = shell;
self
Expand Down Expand Up @@ -293,6 +300,15 @@ impl Test {
panic!("Output mismatch.");
}

if let Some(ref response) = self.response {
assert_eq!(
&serde_json::from_str::<Response>(output_stdout)
.expect("failed to deserialize stdout as response"),
response,
"response mismatch"
);
}

for (path, expected) in &self.expected_files {
let actual = fs::read(self.tempdir.path().join(path)).unwrap();
assert_eq!(
Expand Down

0 comments on commit cdf104b

Please sign in to comment.