packages feed

valiant-0.1.0.0: test/Valiant/Binary/InetSpec.hs

module Valiant.Binary.InetSpec (spec) where

import Data.ByteString qualified as BS
import Valiant.Binary.Inet (PgInet (..), ipv4, ipv4Host, ipv6, ipv6Host, inetToText)
import PgWire.Binary.Types (PgDecode (..), PgEncode (..))
import Test.Hspec

spec :: Spec
spec = do
  describe "inet IPv4 round-trip" $ do
    it "loopback /8" $ do
      let v = ipv4 127 0 0 1 8
      pgDecode (pgEncode v) `shouldBe` Right v

    it "private network /24" $ do
      let v = ipv4 192 168 1 0 24
      pgDecode (pgEncode v) `shouldBe` Right v

    it "host /32" $ do
      let v = ipv4Host 10 0 0 1
      pgDecode (pgEncode v) `shouldBe` Right v

    it "default route /0" $ do
      let v = ipv4 0 0 0 0 0
      pgDecode (pgEncode v) `shouldBe` Right v

  describe "inet IPv6 round-trip" $ do
    it "loopback ::1/128" $ do
      let addr = BS.pack [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]
          v = ipv6Host addr
      pgDecode (pgEncode v) `shouldBe` Right v

    it "network /64" $ do
      let addr = BS.pack [0x20,0x01,0x0d,0xb8,0,0,0,0,0,0,0,0,0,0,0,0]
          v = ipv6 addr 64
      pgDecode (pgEncode v) `shouldBe` Right v

    it "all zeros ::/0" $ do
      let addr = BS.replicate 16 0
          v = ipv6 addr 0
      pgDecode (pgEncode v) `shouldBe` Right v

  describe "inet encode size" $ do
    it "IPv4 produces 8 bytes (4 header + 4 address)" $ do
      BS.length (pgEncode (ipv4 192 168 1 0 24)) `shouldBe` 8

    it "IPv6 produces 20 bytes (4 header + 16 address)" $ do
      let addr = BS.replicate 16 0
      BS.length (pgEncode (ipv6 addr 64)) `shouldBe` 20

  describe "inet decode errors" $ do
    it "fails on too-short input" $ do
      let result = pgDecode (BS.pack [2, 24, 0]) :: Either String PgInet
      result `shouldSatisfy` isLeft

    it "fails on empty input" $ do
      let result = pgDecode BS.empty :: Either String PgInet
      result `shouldSatisfy` isLeft

    it "fails on address family mismatch" $ do
      -- Claim IPv6 family (3) but only 4 address bytes
      let result = pgDecode (BS.pack [3, 32, 0, 4, 10, 0, 0, 1]) :: Either String PgInet
      result `shouldSatisfy` isLeft

  describe "inet cidr flag" $ do
    it "preserves cidr flag on round-trip" $ do
      let v = (ipv4 192 168 1 0 24) { inetIsCidr = True }
      pgDecode (pgEncode v) `shouldBe` Right v

  describe "inetToText" $ do
    it "renders IPv4 in CIDR notation" $
      inetToText (ipv4 192 168 1 0 24) `shouldBe` "192.168.1.0/24"

    it "renders IPv4 host" $
      inetToText (ipv4Host 10 0 0 1) `shouldBe` "10.0.0.1/32"

    it "renders IPv6 loopback" $ do
      let addr = BS.pack [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]
      inetToText (ipv6Host addr) `shouldBe` "::1/128"

    it "renders IPv6 all zeros" $ do
      let addr = BS.replicate 16 0
      inetToText (ipv6 addr 0) `shouldBe` "::/0"

isLeft :: Either a b -> Bool
isLeft (Left _) = True
isLeft _ = False