Skip to content

Commit

Permalink
add ulong constructor and tests to ShortChannelId; (#32)
Browse files Browse the repository at this point in the history
Signed-off-by: Níckolas Goline <nickolas.goline+github@gmail.com>
  • Loading branch information
nGoline authored Jan 23, 2025
1 parent 4e48063 commit f7cfdf6
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 4 deletions.
16 changes: 12 additions & 4 deletions src/NLightning.Common/Types/ShortChannelId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public ShortChannelId(byte[] value)
OUTPUT_INDEX = (ushort)((value[6] << 8) | value[7]);
}

public ShortChannelId(ulong channelId) : this(
(uint)((channelId >> 40) & 0xFFFFFF), // BLOCK_HEIGHT
(uint)((channelId >> 16) & 0xFFFF), // TRANSACTION_INDEX
(ushort)(channelId & 0xFF) // OUTPUT_INDEX
)
{ }

public ValueTask SerializeAsync(Stream stream)
{
return stream.WriteAsync(_value);
Expand Down Expand Up @@ -76,12 +83,12 @@ public static ShortChannelId Parse(string shortChannelId)
}

#region Overrides
public override readonly string ToString()
public override string ToString()
{
return $"{BLOCK_HEIGHT}x{TRANSACTION_INDEX}x{OUTPUT_INDEX}";
}

