Skip to content

Commit 911129b

Browse files
Mpdreamzclaude
andcommitted
Fix MockFileSystem temp path mismatch in write-scope methods
MockFileSystem on non-Windows returns a hardcoded Unix-ified path ('/temp/') for GetTempPath() — it unixifies 'C:\temp' rather than calling System.IO.Path.GetTempPath(). This diverges from the real API which reads TMPDIR. AllowedSpecialFolder.Temp in the ValidationContext uses the real System.IO.Path.GetTempPath() (resolves to '/tmp/' on standard Linux), creating a mismatch: the scope allows '/tmp/' but the mock FS creates staging paths at '/temp/zzyysk35.wbk'. Fix: ScopeCurrentWorkingDirectoryForWrite and ScopeSourceDirectoryForWrite now call inner.Path.GetTempPath() on non-Windows and add the result as an explicit scope root alongside AllowedSpecialFolders.Temp. This covers both the real OS temp ('/tmp/' via AllowedSpecialFolders) and the mock's hardcoded path ('/temp/' via the explicit root), without relaxing permissions on Windows where MockFileSystem temp is consistent with the real API. Co-Authored-By: Claude Sonnet 4.6 (1M context) <[email protected]>
1 parent 0eaf884 commit 911129b

2 files changed

Lines changed: 28 additions & 10 deletions

File tree

src/Elastic.Documentation.Configuration/FileSystemFactory.cs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,36 @@ public static ScopedFileSystem ScopeCurrentWorkingDirectory(IFileSystem inner, I
101101
});
102102
}
103103

104+
// Builds write options that include AllowedSpecialFolders.Temp PLUS the inner FS's own
105+
// GetTempPath() as an explicit root. This is necessary because MockFileSystem on non-Windows
106+
// returns a hardcoded Unix-ified path ("/temp/") instead of System.IO.Path.GetTempPath(),
107+
// causing a mismatch with the AllowedSpecialFolder.Temp validation which uses the real API.
108+
private static ScopedFileSystemOptions BuildWriteOptions(IFileSystem inner, params string[] roots)
109+
{
110+
var allRoots = roots.ToList();
111+
if (!OperatingSystem.IsWindows())
112+
{
113+
// Cover MockFileSystem's unixified hardcoded temp path
114+
var innerTemp = inner.Path.GetTempPath().TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
115+
if (!string.IsNullOrEmpty(innerTemp) && !allRoots.Contains(innerTemp, StringComparer.OrdinalIgnoreCase))
116+
allRoots.Add(innerTemp);
117+
}
118+
return new ScopedFileSystemOptions([.. allRoots])
119+
{
120+
AllowedHiddenFolderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".artifacts" },
121+
AllowedHiddenFileNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".doc.state" },
122+
AllowedSpecialFolders = AllowedSpecialFolder.Temp
123+
};
124+
}
125+
104126
/// <summary>
105127
/// Scopes <paramref name="inner"/> to <see cref="Paths.WorkingDirectoryRoot"/> and
106128
/// <see cref="Paths.ApplicationData"/> for writing (.git not allowed). Use when
107129
/// the inner FS writes into the working-directory tree.
108130
/// </summary>
109131
public static ScopedFileSystem ScopeCurrentWorkingDirectoryForWrite(IFileSystem inner) =>
110-
new(inner, WorkingDirectoryWriteOptions);
132+
new(inner, BuildWriteOptions(
133+
inner, Paths.WorkingDirectoryRoot.FullName, Paths.ApplicationData.FullName));
111134

112135
/// <summary>
113136
/// Scopes <paramref name="inner"/> to an explicit <paramref name="sourceRoot"/> and
@@ -128,12 +151,7 @@ public static ScopedFileSystem ScopeSourceDirectory(IFileSystem inner, string so
128151
/// of <see cref="ScopeSourceDirectory(IFileSystem, string)"/>.
129152
/// </summary>
130153
public static ScopedFileSystem ScopeSourceDirectoryForWrite(IFileSystem inner, string sourceRoot) =>
131-
new(inner, new ScopedFileSystemOptions([sourceRoot, Paths.ApplicationData.FullName])
132-
{
133-
AllowedHiddenFolderNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".artifacts" },
134-
AllowedHiddenFileNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".doc.state" },
135-
AllowedSpecialFolders = AllowedSpecialFolder.Temp
136-
});
154+
new(inner, BuildWriteOptions(inner, sourceRoot, Paths.ApplicationData.FullName));
137155

138156
/// <summary>
139157
/// Creates a read <see cref="ScopedFileSystem"/> scoped to the git root of

tests-integration/Elastic.Assembler.IntegrationTests/DocsSyncTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public async Task TestPlan()
4040
{ "docs/update.md", new MockFileData("# Existing Document") },
4141
}, new MockFileSystemOptions
4242
{
43-
CurrentDirectory = Path.Join(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly")
43+
CurrentDirectory = Path.Join(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly"),
4444
});
4545

4646
var configurationContext = TestHelpers.CreateConfigurationContext(fileSystem);
@@ -181,7 +181,7 @@ bool valid
181181
var mockS3Client = A.Fake<IAmazonS3>();
182182
var fileSystem = new MockFileSystem(new MockFileSystemOptions
183183
{
184-
CurrentDirectory = Path.Join(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly")
184+
CurrentDirectory = Path.Join(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly"),
185185
});
186186
foreach (var i in Enumerable.Range(0, localFiles))
187187
fileSystem.AddFile($"docs/file-{i}.md", new MockFileData($"# Local Document {i}"));
@@ -235,7 +235,7 @@ public async Task TestApply()
235235
{ "docs/update.md", new MockFileData("# Existing Document") },
236236
}, new MockFileSystemOptions
237237
{
238-
CurrentDirectory = Path.Join(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly")
238+
CurrentDirectory = Path.Join(Paths.WorkingDirectoryRoot.FullName, ".artifacts", "assembly"),
239239
});
240240
var configurationContext = TestHelpers.CreateConfigurationContext(fileSystem);
241241
var config = AssemblyConfiguration.Create(configurationContext.ConfigurationFileProvider);

0 commit comments

Comments
 (0)