feat: add suitelink client runtime and test harness

This commit is contained in:
Joseph Doherty
2026-03-16 16:46:32 -04:00
parent 731bfe2237
commit c278f98496
27 changed files with 2515 additions and 15 deletions

View File

@@ -1,5 +1,6 @@
using System.Buffers.Binary;
using SuiteLink.Client.Protocol;
using SuiteLink.Client.Tests.Fixtures;
namespace SuiteLink.Client.Tests.Protocol;
@@ -33,9 +34,7 @@ public sealed class SuiteLinkHandshakeCodecTests
[Fact]
public void ParseNormalHandshakeAck_WithNormalAckFrame_ReturnsAckData()
{
// Fixed vector for normal ACK assumption:
// remaining=0x0006, type=0x0001, payload=0xA1B2C3, marker=0xA5.
byte[] frame = [0x06, 0x00, 0x01, 0x00, 0xA1, 0xB2, 0xC3, 0xA5];
var frame = FixtureBytes.Read("handshake-ack-normal.bin");
var ack = SuiteLinkHandshakeCodec.ParseNormalHandshakeAck(frame);

View File

@@ -1,4 +1,5 @@
using SuiteLink.Client.Protocol;
using SuiteLink.Client.Tests.Fixtures;
namespace SuiteLink.Client.Tests.Protocol;
@@ -9,7 +10,7 @@ public sealed class SuiteLinkSubscriptionCodecTests
{
var bytes = SuiteLinkSubscriptionCodec.EncodeAdvise(0x11223344, "A");
var frame = SuiteLinkFrameReader.ParseFrame(bytes);
byte[] expected = [0x0A, 0x00, 0x10, 0x80, 0x44, 0x33, 0x22, 0x11, 0x01, 0x41, 0x00, 0xA5];
var expected = FixtureBytes.Read("advise-tagid-11223344-item-A.bin");
Assert.Equal(expected, bytes);
Assert.Equal(0x10, bytes[2]);

View File

@@ -1,5 +1,6 @@
using System.Text;
using SuiteLink.Client.Protocol;
using SuiteLink.Client.Tests.Fixtures;
namespace SuiteLink.Client.Tests.Protocol;
@@ -8,16 +9,7 @@ public sealed class SuiteLinkUpdateCodecTests
[Fact]
public void DecodeUpdate_DecodesBinaryValue()
{
byte[] frame =
[
0x0D, 0x00, 0x09, 0x00,
0x34, 0x12, 0x00, 0x00,
0x0A, 0x00,
0xC0, 0x00,
0x01,
0x01,
0xA5
];
var frame = FixtureBytes.Read("update-binary-tag-1234-true.bin");
Assert.Equal(0x09, frame[2]);
Assert.Equal(0x00, frame[3]);

View File

@@ -0,0 +1,66 @@
using SuiteLink.Client.Protocol;
namespace SuiteLink.Client.Tests.Protocol;
public sealed class SuiteLinkWriteCodecTests
{
[Fact]
public void EncodeWrite_BooleanValue_WritesExpectedGoldenVector()
{
var bytes = SuiteLinkWriteCodec.Encode(0x12345678, SuiteLinkValue.FromBoolean(true));
byte[] expected = [0x09, 0x00, 0x0B, 0x08, 0x78, 0x56, 0x34, 0x12, 0x01, 0x01, 0xA5];
Assert.Equal(expected, bytes);
Assert.Equal(0x0B, bytes[2]);
Assert.Equal(0x08, bytes[3]);
}
[Fact]
public void EncodeWrite_Int32Value_WritesExpectedPayload()
{
var bytes = SuiteLinkWriteCodec.Encode(0x89ABCDEF, SuiteLinkValue.FromInt32(42));
var frame = SuiteLinkFrameReader.ParseFrame(bytes);
Assert.Equal(0x0B, bytes[2]);
Assert.Equal(0x08, bytes[3]);
Assert.Equal(SuiteLinkWriteCodec.PokeMessageType, frame.MessageType);
Assert.Equal(0x89ABCDEFu, SuiteLinkEncoding.ReadUInt32LittleEndian(frame.Payload.Span));
Assert.Equal((byte)SuiteLinkWireValueType.Integer, frame.Payload.Span[4]);
Assert.Equal(42, SuiteLinkEncoding.ReadInt32LittleEndian(frame.Payload.Span[5..]));
}
[Fact]
public void EncodeWrite_Float32Value_WritesExpectedPayload()
{
var bytes = SuiteLinkWriteCodec.Encode(0x00000007, SuiteLinkValue.FromFloat32(12.5f));
var frame = SuiteLinkFrameReader.ParseFrame(bytes);
Assert.Equal(0x0B, bytes[2]);
Assert.Equal(0x08, bytes[3]);
Assert.Equal((byte)SuiteLinkWireValueType.Real, frame.Payload.Span[4]);
Assert.Equal(12.5f, SuiteLinkEncoding.ReadSingleLittleEndian(frame.Payload.Span[5..]));
}
[Fact]
public void EncodeWrite_StringValue_WritesExpectedPayload()
{
var bytes = SuiteLinkWriteCodec.Encode(0x00000008, SuiteLinkValue.FromString("OK"));
var frame = SuiteLinkFrameReader.ParseFrame(bytes);
Assert.Equal(0x0B, bytes[2]);
Assert.Equal(0x08, bytes[3]);
Assert.Equal((byte)SuiteLinkWireValueType.Message, frame.Payload.Span[4]);
Assert.Equal((ushort)2, SuiteLinkEncoding.ReadUInt16LittleEndian(frame.Payload.Span[5..]));
Assert.Equal((byte)'O', frame.Payload.Span[7]);
Assert.Equal((byte)'K', frame.Payload.Span[8]);
}
[Fact]
public void EncodeWrite_NoneValue_ThrowsNotSupportedException()
{
var ex = Assert.Throws<NotSupportedException>(
() => SuiteLinkWriteCodec.Encode(0x00000001, default));
Assert.Contains("unsupported", ex.Message, StringComparison.OrdinalIgnoreCase);
}
}