public override readonly bool Equals(object? obj)
public override bool Equals(object? obj)
{
if (obj is ShortChannelId other)
{
Expand All @@ -91,14 +98,14 @@ public override readonly bool Equals(object? obj)
return false;
}

public readonly bool Equals(ShortChannelId other)
public bool Equals(ShortChannelId other)
{
return BLOCK_HEIGHT == other.BLOCK_HEIGHT &&
TRANSACTION_INDEX == other.TRANSACTION_INDEX &&
OUTPUT_INDEX == other.OUTPUT_INDEX;
}

public override readonly int GetHashCode()
public override int GetHashCode()
{
return HashCode.Combine(BLOCK_HEIGHT, TRANSACTION_INDEX, OUTPUT_INDEX);
}
Expand All @@ -109,6 +116,7 @@ public override readonly int GetHashCode()
public static implicit operator ShortChannelId(byte[] value) => new(value);
public static implicit operator ReadOnlySpan<byte>(ShortChannelId s) => s._value;
public static implicit operator ShortChannelId(Span<byte> value) => new(value.ToArray());
public static implicit operator ShortChannelId(ulong value) => new(value);

public static bool operator ==(ShortChannelId left, ShortChannelId right)
{
Expand Down
161 changes: 161 additions & 0 deletions test/NLightning.Common.Tests/Types/ShortChannelIdTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
namespace NLightning.Common.Tests.Types;

using Common.Types;

public class ShortChannelIdTests
{
private const ulong EXPECTED_SHORT_CHANNEL_ID = 956714754222915585;
private const uint EXPECTED_BLOCK_HEIGHT = 870127;
private const uint EXPECTED_TX_INDEX = 1237;
private const ushort EXPECTED_OUTPUT_INDEX = 1;
private const string EXPECTED_STRING = "870127x1237x1";

private readonly byte[] _expectedValue = [0x0D, 0x46, 0xEF, 0x00, 0x04, 0xD5, 0x00, 0x01];

#region Constructor Tests
[Fact]
public void Given_ValidParameters_When_ConstructorCalled_Then_PropertiesAreSetCorrectly()
{
// Given
// When
var shortChannelId = new ShortChannelId(EXPECTED_BLOCK_HEIGHT, EXPECTED_TX_INDEX, EXPECTED_OUTPUT_INDEX);

// Then
Assert.Equal(EXPECTED_BLOCK_HEIGHT, shortChannelId.BLOCK_HEIGHT);
Assert.Equal(EXPECTED_TX_INDEX, shortChannelId.TRANSACTION_INDEX);
Assert.Equal(EXPECTED_OUTPUT_INDEX, shortChannelId.OUTPUT_INDEX);
Assert.Equal(_expectedValue, shortChannelId);
}

[Fact]
public void Given_ValidByteArray_When_ConstructorCalled_Then_PropertiesAreExtractedCorrectly()
{
// Given
// When
var shortChannelId = new ShortChannelId(_expectedValue);

// Then
Assert.Equal(EXPECTED_BLOCK_HEIGHT, shortChannelId.BLOCK_HEIGHT);
Assert.Equal(EXPECTED_TX_INDEX, shortChannelId.TRANSACTION_INDEX);
Assert.Equal(EXPECTED_OUTPUT_INDEX, shortChannelId.OUTPUT_INDEX);
Assert.Equal(_expectedValue, shortChannelId);
}

[Fact]
public void Given_InvalidByteArrayLength_When_ConstructorCalled_Then_ArgumentExceptionIsThrown()
{
// Given
var invalidByteArray = new byte[] { 0x01, 0x02 }; // only 2 bytes, should be 8

// When / Then
var exception = Assert.Throws<ArgumentException>(() => new ShortChannelId(invalidByteArray));
Assert.Contains("ShortChannelId must be 8 bytes", exception.Message);
}

[Fact]
public void Given_ValidUlong_When_ConstructorCalled_Then_PropertiesAreExtractedCorrectly()
{
// Given
// When
var shortChannelId = new ShortChannelId(EXPECTED_SHORT_CHANNEL_ID);

// Then
Assert.Equal(EXPECTED_BLOCK_HEIGHT, shortChannelId.BLOCK_HEIGHT);
Assert.Equal(EXPECTED_TX_INDEX, shortChannelId.TRANSACTION_INDEX);
Assert.Equal(EXPECTED_OUTPUT_INDEX, shortChannelId.OUTPUT_INDEX);
Assert.Equal(_expectedValue, shortChannelId);
}
#endregion

#region Parse Tests
[Fact]
public void Given_ValidString_When_ParseCalled_Then_PropertiesMatch()
{
// Given
// When
var shortChannelId = ShortChannelId.Parse(EXPECTED_STRING);

// Then
Assert.Equal(EXPECTED_BLOCK_HEIGHT, shortChannelId.BLOCK_HEIGHT);
Assert.Equal(EXPECTED_TX_INDEX, shortChannelId.TRANSACTION_INDEX);
Assert.Equal(EXPECTED_OUTPUT_INDEX, shortChannelId.OUTPUT_INDEX);
Assert.Equal(_expectedValue, shortChannelId);
}

[Fact]
public void Given_InvalidStringFormat_When_ParseCalled_Then_FormatExceptionIsThrown()
{
// Given
var invalidString = "this-is-not-valid";

// When / Then
Assert.Throws<FormatException>(() => ShortChannelId.Parse(invalidString));
}
#endregion

#region ToString Tests
[Fact]
public void Given_ValidShortChannelId_When_ToStringCalled_Then_FormattedCorrectly()
{
// Given
var shortChannelId = new ShortChannelId(EXPECTED_BLOCK_HEIGHT, EXPECTED_TX_INDEX, EXPECTED_OUTPUT_INDEX);

// When
var result = shortChannelId.ToString();

// Then
Assert.Equal(EXPECTED_STRING, result);
}
#endregion

#region Equality Tests
[Fact]
public void Given_TwoIdenticalShortChannelIds_When_ComparingEquality_Then_TheyAreEqual()
{
// Given
var scid1 = new ShortChannelId(123, 45, 6);
var scid2 = new ShortChannelId(123, 45, 6);

// When
var areEqual = scid1 == scid2;

// Then
Assert.True(areEqual);
Assert.True(scid1.Equals(scid2));
Assert.Equal(scid1.GetHashCode(), scid2.GetHashCode());
}

[Fact]
public void Given_TwoDifferentShortChannelIds_When_ComparingEquality_Then_TheyAreNotEqual()
{
// Given
var scid1 = new ShortChannelId(123, 45, 6);
var scid2 = new ShortChannelId(321, 54, 6);

// When
var areEqual = scid1 == scid2;

// Then
Assert.False(areEqual);
Assert.False(scid1.Equals(scid2));
}
#endregion

#region Serialization/Deserialization Tests
[Fact]
public async Task Given_ValidShortChannelId_When_SerializedAndDeserialized_Then_PropertiesRemainTheSame()
{
// Given
var original = new ShortChannelId(EXPECTED_BLOCK_HEIGHT, EXPECTED_TX_INDEX, EXPECTED_OUTPUT_INDEX);
using var ms = new MemoryStream();

// When
await original.SerializeAsync(ms);
ms.Position = 0; // reset stream position
var deserialized = await ShortChannelId.DeserializeAsync(ms);

// Then
Assert.Equal(original, deserialized);
}
#endregion
}

0 comments on commit f7cfdf6

Please sign in to comment.