Skip to content

Commit 300795b

Browse files
committed
test: extended ipaddress extensions tests
1 parent 76b4396 commit 300795b

1 file changed

Lines changed: 254 additions & 2 deletions

File tree

CommonNet.Extensions.Tests/CommonNet.Extensions/IPAddressExtensionsTests.cs

Lines changed: 254 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,112 @@ await Assert.That(result)
5656
.EqualTo(expected);
5757
}
5858

59+
[Test]
60+
public async Task Mask_AllZerosMask_ShouldReturnAllZerosAddress()
61+
{
62+
var ip = IPAddress.Parse("192.168.1.100");
63+
var mask = IPAddress.Parse("0.0.0.0");
64+
var expected = IPAddress.Parse("0.0.0.0");
65+
66+
var result = ip.Mask(mask);
67+
68+
await Assert.That(result)
69+
.EqualTo(expected);
70+
}
71+
72+
[Test]
73+
public async Task Mask_AllOnesMask_ShouldReturnOriginalAddress()
74+
{
75+
var ip = IPAddress.Parse("192.168.1.100");
76+
var mask = IPAddress.Parse("255.255.255.255");
77+
78+
var result = ip.Mask(mask);
79+
80+
await Assert.That(result)
81+
.EqualTo(ip);
82+
}
83+
84+
[Test]
85+
[Arguments("192.168.1.200", "255.255.255.128", "192.168.1.128")]
86+
[Arguments("192.168.1.50", "255.255.255.128", "192.168.1.0")]
87+
[Arguments("10.10.10.10", "255.255.255.192", "10.10.10.0")]
88+
[Arguments("10.10.10.200", "255.255.255.192", "10.10.10.192")]
89+
public async Task Mask_NonOctetAlignedMask_ShouldMaskCorrectly(string ipAddress, string maskAddress, string expected)
90+
{
91+
var ip = IPAddress.Parse(ipAddress);
92+
var mask = IPAddress.Parse(maskAddress);
93+
var expectedResult = IPAddress.Parse(expected);
94+
95+
var result = ip.Mask(mask);
96+
97+
await Assert.That(result)
98+
.EqualTo(expectedResult);
99+
}
100+
101+
[Test]
102+
public async Task Mask_BroadcastAddress_ShouldReturnNetworkAddress()
103+
{
104+
var ip = IPAddress.Parse("255.255.255.255");
105+
var mask = IPAddress.Parse("255.255.255.0");
106+
var expected = IPAddress.Parse("255.255.255.0");
107+
108+
var result = ip.Mask(mask);
109+
110+
await Assert.That(result)
111+
.EqualTo(expected);
112+
}
113+
114+
[Test]
115+
public async Task Mask_ZeroAddress_ShouldReturnZeroAddress()
116+
{
117+
var ip = IPAddress.Parse("0.0.0.0");
118+
var mask = IPAddress.Parse("255.255.255.0");
119+
var expected = IPAddress.Parse("0.0.0.0");
120+
121+
var result = ip.Mask(mask);
122+
123+
await Assert.That(result)
124+
.EqualTo(expected);
125+
}
126+
127+
[Test]
128+
public async Task Mask_IPv6_AllZerosMask_ShouldReturnAllZeros()
129+
{
130+
var ip = IPAddress.Parse("fe80::d503:4ee:3882:c586");
131+
var mask = IPAddress.Parse("::");
132+
var expected = IPAddress.Parse("::");
133+
134+
var result = ip.Mask(mask);
135+
136+
await Assert.That(result)
137+
.EqualTo(expected);
138+
}
139+
140+
[Test]
141+
public async Task Mask_IPv6_AllOnesMask_ShouldReturnOriginalAddress()
142+
{
143+
var ip = IPAddress.Parse("fe80::d503:4ee:3882:c586");
144+
var mask = IPAddress.Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
145+
146+
var result = ip.Mask(mask);
147+
148+
await Assert.That(result)
149+
.EqualTo(ip);
150+
}
151+
152+
[Test]
153+
public async Task Mask_IPv6_NonAlignedPrefixMask_ShouldMaskCorrectly()
154+
{
155+
var ip = IPAddress.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
156+
var mask = IPAddress.Parse("ffff:ffff:ffff:ff00::");
157+
var expected = IPAddress.Parse("2001:0db8:85a3::");
158+
159+
var result = ip.Mask(mask);
160+
161+
await Assert.That(result)
162+
.EqualTo(expected);
163+
}
164+
59165
[Test]
60166
[Arguments("192.168.1.100", "192.168.1.1", "255.255.255.0", true)]
61167
[Arguments("192.168.2.100", "192.168.1.1", "255.255.255.0", false)]
@@ -125,10 +231,156 @@ await Assert.That(result)
125231
}
126232
else
127233
{
128-
Action act = () => targetIPAddress.GetLocalAddressOnTheSameNetwork(interfaces);
129-
await Assert.That(act)
234+
await Assert.That(() => targetIPAddress.GetLocalAddressOnTheSameNetwork(interfaces))
130235
.ThrowsExactly<InvalidOperationException>()
131236
.WithMessage("Failed to find local address");
132237
}
133238
}
239+
240+
// ===== New GetLocalAddressOnTheSameNetwork tests =====
241+
242+
private static Mock<UnicastIPAddressInformation> CreateIPv4UnicastMock(string address, string mask)
243+
{
244+
var mock = new Mock<UnicastIPAddressInformation>();
245+
mock.Setup(m => m.Address).Returns(IPAddress.Parse(address));
246+
mock.Setup(m => m.IPv4Mask).Returns(IPAddress.Parse(mask));
247+
return mock;
248+
}
249+
250+
private static Mock<UnicastIPAddressInformation> CreateIPv6UnicastMock(string address, int prefixLength)
251+
{
252+
var mock = new Mock<UnicastIPAddressInformation>();
253+
mock.Setup(m => m.Address).Returns(IPAddress.Parse(address));
254+
mock.Setup(m => m.PrefixLength).Returns(prefixLength);
255+
return mock;
256+
}
257+
258+
private static IPInterfaceProperties CreateMockInterface(params Mock<UnicastIPAddressInformation>[] unicastMocks)
259+
{
260+
var unicastList = unicastMocks.Select(m => m.Object).ToList();
261+
var unicastCollection = new Mock<UnicastIPAddressInformationCollection>();
262+
unicastCollection.Setup(m => m.GetEnumerator()).Returns(unicastList.GetEnumerator);
263+
unicastCollection.Setup(m => m.Count).Returns(unicastList.Count);
264+
265+
var mockProps = new Mock<IPInterfaceProperties>();
266+
mockProps.Setup(m => m.UnicastAddresses).Returns(unicastCollection.Object);
267+
return mockProps.Object;
268+
}
269+
270+
[Test]
271+
public async Task GetLocalAddressOnTheSameNetwork_EmptyInterfaces_ShouldThrow()
272+
{
273+
var target = IPAddress.Parse("192.168.1.1");
274+
var interfaces = Array.Empty<IPInterfaceProperties>();
275+
276+
await Assert.That(() => target.GetLocalAddressOnTheSameNetwork(interfaces))
277+
.ThrowsExactly<InvalidOperationException>()
278+
.WithMessage("Failed to find local address");
279+
}
280+
281+
[Test]
282+
public async Task GetLocalAddressOnTheSameNetwork_InterfaceWithNoUnicastAddresses_ShouldThrow()
283+
{
284+
var target = IPAddress.Parse("192.168.1.1");
285+
286+
var unicastCollection = new Mock<UnicastIPAddressInformationCollection>();
287+
var emptyList = new List<UnicastIPAddressInformation>();
288+
unicastCollection.Setup(m => m.GetEnumerator()).Returns(emptyList.GetEnumerator);
289+
unicastCollection.Setup(m => m.Count).Returns(0);
290+
291+
var mockProps = new Mock<IPInterfaceProperties>();
292+
mockProps.Setup(m => m.UnicastAddresses).Returns(unicastCollection.Object);
293+
294+
await Assert.That(() => target.GetLocalAddressOnTheSameNetwork([mockProps.Object]))
295+
.ThrowsExactly<InvalidOperationException>()
296+
.WithMessage("Failed to find local address");
297+
}
298+
299+
[Test]
300+
public async Task GetLocalAddressOnTheSameNetwork_MultipleIPv4Interfaces_ShouldFindMatchOnSecond()
301+
{
302+
var target = IPAddress.Parse("10.0.0.50");
303+
var iface1 = CreateMockInterface(CreateIPv4UnicastMock("192.168.1.100", "255.255.255.0"));
304+
var iface2 = CreateMockInterface(CreateIPv4UnicastMock("10.0.0.1", "255.255.255.0"));
305+
306+
var result = target.GetLocalAddressOnTheSameNetwork([iface1, iface2]);
307+
308+
await Assert.That(result)
309+
.EqualTo(IPAddress.Parse("10.0.0.1"));
310+
}
311+
312+
[Test]
313+
public async Task GetLocalAddressOnTheSameNetwork_MultipleUnicastAddresses_ShouldFindMatchOnSecond()
314+
{
315+
var target = IPAddress.Parse("10.0.0.50");
316+
var iface = CreateMockInterface(
317+
CreateIPv4UnicastMock("192.168.1.100", "255.255.255.0"),
318+
CreateIPv4UnicastMock("10.0.0.1", "255.255.255.0")
319+
);
320+
321+
var result = target.GetLocalAddressOnTheSameNetwork([iface]);
322+
323+
await Assert.That(result)
324+
.EqualTo(IPAddress.Parse("10.0.0.1"));
325+
}
326+
327+
[Test]
328+
public async Task GetLocalIPv6AddressOnTheSameNetwork_NonByteAlignedPrefix_ShouldMatch()
329+
{
330+
// Prefix /10: fullBytes=1, remainingBits=2
331+
// Both: byte[0]=0x20 (match), remaining mask=0xC0: byte[1] 0x01 & 0xC0 = 0x00 for both → match
332+
var target = IPAddress.Parse("2001:db8::1");
333+
var iface = CreateMockInterface(CreateIPv6UnicastMock("2001:db8::2", 10));
334+
335+
var result = target.GetLocalAddressOnTheSameNetwork([iface]);
336+
337+
await Assert.That(result)
338+
.EqualTo(IPAddress.Parse("2001:db8::2"));
339+
}
340+
341+
[Test]
342+
public async Task GetLocalIPv6AddressOnTheSameNetwork_NonByteAlignedPrefix_FullBytesMismatch_ShouldThrow()
343+
{
344+
// Prefix /10: fullBytes=1, remainingBits=2
345+
// byte[0]: 0x20 vs 0xfe → full byte mismatch → isMatch=false
346+
var target = IPAddress.Parse("2001:db8::1");
347+
var iface = CreateMockInterface(CreateIPv6UnicastMock("fe80::1", 10));
348+
349+
await Assert.That(() => target.GetLocalAddressOnTheSameNetwork([iface]))
350+
.ThrowsExactly<InvalidOperationException>()
351+
.WithMessage("Failed to find local address");
352+
}
353+
354+
[Test]
355+
public async Task GetLocalAddressOnTheSameNetwork_IPv4TargetWithIPv6Interface_ShouldThrow()
356+
{
357+
// IPv4 target: first if (InterNetwork && InterNetwork) → false (interface is IPv6)
358+
// else if (InterNetworkV6) → false (self is IPv4)
359+
// → no match
360+
var target = IPAddress.Parse("192.168.1.1");
361+
var iface = CreateMockInterface(CreateIPv6UnicastMock("fe80::1", 64));
362+
363+
await Assert.That(() => target.GetLocalAddressOnTheSameNetwork([iface]))
364+
.ThrowsExactly<InvalidOperationException>()
365+
.WithMessage("Failed to find local address");
366+
}
367+
368+
[Test]
369+
public async Task GetLocalAddressOnTheSameNetwork_IPv6TargetWithIPv4Interface_ShouldThrow()
370+
{
371+
// IPv6 target with IPv4 interface: enters else-if (self is IPv6),
372+
// compares IPv4 bytes (4) with IPv6 bytes (16) → mismatch on full bytes
373+
var target = IPAddress.Parse("fe80::1");
374+
375+
var mockUnicast = new Mock<UnicastIPAddressInformation>();
376+
mockUnicast.Setup(m => m.Address).Returns(IPAddress.Parse("192.168.1.100"));
377+
mockUnicast.Setup(m => m.IPv4Mask).Returns(IPAddress.Parse("255.255.255.0"));
378+
mockUnicast.Setup(m => m.PrefixLength).Returns(24);
379+
380+
var iface = CreateMockInterface(mockUnicast);
381+
382+
await Assert.That(() => target.GetLocalAddressOnTheSameNetwork([iface]))
383+
.ThrowsExactly<InvalidOperationException>()
384+
.WithMessage("Failed to find local address");
385+
}
134386
}

0 commit comments

Comments
 (0)