Files
wehub-resource-sync 75f3dd141c
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CI and Release / lint-frontend (push) Has been cancelled
CI and Release / dockerfile-scan (push) Has been cancelled
CI and Release / test-frontend (push) Has been cancelled
CI and Release / lint-verification-agent (push) Has been cancelled
CI and Release / test-verification-agent (push) Has been cancelled
CI and Release / e2e-verification-agent (push) Has been cancelled
CI and Release / test-backend (push) Has been cancelled
CI and Release / build-dev-image (push) Has been cancelled
CI and Release / push-dev-image (push) Has been cancelled
CI and Release / build-image (push) Has been cancelled
CI and Release / build-verification-image (push) Has been cancelled
CI and Release / determine-version (push) Has been cancelled
CI and Release / push-image (push) Has been cancelled
CI and Release / push-verification-image (12) (push) Has been cancelled
CI and Release / lint-backend (push) Has been cancelled
CI and Release / push-verification-image (17) (push) Has been cancelled
CI and Release / push-verification-image (18) (push) Has been cancelled
CI and Release / release (push) Has been cancelled
CI and Release / publish-helm-chart (push) Has been cancelled
CI and Release / push-verification-image (13) (push) Has been cancelled
CI and Release / push-verification-image (14) (push) Has been cancelled
CI and Release / push-verification-image (15) (push) Has been cancelled
CI and Release / push-verification-image (16) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:11:07 +08:00

140 lines
3.0 KiB
Go

package runner
import (
"context"
"errors"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"databasus-verification-agent/internal/testutil"
)
type fakeDiskProbe struct {
mu sync.Mutex
values []int64
errs []error
calls int
}
func (p *fakeDiskProbe) GetDiskUsageBytes(context.Context) (int64, error) {
p.mu.Lock()
defer p.mu.Unlock()
i := p.calls
if i >= len(p.values) {
i = len(p.values) - 1
}
p.calls++
var err error
if p.calls-1 < len(p.errs) {
err = p.errs[p.calls-1]
}
return p.values[i], err
}
func newTestDiskWatcher(probe diskUsageProber, ceilingBytes int64, onLimitExceeded func()) *diskWatcher {
w := newDiskWatcher(probe, ceilingBytes, onLimitExceeded, testutil.DiscardLogger())
w.interval = time.Millisecond
return w
}
func Test_DiskWatcher_WhenUsageReachesCeiling_FiresOnce(t *testing.T) {
var fired atomic.Int32
done := make(chan struct{})
w := newTestDiskWatcher(
&fakeDiskProbe{values: []int64{100}}, 100,
func() { fired.Add(1); close(done) },
)
go w.run(t.Context())
select {
case <-done:
case <-time.After(2 * time.Second):
t.Fatal("watcher never fired despite usage at ceiling")
}
time.Sleep(20 * time.Millisecond)
assert.Equal(t, int32(1), fired.Load(), "onLimitExceeded must fire exactly once")
}
func Test_DiskWatcher_WhenUsageBelowCeiling_NeverFires(t *testing.T) {
var fired atomic.Int32
ctx, cancel := context.WithCancel(t.Context())
w := newTestDiskWatcher(
&fakeDiskProbe{values: []int64{50}}, 100,
func() { fired.Add(1) },
)
stopped := make(chan struct{})
go func() { w.run(ctx); close(stopped) }()
time.Sleep(30 * time.Millisecond)
cancel()
select {
case <-stopped:
case <-time.After(2 * time.Second):
t.Fatal("watcher did not stop on context cancel")
}
assert.Equal(t, int32(0), fired.Load(), "must not fire while under the ceiling")
}
func Test_DiskWatcher_WhenProbeErrorsThenRecovers_DoesNotTripOnError(t *testing.T) {
var fired atomic.Int32
done := make(chan struct{})
probe := &fakeDiskProbe{
values: []int64{0, 0, 200},
errs: []error{errors.New("daemon busy"), errors.New("daemon busy"), nil},
}
w := newTestDiskWatcher(probe, 100, func() { fired.Add(1); close(done) })
go w.run(t.Context())
select {
case <-done:
case <-time.After(2 * time.Second):
t.Fatal("watcher never fired after probe recovered above ceiling")
}
assert.Equal(t, int32(1), fired.Load(),
"a probe error must not trip the watcher nor stop the loop")
}
func Test_DiskWatcher_WhenContextCancelled_StopsWithoutFiring(t *testing.T) {
var fired atomic.Int32
ctx, cancel := context.WithCancel(t.Context())
w := newTestDiskWatcher(
&fakeDiskProbe{values: []int64{10}}, 100,
func() { fired.Add(1) },
)
stopped := make(chan struct{})
go func() { w.run(ctx); close(stopped) }()
cancel()
select {
case <-stopped:
case <-time.After(2 * time.Second):
t.Fatal("watcher did not return after context cancel")
}
require.Equal(t, int32(0), fired.Load())
}