//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

import ContainerizationExtras
import ContainerizationOS
import Testing

@testable import ContainerizationNetlink

struct NetlinkSessionTest {
    @Test func testNetworkLinkDown() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name, truncated response with no attributes (not needed at present).
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no RT attrs
            )
        )

        // Link‑down request – 32‑byte payload, no attributes.
        let expectedDownRequest =
            "2000000010000500000000000cc00cc0"  // Netlink header (16 B)
            + "110000000200000000000000ffffffff"  // struct ifinfomsg (16 B) – no RT attrs
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000200000001000050000000000"  // nlmsg_err payload (16 B)
                    + "0c000000"  // first 4 B of echoed header
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.linkSet(interface: "eth0", up: false)

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedDownRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkLinkUp() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0x0cc0_0cc0

        // Lookup interface by name, truncated response with no attributes (not needed at present).
        let expectedLookupRequest =
            "340000001200010000000000c00cc00c"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "200000001000000000000000c00cc00c"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Network up for interface.
        let expectedUpRequest =
            "280000001000050000000000c00cc00c"  // Netlink header (16 B)
            + "110000000200000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "0800040000050000"  // RT attr: IFLA_MTU = 1280 (8 B)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "240000000200000100000000c00cc00c"  // Netlink header (16 B)
                    + "00000000200000001000050000000000"  // nlmsg_err payload (16 B)
                    + "11000000"  // 1st 4 B of echoed offending header
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.linkSet(interface: "eth0", up: true, mtu: 1280)

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedUpRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkLinkUpLoopback() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup loopback interface
        let expectedLookupRequest =
            "3000000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d0009000000080003006c6f0000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“lo”)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100010000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Link up request for loopback, 32‑byte payload and no attributes
        let expectedUpRequest =
            "2000000010000500000000000cc00cc0"  // Netlink header (16 B)
            + "110000000100000001000000ffffffff"  // struct ifinfomsg (16 B) – no RT attrs
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000200000001000050000000000"  // nlmsg_err payload (16 B)
                    + "0c000000"  // first 4 B of echoed offending header
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.linkSet(interface: "lo", up: true)

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedUpRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkLinkGetEth0() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0x1234_5678

        // Lookup interface by name, truncated response with three attributes.
        let expectedLookupRequest =
            "34000000120001000000000078563412"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "48000000100000000000000078563412"  // Netlink header (16 B)
                    + "00000100020000004300010000000000"  // struct ifinfomsg (16 B)
                    + "090003006574683000000000"  // IFLA_IFNAME (“eth0”) attr (12 B)
                    + "08000d00e8030000"  // IFLA_MTU = 1000 attr (8 B)
                    + "0500100006000000"  // attr type 0x0010 (8 B)
                    + "0a000100825524c244030000"  // IFLA_ADDRESS = 82:55:24:c2:44:03 (12 B)
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        let links = try session.linkGet(interface: "eth0")

        #expect(mockSocket.requests.count == 1)
        #expect(mockSocket.responseIndex == 1)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        try #require(links.count == 1)

        #expect(links[0].interfaceIndex == 2)
        #expect(links[0].interfaceFlags == 0x0001_0043)
        #expect(links[0].interfaceType == 1)
        #expect(links[0].isEthernet)
        #expect(!links[0].isLoopback)
        #expect(links[0].address == [0x82, 0x55, 0x24, 0xc2, 0x44, 0x03])
        try #require(links[0].attrDatas.count == 4)
        #expect(links[0].attrDatas[0].attribute.type == 0x0003)
        #expect(links[0].attrDatas[0].attribute.len == 0x0009)
        #expect(links[0].attrDatas[0].data == [0x65, 0x74, 0x68, 0x30, 0x00])
        #expect(links[0].attrDatas[1].attribute.type == 0x000d)
        #expect(links[0].attrDatas[1].attribute.len == 0x0008)
        #expect(links[0].attrDatas[1].data == [0xe8, 0x03, 0x00, 0x00])
        #expect(links[0].attrDatas[2].attribute.type == 0x0010)
        #expect(links[0].attrDatas[2].attribute.len == 0x0005)
        #expect(links[0].attrDatas[2].data == [0x06])
    }

    @Test func testNetworkLinkGet() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0x8765_4321

        // Lookup all interfaces, responses with only the interface name attribute.
        let expectedLookupRequest =
            "28000000120001030000000021436587"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d0009000000"  // RT attr: IFLA_EXT_MASK (8 B)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "28000000100002000000000021436587"  // Netlink header (16 B)
                    + "00000403010000004900010000000000"  // struct ifinfomsg (16 B)
                    + "070003006c6f0000"  // IFLA_IFNAME “lo” (8 B, padded)
            )
        )
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2c000000100002000000000021436587"  // Netlink header (16 B)
                    + "00000003040000008000000000000000"  // struct ifinfomsg (16 B)
                    + "0a00030074756e6c30000000"  // IFLA_IFNAME “tunl0” attr (12 B, padded)
            )
        )
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "14000000030002000000000021436587"  // Netlink header (16 B) – NLMSG_DONE
                    + "00000000"  // 4-byte payload
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        let links = try session.linkGet()

        #expect(mockSocket.requests.count == 1)
        #expect(mockSocket.responseIndex == 3)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        try #require(links.count == 2)

        #expect(links[0].interfaceIndex == 1)
        try #require(links[0].attrDatas.count == 1)
        #expect(links[0].attrDatas[0].attribute.type == 0x0003)
        #expect(links[0].attrDatas[0].attribute.len == 0x0007)
        #expect(links[0].attrDatas[0].data == [0x6c, 0x6f, 0x00])

        #expect(links[1].interfaceIndex == 4)
        try #require(links[1].attrDatas.count == 1)
        #expect(links[1].attrDatas[0].attribute.type == 0x0003)
        #expect(links[1].attrDatas[0].attribute.len == 0x000a)
        #expect(links[1].attrDatas[0].data == [0x74, 0x75, 0x6e, 0x6c, 0x30, 0x00])
    }

    @Test func testNetworkAddressAdd() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name, truncated response with no attributes (not needed at present).
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Network down for interface.
        let expectedAddRequest =
            "2800000014000506000000000cc00cc0"  // Netlink header (16 B)
            + "0218000002000000"  // ifaddrmsg (8 B): AF_INET, /24, ifindex 2
            + "08000200c0a840fa"  // RT attr: IFA_LOCAL    192.168.64.250
            + "08000100c0a840fa"  // RT attr: IFA_ADDRESS  192.168.64.250
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000280000001400050600000000"  // nlmsg_err payload (16 B)
                    + "1f000000"  // first 4 B of echoed offending header
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.addressAdd(interface: "eth0", ipv4Address: try CIDRv4("192.168.64.250/24"))

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkAddressAddIPv6() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name, truncated response with no attributes (not needed at present).
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME ("eth0")
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Add IPv6 address to interface.
        let expectedAddRequest =
            "2c00000014000506000000000cc00cc0"  // Netlink header (16 B): len=44
            + "0a40820002000000"  // ifaddrmsg (8 B): AF_INET6, /64, flags=PERMANENT|NODAD, ifindex 2
            + "14000100fd000000000000000000000000000001"  // RT attr: IFA_ADDRESS  fd00::1
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "0000000040000000140005060000000000000000"  // nlmsg_err payload (20 B)
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.addressAdd(interface: "eth0", ipv6Address: try CIDRv6("fd00::1/64"))

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkRouteAddIpLink() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name, truncated response with no attributes (not needed at present).
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME ("eth0")
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Add link route.
        let expectedAddRequest =
            "3400000018000506000000000cc00cc0"  // Netlink header (16 B)
            + "02180000fe02fd0100000000"  // struct rtmsg (12 B): AF_INET, dst/24,
            //   table=RT_TABLE_MAIN (0xfe), proto=RTPROT_KERNEL (0x02),
            //   scope=RT_SCOPE_LINK (0xfd), type=RTN_UNICAST (0x01)
            + "08000100c0a84000"  // RTA_DST     192.168.64.0
            + "08000700c0a84003"  // RTA_PREFSRC 192.168.64.3
            + "0800040002000000"  // RTA_OIF     ifindex 2 (eth0)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000280000001400050600000000"  // nlmsg_err payload (16 B)
                    + "1f000000"  // first 4 B of echoed offending header
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.routeAdd(
            interface: "eth0",
            dstIpv4Addr: try CIDRv4("192.168.64.0/24"),
            srcIpv4Addr: try IPv4Address("192.168.64.3")
        )

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkRouteAddIpLinkWithoutSrc() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name, truncated response with no attributes (not needed at present).
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME ("eth0")
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Add link route without RTA_PREFSRC.
        let expectedAddRequest =
            "2c00000018000506000000000cc00cc0"  // Netlink header (16 B)
            + "02180000fe02fd0100000000"  // struct rtmsg (12 B): AF_INET, dst/24,
            //   table=RT_TABLE_MAIN (0xfe), proto=RTPROT_KERNEL (0x02),
            //   scope=RT_SCOPE_LINK (0xfd), type=RTN_UNICAST (0x01)
            + "08000100c0a84000"  // RTA_DST     192.168.64.0
            + "0800040002000000"  // RTA_OIF     ifindex 2 (eth0)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000280000001400050600000000"  // nlmsg_err payload (16 B)
                    + "1f000000"  // first 4 B of echoed offending header
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.routeAdd(
            interface: "eth0",
            dstIpv4Addr: try CIDRv4("192.168.64.0/24"),
            srcIpv4Addr: nil
        )

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkRouteAddIpv6Link() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name.
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME ("eth0")
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Add IPv6 link route with source.
        let expectedAddRequest =
            "4c00000018000506000000000cc00cc0"  // Netlink header (16 B): len=76
            + "0a400000fe04fd0100000000"  // struct rtmsg (12 B): AF_INET6, dst/64,
            //   table=MAIN(0xfe), proto=STATIC(0x04), scope=LINK(0xfd), type=UNICAST(0x01)
            + "14000100fd000000000000000000000000000000"  // RTA_DST     fd00::
            + "14000700fd000000000000000000000000000001"  // RTA_PREFSRC fd00::1
            + "0800040002000000"  // RTA_OIF     ifindex 2 (eth0)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000280000001400050600000000"  // nlmsg_err payload (16 B)
                    + "1f000000"
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.routeAdd(
            interface: "eth0",
            dstIpv6Addr: try CIDRv6("fd00::/64"),
            srcIpv6Addr: try IPv6Address("fd00::1")
        )

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkRouteAddIpv6LinkWithoutSrc() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name.
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME ("eth0")
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Add IPv6 link route without source.
        let expectedAddRequest =
            "3800000018000506000000000cc00cc0"  // Netlink header (16 B): len=56
            + "0a400000fe04fd0100000000"  // struct rtmsg (12 B): AF_INET6, dst/64
            + "14000100fd000000000000000000000000000000"  // RTA_DST     fd00::
            + "0800040002000000"  // RTA_OIF     ifindex 2 (eth0)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000280000001400050600000000"  // nlmsg_err payload (16 B)
                    + "1f000000"
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.routeAdd(
            interface: "eth0",
            dstIpv6Addr: try CIDRv6("fd00::/64"),
            srcIpv6Addr: nil
        )

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkRouteAddDefaultIpv6() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0xc00c_c00c

        // Lookup interface by name.
        let expectedLookupRequest =
            "3400000012000100000000000cc00cc0"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d00090000000c0003006574683000000000"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME ("eth0")
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2000000010000000000000000cc00cc0"  // Netlink header (16 B)
                    + "00000100020000004310010000000000"  // struct ifinfomsg (16 B) – no attributes
            )
        )

        // Add default IPv6 route via gateway.
        let expectedAddRequest =
            "3800000018000506000000000cc00cc0"  // Netlink header (16 B): len=56
            + "0a000000fe03000100000000"  // struct rtmsg (12 B): AF_INET6, dst/0,
            //   table=MAIN(0xfe), proto=BOOT(0x03), scope=UNIVERSE(0x00), type=UNICAST(0x01)
            + "14000500fd000000000000000000000000000001"  // RTA_GATEWAY fd00::1
            + "0800040002000000"  // RTA_OIF     ifindex 2 (eth0)
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "2400000002000001000000000cc00cc0"  // Netlink header (16 B)
                    + "00000000280000001400050600000000"  // nlmsg_err payload (16 B)
                    + "1f000000"
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        try session.routeAddDefault(
            interface: "eth0",
            ipv6Gateway: try IPv6Address("fd00::1")
        )

        #expect(mockSocket.requests.count == 2)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())
        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]
        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())
    }

    @Test func testNetworkLinkGetMultipleMessagesInSingleBuffer() throws {
        let mockSocket = try MockNetlinkSocket()
        mockSocket.pid = 0x8765_4321

        // Lookup all interfaces, with multiple messages packed into a single buffer.
        // This tests the fix for parsing multiple netlink messages that arrive in one recv() call.
        let expectedLookupRequest =
            "28000000120001030000000021436587"  // Netlink header (16 B)
            + "110000000000000001000000ffffffff"  // struct ifinfomsg (16 B)
            + "08001d0009000000"  // RT attr: IFLA_EXT_MASK (8 B)

        // Pack three messages into a single response buffer:
        //
        // Message 1: loopback interface with one attribute
        let msg1 =
            "28000000100002000000000021436587"  // Netlink header (16 B), len=40
            + "00000403010000004900010000000000"  // struct ifinfomsg (16 B)
            + "070003006c6f0000"  // IFLA_IFNAME "lo" (8 B, padded)

        // Message 2: tunl0 interface with one attribute
        let msg2 =
            "2c000000100002000000000021436587"  // Netlink header (16 B), len=44
            + "00000003040000008000000000000000"  // struct ifinfomsg (16 B)
            + "0a00030074756e6c30000000"  // IFLA_IFNAME "tunl0" attr (12 B, padded)

        // Message 3: eth0 interface with two attributes
        let msg3 =
            "34000000100002000000000021436587"  // Netlink header (16 B), len=52
            + "00000100020000004300010000000000"  // struct ifinfomsg (16 B)
            + "090003006574683000000000"  // IFLA_IFNAME "eth0" attr (12 B)
            + "08000d00e8030000"  // IFLA_MTU = 1000 attr (8 B)

        // Combine all three messages into a single buffer
        mockSocket.responses.append([UInt8](hex: msg1 + msg2 + msg3))

        // Final NLMSG_DONE message in separate buffer
        mockSocket.responses.append(
            [UInt8](
                hex:
                    "14000000030002000000000021436587"  // Netlink header (16 B) – NLMSG_DONE
                    + "00000000"  // 4-byte payload
            )
        )

        let session = NetlinkSession(socket: mockSocket)
        let links = try session.linkGet()

        #expect(mockSocket.requests.count == 1)
        #expect(mockSocket.responseIndex == 2)
        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]
        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())

        // Verify we got all three interfaces
        try #require(links.count == 3)

        // Verify loopback interface
        #expect(links[0].interfaceIndex == 1)
        try #require(links[0].attrDatas.count == 1)
        #expect(links[0].attrDatas[0].attribute.type == 0x0003)
        #expect(links[0].attrDatas[0].attribute.len == 0x0007)
        #expect(links[0].attrDatas[0].data == [0x6c, 0x6f, 0x00])

        // Verify tunl0 interface
        #expect(links[1].interfaceIndex == 4)
        try #require(links[1].attrDatas.count == 1)
        #expect(links[1].attrDatas[0].attribute.type == 0x0003)
        #expect(links[1].attrDatas[0].attribute.len == 0x000a)
        #expect(links[1].attrDatas[0].data == [0x74, 0x75, 0x6e, 0x6c, 0x30, 0x00])

        // Verify eth0 interface
        #expect(links[2].interfaceIndex == 2)
        try #require(links[2].attrDatas.count == 2)
        #expect(links[2].attrDatas[0].attribute.type == 0x0003)
        #expect(links[2].attrDatas[0].attribute.len == 0x0009)
        #expect(links[2].attrDatas[0].data == [0x65, 0x74, 0x68, 0x30, 0x00])
        #expect(links[2].attrDatas[1].attribute.type == 0x000d)
        #expect(links[2].attrDatas[1].attribute.len == 0x0008)
        #expect(links[2].attrDatas[1].data == [0xe8, 0x03, 0x00, 0x00])
    }
}

extension Array where Element == UInt8 {
    /// Initializes `[UInt8]` from an even-length hex string
    init(hex: String) {
        self = stride(from: 0, to: hex.count, by: 2).compactMap {
            UInt8(
                hex[hex.index(hex.startIndex, offsetBy: $0)...]
                    .prefix(2), radix: 16)
        }
    }
}
