integration: harden ACL test ergonomics

tsic.Curl returned ("", nil) when curl exited 0 with a zero-byte body —
the usual signature of a mid-stream reset — so EventuallyWithT could
not retry. Return an error on empty body instead.

Replace the 56 inline curl + assert.Len(13) blocks with
assertCurlDockerHostname so the empty-body fix benefits every callsite
without further touch-ups.

Gate ACL waits on actual filter visibility: snapshotClientFilters +
waitForClientFilterChange ensure the new PacketFilter has reached the
client before assertions fire; SyncOption + WithPreBarrier feeds a
server-side policy-loaded check into WaitForTailscaleSyncPerUser.

Move advertise-routes mutation out of EventuallyWithT in route_test
(cmd/hi/README forbids retrying state-mutating calls). Pace the
TestNodeOnlineStatus outer loop with a Ticker, not a Sleep.
This commit is contained in:
Kristoffer Dalby 2026-05-13 13:19:49 +00:00
parent 78fd6efb38
commit dfcc96d808
6 changed files with 321 additions and 300 deletions

View file

@ -784,10 +784,42 @@ func (s *Scenario) WaitForTailscaleSync() error {
return err
}
// WaitForTailscaleSyncPerUser blocks execution until each TailscaleClient has the expected
// number of peers for its user. This is useful for policies like autogroup:self where nodes
// only see same-user peers, not all nodes in the network.
func (s *Scenario) WaitForTailscaleSyncPerUser(timeout, retryInterval time.Duration) error {
// SyncOption configures WaitForTailscaleSyncPerUser.
type SyncOption func(*syncOptions)
type syncOptions struct {
preBarrier func(context.Context) error
}
// WithPreBarrier runs a precondition check before per-user peer-count
// waits begin, sharing the outer timeout via context. Use to gate on
// a server-side signal (e.g. policy compile) that the peer-count
// alone cannot observe.
func WithPreBarrier(barrier func(context.Context) error) SyncOption {
return func(o *syncOptions) { o.preBarrier = barrier }
}
// WaitForTailscaleSyncPerUser blocks until each TailscaleClient has
// the expected per-user peer count (necessary for policies like
// autogroup:self where cross-user peers are invisible).
func (s *Scenario) WaitForTailscaleSyncPerUser(timeout, retryInterval time.Duration, opts ...SyncOption) error {
options := syncOptions{}
for _, opt := range opts {
opt(&options)
}
if options.preBarrier != nil {
barrierCtx, cancel := context.WithTimeout(context.Background(), timeout)
err := options.preBarrier(barrierCtx)
cancel()
if err != nil {
return fmt.Errorf("pre-barrier: %w", err)
}
}
var allErrors []error
for _, user := range s.users {