//  [go]
func TestHeaderV2(t *testing.T) {
	type section struct {
		name  string
		value []byte
	}
	check := func(name string, h HeaderV2, exp []section) {
		t.Run(name+"_WriteTo", func(t *testing.T) {
			var buf bytes.Buffer
			_, err := h.WriteTo(&buf)
			assert.NoError(t, err)
			for _, s := range exp {
				cmp := make([]byte, len(s.value))
				_, err := io.ReadFull(&buf, cmp)
				assert.NoError(t, err)
				assert.Equal(t, s.value, cmp, s.name)
			}
		})
		t.Run(name+"_Parse", func(t *testing.T) {
			var buf bytes.Buffer
			for _, s := range exp {
				buf.Write(s.value)
			}
			hdr, err := Parse(bufio.NewReader(&buf))
			assert.NoError(t, err)
			assert.IsType(t, &HeaderV2{}, hdr, "Header Type")
			p := hdr.(*HeaderV2)
			assert.Equal(t, h.Command, p.Command, "Command")
			if h.Src != nil {
				assert.NotNil(t, p.Src)
				assert.Equal(t, h.Src.String(), p.Src.String(), "SrcAddr")
			} else {
				assert.Nil(t, p.Src)
			}
			if h.Dest != nil {
				assert.NotNil(t, p.Dest)
				assert.Equal(t, h.Dest.String(), p.Dest.String(), "DestAddr")
			} else {
				assert.Nil(t, p.Dest)
			}
		})
	}

	check("local", HeaderV2{},
		[]section{
			{name: "Signature", value: sigV2},
			{name: "Version", value: []byte{0x20}},   // v2, Local
			{name: "Fam/Proto", value: []byte{0x00}}, // unspec, unspec
			{name: "Length", value: []byte{0, 0}},    // zero length
		},
	)

	check("tcp-ipv4", HeaderV2{
		Command: CmdProxy,
		Src:     &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 80},
		Dest:    &net.TCPAddr{IP: net.ParseIP("192.168.0.2"), Port: 90},
	},
		[]section{
			{name: "Signature", value: sigV2},
			{name: "Version", value: []byte{0x21}},   // v2, Proxy
			{name: "Fam/Proto", value: []byte{0x11}}, // INET, STREAM
			{name: "Length", value: []byte{0, 12}},   // length=12

			{name: "SrcAddr", value: []byte{192, 168, 0, 1}},
			{name: "DestAddr", value: []byte{192, 168, 0, 2}},

			{name: "SrcPort", value: []byte{0, 80}},
			{name: "DstPort", value: []byte{0, 90}},
		},
	)

	check("udp-ipv4", HeaderV2{
		Command: CmdProxy,
		Src:     &net.UDPAddr{IP: net.ParseIP("192.168.0.1"), Port: 80},
		Dest:    &net.UDPAddr{IP: net.ParseIP("192.168.0.2"), Port: 90},
	},
		[]section{
			{name: "Signature", value: sigV2},
			{name: "Version", value: []byte{0x21}},   // v2, Proxy
			{name: "Fam/Proto", value: []byte{0x12}}, // INET, DGRAM
			{name: "Length", value: []byte{0, 12}},   // length=12

			{name: "SrcAddr", value: []byte{192, 168, 0, 1}},
			{name: "DestAddr", value: []byte{192, 168, 0, 2}},

			{name: "SrcPort", value: []byte{0, 80}},
			{name: "DstPort", value: []byte{0, 90}},
		},
	)

	check("udp-ipv6", HeaderV2{
		Command: CmdProxy,
		Src:     &net.UDPAddr{IP: net.ParseIP("2001::1"), Port: 80},
		Dest:    &net.UDPAddr{IP: net.ParseIP("2002::2"), Port: 90},
	},
		[]section{
			{name: "Signature", value: sigV2},
			{name: "Version", value: []byte{0x21}},   // v2, Proxy
			{name: "Fam/Proto", value: []byte{0x22}}, // INET6, DGRAM
			{name: "Length", value: []byte{0, 36}},   // length=36

			{name: "SrcAddr", value: []byte{0x20, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}},
			{name: "DestAddr", value: []byte{0x20, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}},

			{name: "SrcPort", value: []byte{0, 80}},
			{name: "DstPort", value: []byte{0, 90}},
		},
	)

	check("unixstream", HeaderV2{
		Command: CmdProxy,
		Src:     &net.UnixAddr{Net: "unix", Name: "foo"},
		Dest:    &net.UnixAddr{Net: "unix", Name: "bar"},
	},
		[]section{
			{name: "Signature", value: sigV2},
			{name: "Version", value: []byte{0x21}},   // v2, Proxy
			{name: "Fam/Proto", value: []byte{0x31}}, // UNIX, STREAM
			{name: "Length", value: []byte{0, 216}},  // length=216

			{name: "SrcAddr", value: append([]byte("foo"), make([]byte, 105)...)},
			{name: "DestAddr", value: append([]byte("bar"), make([]byte, 105)...)},
		},
	)

	check("unixgram", HeaderV2{
		Command: CmdProxy,
		Src:     &net.UnixAddr{Net: "unixgram", Name: "foo"},
		Dest:    &net.UnixAddr{Net: "unixgram", Name: "bar"},
	},
		[]section{
			{name: "Signature", value: sigV2},
			{name: "Version", value: []byte{0x21}},   // v2, Proxy
			{name: "Fam/Proto", value: []byte{0x32}}, // UNIX, DGRAM
			{name: "Length", value: []byte{0, 216}},  // length=216

			{name: "SrcAddr", value: append([]byte("foo"), make([]byte, 105)...)},
			{name: "DestAddr", value: append([]byte("bar"), make([]byte, 105)...)},
		},
	)

}