Resolve IntegrationTests-025: stopBoundary for repo-root walker
ResolveRepositoryRoot accepts an optional stopBoundary parameter that caps the upward walk; production callers pass null and behavior is unchanged. The two repository-marker tests now seal their walkers inside their own temp directories, so a redirected TMP or a co-located C:\src checkout no longer leaks ambient marker-bearing ancestors into the assertion. Regression test ResolveRepositoryRoot_StopBoundary_IsolatesWalkerFromAmbientAncestorMarkers constructs an outer ancestor that carries src/ + .git, confirms the walker leaks into it without the boundary, then asserts the same call throws with the boundary supplied. Resolved at 2026-05-24; IntegrationTestEnvironmentTests 5/5 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,12 +109,26 @@ public static class IntegrationTestEnvironment
|
||||
/// remains the escape hatch for unusual deployments.
|
||||
/// </summary>
|
||||
/// <param name="startDirectory">Starting directory to search from.</param>
|
||||
/// <param name="stopBoundary">
|
||||
/// Optional upper bound for the parent walk. When supplied, the walker checks
|
||||
/// <paramref name="stopBoundary"/> for markers and then stops, ignoring all
|
||||
/// ancestors above it. Tests pass an isolated boundary so the walker cannot
|
||||
/// leak into ambient ancestors (a redirected <c>TMP</c>, a co-located checkout
|
||||
/// at <c>C:\src</c>, an enclosing CI workspace, etc.) that would silently
|
||||
/// satisfy <see cref="IsRepositoryRoot"/> — see IntegrationTests-025.
|
||||
/// Production callers pass <see langword="null"/> so the walk continues to the
|
||||
/// drive root as before.
|
||||
/// </param>
|
||||
/// <returns>The repository root path.</returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when the parent walk exhausts without finding a repository root.
|
||||
/// </exception>
|
||||
internal static string ResolveRepositoryRoot(string startDirectory)
|
||||
internal static string ResolveRepositoryRoot(string startDirectory, string? stopBoundary = null)
|
||||
{
|
||||
string? normalizedBoundary = stopBoundary is null
|
||||
? null
|
||||
: Path.TrimEndingDirectorySeparator(Path.GetFullPath(stopBoundary));
|
||||
|
||||
DirectoryInfo? directory = new(startDirectory);
|
||||
while (directory is not null)
|
||||
{
|
||||
@@ -123,6 +137,15 @@ public static class IntegrationTestEnvironment
|
||||
return directory.FullName;
|
||||
}
|
||||
|
||||
if (normalizedBoundary is not null
|
||||
&& string.Equals(
|
||||
Path.TrimEndingDirectorySeparator(directory.FullName),
|
||||
normalizedBoundary,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
directory = directory.Parent;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user