refactor-for-testability #3
| @@ -1,6 +1,9 @@ | |||||||
| package peer | package peer | ||||||
|  |  | ||||||
| import "testing" | import ( | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
| func TestStateClient_peerUpdate(t *testing.T) { | func TestStateClient_peerUpdate(t *testing.T) { | ||||||
| 	h := NewPeerStateTestHarness() | 	h := NewPeerStateTestHarness() | ||||||
| @@ -32,3 +35,78 @@ func TestStateClient_onAck_incorrectTraceID(t *testing.T) { | |||||||
| 	assertType[*stateClient](t, h.State) | 	assertType[*stateClient](t, h.State) | ||||||
| 	assertEqual(t, len(h.Sent), 0) | 	assertEqual(t, len(h.Sent), 0) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestStateClient_onAck_direct_downToUp(t *testing.T) { | ||||||
|  | 	h := NewPeerStateTestHarness() | ||||||
|  | 	h.ConfigClientDirect(t) | ||||||
|  |  | ||||||
|  | 	assertEqual(t, len(h.Sent), 2) | ||||||
|  | 	syn := assertType[packetSyn](t, h.Sent[1].Packet) | ||||||
|  | 	h.Sent = h.Sent[:0] | ||||||
|  |  | ||||||
|  | 	assertEqual(t, h.Published.Up, false) | ||||||
|  |  | ||||||
|  | 	ack := controlMsg[packetAck]{ | ||||||
|  | 		Packet: packetAck{TraceID: syn.TraceID}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	h.OnAck(ack) | ||||||
|  |  | ||||||
|  | 	assertEqual(t, len(h.Sent), 0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStateClient_onAck_relayed_sendsProbes(t *testing.T) { | ||||||
|  | 	h := NewPeerStateTestHarness() | ||||||
|  | 	h.ConfigClientRelayed(t) | ||||||
|  |  | ||||||
|  | 	assertEqual(t, len(h.Sent), 2) | ||||||
|  | 	syn := assertType[packetSyn](t, h.Sent[1].Packet) | ||||||
|  | 	h.Sent = h.Sent[:0] | ||||||
|  |  | ||||||
|  | 	assertEqual(t, h.Published.Up, false) | ||||||
|  |  | ||||||
|  | 	ack := controlMsg[packetAck]{ | ||||||
|  | 		Packet: packetAck{TraceID: syn.TraceID}, | ||||||
|  | 	} | ||||||
|  | 	ack.Packet.PossibleAddrs[0] = addrPort4(1, 2, 3, 4, 100) | ||||||
|  | 	ack.Packet.PossibleAddrs[1] = addrPort4(2, 3, 4, 5, 200) | ||||||
|  |  | ||||||
|  | 	h.OnAck(ack) | ||||||
|  |  | ||||||
|  | 	assertEqual(t, len(h.Sent), 2) | ||||||
|  | 	assertType[packetProbe](t, h.Sent[0].Packet) | ||||||
|  | 	assertEqual(t, h.Sent[0].Peer.DirectAddr, ack.Packet.PossibleAddrs[0]) | ||||||
|  | 	assertType[packetProbe](t, h.Sent[1].Packet) | ||||||
|  | 	assertEqual(t, h.Sent[1].Peer.DirectAddr, ack.Packet.PossibleAddrs[1]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStateClient_onPing(t *testing.T) { | ||||||
|  | 	h := NewPeerStateTestHarness() | ||||||
|  | 	h.ConfigClientRelayed(t) | ||||||
|  | 	h.Sent = h.Sent[:0] | ||||||
|  | 	h.OnPingTimer() | ||||||
|  | 	assertEqual(t, len(h.Sent), 1) | ||||||
|  | 	assertType[*stateClient](t, h.State) | ||||||
|  | 	assertType[packetSyn](t, h.Sent[0].Packet) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStateClient_onPing_timeout(t *testing.T) { | ||||||
|  | 	h := NewPeerStateTestHarness() | ||||||
|  | 	h.ConfigClientRelayed(t) | ||||||
|  | 	h.Sent = h.Sent[:0] | ||||||
|  | 	state := assertType[*stateClient](t, h.State) | ||||||
|  | 	state.lastSeen = time.Now().Add(-2 * timeoutInterval) | ||||||
|  | 	state.staged.Up = true | ||||||
|  | 	h.OnPingTimer() | ||||||
|  |  | ||||||
|  | 	newState := assertType[*stateClientInit](t, h.State) | ||||||
|  | 	assertEqual(t, newState.staged.Up, false) | ||||||
|  | 	assertEqual(t, len(h.Sent), 1) | ||||||
|  | 	assertType[packetInit](t, h.Sent[0].Packet) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // probe direct | ||||||
|  |  | ||||||
|  | // probe relayed - no match | ||||||
|  |  | ||||||
|  | // probe relayed - match | ||||||
|   | |||||||
| @@ -47,10 +47,20 @@ func (s *stateClientInit) OnMsg(raw any) peerState { | |||||||
| 		return initPeerState(s.peerData, msg.Peer) | 		return initPeerState(s.peerData, msg.Peer) | ||||||
| 	case controlMsg[packetInit]: | 	case controlMsg[packetInit]: | ||||||
| 		return s.onInit(msg) | 		return s.onInit(msg) | ||||||
|  | 	case controlMsg[packetSyn]: | ||||||
|  | 		s.logf("Unexpected SYN") | ||||||
|  | 		return s | ||||||
|  | 	case controlMsg[packetAck]: | ||||||
|  | 		s.logf("Unexpected ACK") | ||||||
|  | 		return s | ||||||
|  | 	case controlMsg[packetProbe]: | ||||||
|  | 		return s | ||||||
|  | 	case controlMsg[packetLocalDiscovery]: | ||||||
|  | 		return s | ||||||
| 	case pingTimerMsg: | 	case pingTimerMsg: | ||||||
| 		return s.onPing() | 		return s.onPing() | ||||||
| 	default: | 	default: | ||||||
| 		s.logf("Ignoring message: %v", raw) | 		s.logf("Ignoring message: %#v", raw) | ||||||
| 		return s | 		return s | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,8 +26,25 @@ func (s *stateDisconnected) OnMsg(raw any) peerState { | |||||||
| 	switch msg := raw.(type) { | 	switch msg := raw.(type) { | ||||||
| 	case peerUpdateMsg: | 	case peerUpdateMsg: | ||||||
| 		return initPeerState(s.peerData, msg.Peer) | 		return initPeerState(s.peerData, msg.Peer) | ||||||
|  | 	case controlMsg[packetInit]: | ||||||
|  | 		s.logf("Unexpected INIT") | ||||||
|  | 		return s | ||||||
|  | 	case controlMsg[packetSyn]: | ||||||
|  | 		s.logf("Unexpected SYN") | ||||||
|  | 		return s | ||||||
|  | 	case controlMsg[packetAck]: | ||||||
|  | 		s.logf("Unexpected ACK") | ||||||
|  | 		return s | ||||||
|  | 	case controlMsg[packetProbe]: | ||||||
|  | 		s.logf("Unexpected probe") | ||||||
|  | 		return s | ||||||
|  | 	case controlMsg[packetLocalDiscovery]: | ||||||
|  | 		return s | ||||||
|  | 	case pingTimerMsg: | ||||||
|  | 		s.logf("Unexpected ping") | ||||||
|  | 		return s | ||||||
| 	default: | 	default: | ||||||
| 		s.logf("Ignoring message: %v", raw) | 		s.logf("Ignoring message: %#v", raw) | ||||||
| 		return s | 		return s | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -44,6 +44,9 @@ func (s *stateServer) OnMsg(raw any) peerState { | |||||||
| 		return s.onInit(msg) | 		return s.onInit(msg) | ||||||
| 	case controlMsg[packetSyn]: | 	case controlMsg[packetSyn]: | ||||||
| 		return s.onSyn(msg) | 		return s.onSyn(msg) | ||||||
|  | 	case controlMsg[packetAck]: | ||||||
|  | 		s.logf("Unexpected ACK") | ||||||
|  | 		return s | ||||||
| 	case controlMsg[packetProbe]: | 	case controlMsg[packetProbe]: | ||||||
| 		return s.onProbe(msg) | 		return s.onProbe(msg) | ||||||
| 	case controlMsg[packetLocalDiscovery]: | 	case controlMsg[packetLocalDiscovery]: | ||||||
| @@ -51,7 +54,7 @@ func (s *stateServer) OnMsg(raw any) peerState { | |||||||
| 	case pingTimerMsg: | 	case pingTimerMsg: | ||||||
| 		return s.onPingTimer() | 		return s.onPingTimer() | ||||||
| 	default: | 	default: | ||||||
| 		s.logf("Ignoring message: %v", raw) | 		s.logf("Unexpected message: %#v", raw) | ||||||
| 		return s | 		return s | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ type PeerStateControlMsg struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type PeerStateTestHarness struct { | type PeerStateTestHarness struct { | ||||||
|  | 	data      *peerData | ||||||
| 	State     peerState | 	State     peerState | ||||||
| 	Published remotePeer | 	Published remotePeer | ||||||
| 	Sent      []PeerStateControlMsg | 	Sent      []PeerStateControlMsg | ||||||
| @@ -42,6 +43,7 @@ func NewPeerStateTestHarness() *PeerStateTestHarness { | |||||||
| 			MaxWaitCount: 1, | 			MaxWaitCount: 1, | ||||||
| 		}), | 		}), | ||||||
| 	} | 	} | ||||||
|  | 	h.data = state | ||||||
|  |  | ||||||
| 	h.State = enterStateDisconnected(state) | 	h.State = enterStateDisconnected(state) | ||||||
| 	return h | 	return h | ||||||
| @@ -109,6 +111,8 @@ func (h *PeerStateTestHarness) ConfigServer_Relayed(t *testing.T) *stateServer { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (h *PeerStateTestHarness) ConfigClientInit(t *testing.T) *stateClientInit { | func (h *PeerStateTestHarness) ConfigClientInit(t *testing.T) *stateClientInit { | ||||||
|  | 	// Remote IP should be less than local IP. | ||||||
|  | 	h.data.localIP = 4 | ||||||
| 	keys := generateKeys() | 	keys := generateKeys() | ||||||
| 	peer := &m.Peer{ | 	peer := &m.Peer{ | ||||||
| 		PeerIP:     3, | 		PeerIP:     3, | ||||||
| @@ -134,21 +138,14 @@ func (h *PeerStateTestHarness) ConfigClientDirect(t *testing.T) *stateClient { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (h *PeerStateTestHarness) ConfigClientRelayed(t *testing.T) *stateClient { | func (h *PeerStateTestHarness) ConfigClientRelayed(t *testing.T) *stateClient { | ||||||
| 	keys := generateKeys() | 	h.ConfigClientInit(t) | ||||||
|  | 	state := assertType[*stateClientInit](t, h.State) | ||||||
|  | 	state.peer.PublicIP = nil // Force relay. | ||||||
|  |  | ||||||
| 	state := h.State.(*stateDisconnected) | 	init := assertType[packetInit](t, h.Sent[0].Packet) | ||||||
| 	state.remoteIP = 1 | 	h.OnInit(controlMsg[packetInit]{ | ||||||
|  | 		Packet: init, | ||||||
|  | 	}) | ||||||
|  |  | ||||||
| 	peer := &m.Peer{ |  | ||||||
| 		PeerIP:     3, |  | ||||||
| 		Port:       456, |  | ||||||
| 		PubKey:     keys.PubKey, |  | ||||||
| 		PubSignKey: keys.PubSignKey, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// TODO: Fix me. |  | ||||||
|  |  | ||||||
| 	h.PeerUpdate(peer) |  | ||||||
| 	assertEqual(t, h.Published.Up, false) |  | ||||||
| 	return assertType[*stateClient](t, h.State) | 	return assertType[*stateClient](t, h.State) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ func assertType[T any](t *testing.T, obj any) T { | |||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 	x, ok := obj.(T) | 	x, ok := obj.(T) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		t.Fatal("invalid type", obj) | 		t.Fatalf("invalid type: %#v", obj) | ||||||
| 	} | 	} | ||||||
| 	return x | 	return x | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user