using ScadaLink.CLI.Commands; namespace ScadaLink.CLI.Tests.Commands; /// /// CLI-019 regression tests for . /// The pre-fix code did Convert.FromBase64String(...) → File.WriteAllBytes(...), /// doubling the bundle's bytes onto the LOH and writing synchronously. The new /// streaming helper decodes the base64 string in fixed-size chunks straight into /// a , so peak working set is bounded by the chunk size /// regardless of how large the bundle is. /// public class BundleCommandsStreamingTests : IDisposable { private readonly string _tempPath; public BundleCommandsStreamingTests() { _tempPath = Path.Combine(Path.GetTempPath(), $"bundle-stream-test-{Guid.NewGuid():N}.bin"); } public void Dispose() { if (File.Exists(_tempPath)) { File.Delete(_tempPath); } } [Fact] public void StreamBase64ToFile_SmallPayload_RoundTrips() { var bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var base64 = Convert.ToBase64String(bytes); var written = BundleCommands.StreamBase64ToFile(base64, _tempPath); Assert.Equal(bytes.Length, written); var roundTripped = File.ReadAllBytes(_tempPath); Assert.Equal(bytes, roundTripped); } [Fact] public void StreamBase64ToFile_PayloadCrossesChunkBoundary_RoundTrips() { // Build a payload several chunks wide so the slicing loop runs more than // once, with enough trailing bytes that the final slice is short and // exercises the padding/short-final-chunk path. var size = (BundleCommands.Base64StreamChunkChars / 4 * 3) * 3 + 17; var bytes = new byte[size]; for (var i = 0; i < size; i++) bytes[i] = (byte)(i & 0xFF); var base64 = Convert.ToBase64String(bytes); var written = BundleCommands.StreamBase64ToFile(base64, _tempPath); Assert.Equal(size, written); var roundTripped = File.ReadAllBytes(_tempPath); Assert.Equal(bytes, roundTripped); } [Fact] public void StreamBase64ToFile_EmptyString_WritesEmptyFile() { var written = BundleCommands.StreamBase64ToFile(string.Empty, _tempPath); Assert.Equal(0, written); Assert.True(File.Exists(_tempPath)); Assert.Empty(File.ReadAllBytes(_tempPath)); } [Fact] public void StreamBase64ToFile_InvalidBase64_ThrowsFormatException() { // '*' is not a valid base64 character, so TryFromBase64Chars returns // false and the helper throws — the pre-fix code threw FormatException // from Convert.FromBase64String, so the contract is preserved. var invalid = "this is not valid base64 !!!*"; Assert.Throws(() => BundleCommands.StreamBase64ToFile(invalid, _tempPath)); } [Fact] public void StreamBase64ToFile_NullBase64_Throws() { Assert.Throws(() => BundleCommands.StreamBase64ToFile(null!, _tempPath)); } [Fact] public void StreamBase64ToFile_EmptyOutputPath_Throws() { Assert.Throws(() => BundleCommands.StreamBase64ToFile("AAAA", string.Empty)); } }