alexliesenfeld / httpmock

HTTP mocking library for Rust
https://httpmock.rs
MIT License
472 stars 42 forks source link

RFC: Portability/Relative Path - Then body_from_file Into type String #45

Closed pinkforest closed 1 year ago

pinkforest commented 3 years ago

Error and/or Documentation Error and/or Relative Path Handling Error

I was using a relative path for the fn call directly per doc example for Then::body_from_file

But environment variable not found error popped up instead:

Cannot create absolute path from string 'x':  "environment variable not found"

I tried to look whether the doc said much about environment variables but nothing to evaluate.

However Looking into I can find the call to crate::util::get_test_resource_file_path() when path.is_absolute is false:

match env::var("CARGO_MANIFEST_DIR") {

Problem is this env may not be visible to cargo sub-commands as I found the hard way with tarpaulin:

env RUN_MODE=development cargo +nightly tarpaulin --run-types Tests,Doctests,Benchmarks,Examples,Lib,Bins -v

Doc wise Mock::return_body_from_file says explicitly either relative/absolute:

resource_file_path - The file path to the file with the response body content. The file path can either be absolute or relative to the project root directory.

As for relative path definition, I would expect this to be the OsStr<> from the Current working directory (CWD) which may be set to be elsewhere than the directory holding the cargo manifest.

For backwards compatibility the document probably should say that the environment variable is used for relative path or behavior could be changed some way to rely on CWD that would break things relying on it to be the manifest path

Plus CWD typically is not UTF-8 guaranteed path if Into is relied (not absolutely sure whether CARGO_MANIFEST_DIR is guaranteed to be either for lossless conversion Into either if it's allowed to be non UTF-8 :1234: ?)

I personally use this pattern and document the var_os OssStr returning environment variable use for my app:

  let mut path_locator = match env::var_os("APP_CONFIG_PATH") {
      Some(val) => PathBuf::from(val),
      None => env::current_dir().unwrap()
};

If I feel I need to enforce UTF-8 paths like Rust ecosystem like cargo does I use camino Utf8Path to see if I can upgrade from Path and match appropriate error.

Happy to submit a PR to fix doc and automatically check the truth whatever is decided when/if there is a decision what to do with this.

Code Blocks Involved

Then::body_from_file(httpmock/0.5.8/source/src/lib.rs)

pub fn body_from_file<S: Into<String>>(self, body: S) -> Self {
        self.mock.set(self.mock.take().return_body_from_file(body));

Mock::return_body_from_file(httpmock/0.5.8/source/src/api/mock.rs):around 1317

 pub fn return_body_from_file<S: Into<String>>(mut self, resource_file_path: S) -> Self {
        let resource_file_path = resource_file_path.into();
        let path = Path::new(&resource_file_path);
        let absolute_path = match path.is_absolute() {
            true => path.to_path_buf(),
            false => get_test_resource_file_path(&resource_file_path).expect(&format!(
                "Cannot create absolute path from string '{}'",
     //--snip--

Dilemma

Solution 1: Allow From &std::path::Path without using CARGO env

Pros:

Cons:

Example is using config::File::from

impl<'a> From<&'a Path> for File<source::file::FileSourceFile> {
    fn from(path: &'a Path) -> Self {
        File {
            source: source::file::FileSourceFile::new(path.to_path_buf()),
            //--snip--

Where I can easily construct and pass the full Path natively and use relative path if I want

let mut path_locator = match env::var_os("CONFIG_PATH") {
  Some(val) => PathBuf::from(val),
  None => env::current_dir().unwrap()
};

path_locator.push("subdir");

// And then finally merge the file I want with full POSIX compatible Path

s.merge(File::from(path_locator.join("default")).required(false))?;

Solution 2: Enforce and be explicit about UTF-8 and handle Error thru camino

Pros:

Cons:

Solution 3: New fn pass by Buffer/Stream/Channel oneshot etc.

Pros:

Cons:

Solution 4: Status quo

Pros:

Cons:

Solution is probably somekind combination?

I usually do both integration / units along my /// docs or even on README.md that gets included in test run and tarpaulin --run-types DocTests takes those into account

#[cfg(doctest)]
doc_comment::doctest!("../README.md");

I will check with tarpaulin too to expose the environment var but it would be nice to get some clarity as it caused a bit confusion initially for me and not sure if there is anyone doing things without cargo env :)

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] commented 1 year ago

This issue was closed because it has been inactive for 14 days since being marked as stale.