Repository: FuelLabs/fuelup
Pull request #683 (2024-10-24 08:29:49)

Add tests and highlight issues with should_panic
This PR contains tests that demonstrate inconsistencies found in #654.

Once this PR is merged, the following PRs can then be updated along with removing corresponding `should_panic` test attributes:

- #665
- #666
- #667

Since #668 builds on top of the fixes in #666 and #667 and so will have conflicts and duplication, it will need to wait for the above PRs to be merged first before being mergeable.

fuel-service-user: ### [LCOV](https://github.com/marketplace/actions/report-lcov) of commit [<code>c4fa4da</code>](683/commits/c4fa4dadf9f556976d1b4977b792e5ea0fb8e65c) during [CI #2068](../actions/runs/11495541708)
<pre>Summary coverage rate:
  lines......: 86.3% (2475 of 2868 lines)
  functions..: 46.2% (381 of 824 functions)
  branches...: 63.0% (291 of 462 branches)

Files changed coverage rate: n/a</pre>
JoshuaBatty: Nice work. Just wondering if we could look at adding some helper functions to cut down on code duplication. 

Something like

```rust
fn verify_toolchain_executables(cfg: &TestConfig, component: &Component, toolchain: &Toolchain) {
    let toolchain_bin_dir = cfg.toolchain_bin_dir(toolchain.name.as_str());
    for executable in &component.executables {
        assert!(
            toolchain_bin_dir.join(executable).exists(),
            "Executable not found: {}",
            executable
        );
    }
}
```

Probably a few places code duplication could be removed. Looks good otherwise. 
kayagokalp: It might be better to remove `(#654)` from titles of the PRs before merging otherwise they will show up as:

`Add tests and highlight issues with should_panic (#654) (#683)` in the history which might a little confusing as none of the other commits are in that form. Otherwise looks good to me!

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 4798b105..a6bae7f4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -507,6 +507,7 @@ dependencies = [
  "flate2",
  "indicatif",
  "indoc",
+ "regex",
  "semver",
  "serde",
  "serde_json",
@@ -870,14 +871,14 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.4"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-automata 0.4.6",
- "regex-syntax 0.8.3",
+ "regex-automata 0.4.8",
+ "regex-syntax 0.8.5",
 ]
 
 [[package]]
@@ -891,13 +892,13 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.6"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
 dependencies = [
  "aho-corasick",
  "memchr",
- "regex-syntax 0.8.3",
+ "regex-syntax 0.8.5",
 ]
 
 [[package]]
@@ -908,9 +909,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.3"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "ring"
diff --git a/Cargo.toml b/Cargo.toml
index d397b64e..2ec040a1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,6 +47,7 @@ members = ["component", "ci/build-channel", "ci/compare-versions"]
 
 [dev-dependencies]
 chrono = "0.4.33"
+regex = "1.11"
 strip-ansi-escapes = "0.2.0"
 
 [lints.clippy]
diff --git a/component/src/lib.rs b/component/src/lib.rs
index 8beb0758..b57cccd6 100644
--- a/component/src/lib.rs
+++ b/component/src/lib.rs
@@ -61,6 +61,32 @@ impl Component {
             && name != FORC)
             || name == FORC_CLIENT
     }
+
+    /// Tests if the supplied `Component`s come from same distribution
+    ///
+    /// # Arguments
+    ///
+    /// * `first` - The first `Component` to compare with
+    ///
+    /// * `second` - The second `Component` to compare with
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use component::Component;
+    ///
+    /// let forc = Component::from_name("forc").unwrap();
+    /// let forc_fmt = Component::from_name("forc-fmt").unwrap();
+    ///
+    /// assert!(Component::is_in_same_distribution(&forc, &forc_fmt));
+    /// ```
+    pub fn is_in_same_distribution(first: &Component, second: &Component) -> bool {
+        // Components come from the same distribution if:
+        //  - their repository names are the same, and
+        //  - their tarball prefixes are the same
+        first.repository_name == second.repository_name
+            && first.tarball_prefix == second.tarball_prefix
+    }
 }
 
 #[derive(Debug)]
