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));
}
}