From 278b2c68acc9ee46185f5f0f7b96c01b093e9444 Mon Sep 17 00:00:00 2001
From: tobias-tengler <45513122+tobias-tengler@users.noreply.github.com>
Date: Mon, 18 May 2026 16:08:36 +0200
Subject: [PATCH 1/4] Add OperationRequestBuilder.SetFiles
---
src/All.slnx | 4 ++
.../src/AspNetCore.Pipeline/FormFileLookup.cs | 2 +-
.../HotChocolate.AspNetCore.Pipeline.csproj | 1 +
.../Testing/Directory.Build.props | 3 ++
.../Testing/src/Directory.Build.props | 8 ++++
.../src/Testing/HotChocolate.Testing.csproj | 14 +++++++
.../OperationRequestBuilderExtensions.cs | 38 +++++++++++++++++++
7 files changed, 69 insertions(+), 1 deletion(-)
create mode 100644 src/HotChocolate/Testing/Directory.Build.props
create mode 100644 src/HotChocolate/Testing/src/Directory.Build.props
create mode 100644 src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj
create mode 100644 src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs
diff --git a/src/All.slnx b/src/All.slnx
index d1ed667624e..f3b1576edba 100644
--- a/src/All.slnx
+++ b/src/All.slnx
@@ -333,6 +333,10 @@
+
+
+
+
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs
index 2d6cb78abe6..2ca30890c33 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs
@@ -9,7 +9,7 @@ internal sealed class FormFileLookup : IFileLookup
{
private readonly Dictionary _fileMap;
- private FormFileLookup(Dictionary fileMap)
+ public FormFileLookup(Dictionary fileMap)
{
_fileMap = fileMap;
}
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj
index 10fcfa74bad..a6720ae021d 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj
@@ -34,6 +34,7 @@
+
diff --git a/src/HotChocolate/Testing/Directory.Build.props b/src/HotChocolate/Testing/Directory.Build.props
new file mode 100644
index 00000000000..106b12775b2
--- /dev/null
+++ b/src/HotChocolate/Testing/Directory.Build.props
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/HotChocolate/Testing/src/Directory.Build.props b/src/HotChocolate/Testing/src/Directory.Build.props
new file mode 100644
index 00000000000..db20bbe5a51
--- /dev/null
+++ b/src/HotChocolate/Testing/src/Directory.Build.props
@@ -0,0 +1,8 @@
+
+
+
+
+ true
+
+
+
diff --git a/src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj b/src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj
new file mode 100644
index 00000000000..842390f449e
--- /dev/null
+++ b/src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj
@@ -0,0 +1,14 @@
+
+
+
+ HotChocolate.Testing
+ HotChocolate.Testing
+ HotChocolate.Testing
+ Testing helpers for Hot Chocolate.
+
+
+
+
+
+
+
diff --git a/src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs b/src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs
new file mode 100644
index 00000000000..5eefad717b3
--- /dev/null
+++ b/src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs
@@ -0,0 +1,38 @@
+using HotChocolate.AspNetCore;
+using HotChocolate.Execution;
+using HotChocolate.Types;
+
+namespace HotChocolate.Testing;
+
+///
+/// Testing extensions for .
+///
+public static class OperationRequestBuilderExtensions
+{
+ ///
+ /// Registers a set of uploaded files with the operation request so that
+ /// Upload variables can be resolved through an feature.
+ ///
+ ///
+ /// The operation request builder.
+ ///
+ ///
+ /// A map of multipart request keys to instances.
+ ///
+ ///
+ /// The same for configuration chaining.
+ ///
+ ///
+ /// or is null.
+ ///
+ public static OperationRequestBuilder SetFiles(
+ this OperationRequestBuilder builder,
+ Dictionary fileMap)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentNullException.ThrowIfNull(fileMap);
+
+ builder.Features.Set(new FormFileLookup(fileMap));
+ return builder;
+ }
+}
From fd31d928125e3ec1edc9cfa998d6c2f7fdf0862d Mon Sep 17 00:00:00 2001
From: tobias-tengler <45513122+tobias-tengler@users.noreply.github.com>
Date: Tue, 26 May 2026 11:23:48 +0200
Subject: [PATCH 2/4] Cleanup
---
src/All.slnx | 4 -
.../src/AspNetCore.Pipeline/FormFileLookup.cs | 2 +-
.../HotChocolate.AspNetCore.Pipeline.csproj | 1 -
.../OperationRequestBuilderExtensions.cs | 48 ++++++
.../Execution/OperationRequestFileLookup.cs | 17 +++
.../OperationRequestBuilderExtensionsTests.cs | 144 ++++++++++++++++++
.../Testing/Directory.Build.props | 3 -
.../Testing/src/Directory.Build.props | 8 -
.../src/Testing/HotChocolate.Testing.csproj | 14 --
.../OperationRequestBuilderExtensions.cs | 38 -----
10 files changed, 210 insertions(+), 69 deletions(-)
create mode 100644 src/HotChocolate/Core/src/Execution.Abstractions/Execution/OperationRequestFileLookup.cs
create mode 100644 src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs
delete mode 100644 src/HotChocolate/Testing/Directory.Build.props
delete mode 100644 src/HotChocolate/Testing/src/Directory.Build.props
delete mode 100644 src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj
delete mode 100644 src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs
diff --git a/src/All.slnx b/src/All.slnx
index f3b1576edba..d1ed667624e 100644
--- a/src/All.slnx
+++ b/src/All.slnx
@@ -333,10 +333,6 @@
-
-
-
-
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs
index 2ca30890c33..2d6cb78abe6 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/FormFileLookup.cs
@@ -9,7 +9,7 @@ internal sealed class FormFileLookup : IFileLookup
{
private readonly Dictionary _fileMap;
- public FormFileLookup(Dictionary fileMap)
+ private FormFileLookup(Dictionary fileMap)
{
_fileMap = fileMap;
}
diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj
index a6720ae021d..10fcfa74bad 100644
--- a/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj
+++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Pipeline/HotChocolate.AspNetCore.Pipeline.csproj
@@ -34,7 +34,6 @@
-
diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs b/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs
index f5feee26b18..18daff32ce8 100644
--- a/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs
+++ b/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs
@@ -1,4 +1,6 @@
using System.Security.Claims;
+using HotChocolate.Features;
+using HotChocolate.Types;
namespace HotChocolate.Execution;
@@ -21,4 +23,50 @@ public static OperationRequestBuilder SetUser(
public static OperationRequestBuilder MarkAsWarmupRequest(
this OperationRequestBuilder builder)
=> builder.SetGlobalState(ExecutionContextData.IsWarmupRequest, true);
+
+ ///
+ /// Registers an uploaded file with the operation request so that an Upload variable
+ /// referencing can be resolved through the
+ /// feature.
+ ///
+ ///
+ /// The operation request builder.
+ ///
+ ///
+ /// The multipart request map key that identifies the file.
+ ///
+ ///
+ /// The file to associate with .
+ ///
+ ///
+ /// The same for configuration chaining.
+ ///
+ ///
+ /// or is null.
+ ///
+ ///
+ /// is null or empty.
+ ///
+ public static OperationRequestBuilder AddFile(
+ this OperationRequestBuilder builder,
+ string name,
+ IFile file)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentException.ThrowIfNullOrEmpty(name);
+ ArgumentNullException.ThrowIfNull(file);
+
+ var lookup = builder.Features.GetOrSet(
+ static () => new OperationRequestFileLookup());
+
+ if (lookup is not OperationRequestFileLookup mutable)
+ {
+ throw new InvalidOperationException(
+ "An IFileLookup feature has already been set on this request "
+ + "and cannot be extended.");
+ }
+
+ mutable.Add(name, file);
+ return builder;
+ }
}
diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/Execution/OperationRequestFileLookup.cs b/src/HotChocolate/Core/src/Execution.Abstractions/Execution/OperationRequestFileLookup.cs
new file mode 100644
index 00000000000..69e4baff2ab
--- /dev/null
+++ b/src/HotChocolate/Core/src/Execution.Abstractions/Execution/OperationRequestFileLookup.cs
@@ -0,0 +1,17 @@
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Types;
+
+namespace HotChocolate.Execution;
+
+internal sealed class OperationRequestFileLookup : IFileLookup
+{
+ private readonly Dictionary _files = [];
+
+ public void Add(string name, IFile file)
+ {
+ _files[name] = file;
+ }
+
+ public bool TryGetFile(string name, [NotNullWhen(true)] out IFile? file)
+ => _files.TryGetValue(name, out file);
+}
diff --git a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs
new file mode 100644
index 00000000000..464e58d0736
--- /dev/null
+++ b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs
@@ -0,0 +1,144 @@
+using HotChocolate.Features;
+using HotChocolate.Types;
+
+namespace HotChocolate.Execution;
+
+public class OperationRequestBuilderExtensionsTests
+{
+ [Fact]
+ public void AddFile_Should_RegisterFileInLookup_When_NamedFileProvided()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+ var file = new TestFile("a.txt");
+
+ // act
+ builder.AddFile("variables.file", file);
+
+ // assert
+ var lookup = builder.Features.Get();
+ Assert.NotNull(lookup);
+ Assert.True(lookup.TryGetFile("variables.file", out var resolved));
+ Assert.Same(file, resolved);
+ }
+
+ [Fact]
+ public void AddFile_Should_RegisterAllFiles_When_CalledMultipleTimes()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+ var first = new TestFile("a.txt");
+ var second = new TestFile("b.txt");
+
+ // act
+ builder.AddFile("0", first).AddFile("1", second);
+
+ // assert
+ var lookup = builder.Features.GetRequired();
+ Assert.True(lookup.TryGetFile("0", out var resolvedFirst));
+ Assert.True(lookup.TryGetFile("1", out var resolvedSecond));
+ Assert.Same(first, resolvedFirst);
+ Assert.Same(second, resolvedSecond);
+ }
+
+ [Fact]
+ public void AddFile_Should_ReuseSingleLookupFeature_When_CalledMultipleTimes()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+
+ // act
+ builder
+ .AddFile("0", new TestFile("a.txt"))
+ .AddFile("1", new TestFile("b.txt"));
+
+ // assert
+ var lookupAfterFirst = builder.Features.Get();
+ builder.AddFile("2", new TestFile("c.txt"));
+ var lookupAfterThird = builder.Features.Get();
+ Assert.Same(lookupAfterFirst, lookupAfterThird);
+ }
+
+ [Fact]
+ public void AddFile_Should_OverwriteEntry_When_SameNameUsedTwice()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+ var initial = new TestFile("a.txt");
+ var replacement = new TestFile("b.txt");
+
+ // act
+ builder.AddFile("file", initial).AddFile("file", replacement);
+
+ // assert
+ var lookup = builder.Features.GetRequired();
+ Assert.True(lookup.TryGetFile("file", out var resolved));
+ Assert.Same(replacement, resolved);
+ }
+
+ [Fact]
+ public void AddFile_Should_Throw_When_BuilderIsNull()
+ {
+ // arrange
+ OperationRequestBuilder builder = null!;
+
+ // act
+ void Act() => builder.AddFile("file", new TestFile("a.txt"));
+
+ // assert
+ Assert.Throws(Act);
+ }
+
+ [Fact]
+ public void AddFile_Should_Throw_When_FileIsNull()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+
+ // act
+ void Act() => builder.AddFile("file", null!);
+
+ // assert
+ Assert.Throws(Act);
+ }
+
+ [Fact]
+ public void AddFile_Should_Throw_When_NameIsNull()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+
+ // act
+ void Act() => builder.AddFile(null!, new TestFile("a.txt"));
+
+ // assert
+ Assert.Throws(Act);
+ }
+
+ [Fact]
+ public void AddFile_Should_Throw_When_NameIsEmpty()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+
+ // act
+ void Act() => builder.AddFile(string.Empty, new TestFile("a.txt"));
+
+ // assert
+ Assert.Throws(Act);
+ }
+
+ private sealed class TestFile(string name) : IFile
+ {
+ public string Name { get; } = name;
+
+ public long? Length => null;
+
+ public string? ContentType => null;
+
+ public Task CopyToAsync(Stream target, CancellationToken cancellationToken = default)
+ => Task.CompletedTask;
+
+ public Stream OpenReadStream() => Stream.Null;
+ }
+}
diff --git a/src/HotChocolate/Testing/Directory.Build.props b/src/HotChocolate/Testing/Directory.Build.props
deleted file mode 100644
index 106b12775b2..00000000000
--- a/src/HotChocolate/Testing/Directory.Build.props
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/src/HotChocolate/Testing/src/Directory.Build.props b/src/HotChocolate/Testing/src/Directory.Build.props
deleted file mode 100644
index db20bbe5a51..00000000000
--- a/src/HotChocolate/Testing/src/Directory.Build.props
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- true
-
-
-
diff --git a/src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj b/src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj
deleted file mode 100644
index 842390f449e..00000000000
--- a/src/HotChocolate/Testing/src/Testing/HotChocolate.Testing.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- HotChocolate.Testing
- HotChocolate.Testing
- HotChocolate.Testing
- Testing helpers for Hot Chocolate.
-
-
-
-
-
-
-
diff --git a/src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs b/src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs
deleted file mode 100644
index 5eefad717b3..00000000000
--- a/src/HotChocolate/Testing/src/Testing/OperationRequestBuilderExtensions.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using HotChocolate.AspNetCore;
-using HotChocolate.Execution;
-using HotChocolate.Types;
-
-namespace HotChocolate.Testing;
-
-///
-/// Testing extensions for .
-///
-public static class OperationRequestBuilderExtensions
-{
- ///
- /// Registers a set of uploaded files with the operation request so that
- /// Upload variables can be resolved through an feature.
- ///
- ///
- /// The operation request builder.
- ///
- ///
- /// A map of multipart request keys to instances.
- ///
- ///
- /// The same for configuration chaining.
- ///
- ///
- /// or is null.
- ///
- public static OperationRequestBuilder SetFiles(
- this OperationRequestBuilder builder,
- Dictionary fileMap)
- {
- ArgumentNullException.ThrowIfNull(builder);
- ArgumentNullException.ThrowIfNull(fileMap);
-
- builder.Features.Set(new FormFileLookup(fileMap));
- return builder;
- }
-}
From 61369a6064c6294a4fb9d0820f5b20da2b2d7b6b Mon Sep 17 00:00:00 2001
From: tobias-tengler <45513122+tobias-tengler@users.noreply.github.com>
Date: Tue, 26 May 2026 11:50:24 +0200
Subject: [PATCH 3/4] Update migration guide
---
.../v16/migrating/migrate-from-15-to-16.md | 29 ++++++-------------
1 file changed, 9 insertions(+), 20 deletions(-)
diff --git a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md
index 15b009b172d..8aa7272c122 100644
--- a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md
+++ b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md
@@ -646,30 +646,19 @@ If you hit a mismatch, you have two options:
1. Provide variables as raw JSON through `SetVariableValues(string)`, bypassing CLR serialization entirely.
2. Register a custom `JsonConverter` for the affected type so the emitted JSON matches the scalar's expected format.
-If you need to pass an Upload scalar value, you can do the following:
+If you need to pass an `Upload` scalar value, register the file on the builder via `AddFile` and reference it from your variables by the same key:
```csharp
-var requestBuilder = new OperationRequestBuilder();
-requestBuilder.SetVariableValues("""{ "file" : "yourKey" }""");
-requestBuilder.Features.Set(fileLookup);
+var file = new StreamFile("Foo.txt", () => new MemoryStream(/* your bytes */));
-public class FileLookup : IFileLookup
-{
- public bool TryGetFile(string name, [NotNullWhen(true)] out IFile? file)
- {
- if (name == "yourKey")
- {
- file = new StreamFile("Foo.txt", () => new MemoryStream());
- return true;
- }
-
- file = null;
- return false;
- }
-}
+var request = OperationRequestBuilder.New()
+ .SetDocument("mutation ($file: Upload!) { upload(file: $file) }")
+ .SetVariableValues("""{ "file": "yourKey" }""")
+ .AddFile("yourKey", file)
+ .Build();
```
-`yourKey` in this case is just a marker you choose for the file.
+`yourKey` is just a marker you choose to correlate the variable value with the file. Call `AddFile` multiple times to register additional files on the same request.
**Global state methods**
@@ -688,7 +677,7 @@ Use `OperationRequestBuilder.From(request)` to create a builder pre-populated fr
**Features collection**
-The builder now exposes a `Features` property of type `IFeatureCollection` for attaching extensibility features (such as `IFileLookup` for file uploads).
+The builder now exposes a `Features` property of type `IFeatureCollection` for attaching extensibility features.
## Snapshot matching on IExecutionResult
From 4091f9fde14ec39ca1b1f6ee0c3ff7a2d9e73e3e Mon Sep 17 00:00:00 2001
From: tobias-tengler <45513122+tobias-tengler@users.noreply.github.com>
Date: Tue, 26 May 2026 12:01:51 +0200
Subject: [PATCH 4/4] Cleanup
---
.../OperationRequestBuilderExtensions.cs | 9 +++++--
.../OperationRequestBuilderExtensionsTests.cs | 24 +++++++++++++++++++
2 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs b/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs
index 18daff32ce8..9fdb077a4b2 100644
--- a/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs
+++ b/src/HotChocolate/Core/src/Execution.Abstractions/Execution/Extensions/OperationRequestBuilderExtensions.cs
@@ -42,10 +42,15 @@ public static OperationRequestBuilder MarkAsWarmupRequest(
/// The same for configuration chaining.
///
///
- /// or is null.
+ /// , , or
+ /// is null.
///
///
- /// is null or empty.
+ /// is empty.
+ ///
+ ///
+ /// An feature has already been set on the builder
+ /// and cannot be extended.
///
public static OperationRequestBuilder AddFile(
this OperationRequestBuilder builder,
diff --git a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs
index 464e58d0736..7359b04e125 100644
--- a/src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs
+++ b/src/HotChocolate/Core/test/Execution.Abstractions.Tests/Execution/OperationRequestBuilderExtensionsTests.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using HotChocolate.Features;
using HotChocolate.Types;
@@ -128,6 +129,29 @@ public void AddFile_Should_Throw_When_NameIsEmpty()
Assert.Throws(Act);
}
+ [Fact]
+ public void AddFile_Should_Throw_When_DifferentFileLookupAlreadySet()
+ {
+ // arrange
+ var builder = OperationRequestBuilder.New().SetDocument("{ foo }");
+ builder.Features.Set(new ForeignFileLookup());
+
+ // act
+ void Act() => builder.AddFile("file", new TestFile("a.txt"));
+
+ // assert
+ Assert.Throws(Act);
+ }
+
+ private sealed class ForeignFileLookup : IFileLookup
+ {
+ public bool TryGetFile(string name, [NotNullWhen(true)] out IFile? file)
+ {
+ file = null;
+ return false;
+ }
+ }
+
private sealed class TestFile(string name) : IFile
{
public string Name { get; } = name;