mirror of
https://github.com/casey/just.git
synced 2025-08-04 15:08:39 +00:00
Add --request
subcommand for testing (#2498)
This commit is contained in:
parent
7d56d5214e
commit
cdf104bf8c
10 changed files with 115 additions and 16 deletions
|
@ -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";
|
||||
|
@ -69,6 +70,7 @@ mod cmd {
|
|||
INIT,
|
||||
LIST,
|
||||
MAN,
|
||||
REQUEST,
|
||||
SHOW,
|
||||
SUMMARY,
|
||||
VARIABLES,
|
||||
|
@ -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')
|
||||
|
@ -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)?,
|
||||
|
|
|
@ -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`."
|
||||
))]
|
||||
|
|
|
@ -81,7 +81,7 @@ pub(crate) enum Error<'src> {
|
|||
},
|
||||
DotenvRequired,
|
||||
DumpJson {
|
||||
serde_json_error: serde_json::Error,
|
||||
source: serde_json::Error,
|
||||
},
|
||||
EditorInvoke {
|
||||
editor: OsString,
|
||||
|
@ -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();
|
||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -42,7 +42,7 @@ pub(crate) use {
|
|||
regex::Regex,
|
||||
serde::{
|
||||
ser::{SerializeMap, SerializeSeq},
|
||||
Serialize, Serializer,
|
||||
Deserialize, Serialize, Serializer,
|
||||
},
|
||||
snafu::{ResultExt, Snafu},
|
||||
std::{
|
||||
|
@ -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>;
|
||||
|
@ -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;
|
||||
|
|
13
src/request.rs
Normal file
13
src/request.rs
Normal file
|
@ -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>),
|
||||
}
|
|
@ -35,6 +35,9 @@ pub(crate) enum Subcommand {
|
|||
path: ModulePath,
|
||||
},
|
||||
Man,
|
||||
Request {
|
||||
request: Request,
|
||||
},
|
||||
Run {
|
||||
arguments: Vec<String>,
|
||||
overrides: BTreeMap<String, String>,
|
||||
|
@ -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())?;
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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()),
|
||||
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
@ -99,6 +99,7 @@ mod quote;
|
|||
mod readme;
|
||||
mod recursion_limit;
|
||||
mod regexes;
|
||||
mod request;
|
||||
mod run;
|
||||
mod script;
|
||||
mod search;
|
||||
|
|
29
tests/request.rs
Normal file
29
tests/request.rs
Normal file
|
@ -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();
|
||||
}
|
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue