Skip to content

Commit 005a1d6

Browse files
committed
Fix flaky tests and restructure
1 parent 3340659 commit 005a1d6

File tree

1 file changed

+56
-79
lines changed

1 file changed

+56
-79
lines changed

internal/client/udp_conn_test.go

Lines changed: 56 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,6 @@ import (
1414
)
1515

1616
func TestUDPConn(t *testing.T) {
17-
staleTime := func() time.Time {
18-
return time.Now().Add(-(bindingRefreshInterval + 1*time.Minute))
19-
}
20-
21-
staleNonceMsg := func() *stun.Message {
22-
return stun.MustBuild(
23-
stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse),
24-
stun.CodeStaleNonce,
25-
stun.NewNonce("new-nonce-123"),
26-
)
27-
}
28-
2917
makeConn := func(client *mockClient, bm *bindingManager) UDPConn {
3018
return UDPConn{
3119
allocation: allocation{
@@ -36,77 +24,68 @@ func TestUDPConn(t *testing.T) {
3624
}
3725
}
3826

39-
t.Run("maybeBind()", func(t *testing.T) {
40-
makeClient := func(shouldSucceed bool) *mockClient {
41-
return &mockClient{
42-
performTransaction: func(msg *stun.Message, addr net.Addr, dontWait bool) (TransactionResult, error) {
43-
if shouldSucceed {
44-
return TransactionResult{Msg: new(stun.Message)}, nil
45-
}
27+
staleNonceMsg := func() *stun.Message {
28+
return stun.MustBuild(
29+
stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse),
30+
stun.CodeStaleNonce,
31+
stun.NewNonce("new-nonce-123"),
32+
)
33+
}
4634

47-
return TransactionResult{Msg: staleNonceMsg()}, nil
48-
},
49-
}
35+
t.Run("maybeBind()", func(t *testing.T) {
36+
tests := []struct {
37+
name string
38+
initialState bindingState
39+
interimState bindingState
40+
finalState bindingState
41+
pastInterval bool
42+
shouldSucceed bool
43+
}{
44+
{"idle -> request -> ready", bindingStateIdle, bindingStateRequest, bindingStateReady, false, true},
45+
{"idle -> request -> failed", bindingStateIdle, bindingStateRequest, bindingStateFailed, false, false},
46+
{"ready (stale) -> refresh -> ready", bindingStateReady, bindingStateRefresh, bindingStateReady, true, true},
47+
{"ready (stale) -> refresh -> failed", bindingStateReady, bindingStateRefresh, bindingStateFailed, true, false},
48+
49+
// Noop cases:
50+
{"ready (noop)", bindingStateReady, bindingStateReady, bindingStateReady, false, true},
51+
{"request (noop)", bindingStateRequest, bindingStateRequest, bindingStateRequest, false, true},
52+
{"refresh (noop)", bindingStateRefresh, bindingStateRefresh, bindingStateRefresh, false, true},
53+
{"failed (noop)", bindingStateFailed, bindingStateFailed, bindingStateFailed, false, true},
5054
}
5155

52-
t.Run("state transitions", func(t *testing.T) {
53-
bm := newBindingManager()
54-
bound := bm.create(&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234})
55-
conn := makeConn(makeClient(true), bm)
56+
for _, tt := range tests {
57+
t.Run(tt.name, func(t *testing.T) {
58+
unblock := make(chan struct{})
5659

57-
t.Run("idle to request", func(t *testing.T) {
58-
bound.setState(bindingStateIdle)
59-
conn.maybeBind(bound)
60-
assert.Equal(t, bindingStateRequest, bound.state(), "should be request")
61-
})
60+
bm := newBindingManager()
61+
bound := bm.create(&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234})
62+
conn := makeConn(&mockClient{
63+
performTransaction: func(msg *stun.Message, addr net.Addr, dontWait bool) (TransactionResult, error) {
64+
<-unblock
65+
if tt.shouldSucceed {
66+
return TransactionResult{Msg: new(stun.Message)}, nil
67+
}
68+
69+
return TransactionResult{Msg: staleNonceMsg()}, nil
70+
},
71+
}, bm)
72+
73+
bound.setState(tt.initialState)
74+
if tt.pastInterval {
75+
bound.setRefreshedAt(time.Now().Add(-(bindingRefreshInterval + 1*time.Minute)))
76+
}
6277

63-
t.Run("ready to refresh (past interval)", func(t *testing.T) {
64-
bound.setState(bindingStateReady)
65-
bound.setRefreshedAt(staleTime())
6678
conn.maybeBind(bound)
67-
assert.Equal(t, bindingStateRefresh, bound.state(), "should be refresh")
68-
})
79+
assert.Equal(t, tt.interimState, bound.state())
6980

70-
t.Run("ready to ready (not past interval)", func(t *testing.T) {
71-
bound.setState(bindingStateReady)
72-
bound.setRefreshedAt(time.Now())
73-
conn.maybeBind(bound)
74-
assert.Equal(t, bindingStateReady, bound.state(), "should remain ready")
81+
// Release barrier so inner bind() can move forward.
82+
close(unblock)
83+
84+
assert.Eventually(t, func() bool {
85+
return bound.state() == tt.finalState
86+
}, 5*time.Second, 10*time.Millisecond)
7587
})
76-
})
77-
78-
t.Run("outcomes", func(t *testing.T) {
79-
tests := []struct {
80-
name string
81-
initialState bindingState
82-
pastInterval bool
83-
shouldSucceed bool
84-
expectState bindingState
85-
}{
86-
{"idle bind success", bindingStateIdle, false, true, bindingStateReady},
87-
{"idle bind fail", bindingStateIdle, false, false, bindingStateFailed},
88-
{"refresh success", bindingStateReady, true, true, bindingStateReady},
89-
{"refresh fail", bindingStateReady, true, false, bindingStateFailed},
90-
}
91-
92-
for _, tt := range tests {
93-
t.Run(tt.name, func(t *testing.T) {
94-
bm := newBindingManager()
95-
bound := bm.create(&net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234})
96-
conn := makeConn(makeClient(tt.shouldSucceed), bm)
97-
98-
bound.setState(tt.initialState)
99-
if tt.pastInterval {
100-
bound.setRefreshedAt(staleTime())
101-
}
102-
103-
conn.maybeBind(bound)
104-
assert.Eventually(t, func() bool {
105-
return bound.state() == tt.expectState
106-
}, 5*time.Second, 10*time.Millisecond)
107-
})
108-
}
109-
})
88+
}
11089
})
11190

11291
t.Run("bind()", func(t *testing.T) {
@@ -124,16 +103,14 @@ func TestUDPConn(t *testing.T) {
124103
},
125104
expectErr: errFake,
126105
expectBindingDeleted: true,
127-
expectNonceChanged: false,
128106
},
129107
{
130108
name: "ErrorResponse with CodeStaleNonce triggers nonce update",
131109
transactionFn: func(*stun.Message, net.Addr, bool) (TransactionResult, error) {
132110
return TransactionResult{Msg: staleNonceMsg()}, nil
133111
},
134-
expectErr: errTryAgain,
135-
expectBindingDeleted: false,
136-
expectNonceChanged: true,
112+
expectErr: errTryAgain,
113+
expectNonceChanged: true,
137114
},
138115
}
139116

0 commit comments

Comments
 (0)