using SuiteLink.Client.Protocol; namespace SuiteLink.Client.Tests.Protocol; public sealed class SuiteLinkEncodingTests { [Fact] public void EncodeLengthPrefixedUtf16_WithAsciiText_WritesCharacterCountAndUtf16Bytes() { var bytes = SuiteLinkEncoding.EncodeLengthPrefixedUtf16("AB"); Assert.Equal(new byte[] { 0x02, 0x41, 0x00, 0x42, 0x00 }, bytes); } [Fact] public void DecodeNullTerminatedUtf16_ReadsStringAndConsumedBytes() { var buffer = new byte[] { 0x48, 0x00, 0x69, 0x00, 0x00, 0x00, 0x20, 0x00 }; var text = SuiteLinkEncoding.DecodeNullTerminatedUtf16(buffer, out var consumed); Assert.Equal("Hi", text); Assert.Equal(6, consumed); } [Fact] public void WriteAndReadUInt32LittleEndian_RoundTripsValue() { Span bytes = stackalloc byte[4]; SuiteLinkEncoding.WriteUInt32LittleEndian(bytes, 0x11223344); var value = SuiteLinkEncoding.ReadUInt32LittleEndian(bytes); Assert.Equal((uint)0x11223344, value); Assert.Equal(new byte[] { 0x44, 0x33, 0x22, 0x11 }, bytes.ToArray()); } [Fact] public void FileTimeToUtcDateTime_ConvertsKnownEpochValue() { const long unixEpochFileTime = 116444736000000000L; var value = SuiteLinkEncoding.FileTimeToUtcDateTime(unixEpochFileTime); Assert.Equal(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), value); } [Fact] public void DecodeNullTerminatedUtf16_WithOddLength_ThrowsFormatException() { var buffer = new byte[] { 0x41, 0x00, 0x00 }; Assert.Throws(() => SuiteLinkEncoding.DecodeNullTerminatedUtf16(buffer, out _)); } [Fact] public void DecodeNullTerminatedUtf16_WithoutNullTerminator_ThrowsFormatException() { var buffer = new byte[] { 0x41, 0x00, 0x42, 0x00 }; Assert.Throws(() => SuiteLinkEncoding.DecodeNullTerminatedUtf16(buffer, out _)); } [Fact] public void EncodeLengthPrefixedUtf16_WhenInputIsTooLong_ThrowsArgumentOutOfRangeException() { var value = new string('A', 256); Assert.Throws(() => SuiteLinkEncoding.EncodeLengthPrefixedUtf16(value)); } [Fact] public void EncodeLengthPrefixedUtf16_WithSurrogatePair_PrefixUsesUtf16CodeUnits() { var bytes = SuiteLinkEncoding.EncodeLengthPrefixedUtf16("A\U0001F600"); Assert.Equal(3, bytes[0]); } [Fact] public void ReadUInt32LittleEndian_WhenInputIsTooShort_ThrowsFormatException() { var buffer = new byte[] { 0x01, 0x02, 0x03 }; Assert.Throws(() => SuiteLinkEncoding.ReadUInt32LittleEndian(buffer)); } [Fact] public void WriteAndReadSingleLittleEndian_RoundTripsNonTrivialValue() { Span bytes = stackalloc byte[4]; const float expected = 123.4567f; SuiteLinkEncoding.WriteSingleLittleEndian(bytes, expected); var value = SuiteLinkEncoding.ReadSingleLittleEndian(bytes); Assert.Equal(expected, value); } [Fact] public void WriteAndReadSingleLittleEndian_RoundTripsNaN() { Span bytes = stackalloc byte[4]; var expected = float.NaN; SuiteLinkEncoding.WriteSingleLittleEndian(bytes, expected); var value = SuiteLinkEncoding.ReadSingleLittleEndian(bytes); Assert.True(float.IsNaN(value)); } }