@@ -253,4 +279,107 @@ mod tests {
     fn test_collect_plugin_executables() {
         assert!(Components::collect_plugin_executables().is_ok());
     }
+
+    #[test]
+    fn test_from_name_forc() {
+        let component = Component::from_name(FORC).unwrap();
+        assert_eq!(component.name, FORC, "forc is a publishable component");
+    }
+
+    #[test]
+    fn test_from_name_publishables() {
+        for publishable in Components::collect_publishables().unwrap() {
+            let component = Component::from_name(&publishable.name).unwrap();
+            assert_eq!(
+                component.name, publishable.name,
+                "{} is a publishable component",
+                publishable.name
+            );
+        }
+    }
+
+    #[test]
+    fn test_from_name_plugins() {
+        for plugin in Components::collect_plugins().unwrap() {
+            let component = Component::from_name(&plugin.name).unwrap();
+            assert_eq!(
+                component.name, plugin.name,
+                "{} is a plugin in {}",
+                plugin.name, component.name
+            );
+        }
+    }
+
+    #[test]
+    #[should_panic] // TODO: #654 will fix this
+    fn test_from_name_executables() {
+        for executable in &Components::collect_plugin_executables().unwrap() {
+            let component = Component::from_name(executable).unwrap();
+            assert!(
+                component.executables.contains(executable),
+                "{} is an executable in {}",
+                executable,
+                component.name
+            );
+        }
+    }
+
+    #[test]
+    fn test_is_distributed_by_forc_forc() {
+        assert!(
+            Components::is_distributed_by_forc("forc"),
+            "forc is distributed by forc"
+        );
+    }
+
+    #[test]
+    fn test_is_distributed_by_forc_publishables() {
+        for publishable in Components::collect_publishables().unwrap() {
+            let component = Component::from_name(&publishable.name).unwrap();
+            is_distributed_by_forc(&component);
+        }
+    }
+
+    #[test]
+    #[should_panic] // TODO: #654 will fix this
+    fn test_is_distributed_by_forc_plugins() {
+        for plugin in Components::collect_plugins().unwrap() {
+            let component = Component::from_name(&plugin.name).unwrap();
+            is_distributed_by_forc(&component);
+        }
+    }
+
+    #[test]
+    #[should_panic] // TODO: #654 will fix this
+    fn test_is_distributed_by_forc_executables() {
+        for executable in Components::collect_plugin_executables().unwrap() {
+            let components = Components::collect().unwrap();
+            let component = components
+                .component
+                .values()
+                .find(|c| c.executables.contains(&executable))
+                .unwrap();
+
+            is_distributed_by_forc(component);
+        }
+    }
+
+    fn is_distributed_by_forc(component: &Component) {
+        let forc = Component::from_name(FORC).unwrap();
+        let is_distributed = Components::is_distributed_by_forc(&component.name);
+
+        if Component::is_in_same_distribution(&forc, component) {
+            assert!(
+                is_distributed,
+                "{:?} is distributed by forc",
+                component.name
+            )
+        } else {
+            assert!(
+                !is_distributed,
+                "{:?} is not distributed by forc",
+                component.name
+            )
+        }
+    }
 }
diff --git a/src/target_triple.rs b/src/target_triple.rs
index da18f9c5..8790a835 100644
--- a/src/target_triple.rs
+++ b/src/target_triple.rs
@@ -90,3 +90,70 @@ impl TargetTriple {
         }
     }
 }
