@@ -176,3 +176,68 @@ where
176176 } ) ?;
177177 Ok ( m)
178178}
179+
180+ #[ cfg( test) ]
181+ mod tests {
182+ use super :: * ;
183+ use camino_tempfile:: Utf8TempDir ;
184+
185+ fn write_manifest ( dir : & Path ) {
186+ std:: fs:: write ( dir. join ( PROJECT_MANIFEST ) , "" ) . unwrap ( ) ;
187+ }
188+
189+ #[ test]
190+ fn locate_returns_cwd_when_manifest_present ( ) {
191+ let tmp = Utf8TempDir :: new ( ) . unwrap ( ) ;
192+ write_manifest ( tmp. path ( ) ) ;
193+
194+ let locator = ProjectRootLocateImpl :: new ( tmp. path ( ) . to_path_buf ( ) , None ) ;
195+ assert_eq ! ( locator. locate( ) . unwrap( ) , tmp. path( ) ) ;
196+ }
197+
198+ #[ test]
199+ fn locate_walks_up_to_manifest ( ) {
200+ let tmp = Utf8TempDir :: new ( ) . unwrap ( ) ;
201+ write_manifest ( tmp. path ( ) ) ;
202+
203+ let nested = tmp. path ( ) . join ( "a/b/c" ) ;
204+ std:: fs:: create_dir_all ( & nested) . unwrap ( ) ;
205+
206+ let locator = ProjectRootLocateImpl :: new ( nested, None ) ;
207+ assert_eq ! ( locator. locate( ) . unwrap( ) , tmp. path( ) ) ;
208+ }
209+
210+ #[ test]
211+ fn locate_returns_not_found_when_no_manifest_anywhere ( ) {
212+ let tmp = Utf8TempDir :: new ( ) . unwrap ( ) ;
213+ let nested = tmp. path ( ) . join ( "a/b" ) ;
214+ std:: fs:: create_dir_all ( & nested) . unwrap ( ) ;
215+
216+ // Host filesystem contains no icp.yaml above the tempdir (assumed in CI).
217+ let locator = ProjectRootLocateImpl :: new ( nested, None ) ;
218+ assert ! ( matches!(
219+ locator. locate( ) ,
220+ Err ( ProjectRootLocateError :: NotFound { .. } )
221+ ) ) ;
222+ }
223+
224+ // When cwd is a symlinked directory, locate() walks up via the symlink's
225+ // lexical parents
226+ #[ cfg( unix) ]
227+ #[ test]
228+ fn locate_walks_up_through_symlink ( ) {
229+ // target/ has no manifest anywhere above it within the test's scope.
230+ let target = Utf8TempDir :: new ( ) . unwrap ( ) ;
231+
232+ // project/ contains the manifest; `project/link` is a symlink to target/.
233+ let project = Utf8TempDir :: new ( ) . unwrap ( ) ;
234+ write_manifest ( project. path ( ) ) ;
235+ let link = project. path ( ) . join ( "link" ) ;
236+ std:: os:: unix:: fs:: symlink ( target. path ( ) . as_std_path ( ) , link. as_std_path ( ) ) . unwrap ( ) ;
237+
238+ // cwd is the symlink path; its lexical parent is `project`,
239+ // which contains the manifest.
240+ let locator = ProjectRootLocateImpl :: new ( link, None ) ;
241+ assert_eq ! ( locator. locate( ) . unwrap( ) , project. path( ) ) ;
242+ }
243+ }
0 commit comments