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