using System;
using System.Collections.Generic;
using Esiur.Data;
using Esiur.Resource;
namespace Esiur.Tests.Unit;
///
/// Round-trips values through Codec.Compose -> Codec.ParseSync and asserts the value
/// survives. Because the serializer narrows integers/floats to the smallest wire type,
/// the parsed CLR type often differs from the input, so comparisons are value-based.
/// Exact wire bytes are pinned separately by .
///
public class SerializationRoundTripTests
{
static object RoundTrip(object value)
{
var bytes = Codec.Compose(value, Warehouse.Default, null);
var (consumed, parsed) = Codec.ParseSync(bytes, 0, Warehouse.Default);
Assert.Equal((uint)bytes.Length, consumed);
return parsed;
}
static void AssertIntRoundTrip(long value)
{
var parsed = RoundTrip(value);
Assert.Equal(value, Convert.ToInt64(parsed));
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(-1)]
[InlineData(sbyte.MinValue)]
[InlineData(sbyte.MaxValue)]
[InlineData((long)short.MinValue)]
[InlineData((long)short.MaxValue)]
[InlineData((long)int.MinValue)]
[InlineData((long)int.MaxValue)]
[InlineData(long.MinValue)]
[InlineData(long.MaxValue)]
public void Int64_NarrowsAndRoundTrips(long value) => AssertIntRoundTrip(value);
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(-1)]
[InlineData(int.MinValue)]
[InlineData(int.MaxValue)]
public void Int32_RoundTrips(int value)
{
var parsed = RoundTrip(value);
Assert.Equal(value, Convert.ToInt32(parsed));
}
[Theory]
[InlineData((ulong)0)]
[InlineData((ulong)255)]
[InlineData((ulong)65535)]
[InlineData((ulong)uint.MaxValue)]
[InlineData(ulong.MaxValue)]
public void UInt64_NarrowsAndRoundTrips(ulong value)
{
var parsed = RoundTrip(value);
Assert.Equal(value, Convert.ToUInt64(parsed));
}
[Theory]
[InlineData(0f)]
[InlineData(1.5f)]
[InlineData(-3.25f)]
[InlineData(3.4028235e38f)]
public void Float32_RoundTrips(float value)
{
var parsed = RoundTrip(value);
Assert.Equal(value, Convert.ToSingle(parsed), 3);
}
[Theory]
[InlineData(0d)]
[InlineData(1.5d)]
[InlineData(-3.25d)]
[InlineData(0.1d)]
[InlineData(1.7976931348623157e308d)]
public void Float64_RoundTrips(double value)
{
var parsed = RoundTrip(value);
Assert.Equal(value, Convert.ToDouble(parsed), 10);
}
[Theory]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
[InlineData(double.NegativeInfinity)]
public void Float_NaN_And_Infinity_Encode_As_Infinity_Token(double value)
{
// The serializer collapses NaN and +/- Infinity onto the single 1-byte Infinity
// token (0x04), which now decodes to a canonical +Infinity rather than crashing.
var bytes = Codec.Compose(value, Warehouse.Default, null);
Assert.Equal(new byte[] { 0x04 }, bytes);
var parsed = RoundTrip(value);
Assert.True(double.IsPositiveInfinity(Convert.ToDouble(parsed)));
}
[Theory]
[InlineData(0.5)]
[InlineData(1.1)] // hits the decimal -> Float64 branch (regression for the byte[4] overrun)
[InlineData(-1234.5)]
public void Decimal_FloatBranches_RoundTrip(double asDouble)
{
var value = (decimal)asDouble;
var parsed = RoundTrip(value);
Assert.Equal(Convert.ToDouble(value), Convert.ToDouble(parsed), 6);
}
[Fact]
public void Decimal_IntegerValue_RoundTrips()
{
var parsed = RoundTrip(42m);
Assert.Equal(42L, Convert.ToInt64(parsed));
}
[Fact]
public void Decimal_HighPrecision_RoundTrips()
{
// A value with a non-zero scale that is not exactly representable as float or
// double, so it stays a full 16-byte Decimal128 on the wire.
var value = 1.2345678901234567890123456789m;
var parsed = RoundTrip(value);
Assert.Equal(value, Convert.ToDecimal(parsed));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Bool_RoundTrips(bool value)
{
var parsed = RoundTrip(value);
Assert.Equal(value, Convert.ToBoolean(parsed));
}
[Theory]
[InlineData("")]
[InlineData("Hello, Esiur")]
[InlineData("Unicode éü☃😀")]
public void String_RoundTrips(string value)
{
var parsed = RoundTrip(value);
Assert.Equal(value, (string)parsed);
}
[Fact]
public void Null_RoundTrips()
{
var parsed = RoundTrip(null);
Assert.Null(parsed);
}
[Fact]
public void Char_RoundTrips()
{
var parsed = RoundTrip('A');
Assert.Equal('A', Convert.ToChar(parsed));
}
[Fact]
public void DateTime_RoundTrips_AsUtc()
{
var value = new DateTime(2026, 6, 2, 13, 45, 30, DateTimeKind.Utc);
var parsed = (DateTime)RoundTrip(value);
Assert.Equal(value.ToUniversalTime().Ticks, parsed.ToUniversalTime().Ticks);
}
[Fact]
public void Uuid_RoundTrips()
{
var value = new Uuid(Guid.Parse("12345678-90ab-cdef-1234-567890abcdef").ToByteArray());
var parsed = (Uuid)RoundTrip(value);
Assert.Equal(value.ToString(), parsed.ToString());
}
static List