File: Dashboard\InteractionFileUploadStoreTests.cs
Web Access
Project: src\tests\Aspire.Hosting.Tests\Aspire.Hosting.Tests.csproj (Aspire.Hosting.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#pragma warning disable ASPIREFILESYSTEM001 // Type is for evaluation purposes only
 
using System.Runtime.CompilerServices;
using Aspire.Hosting.Dashboard;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.Logging.Abstractions;
 
namespace Aspire.Hosting.Tests.Dashboard;
 
public class InteractionFileUploadStoreTests
{
    private const int InteractionId = 1;
    private const string InputName = "File";
 
    [Fact]
    public void CreateEntry_ValidFileName_ReturnsIdAndPath()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "test.txt");
 
        Assert.NotNull(fileId);
        Assert.NotEmpty(fileId);
        Assert.NotEqual("test.txt", Path.GetFileName(filePath));
        fileUploadStore.CompleteUpload(InteractionId, fileId);
        Assert.Equal("test.txt", Assert.Single(fileUploadStore.GetCompletedFiles(InteractionId, InputName)).Name);
        Assert.True(File.Exists(filePath));
    }
 
    [Fact]
    public void GetFilePath_ExistingEntry_ReturnsPath()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "test.txt");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.Equal(filePath, fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
    }
 
    [Fact]
    public void GetFilePath_UploadInProgress_ReturnsNull()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "test.txt");
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
 
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.Equal(filePath, fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
    }
 
    [Fact]
    public void GetFilePath_NonexistentEntry_ReturnsNull()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        Assert.Null(fileUploadStore.GetFilePath("nonexistent", InteractionId, InputName));
    }
 
    [Fact]
    public void GetFileName_ExistingEntry_ReturnsFileName()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, _) = CreateEntry(fileUploadStore, "cert.pem");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.Equal("cert.pem", Assert.Single(fileUploadStore.GetCompletedFiles(InteractionId, InputName)).Name);
    }
 
    [Fact]
    public void RemoveEntry_ExistingEntry_DeletesFile()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        Assert.True(File.Exists(filePath));
 
        fileUploadStore.RemoveEntry(InteractionId, fileId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.False(File.Exists(filePath));
    }
 
    [Fact]
    public void RemoveEntry_LastFile_RemovesCompletedInteraction()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
        ResolveValidateAndMarkFiles(fileUploadStore, FileReferences(fileId));
        fileUploadStore.CompleteInteraction(InteractionId);
 
        Assert.Throws<InvalidOperationException>(() => fileUploadStore.CreateEntry("other.bin", InteractionId, InputName));
 
        fileUploadStore.RemoveEntry(InteractionId, fileId);
        StartInteraction(fileUploadStore);
        var (_, replacementPath) = fileUploadStore.CreateEntry("replacement.bin", InteractionId, InputName);
 
        Assert.True(File.Exists(replacementPath));
    }
 
    [Fact]
    public void RemoveEntry_TerminalInteraction_RemainsUntilLastFileRemoved()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId1, filePath1) = CreateEntry(fileUploadStore, "file1.bin");
        var (fileId2, filePath2) = fileUploadStore.CreateEntry("file2.bin", InteractionId, InputName);
        fileUploadStore.CompleteUpload(InteractionId, fileId1);
        fileUploadStore.CompleteUpload(InteractionId, fileId2);
        ResolveValidateAndMarkFiles(fileUploadStore, FileReferences(fileId1, fileId2));
        fileUploadStore.CompleteInteraction(InteractionId);
 
        fileUploadStore.RemoveEntry(InteractionId, fileId1);
 
        StartInteraction(fileUploadStore);
        Assert.Throws<InvalidOperationException>(() => fileUploadStore.CreateEntry("other.bin", InteractionId, InputName));
        Assert.Equal(filePath2, fileUploadStore.GetFilePath(fileId2, InteractionId, InputName));
 
        fileUploadStore.RemoveEntry(InteractionId, fileId2);
        StartInteraction(fileUploadStore);
        var (_, replacementPath) = fileUploadStore.CreateEntry("replacement.bin", InteractionId, InputName);
 
        Assert.True(File.Exists(replacementPath));
    }
 
    [Fact]
    public void FileOperations_DifferentInteractionId_DoNotMutateEntry()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        var otherInteractionId = InteractionId + 1;
        StartInteraction(fileUploadStore, otherInteractionId);
 
        Assert.Empty(fileUploadStore.GetCompletedFiles(otherInteractionId, InputName));
        fileUploadStore.CompleteUpload(otherInteractionId, fileId);
        fileUploadStore.RemoveEntry(otherInteractionId, fileId);
        fileUploadStore.CancelInteraction(InteractionId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.True(File.Exists(filePath));
 
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.False(File.Exists(filePath));
    }
 
    [Fact]
    public void CompleteInteraction_UntransferredUploadInProgress_RemovesFileAfterUploadCompletes()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        fileUploadStore.CompleteInteraction(InteractionId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.True(File.Exists(filePath));
 
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.False(File.Exists(filePath));
    }
 
    [Fact]
    public void UploadCompleteInteractionInProgress_KeepsFile()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.Equal(filePath, fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.True(File.Exists(filePath));
    }
 
    [Fact]
    public void CancelInteraction_UploadComplete_RemovesFileImmediately()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        fileUploadStore.CancelInteraction(InteractionId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.False(File.Exists(filePath));
    }
 
    [Fact]
    public void CancelInteraction_UploadInProgress_RemovesFileAfterUploadCompletes()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
 
        fileUploadStore.CancelInteraction(InteractionId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.True(File.Exists(filePath));
 
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.Null(fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.False(File.Exists(filePath));
    }
 
    [Fact]
    public void CompleteInteraction_TransferredUploadKeepsFile()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
        ResolveValidateAndMarkFiles(fileUploadStore, FileReferences(fileId));
        fileUploadStore.CompleteInteraction(InteractionId);
 
        Assert.Equal(filePath, fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.True(File.Exists(filePath));
    }
 
    [Fact]
    public void CompleteInteraction_UploadCompletedAfterResolution_RemovesLateFile()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
        var (acceptedFileId, acceptedFilePath) = CreateEntry(fileUploadStore, "accepted.bin");
        var (lateFileId, lateFilePath) = fileUploadStore.CreateEntry("late.bin", InteractionId, InputName);
        fileUploadStore.CompleteUpload(InteractionId, acceptedFileId);
        ResolveValidateAndMarkFiles(fileUploadStore, FileReferences(acceptedFileId));
 
        fileUploadStore.CompleteUpload(InteractionId, lateFileId);
        fileUploadStore.CompleteInteraction(InteractionId);
 
        Assert.Equal(acceptedFilePath, fileUploadStore.GetFilePath(acceptedFileId, InteractionId, InputName));
        Assert.True(File.Exists(acceptedFilePath));
        Assert.Null(fileUploadStore.GetFilePath(lateFileId, InteractionId, InputName));
        Assert.False(File.Exists(lateFilePath));
    }
 
    [Fact]
    public void CompleteInteraction_AfterInteractionFileCollected_KeepsFile()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, "temp.bin");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
        var weakReference = CompleteInteractionWithFile(fileUploadStore, fileId, filePath);
 
        GC.Collect();
        Assert.False(weakReference.TryGetTarget(out _));
 
        Assert.Equal(filePath, fileUploadStore.GetFilePath(fileId, InteractionId, InputName));
        Assert.True(File.Exists(filePath));
    }
 
    [Theory]
    [InlineData("../../../etc/passwd", "passwd")]
    [InlineData("/etc/cron.d/evil", "evil")]
    [InlineData("..\\..\\windows\\system32\\evil.exe", "evil.exe")]
    [InlineData("C:\\windows\\system32\\config.sys", "config.sys")]
    public void CreateEntry_PathTraversalFileName_SanitizesToLeafName(string maliciousFileName, string expectedLeafName)
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, maliciousFileName);
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.NotEqual(maliciousFileName, filePath);
        Assert.NotEqual(expectedLeafName, Path.GetFileName(filePath));
        Assert.Equal(expectedLeafName, Assert.Single(fileUploadStore.GetCompletedFiles(InteractionId, InputName)).Name);
    }
 
    [Theory]
    [InlineData("bad*.txt")]
    [InlineData("bad:name.txt")]
    [InlineData("CON.txt")]
    [InlineData("bad\0name.txt")]
    public void CreateEntry_PlatformSensitiveFileName_UsesRandomDiskName(string originalFileName)
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, originalFileName);
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        Assert.True(File.Exists(filePath));
        Assert.NotEqual(originalFileName, Path.GetFileName(filePath));
        Assert.Equal(originalFileName, Assert.Single(fileUploadStore.GetCompletedFiles(InteractionId, InputName)).Name);
    }
 
    [Theory]
    [InlineData("")]
    [InlineData("/")]
    [InlineData("\\")]
    public void CreateEntry_EmptyOrRootOnlyFileName_GeneratesRandomName(string emptyFileName)
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, filePath) = CreateEntry(fileUploadStore, emptyFileName);
 
        Assert.NotNull(fileId);
        Assert.NotEmpty(Path.GetFileName(filePath));
    }
 
    [Theory]
    [InlineData(1)]
    [InlineData(InteractionHelpers.MaxFileCount)]
    public void CreateEntry_ExceedsInputLimit_Throws(int maxFileCount)
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
        StartInteraction(fileUploadStore, maxFileCount: maxFileCount);
 
        for (var i = 0; i < maxFileCount; i++)
        {
            fileUploadStore.CreateEntry($"file-{i}.txt", InteractionId, InputName);
        }
 
        var exception = Assert.Throws<InvalidOperationException>(() => fileUploadStore.CreateEntry("excess.txt", InteractionId, InputName));
        var fileLabel = maxFileCount == 1 ? "file" : "files";
        Assert.Equal($"File input '{InputName}' accepts at most {maxFileCount} {fileLabel}.", exception.Message);
    }
 
    [Fact]
    public void CreateEntry_RemovedEntry_FreesInputCapacity()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
        StartInteraction(fileUploadStore, maxFileCount: 1);
        var (fileId, _) = fileUploadStore.CreateEntry("first.txt", InteractionId, InputName);
 
        fileUploadStore.RemoveEntry(InteractionId, fileId);
        var (_, replacementPath) = fileUploadStore.CreateEntry("replacement.txt", InteractionId, InputName);
 
        Assert.True(File.Exists(replacementPath));
    }
 
    [Fact]
    public void CreateEntry_UnregisteredInput_Throws()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
        StartInteraction(fileUploadStore);
 
        var exception = Assert.Throws<InvalidOperationException>(() => fileUploadStore.CreateEntry("file.txt", InteractionId, "OtherFile"));
 
        Assert.Equal($"Interaction '{InteractionId}' is not accepting file uploads for input 'OtherFile'.", exception.Message);
    }
 
    [Fact]
    public void ValidateFileReferences_ReturnsFilesInClientOrder()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (firstFileId, firstFilePath) = CreateEntry(fileUploadStore, "first.pem", "CertInput");
        var (secondFileId, secondFilePath) = fileUploadStore.CreateEntry("second.pem", InteractionId, "CertInput");
        fileUploadStore.CompleteUpload(InteractionId, secondFileId);
        fileUploadStore.CompleteUpload(InteractionId, firstFileId);
 
        IReadOnlyList<InteractionFileUpload>? resolvedFiles = fileUploadStore.GetCompletedFiles(InteractionId, "CertInput");
        resolvedFiles = InteractionFileUploadStore.ValidateFileReferences(
            FileReferences(secondFileId, firstFileId),
            "CertInput",
            resolvedFiles);
 
        Assert.NotNull(resolvedFiles);
        Assert.Collection(
            resolvedFiles,
            file =>
            {
                Assert.Equal(secondFileId, file.Id);
                Assert.Equal("second.pem", file.Name);
                Assert.Equal(secondFilePath, file.FilePath);
            },
            file =>
            {
                Assert.Equal(firstFileId, file.Id);
                Assert.Equal("first.pem", file.Name);
                Assert.Equal(firstFilePath, file.FilePath);
            });
    }
 
    [Fact]
    public void GetCompletedFiles_UsesStoredFileName()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, _) = CreateEntry(fileUploadStore, "../../../cert.pem", "CertInput");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
        IReadOnlyList<InteractionFileUpload>? resolvedFiles = fileUploadStore.GetCompletedFiles(InteractionId, "CertInput");
        InteractionFileUploadStore.ValidateFileReferences(FileReferences(fileId), "CertInput", resolvedFiles);
 
        var file = Assert.Single(resolvedFiles!);
        Assert.Equal("cert.pem", file.Name);
    }
 
    [Fact]
    public void ValidateFileReferences_MismatchedSubmission_Throws()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
        var (firstFileId, _) = CreateEntry(fileUploadStore, "first.txt");
        var (secondFileId, _) = fileUploadStore.CreateEntry("second.txt", InteractionId, InputName);
        fileUploadStore.CompleteUpload(InteractionId, firstFileId);
        fileUploadStore.CompleteUpload(InteractionId, secondFileId);
        IReadOnlyList<InteractionFileUpload>? files = fileUploadStore.GetCompletedFiles(InteractionId, InputName);
        var mismatchedValues = new[]
        {
            "[]",
            FileReferences(firstFileId),
            FileReferences(firstFileId, "unknown"),
            FileReferences(firstFileId, firstFileId),
            "not-json",
            "[null]"
        };
 
        foreach (var value in mismatchedValues)
        {
            var exception = Assert.Throws<InvalidOperationException>(() =>
                InteractionFileUploadStore.ValidateFileReferences(value, InputName, files));
 
            Assert.Equal($"Submitted files for input '{InputName}' do not match the completed uploads.", exception.Message);
        }
    }
 
    [Fact]
    public void ValidateFileReferences_DifferentInputName_Throws()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, _) = CreateEntry(fileUploadStore, "cert.pem");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        IReadOnlyList<InteractionFileUpload>? files = fileUploadStore.GetCompletedFiles(InteractionId, "OtherFile");
        var exception = Assert.Throws<InvalidOperationException>(() =>
            InteractionFileUploadStore.ValidateFileReferences(FileReferences(fileId), "OtherFile", files));
 
        Assert.Equal("Submitted files for input 'OtherFile' do not match the completed uploads.", exception.Message);
    }
 
    [Fact]
    public void ValidateFileReferences_DifferentInteractionId_Throws()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (fileId, _) = CreateEntry(fileUploadStore, "cert.pem");
        fileUploadStore.CompleteUpload(InteractionId, fileId);
 
        IReadOnlyList<InteractionFileUpload>? files = fileUploadStore.GetCompletedFiles(InteractionId + 1, InputName);
        var exception = Assert.Throws<InvalidOperationException>(() =>
            InteractionFileUploadStore.ValidateFileReferences(FileReferences(fileId), InputName, files));
 
        Assert.Equal($"Submitted files for input '{InputName}' do not match the completed uploads.", exception.Message);
    }
 
    [Fact]
    public void ValidateFileReferences_UploadInProgress_Throws()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
        var (fileId, _) = CreateEntry(fileUploadStore, "file.txt");
 
        IReadOnlyList<InteractionFileUpload>? files = fileUploadStore.GetCompletedFiles(InteractionId, InputName);
        var exception = Assert.Throws<InvalidOperationException>(() =>
            InteractionFileUploadStore.ValidateFileReferences(FileReferences(fileId), InputName, files));
 
        Assert.Equal($"Submitted files for input '{InputName}' do not match the completed uploads.", exception.Message);
    }
 
    [Fact]
    public void Dispose_CleansUpAllFiles()
    {
        using var fileSystemService = new TestFileSystemService();
        var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var (_, filePath1) = CreateEntry(fileUploadStore, "file1.txt");
        var (_, filePath2) = CreateEntry(fileUploadStore, "file2.txt");
 
        Assert.True(File.Exists(filePath1));
        Assert.True(File.Exists(filePath2));
 
        fileUploadStore.Dispose();
        fileUploadStore.Dispose();
 
        Assert.Null(fileUploadStore.GetFilePath("anything", InteractionId, InputName));
        Assert.False(File.Exists(filePath1));
        Assert.False(File.Exists(filePath2));
    }
 
    [Fact]
    public void CreateEntry_UnknownInteraction_Throws()
    {
        using var fileSystemService = new TestFileSystemService();
        using var fileUploadStore = CreateFileUploadStore(fileSystemService);
 
        var exception = Assert.Throws<InvalidOperationException>(() => fileUploadStore.CreateEntry("temp.bin", InteractionId, InputName));
 
        Assert.Equal($"Interaction '{InteractionId}' is not accepting file uploads.", exception.Message);
    }
 
    private static InteractionFileUploadStore CreateFileUploadStore(IFileSystemService fileSystemService) =>
        new(fileSystemService, NullLogger<InteractionFileUploadStore>.Instance);
 
    private static (string FileId, string FilePath) CreateEntry(InteractionFileUploadStore fileUploadStore, string fileName, string inputName = InputName)
    {
        StartInteraction(fileUploadStore, inputName: inputName);
        return fileUploadStore.CreateEntry(fileName, InteractionId, inputName);
    }
 
    private static void StartInteraction(
        InteractionFileUploadStore fileUploadStore,
        int interactionId = InteractionId,
        string inputName = InputName,
        int maxFileCount = InteractionHelpers.MaxFileCount)
    {
        fileUploadStore.StartInteraction(interactionId, [(inputName, maxFileCount)]);
    }
 
    private static string FileReferences(params string[] fileIds) =>
        $"[{string.Join(',', fileIds.Select(fileId => $"{{\"Id\":\"{fileId}\"}}"))}]";
 
    private static void ResolveValidateAndMarkFiles(InteractionFileUploadStore fileUploadStore, string jsonValue)
    {
        IReadOnlyList<InteractionFileUpload>? files = fileUploadStore.GetCompletedFiles(InteractionId, InputName);
        files = InteractionFileUploadStore.ValidateFileReferences(jsonValue, InputName, files);
        fileUploadStore.MarkFilesAccepted(InteractionId, InputName, files!.Select(file => file.Id).ToArray());
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private static WeakReference<InteractionFile> CompleteInteractionWithFile(InteractionFileUploadStore fileUploadStore, string fileId, string filePath)
    {
        var interactionFile = new InteractionFile(fileId, "temp.bin", filePath);
        ResolveValidateAndMarkFiles(fileUploadStore, FileReferences(fileId));
        fileUploadStore.CompleteInteraction(InteractionId);
        return new WeakReference<InteractionFile>(interactionFile);
    }
}