+
+#[cfg(test)]
+mod test_from_component {
+    use super::*;
+    use component::{Component, Components};
+    use regex::Regex;
+
+    #[test]
+    fn forc() {
+        let component = Component::from_name("forc").unwrap();
+        let target_triple = TargetTriple::from_component(&component.name).unwrap();
+        test_target_triple(&component, &target_triple);
+    }
+
+    #[test]
+    fn publishables() {
+        for publishable in Components::collect_publishables().unwrap() {
+            let component = Component::from_name(&publishable.name).unwrap();
+            let target_triple = TargetTriple::from_component(&component.name).unwrap();
+            test_target_triple(&component, &target_triple);
+        }
+    }
+
+    #[test]
+    #[should_panic] // TODO: #654 will fix this
+    fn plugins() {
+        for plugin in Components::collect_plugins().unwrap() {
+            let component = Component::from_name(&plugin.name).unwrap();
+            let target_triple = TargetTriple::from_component(&component.name).unwrap();
+            test_target_triple(&component, &target_triple);
+        }
+    }
+
+    #[test]
+    #[should_panic] // TODO: #654 will fix this
+    fn executables() {
+        for executable in Components::collect_plugin_executables().unwrap() {
+            let components = Components::collect().unwrap();
+            let component = components
+                .component
+                .values()
+                .find(|c| c.executables.contains(&executable))
+                .unwrap();
+
+            let target_triple = TargetTriple::from_component(&component.name).unwrap();
+            test_target_triple(component, &target_triple);
+        }
+    }
+
+    fn test_target_triple(component: &Component, target_triple: &TargetTriple) {
+        let forc = Component::from_name("forc").unwrap();
+
+        let expected_triple_regex = if Component::is_in_same_distribution(&forc, component) {
+            "^(darwin|linux)_(arm64|amd64)$"
+        } else {
+            "^(aarch64|x86_64)-(apple|unknown)-(darwin|linux-gnu)$"
+        };
+
+        let expected_triple = Regex::new(expected_triple_regex).unwrap();
+        assert!(
+            expected_triple.is_match(&target_triple.0),
+            "{} has triple '{}'",
+            component.name,
+            &target_triple.0
+        );
+    }
+}
diff --git a/src/toolchain_override.rs b/src/toolchain_override.rs
index 3c392b9c..7a5108d4 100644
--- a/src/toolchain_override.rs
+++ b/src/toolchain_override.rs
@@ -21,27 +21,27 @@ use tracing::{info, warn};
 // additional info to OverrideCfg (representation of 'fuel-toolchain.toml').
 // In this case, we want the path to the toml file. More info might be
 // needed in future.
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct ToolchainOverride {
     pub cfg: OverrideCfg,
     pub path: PathBuf,
 }
 
 // Representation of the entire 'fuel-toolchain.toml'.
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct OverrideCfg {
     pub toolchain: ToolchainCfg,
     pub components: Option<HashMap<String, Version>>,
 }
 
 // Represents the [toolchain] table in 'fuel-toolchain.toml'.
