You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
How to detect the context in DefaultAccessControlStrategy() #1158
I can list both files _fileSystem.Directory.GetFiles(path) (no exception expected)
I can read the data only of myFile1.txt _fileSystem.File.ReadAllBytesAsync(path)
My setup:
MockFileSystem.WithAccessControlStrategy(new DefaultAccessControlStrategy((path, ext) =>
{
var isDirectory = FileSystem.MockFileSystem.Directory.Exists(path);
if (isDirectory)
{
return true;
}
if (path.Contains("myFile2.txt"))
{
return false;
}
return true;
}));
If I call _fileSystem.Directory.GetFiles(directoryOfFiles) then I got 3 calls into this hook:
first the directory path => true
then path to myFile1.txt => true
then path to myFile2.txt => false
In summary I got an Exception if I call _fileSystem.Directory.GetFiles(directoryOfFiles). How can I solve my problem? How can I detect which method is called _fileSystem.Directory.GetFiles(path) or _fileSystem.File.ReadAllBytesAsync(path)
DefaultAccessControlStrategy gets the full path and extension data, but no method/operation context. A path denied by that callback can also fail GetFiles, so the callback cannot express “list this file, but deny ReadAllBytesAsync”.
For that specific application test, you can compose the IFileSystem interface: keep MockFileSystem as the target and intercept only IFile.ReadAllBytesAsync for the second path. Your service still receives IFileSystem; no changes to its calls are needed.
Runnable example and the small helper use Testably 7.1.1 and Castle.Core 5.2.1. The executable lists both files, reads the first, observes denial for the second, then lists and reads again. It also checks cancellation against the underlying mock, concurrent reads, relative paths, a matching filename in another directory, and unchanged content. These were scripted .NET 8 checks on macOS, with a separate Windows-simulated fixture reproducing the ACL behavior.
This is a test-only policy for that method, not full ACL emulation. Other read methods retain their underlying behavior. Start with a fresh mock using this setup instead of the blanket path-denial callback; wrapping an already restrictive ACL does not remove its restriction.
You've found a bug in the MockFileSystem. Enumerating a directory called the access control strategy for every item it found. So denying access to a single file made Directory.GetFiles throw, even though only reading the file should fail. The real file system doesn't do that: listing a directory only needs access to the directory itself, and the files in it aren't opened.
I fixed it so the mock behaves like the real file system. During enumeration, only the directories that are actually opened are checked: the directory you list and, when searching recursively, the subdirectories it recurses into. With the fix, your setup works as you expected: both files are listed, and File.ReadAllBytesAsync for myFile2.txt throws an UnauthorizedAccessException. You can also drop the directory check from your callback:
Until the fix is released, you can work around it with a strategy that only starts denying access right before the read:
booldenyMyFile2=false;MockFileSystem.WithAccessControlStrategy(newDefaultAccessControlStrategy((path,_)=>!(denyMyFile2&&path.Contains("myFile2.txt"))));string[]files=_fileSystem.Directory.GetFiles(directoryOfFiles);// lists both filesdenyMyFile2=true;// _fileSystem.File.ReadAllBytesAsync(...) for myFile2.txt now throws
This only works if your test can call the listing and the read separately.
I am also thinking about giving more information in the interface, but that is a separate feature and needs more thought.
Activity
tevinch commentedon Oct 28, 2026
DefaultAccessControlStrategygets the full path and extension data, but no method/operation context. A path denied by that callback can also failGetFiles, so the callback cannot express “list this file, but denyReadAllBytesAsync”.For that specific application test, you can compose the
IFileSysteminterface: keepMockFileSystemas the target and intercept onlyIFile.ReadAllBytesAsyncfor the second path. Your service still receivesIFileSystem; no changes to its calls are needed.Runnable example and the small helper use Testably 7.1.1 and Castle.Core 5.2.1. The executable lists both files, reads the first, observes denial for the second, then lists and reads again. It also checks cancellation against the underlying mock, concurrent reads, relative paths, a matching filename in another directory, and unchanged content. These were scripted .NET 8 checks on macOS, with a separate Windows-simulated fixture reproducing the ACL behavior.
This is a test-only policy for that method, not full ACL emulation. Other read methods retain their underlying behavior. Start with a fresh mock using this setup instead of the blanket path-denial callback; wrapping an already restrictive ACL does not remove its restriction.
vbreuss commentedon Oct 28, 2026
Thank you for the detailed report, @altmann!
You've found a bug in the
MockFileSystem. Enumerating a directory called the access control strategy for every item it found. So denying access to a single file madeDirectory.GetFilesthrow, even though only reading the file should fail. The real file system doesn't do that: listing a directory only needs access to the directory itself, and the files in it aren't opened.I fixed it so the mock behaves like the real file system. During enumeration, only the directories that are actually opened are checked: the directory you list and, when searching recursively, the subdirectories it recurses into. With the fix, your setup works as you expected: both files are listed, and
File.ReadAllBytesAsyncformyFile2.txtthrows anUnauthorizedAccessException. You can also drop the directory check from your callback:Until the fix is released, you can work around it with a strategy that only starts denying access right before the read:
This only works if your test can call the listing and the read separately.
I am also thinking about giving more information in the interface, but that is a separate feature and needs more thought.
vbreuss commentedon Oct 29, 2026
@altmann The immediate fix for you problem is released in v7.1.2.
github-actions commentedon Oct 30, 2026
This is addressed in release v7.2.0.