1mod app;
4mod error;
5mod models;
6mod plugin_source;
7mod registry_source;
8mod service;
9
10pub use app::App;
11pub use error::CoreError;
12pub use models::{
13 CurrentTool, InstalledVersion, MigrateSummary, PluginInfo, SelfUpgradeSummary, UninstallResult,
14 UseScope, VersionInfo,
15};
16
17#[cfg(test)]
18mod tests {
19 #[cfg(feature = "lua")]
20 use std::error::Error;
21 #[cfg(feature = "lua")]
22 use std::fs;
23
24 #[cfg(feature = "lua")]
25 use tempfile::TempDir;
26 #[cfg(feature = "lua")]
27 use vs_config::{AppConfig, HomeLayout, RegistryConfig, write_app_config};
28 #[cfg(feature = "lua")]
29 use vs_plugin_api::PluginBackendKind;
30
31 #[cfg(feature = "lua")]
32 use crate::{App, UseScope};
33
34 #[cfg(feature = "lua")]
35 #[test]
36 fn use_tool_should_write_project_config() -> Result<(), Box<dyn Error>> {
37 let temp_dir = TempDir::new()?;
38 let home = temp_dir.path().join("home");
39 let cwd = temp_dir.path().join("project");
40 fs::create_dir_all(&cwd)?;
41 let app = App::new(
42 HomeLayout {
43 active_home: home,
44 migration_candidates: Vec::new(),
45 },
46 cwd.clone(),
47 Some(String::from("session")),
48 )?;
49
50 let source = temp_dir.path().join("nodejs-lua");
51 write_lua_fixture(&source);
52 app.add_plugin(
53 Some("nodejs"),
54 Some(source.display().to_string()),
55 Some(PluginBackendKind::Lua),
56 None,
57 )?;
58 app.install_plugin_version("nodejs", Some("20.11.1"))?;
59
60 app.use_tool("nodejs", "20.11.1", UseScope::Project, false)?;
61
62 let config = fs::read_to_string(cwd.join(".vs.toml"))?;
63 assert!(config.contains("nodejs = \"20.11.1\""));
64 Ok(())
65 }
66
67 #[cfg(feature = "lua")]
68 #[test]
69 fn available_plugins_should_bootstrap_registry_when_source_is_configured()
70 -> Result<(), Box<dyn Error>> {
71 let temp_dir = TempDir::new()?;
72 let home = temp_dir.path().join("home");
73 let cwd = temp_dir.path().join("project");
74 fs::create_dir_all(&cwd)?;
75 let registry_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
76 .join("../../fixtures/registry/index.json");
77
78 write_app_config(
79 &home,
80 &AppConfig {
81 proxy: Default::default(),
82 storage: Default::default(),
83 registry: RegistryConfig {
84 address: registry_path.display().to_string(),
85 },
86 legacy_version_file: Default::default(),
87 cache: Default::default(),
88 },
89 )?;
90
91 let app = App::new(
92 HomeLayout {
93 active_home: home,
94 migration_candidates: Vec::new(),
95 },
96 cwd,
97 Some(String::from("session")),
98 )?;
99
100 let entries = app.available_plugins()?;
101 assert!(!entries.is_empty());
102 assert!(entries.iter().any(|entry| entry.name == "nodejs"));
103 Ok(())
104 }
105
106 #[cfg(feature = "lua")]
107 #[test]
108 fn available_plugins_should_fallback_to_cached_registry_when_refresh_fails()
109 -> Result<(), Box<dyn Error>> {
110 let temp_dir = TempDir::new()?;
111 let home = temp_dir.path().join("home");
112 let cwd = temp_dir.path().join("project");
113 fs::create_dir_all(&cwd)?;
114 let registry_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
115 .join("../../fixtures/registry/index.json");
116
117 write_app_config(
118 &home,
119 &AppConfig {
120 proxy: Default::default(),
121 storage: Default::default(),
122 registry: RegistryConfig {
123 address: registry_path.display().to_string(),
124 },
125 legacy_version_file: Default::default(),
126 cache: Default::default(),
127 },
128 )?;
129
130 let app = App::new(
131 HomeLayout {
132 active_home: home.clone(),
133 migration_candidates: Vec::new(),
134 },
135 cwd.clone(),
136 Some(String::from("session")),
137 )?;
138 assert!(!app.available_plugins()?.is_empty());
139
140 write_app_config(
141 &home,
142 &AppConfig {
143 proxy: Default::default(),
144 storage: Default::default(),
145 registry: RegistryConfig {
146 address: temp_dir
147 .path()
148 .join("missing/index.json")
149 .display()
150 .to_string(),
151 },
152 legacy_version_file: Default::default(),
153 cache: Default::default(),
154 },
155 )?;
156
157 let fallback = App::new(
158 HomeLayout {
159 active_home: home,
160 migration_candidates: Vec::new(),
161 },
162 cwd,
163 Some(String::from("session")),
164 )?;
165 let entries = fallback.available_plugins()?;
166 assert!(entries.iter().any(|entry| entry.name == "nodejs"));
167 Ok(())
168 }
169
170 #[cfg(feature = "lua")]
171 #[test]
172 fn add_plugin_should_fallback_to_cached_registry_when_refresh_fails()
173 -> Result<(), Box<dyn Error>> {
174 let temp_dir = TempDir::new()?;
175 let home = temp_dir.path().join("home");
176 let cwd = temp_dir.path().join("project");
177 fs::create_dir_all(&cwd)?;
178 let registry_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
179 .join("../../fixtures/registry/index.json");
180
181 write_app_config(
182 &home,
183 &AppConfig {
184 proxy: Default::default(),
185 storage: Default::default(),
186 registry: RegistryConfig {
187 address: registry_path.display().to_string(),
188 },
189 legacy_version_file: Default::default(),
190 cache: Default::default(),
191 },
192 )?;
193
194 let app = App::new(
195 HomeLayout {
196 active_home: home.clone(),
197 migration_candidates: Vec::new(),
198 },
199 cwd.clone(),
200 Some(String::from("session")),
201 )?;
202 assert!(!app.available_plugins()?.is_empty());
203
204 write_app_config(
205 &home,
206 &AppConfig {
207 proxy: Default::default(),
208 storage: Default::default(),
209 registry: RegistryConfig {
210 address: temp_dir
211 .path()
212 .join("missing/index.json")
213 .display()
214 .to_string(),
215 },
216 legacy_version_file: Default::default(),
217 cache: Default::default(),
218 },
219 )?;
220
221 let fallback = App::new(
222 HomeLayout {
223 active_home: home,
224 migration_candidates: Vec::new(),
225 },
226 cwd,
227 Some(String::from("session")),
228 )?;
229 let entry = fallback.add_plugin(Some("nodejs"), None, None, None)?;
230 assert_eq!(entry.name, "nodejs");
231 Ok(())
232 }
233
234 #[cfg(feature = "lua")]
235 fn write_lua_fixture(root: &std::path::Path) {
236 if let Err(error) = fs::create_dir_all(root.join("hooks")) {
237 panic!("failed to create hooks directory: {error}");
238 }
239 if let Err(error) = fs::create_dir_all(root.join("packages/20.11.1/bin")) {
240 panic!("failed to create package directory: {error}");
241 }
242 fs::write(
243 root.join("metadata.lua"),
244 "PLUGIN = {}\nPLUGIN.name = 'nodejs'\nPLUGIN.version = '0.1.0'\nPLUGIN.legacyFilenames = { '.nvmrc' }\n",
245 )
246 .unwrap_or_else(|error| panic!("failed to write metadata fixture: {error}"));
247 fs::write(
248 root.join("hooks/available.lua"),
249 "function PLUGIN:Available(ctx)\n return { { version = '20.11.1' } }\nend\n",
250 )
251 .unwrap_or_else(|error| panic!("failed to write available fixture: {error}"));
252 fs::write(
253 root.join("hooks/pre_install.lua"),
254 "function PLUGIN:PreInstall(ctx)\n return { version = '20.11.1', url = 'packages/20.11.1' }\nend\n",
255 )
256 .unwrap_or_else(|error| panic!("failed to write pre_install fixture: {error}"));
257 fs::write(
258 root.join("hooks/env_keys.lua"),
259 "function PLUGIN:EnvKeys(ctx)\n return { { key = 'NODEJS_HOME', value = ctx.path }, { key = 'PATH', value = ctx.path .. '/bin' } }\nend\n",
260 )
261 .unwrap_or_else(|error| panic!("failed to write env_keys fixture: {error}"));
262 }
263}