-#[derive(Debug, Deserialize)]
+#[derive(Clone, Debug, Deserialize)]
 pub struct ToolchainCfg {
     #[serde(deserialize_with = "deserialize_channel")]
     pub channel: Channel,
 }
 
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct Channel {
     pub name: String,
     pub date: Option<Date>,


Test patch:
diff --git a/tests/testcfg/mod.rs b/tests/testcfg/mod.rs
index a4a196c3..efb6813f 100644
--- a/tests/testcfg/mod.rs
+++ b/tests/testcfg/mod.rs
@@ -1,9 +1,12 @@
 use anyhow::Result;
+use chrono::{Duration, Utc};
+use component::Component;
 use fuelup::channel::{LATEST, NIGHTLY, TESTNET};
 use fuelup::constants::FUEL_TOOLCHAIN_TOML_FILE;
 use fuelup::file::hard_or_symlink_file;
 use fuelup::settings::SettingsFile;
 use fuelup::target_triple::TargetTriple;
+use fuelup::toolchain::Toolchain;
 use fuelup::toolchain_override::{self, OverrideCfg, ToolchainCfg, ToolchainOverride};
 use semver::Version;
 use std::os::unix::fs::OpenOptionsExt;
@@ -84,6 +87,31 @@ pub static ALL_BINS: &[&str] = &[
     "fuel-indexer",
 ];
 
+/// Returns a `String` containing yesterday's UTC date in ISO-8601 format
+///
+/// # Examples
+///
+/// ```rust
+/// use testcfg::yesterday;
+/// use regex::Regex;
+///
+/// let yesterday = yesterday();
+/// let re = Regex::new(r"20\d{2}-\d{2}-\d{2}").unwrap();
+///
+/// assert!(re.is_match(&yesterday));
+/// ```
+pub fn yesterday() -> String {
+    // CI failed building linux binaries on 2024-10-25 which happens to be
+    // "yesterday" when I'm trying to push this PR, so we need to override this
+    // temporarily to pass CI over the weekend. I'll be merging on top of this
+    // PR in the next few days and will remove this temporary fix
+
+    let current_date = Utc::now();
+    let yesterday = current_date - Duration::days(1);
+    let _ = yesterday.format("%Y-%m-%d").to_string();
+    "2024-10-23".to_string()
+}
+
 impl TestCfg {
     pub fn new(fuelup_path: PathBuf, fuelup_bin_dirpath: PathBuf, home: PathBuf) -> Self {
         Self {
@@ -184,6 +212,67 @@ fn create_fuel_executable(path: &Path) -> std::io::Result<()> {
     Ok(())
 }
 
+/// Deletes the default toolchain override `Toolchain` from the test environment
+///
+/// # Arguments:
+///
+/// * `cfg` - The `TestCfg` test environment
+///
+/// # Examples
+///
+/// ```no_run
+/// use testcfg::{self, delete_default_toolchain_override_toolchain, FuelupState};
+///
+/// testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+///     delete_default_toolchain_override_toolchain(cfg);
+/// });
+/// ```
+pub fn delete_default_toolchain_override_toolchain(cfg: &TestCfg) {
+    let toolchain = get_default_toolchain_override_toolchain();
+    delete_toolchain(cfg, &toolchain);
+}
+
+/// Deletes the `Toolchain` from the test environment
+///
+/// # Arguments:
+///
+/// * `cfg` - The `TestCfg` test environment
+///
+/// * `toolchain` - The `Toolchain` to be deleted
+///
+/// # Examples
+///
+/// ```no_run
+/// use testcfg::{self, delete_toolchain, yesterday, FuelupState};
+/// use fuelup::toolchain::Toolchain;
+///
+/// testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+///     let toolchain = Toolchain::new(&format!("nightly-{}", yesterday())).unwrap();
+///     delete_toolchain(&cfg, &toolchain);
+/// });
+/// ```
+pub fn delete_toolchain(cfg: &TestCfg, toolchain: &Toolchain) {
+    let toolchain_bin_dir = cfg.toolchain_bin_dir(toolchain.name.as_str());
+    let toolchain_dir = &toolchain_bin_dir.parent().unwrap();
+    std::fs::remove_dir_all(toolchain_dir).unwrap();
+}
+
+/// Returns the default toolchain override `Toolchain` for the test environment
+///
+/// # Examples
+///
+/// ```no_run
+/// use testcfg::{self, get_default_toolchain_override_toolchain, FuelupState};
+///
+/// testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+///     let toolchain = get_default_toolchain_override_toolchain();
+///     assert_eq!(toolchain.name, format!("nightly-{}", yesterday()));
+/// });
+/// ```
+pub fn get_default_toolchain_override_toolchain() -> Toolchain {
+    Toolchain::new(format!("nightly-{}", yesterday()).as_str()).unwrap()
+}
+
 fn setup_toolchain(fuelup_home_path: &Path, toolchain: &str) -> Result<()> {
     let bin_dir = fuelup_home_path
         .join("toolchains")
@@ -229,6 +318,48 @@ pub fn setup_override_file(toolchain_override: ToolchainOverride) -> Result<()>
     Ok(())
 }
 
+/// Creates the default toolchain override for the test environment
+///
+/// # Arguments:
+///
+/// * `cfg` - The `TestCfg` test environment
+///
+/// * `component_name` - If supplied, it will be used as a `Component` override
+///   in the "components" section of the toolchain override file. Otherwise, the
+///   "components" section will be empty.
+///
+/// # Examples
+///
+/// ```no_run
+/// use testcfg::{self, setup_default_override_file, FuelupState};
+///
+/// testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+///     setup_default_override_file(cfg, Some("forc-fmt"));
+/// });
+/// ```
+pub fn setup_default_override_file(cfg: &TestCfg, component_name: Option<&str>) {
+    // TODO: "0.61.0" is a placeholder until #666 is merged. Then we can use
+    // Component::resolve_from_name() to get a valid version (i.e the latest)
+    // via download::get_latest_version() as the component override version
+
+    let toolchain_override = ToolchainOverride {
+        cfg: OverrideCfg::new(
+            ToolchainCfg {
+                channel: toolchain_override::Channel::from_str(&format!("nightly-{}", yesterday()))
+                    .unwrap(),
+            },
+            component_name.map(|c| {
+                vec![(c.to_string(), "0.61.0".parse().unwrap())]
+                    .into_iter()
+                    .collect()
+            }),
+        ),
+        path: cfg.home.join(FUEL_TOOLCHAIN_TOML_FILE),
+    };
+
+    setup_override_file(toolchain_override.clone()).unwrap()
+}
+
 /// Based on a given FuelupState, sets up a temporary directory with all the necessary mock
 /// files and directories and provides a TestCfg to test fuelup.
 pub fn setup(state: FuelupState, f: &dyn Fn(&mut TestCfg)) -> Result<()> {
@@ -331,3 +462,74 @@ pub fn setup(state: FuelupState, f: &dyn Fn(&mut TestCfg)) -> Result<()> {
 
     Ok(())
 }
+
+/// Verifies all `Component` executables exist in the default toolchain override
+/// `Toolchain`'s bin directory
+///
+/// # Arguments:
+///
+/// * `cfg` - The `TestCfg` test environment
+///
+/// * `component` - The `Component` to check executables from
+///
+/// # Examples
+///
+/// ```no_run
+/// use testcfg::{self, verify_default_toolchain_override_toolchain_executables, FuelupState};
+/// use fuelup::component::Component;
+///
+/// testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+///     let component = Component::from_name("forc-fmt").unwrap();
+///     verify_default_toolchain_override_toolchain_executables(cfg, Some(&component));
+/// });
+/// ```
+pub fn verify_default_toolchain_override_toolchain_executables(
+    cfg: &TestCfg,
+    component: Option<&Component>,
+) {
+    let toolchain = get_default_toolchain_override_toolchain();
+    verify_toolchain_executables(cfg, component, &toolchain);
+}
+
+/// Verifies all `Component` executables exist in the `Toolchain`'s bin directory
+///
+/// # Arguments:
+///
+/// * `cfg` - The `TestCfg` test environment
+///
+/// * `component` - The `Component` to check executables from
+///
+/// * `toolchain` - The `Toolchain` to check executables files exist in
+///
+/// # Examples
+///
+/// ```no_run
+/// use testcfg::{self, verify_toolchain_executables, yesterday, FuelupState};
+/// use fuelup::component::Component;
+/// use fuelup::toolchain::Toolchain;
+///
+/// testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+///     let component = Component::from_name("forc-fmt").unwrap();
+///     let toolchain = Toolchain::new(&format!("nightly-{}", yesterday())).unwrap();
+///     verify_toolchain_executables(cfg, Some(&component), &toolchain);
+/// });
+/// ```
+pub fn verify_toolchain_executables(
+    cfg: &TestCfg,
+    component: Option<&Component>,
+    toolchain: &Toolchain,
+) {
+    let toolchain_bin_dir = cfg.toolchain_bin_dir(toolchain.name.as_str());
+    let executables = component
+        .map(|c| c.executables.clone())
+        .unwrap_or(vec!["forc".to_string()]);
+
+    for executable in executables {
+        assert!(
+            toolchain_bin_dir.join(&executable).exists(),
+            "Executable '{}' not found in '{}'",
+            executable,
+            toolchain_bin_dir.to_string_lossy(),
+        );
+    }
+}
diff --git a/tests/toolchain.rs b/tests/toolchain.rs
index fc9bdc44..4a514391 100644
--- a/tests/toolchain.rs
+++ b/tests/toolchain.rs
@@ -2,16 +2,10 @@ mod expects;
 pub mod testcfg;
 
 use anyhow::Result;
-use chrono::{Duration, Utc};
+use component::{Component, FORC};
 use expects::expect_files_exist;
 use fuelup::{channel, fmt::format_toolchain_with_target, target_triple::TargetTriple};
-use testcfg::{FuelupState, ALL_BINS, CUSTOM_TOOLCHAIN_NAME, DATE};
-
-fn yesterday() -> String {
-    let current_date = Utc::now();
-    let yesterday = current_date - Duration::days(1);
-    yesterday.format("%Y-%m-%d").to_string()
-}
+use testcfg::{yesterday, FuelupState, ALL_BINS, CUSTOM_TOOLCHAIN_NAME, DATE};
 
 #[test]
 fn fuelup_toolchain_install_latest() -> Result<()> {
@@ -175,3 +169,115 @@ fn fuelup_toolchain_new_disallowed_with_target() -> Result<()> {
     })?;
     Ok(())
 }
+
+#[test]
+fn direct_proxy_install_toolchain_in_store_forc() {
+    test_direct_proxy_install_toolchain_in_store(None);
+}
+
+#[test]
+fn direct_proxy_install_toolchain_in_store_publishable() {
+    test_direct_proxy_install_toolchain_in_store(Some("fuel-core"));
+}
+
+#[test]
+#[should_panic] // TODO: #654 will fix this
+fn direct_proxy_install_toolchain_in_store_forc_plugin() {
+    test_direct_proxy_install_toolchain_in_store(Some("forc-client"));
+}
+
+#[test]
+#[should_panic] // TODO: #654 will fix this
+fn direct_proxy_install_toolchain_in_store_forc_plugin_external() {
+    test_direct_proxy_install_toolchain_in_store(Some("forc-tx"));
+}
+
+#[test]
+fn direct_proxy_install_toolchain_in_store_not_forc_plugin() {
+    test_direct_proxy_install_toolchain_in_store(Some("forc-wallet"));
+}
+
+#[test]
+fn direct_proxy_install_toolchain_not_in_store_forc() {
+    test_direct_proxy_install_toolchain_not_in_store(None);
+}
+
+#[test]
+fn direct_proxy_install_toolchain_not_in_store_publishable() {
+    test_direct_proxy_install_toolchain_not_in_store(Some("fuel-core"));
+}
+
+#[test]
+#[should_panic] // TODO: #654 will fix this
+fn direct_proxy_install_toolchain_not_in_store_forc_plugin() {
+    test_direct_proxy_install_toolchain_not_in_store(Some("forc-client"));
+}
+
+#[test]
+#[should_panic] // TODO: #654 will fix this
+fn direct_proxy_install_toolchain_not_in_store_forc_plugin_external() {
+    test_direct_proxy_install_toolchain_not_in_store(Some("forc-tx"));
+}
+
+#[test]
+fn direct_proxy_install_toolchain_not_in_store_not_forc_plugin() {
+    test_direct_proxy_install_toolchain_not_in_store(Some("forc-wallet"));
+}
+
+fn test_direct_proxy_install_toolchain_in_store(component_name: Option<&str>) {
+    // Test steps:
+    //   - trigger direct proxy call
+    //     - install override toolchain
+    //   - delete toolchain but keep it in store
+    //   - trigger another direct proxy call
+    //     - install override toolchain from store
+    //   - check executables are symlinked from the store
+
+    let component = component_name.map(|name| Component::from_name(name).unwrap());
+
+    testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+        testcfg::setup_default_override_file(cfg, component_name);
+
+        // trigger direct_proxy install with toolchain override
+        let executable = component
+            .as_ref()
+            .map(|c| c.executables.first().unwrap().clone())
+            .unwrap_or_else(|| FORC.to_string());
+
+        // trigger direct_proxy install with toolchain override
+        cfg.exec(&executable, &["--version"]);
+
+        // delete toolchain but keep it in store
+        testcfg::delete_default_toolchain_override_toolchain(cfg);
+
+        // trigger direct_proxy install with toolchain override already in store
+        cfg.exec(&executable, &["--version"]);
+
+        testcfg::verify_default_toolchain_override_toolchain_executables(cfg, component.as_ref());
+    })
+    .unwrap();
+}
+
+fn test_direct_proxy_install_toolchain_not_in_store(component_name: Option<&str>) {
+    // Test steps:
+    //   - trigger direct proxy call
+    //     - install override toolchain
+    //   - check executables are symlinked from the store
+
+    let component = component_name.map(|name| Component::from_name(name).unwrap());
+
+    testcfg::setup(FuelupState::LatestToolchainInstalled, &|cfg| {
+        testcfg::setup_default_override_file(cfg, component_name);
+
+        // trigger direct_proxy install with toolchain override
+        let executable = component
+            .as_ref()
+            .map(|c| c.executables.first().unwrap().clone())
+            .unwrap_or_else(|| FORC.to_string());
+
+        cfg.exec(&executable, &["--version"]);
+
+        testcfg::verify_default_toolchain_override_toolchain_executables(cfg, component.as_ref());
+    })
+    .unwrap();
+}