@@ -13,7 +13,23 @@ use turbopath::{AbsoluteSystemPathBuf, PathError};
1313/// is set, it will return that path instead of `dirs_next::config_dir`.
1414pub fn config_dir ( ) -> Result < Option < AbsoluteSystemPathBuf > , PathError > {
1515 if let Ok ( dir) = std:: env:: var ( "TURBO_CONFIG_DIR_PATH" ) {
16- return AbsoluteSystemPathBuf :: new ( dir) . map ( Some ) ;
16+ // Reject empty strings per Unix convention
17+ if dir. is_empty ( ) {
18+ return Err ( PathError :: InvalidUnicode ( dir) ) ;
19+ }
20+
21+ let raw = std:: path:: PathBuf :: from ( & dir) ;
22+
23+ // Resolve to absolute path if necessary
24+ let abs = if raw. is_absolute ( ) {
25+ raw
26+ } else {
27+ std:: env:: current_dir ( ) ?. join ( raw)
28+ } ;
29+
30+ let abs_str = abs. to_str ( ) . ok_or ( PathError :: InvalidUnicode ( dir) ) ?;
31+
32+ return AbsoluteSystemPathBuf :: new ( abs_str) . map ( Some ) ;
1733 }
1834
1935 dirs_config_dir ( )
@@ -28,6 +44,11 @@ pub fn config_dir() -> Result<Option<AbsoluteSystemPathBuf>, PathError> {
2844/// is set, it will return that path instead of `dirs_next::config_dir`.
2945pub fn vercel_config_dir ( ) -> Result < Option < AbsoluteSystemPathBuf > , PathError > {
3046 if let Ok ( dir) = std:: env:: var ( "VERCEL_CONFIG_DIR_PATH" ) {
47+ // Reject empty strings per Unix convention.
48+ if dir. is_empty ( ) {
49+ return Err ( PathError :: InvalidUnicode ( dir) ) ;
50+ }
51+
3152 return AbsoluteSystemPathBuf :: new ( dir) . map ( Some ) ;
3253 }
3354
@@ -41,3 +62,242 @@ pub enum Error {
4162 #[ error( "Config directory not found." ) ]
4263 ConfigDirNotFound ,
4364}
65+
66+ #[ cfg( test) ]
67+ mod tests {
68+ use std:: env;
69+
70+ use super :: * ;
71+
72+ #[ test]
73+ fn test_config_dir_with_env_var ( ) {
74+ // Set TURBO_CONFIG_DIR_PATH to an absolute path
75+ let test_path = if cfg ! ( windows) {
76+ "C:\\ test\\ config"
77+ } else {
78+ "/test/config"
79+ } ;
80+
81+ unsafe {
82+ env:: set_var ( "TURBO_CONFIG_DIR_PATH" , test_path) ;
83+ }
84+
85+ let result = config_dir ( ) ;
86+ assert ! ( result. is_ok( ) ) ;
87+ let path = result. unwrap ( ) ;
88+ assert ! ( path. is_some( ) ) ;
89+ assert_eq ! ( path. unwrap( ) . as_str( ) , test_path) ;
90+
91+ unsafe {
92+ env:: remove_var ( "TURBO_CONFIG_DIR_PATH" ) ;
93+ }
94+ }
95+
96+ #[ test]
97+ fn test_config_dir_with_relative_path ( ) {
98+ // Set TURBO_CONFIG_DIR_PATH to a relative path (should be resolved to absolute)
99+ unsafe {
100+ env:: set_var ( "TURBO_CONFIG_DIR_PATH" , "relative/path" ) ;
101+ }
102+
103+ let result = config_dir ( ) ;
104+ assert ! ( result. is_ok( ) ) ;
105+ let path = result. unwrap ( ) ;
106+ assert ! ( path. is_some( ) ) ;
107+ // Verify it was resolved to an absolute path
108+ assert ! ( path. unwrap( ) . as_path( ) . is_absolute( ) ) ;
109+
110+ unsafe {
111+ env:: remove_var ( "TURBO_CONFIG_DIR_PATH" ) ;
112+ }
113+ }
114+
115+ #[ test]
116+ fn test_config_dir_without_env_var ( ) {
117+ // Ensure TURBO_CONFIG_DIR_PATH is not set
118+ unsafe {
119+ env:: remove_var ( "TURBO_CONFIG_DIR_PATH" ) ;
120+ }
121+
122+ let result = config_dir ( ) ;
123+ assert ! ( result. is_ok( ) ) ;
124+ // On most systems, config_dir should return Some path
125+ // We can't assert the exact path since it's platform-specific
126+ let path = result. unwrap ( ) ;
127+ if let Some ( p) = path {
128+ // Verify it's an absolute path
129+ assert ! ( p. as_path( ) . is_absolute( ) ) ;
130+ }
131+ }
132+
133+ #[ test]
134+ fn test_vercel_config_dir_with_env_var ( ) {
135+ // Set VERCEL_CONFIG_DIR_PATH to an absolute path
136+ let test_path = if cfg ! ( windows) {
137+ "C:\\ vercel\\ config"
138+ } else {
139+ "/vercel/config"
140+ } ;
141+
142+ unsafe {
143+ env:: set_var ( "VERCEL_CONFIG_DIR_PATH" , test_path) ;
144+ }
145+
146+ let result = vercel_config_dir ( ) ;
147+ assert ! ( result. is_ok( ) ) ;
148+ let path = result. unwrap ( ) ;
149+ assert ! ( path. is_some( ) ) ;
150+ assert_eq ! ( path. unwrap( ) . as_str( ) , test_path) ;
151+
152+ unsafe {
153+ env:: remove_var ( "VERCEL_CONFIG_DIR_PATH" ) ;
154+ }
155+ }
156+
157+ #[ test]
158+ fn test_vercel_config_dir_with_invalid_env_var ( ) {
159+ // Set VERCEL_CONFIG_DIR_PATH to a relative path (invalid)
160+ unsafe {
161+ env:: set_var ( "VERCEL_CONFIG_DIR_PATH" , "relative/path" ) ;
162+ }
163+
164+ let result = vercel_config_dir ( ) ;
165+ assert ! ( result. is_err( ) ) ;
166+
167+ unsafe {
168+ env:: remove_var ( "VERCEL_CONFIG_DIR_PATH" ) ;
169+ }
170+ }
171+
172+ #[ test]
173+ fn test_vercel_config_dir_without_env_var ( ) {
174+ // Ensure VERCEL_CONFIG_DIR_PATH is not set
175+ unsafe {
176+ env:: remove_var ( "VERCEL_CONFIG_DIR_PATH" ) ;
177+ }
178+
179+ let result = vercel_config_dir ( ) ;
180+ assert ! ( result. is_ok( ) ) ;
181+ // On most systems, config_dir should return Some path
182+ // We can't assert the exact path since it's platform-specific
183+ let path = result. unwrap ( ) ;
184+ if let Some ( p) = path {
185+ // Verify it's an absolute path
186+ assert ! ( p. as_path( ) . is_absolute( ) ) ;
187+ }
188+ }
189+
190+ #[ test]
191+ fn test_config_dir_empty_env_var ( ) {
192+ // Set TURBO_CONFIG_DIR_PATH to empty string
193+ unsafe {
194+ env:: set_var ( "TURBO_CONFIG_DIR_PATH" , "" ) ;
195+ }
196+
197+ let result = config_dir ( ) ;
198+ // Empty string should be invalid as it's not an absolute path
199+ assert ! ( result. is_err( ) ) ;
200+
201+ unsafe {
202+ env:: remove_var ( "TURBO_CONFIG_DIR_PATH" ) ;
203+ }
204+ }
205+
206+ #[ test]
207+ fn test_vercel_config_dir_empty_env_var ( ) {
208+ // Set VERCEL_CONFIG_DIR_PATH to empty string
209+ unsafe {
210+ env:: set_var ( "VERCEL_CONFIG_DIR_PATH" , "" ) ;
211+ }
212+
213+ let result = vercel_config_dir ( ) ;
214+ // Empty string should be invalid as it's not an absolute path
215+ assert ! ( result. is_err( ) ) ;
216+
217+ unsafe {
218+ env:: remove_var ( "VERCEL_CONFIG_DIR_PATH" ) ;
219+ }
220+ }
221+
222+ #[ test]
223+ fn test_config_dir_and_vercel_config_dir_independence ( ) {
224+ // Test that TURBO_CONFIG_DIR_PATH doesn't affect vercel_config_dir
225+ // Use a path that would be created by dirs_config_dir to ensure both succeed
226+ let turbo_path = if cfg ! ( windows) {
227+ "C:\\ Users\\ test\\ config"
228+ } else {
229+ "/Users/test/config"
230+ } ;
231+
232+ unsafe {
233+ env:: set_var ( "TURBO_CONFIG_DIR_PATH" , turbo_path) ;
234+ env:: remove_var ( "VERCEL_CONFIG_DIR_PATH" ) ;
235+ }
236+
237+ let turbo_result = config_dir ( ) ;
238+ let vercel_result = vercel_config_dir ( ) ;
239+
240+ assert ! ( turbo_result. is_ok( ) , "turbo_result should be ok" ) ;
241+ let turbo_path_result = turbo_result. unwrap ( ) ;
242+ assert ! ( turbo_path_result. is_some( ) , "turbo path should be some" ) ;
243+ assert_eq ! ( turbo_path_result. unwrap( ) . as_str( ) , turbo_path) ;
244+
245+ assert ! ( vercel_result. is_ok( ) , "vercel_result should be ok" ) ;
246+ // vercel_config_dir should return the default, not turbo_path
247+ if let Some ( vercel_path) = vercel_result. unwrap ( ) {
248+ assert_ne ! ( vercel_path. as_str( ) , turbo_path) ;
249+ }
250+
251+ unsafe {
252+ env:: remove_var ( "TURBO_CONFIG_DIR_PATH" ) ;
253+ }
254+ }
255+
256+ #[ test]
257+ fn test_vercel_config_dir_and_config_dir_independence ( ) {
258+ // Test that VERCEL_CONFIG_DIR_PATH doesn't affect config_dir
259+ let vercel_path = if cfg ! ( windows) {
260+ "C:\\ Users\\ test\\ vercel"
261+ } else {
262+ "/Users/test/vercel"
263+ } ;
264+
265+ unsafe {
266+ env:: set_var ( "VERCEL_CONFIG_DIR_PATH" , vercel_path) ;
267+ env:: remove_var ( "TURBO_CONFIG_DIR_PATH" ) ;
268+ }
269+
270+ let vercel_result = vercel_config_dir ( ) ;
271+ let turbo_result = config_dir ( ) ;
272+
273+ assert ! ( vercel_result. is_ok( ) , "vercel_result should be ok" ) ;
274+ let vercel_path_result = vercel_result. unwrap ( ) ;
275+ assert ! ( vercel_path_result. is_some( ) , "vercel path should be some" ) ;
276+ assert_eq ! ( vercel_path_result. unwrap( ) . as_str( ) , vercel_path) ;
277+
278+ assert ! ( turbo_result. is_ok( ) , "turbo_result should be ok" ) ;
279+ // config_dir should return the default, not vercel_path
280+ if let Some ( turbo_path) = turbo_result. unwrap ( ) {
281+ assert_ne ! ( turbo_path. as_str( ) , vercel_path) ;
282+ }
283+
284+ unsafe {
285+ env:: remove_var ( "VERCEL_CONFIG_DIR_PATH" ) ;
286+ }
287+ }
288+
289+ #[ test]
290+ fn test_error_display ( ) {
291+ // Test that the Error enum formats correctly
292+ let error = Error :: ConfigDirNotFound ;
293+ assert_eq ! ( error. to_string( ) , "Config directory not found." ) ;
294+ }
295+
296+ #[ test]
297+ fn test_error_debug ( ) {
298+ // Test that the Error enum can be debug formatted
299+ let error = Error :: ConfigDirNotFound ;
300+ let debug_str = format ! ( "{:?}" , error) ;
301+ assert ! ( debug_str. contains( "ConfigDirNotFound" ) ) ;
302+ }
303+ }
0 commit comments