using FluentAssertions; using FreeCode.Tools; using FreeCode.Tests.Unit.Helpers; using Xunit; namespace FreeCode.Tests.Unit.Tools; public sealed class FileReadToolTests : IDisposable { private readonly string _tempDirectory = Path.Combine(Path.GetTempPath(), "free-code-tests", Guid.NewGuid().ToString("N")); private readonly FileReadTool _sut = new(); public FileReadToolTests() { Directory.CreateDirectory(_tempDirectory); } [Fact] public async Task ExecuteAsync_ReadsExistingFile() { var path = Path.Combine(_tempDirectory, "sample.txt"); await File.WriteAllTextAsync(path, "first\nsecond\nthird"); var result = await _sut.ExecuteAsync(new FileReadToolInput(path), TestHelper.CreateContext()); result.IsError.Should().BeFalse(); result.Data.Should().Contain("second"); } [Fact] public async Task ExecuteAsync_WithOffsetAndLimit_ReturnsRequestedSlice() { var path = Path.Combine(_tempDirectory, "sample.txt"); await File.WriteAllTextAsync(path, "zero\none\ntwo\nthree"); var result = await _sut.ExecuteAsync(new FileReadToolInput(path, offset: 1, limit: 2), TestHelper.CreateContext()); result.Data.Should().Be($"one{Environment.NewLine}two"); } [Fact] public async Task ExecuteAsync_ForMissingFile_ReturnsError() { var result = await _sut.ExecuteAsync(new FileReadToolInput(Path.Combine(_tempDirectory, "missing.txt")), TestHelper.CreateContext()); result.IsError.Should().BeTrue(); result.ErrorMessage.Should().NotBeNullOrWhiteSpace(); } [Fact] public async Task ValidateInputAsync_WithEmptyPath_IsInvalid() { var result = await _sut.ValidateInputAsync(new FileReadToolInput(string.Empty)); result.IsValid.Should().BeFalse(); result.Errors.Should().ContainSingle("FilePath is required."); } public void Dispose() { if (Directory.Exists(_tempDirectory)) { Directory.Delete(_tempDirectory, recursive: true); } } }