mirror of
https://github.com/lukaszraczylo/kportal.git
synced 2026-07-12 07:41:42 +00:00
test: make MockStatusUpdater thread-safe for concurrent callbacks
The mock recorded calls into unprotected slices while being invoked concurrently from the test goroutine and from the health-checker callback registered by Manager.startWorker (and the watchdog hung callback). go test -race -count=20 reliably tripped the race detector on TestStartWorker_HealthCallback_StatusChange. Wrap the slice writes in a sync.Mutex. Read-side code in tests reads only after manager.Stop() drains the background goroutines (Stop's wg.Wait establishes a happens-before edge), so direct reads remain race-free.
This commit is contained in:
@@ -2,6 +2,7 @@ package forward
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -172,12 +173,18 @@ func TestManager_getResourceForPort(t *testing.T) {
|
|||||||
assert.Equal(t, "unknown", resource)
|
assert.Equal(t, "unknown", resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockStatusUpdater is a mock implementation of StatusUpdater
|
// MockStatusUpdater is a mock implementation of StatusUpdater. Methods are
|
||||||
|
// invoked concurrently from the test goroutine and from the health-checker /
|
||||||
|
// watchdog goroutines registered by Manager.startWorker, so the recorded
|
||||||
|
// slices are guarded by mu. Tests inspect the slices only after Manager.Stop
|
||||||
|
// has drained the background goroutines (Stop's wg.Wait establishes a
|
||||||
|
// happens-before edge) so the read side does not need to hold mu.
|
||||||
type MockStatusUpdater struct {
|
type MockStatusUpdater struct {
|
||||||
updates []StatusUpdate
|
updates []StatusUpdate
|
||||||
adds []ForwardAdd
|
adds []ForwardAdd
|
||||||
removes []string
|
removes []string
|
||||||
errorSets []ErrorSet
|
errorSets []ErrorSet
|
||||||
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type StatusUpdate struct {
|
type StatusUpdate struct {
|
||||||
@@ -196,18 +203,26 @@ type ErrorSet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStatusUpdater) UpdateStatus(id string, status string) {
|
func (m *MockStatusUpdater) UpdateStatus(id string, status string) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
m.updates = append(m.updates, StatusUpdate{ID: id, Status: status})
|
m.updates = append(m.updates, StatusUpdate{ID: id, Status: status})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStatusUpdater) AddForward(id string, fwd *config.Forward) {
|
func (m *MockStatusUpdater) AddForward(id string, fwd *config.Forward) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
m.adds = append(m.adds, ForwardAdd{ID: id, Fwd: fwd})
|
m.adds = append(m.adds, ForwardAdd{ID: id, Fwd: fwd})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStatusUpdater) Remove(id string) {
|
func (m *MockStatusUpdater) Remove(id string) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
m.removes = append(m.removes, id)
|
m.removes = append(m.removes, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStatusUpdater) SetError(id, msg string) {
|
func (m *MockStatusUpdater) SetError(id, msg string) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
m.errorSets = append(m.errorSets, ErrorSet{ID: id, Msg: msg})
|
m.errorSets = append(m.errorSets, ErrorSet{ID: id, Msg: msg})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user