// Copyright (c) Microsoft. All rights reserved. using System.IO; using System.Linq; using Microsoft.Agents.AI.LocalCodeAct.Internal; using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI.LocalCodeAct.UnitTests; /// /// Unit tests for covering the capture-limit branches /// (per-file, per-mount, and total) that produce textual omission placeholders /// instead of . /// public sealed class FileMountHelperTests { [Fact] public void CaptureWrittenFiles_PerFileLimit_ReturnsTextPlaceholder() { var dir = Directory.CreateTempSubdirectory("fmh-perfile-").FullName; try { var mount = FileMountHelper.Normalize(new FileMount(dir, "/output", FileMountMode.ReadWrite)); var pre = FileMountHelper.SnapshotWritableMounts(new[] { mount }); File.WriteAllBytes(Path.Combine(dir, "big.bin"), new byte[2048]); // Per-file limit of 1024 bytes — file is 2048 -> should be omitted via TextContent. var limits = new ProcessExecutionLimits { MaxCapturedFileBytes = 1024 }; var captured = FileMountHelper.CaptureWrittenFiles(new[] { mount }, pre, limits); var text = Assert.Single(captured.OfType()); Assert.Contains("/output/big.bin", text.Text); Assert.Contains("per-file capture limit", text.Text); Assert.Empty(captured.OfType()); } finally { Directory.Delete(dir, recursive: true); } } [Fact] public void CaptureWrittenFiles_PerMountLimit_OmitsSecondFile() { var dir = Directory.CreateTempSubdirectory("fmh-permount-").FullName; try { // WriteBytesLimit caps total bytes captured *for this mount*. var mount = FileMountHelper.Normalize( new FileMount(dir, "/output", FileMountMode.ReadWrite, writeBytesLimit: 600)); var pre = FileMountHelper.SnapshotWritableMounts(new[] { mount }); // Two files of 400 bytes each — first fits, second exceeds the 600-byte per-mount cap. File.WriteAllBytes(Path.Combine(dir, "a.bin"), new byte[400]); File.WriteAllBytes(Path.Combine(dir, "b.bin"), new byte[400]); var limits = new ProcessExecutionLimits(); // per-file/total caps high enough not to fire. var captured = FileMountHelper.CaptureWrittenFiles(new[] { mount }, pre, limits); Assert.Single(captured.OfType()); var text = Assert.Single(captured.OfType()); Assert.Contains("per-mount capture limit", text.Text); Assert.Contains("/output/b.bin", text.Text); } finally { Directory.Delete(dir, recursive: true); } } [Fact] public void CaptureWrittenFiles_TotalLimit_OmitsAcrossMounts() { var dirA = Directory.CreateTempSubdirectory("fmh-totalA-").FullName; var dirB = Directory.CreateTempSubdirectory("fmh-totalB-").FullName; try { var mountA = FileMountHelper.Normalize(new FileMount(dirA, "/a", FileMountMode.ReadWrite)); var mountB = FileMountHelper.Normalize(new FileMount(dirB, "/b", FileMountMode.ReadWrite)); var mounts = new[] { mountA, mountB }; var pre = FileMountHelper.SnapshotWritableMounts(mounts); File.WriteAllBytes(Path.Combine(dirA, "a.bin"), new byte[500]); File.WriteAllBytes(Path.Combine(dirB, "b.bin"), new byte[500]); // Total capture limit set so the first file fits and the second triggers // the cross-mount total cap. var limits = new ProcessExecutionLimits { MaxTotalCapturedFileBytes = 600 }; var captured = FileMountHelper.CaptureWrittenFiles(mounts, pre, limits); Assert.Single(captured.OfType()); var text = Assert.Single(captured.OfType()); Assert.Contains("total capture limit", text.Text); } finally { Directory.Delete(dirA, recursive: true); Directory.Delete(dirB, recursive: true); } } }