chore: import upstream snapshot with attribution
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/zzet/gortex/internal/daemon"
|
||||
)
|
||||
|
||||
// withFastDialRetry shortens the retry cadence so tests don't sleep on the
|
||||
// production window, restoring the originals on cleanup.
|
||||
func withFastDialRetry(t *testing.T, window time.Duration) {
|
||||
t.Helper()
|
||||
origDial, origWin, origInt := dialDaemon, proxyDialRetryWindow, proxyDialRetryInterval
|
||||
proxyDialRetryWindow = window
|
||||
proxyDialRetryInterval = time.Millisecond
|
||||
t.Cleanup(func() {
|
||||
dialDaemon = origDial
|
||||
proxyDialRetryWindow = origWin
|
||||
proxyDialRetryInterval = origInt
|
||||
})
|
||||
}
|
||||
|
||||
func TestDialDaemonWithRetry_SuccessFirstTry(t *testing.T) {
|
||||
withFastDialRetry(t, time.Second)
|
||||
var calls int32
|
||||
dialDaemon = func(daemon.Handshake) (*daemon.Client, error) {
|
||||
atomic.AddInt32(&calls, 1)
|
||||
return &daemon.Client{}, nil
|
||||
}
|
||||
client, recoverable, err := dialDaemonWithRetry(context.Background(), daemon.Handshake{})
|
||||
if client == nil || recoverable || err != nil {
|
||||
t.Fatalf("want (client, false, nil), got (%v, %v, %v)", client, recoverable, err)
|
||||
}
|
||||
if got := atomic.LoadInt32(&calls); got != 1 {
|
||||
t.Fatalf("want 1 dial, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialDaemonWithRetry_RetriesThenConnects(t *testing.T) {
|
||||
withFastDialRetry(t, 5*time.Second)
|
||||
var calls int32
|
||||
dialDaemon = func(daemon.Handshake) (*daemon.Client, error) {
|
||||
// Fail the first two attempts as "unavailable" (socket up but accept()
|
||||
// starved by warmup), then connect — the exact race the retry exists
|
||||
// to ride out.
|
||||
if atomic.AddInt32(&calls, 1) < 3 {
|
||||
return nil, fmt.Errorf("%w: connection refused", daemon.ErrDaemonUnavailable)
|
||||
}
|
||||
return &daemon.Client{}, nil
|
||||
}
|
||||
client, recoverable, err := dialDaemonWithRetry(context.Background(), daemon.Handshake{})
|
||||
if client == nil || recoverable || err != nil {
|
||||
t.Fatalf("want (client, false, nil), got (%v, %v, %v)", client, recoverable, err)
|
||||
}
|
||||
if got := atomic.LoadInt32(&calls); got != 3 {
|
||||
t.Fatalf("want 3 dials, got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialDaemonWithRetry_WindowExpiry(t *testing.T) {
|
||||
// A tiny window so the loop concedes almost immediately.
|
||||
withFastDialRetry(t, 5*time.Millisecond)
|
||||
dialDaemon = func(daemon.Handshake) (*daemon.Client, error) {
|
||||
return nil, fmt.Errorf("%w: connection refused", daemon.ErrDaemonUnavailable)
|
||||
}
|
||||
client, recoverable, err := dialDaemonWithRetry(context.Background(), daemon.Handshake{})
|
||||
if client != nil || !recoverable {
|
||||
t.Fatalf("want (nil, true, err), got (%v, %v, %v)", client, recoverable, err)
|
||||
}
|
||||
if !errors.Is(err, daemon.ErrDaemonUnavailable) {
|
||||
t.Fatalf("want ErrDaemonUnavailable, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialDaemonWithRetry_ProtocolMismatchConcedesImmediately(t *testing.T) {
|
||||
withFastDialRetry(t, 5*time.Second)
|
||||
var calls int32
|
||||
dialDaemon = func(daemon.Handshake) (*daemon.Client, error) {
|
||||
atomic.AddInt32(&calls, 1)
|
||||
return nil, fmt.Errorf("%w: stale", daemon.ErrProtocolVersionMismatch)
|
||||
}
|
||||
client, recoverable, err := dialDaemonWithRetry(context.Background(), daemon.Handshake{})
|
||||
if client != nil || !recoverable || !errors.Is(err, daemon.ErrProtocolVersionMismatch) {
|
||||
t.Fatalf("want (nil, true, mismatch), got (%v, %v, %v)", client, recoverable, err)
|
||||
}
|
||||
// A mismatch never resolves by waiting — must not retry.
|
||||
if got := atomic.LoadInt32(&calls); got != 1 {
|
||||
t.Fatalf("want 1 dial (no retry on mismatch), got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialDaemonWithRetry_NonRecoverableSurfaced(t *testing.T) {
|
||||
withFastDialRetry(t, 5*time.Second)
|
||||
var calls int32
|
||||
boom := errors.New("permission denied")
|
||||
dialDaemon = func(daemon.Handshake) (*daemon.Client, error) {
|
||||
atomic.AddInt32(&calls, 1)
|
||||
return nil, boom
|
||||
}
|
||||
client, recoverable, err := dialDaemonWithRetry(context.Background(), daemon.Handshake{})
|
||||
if client != nil || recoverable || !errors.Is(err, boom) {
|
||||
t.Fatalf("want (nil, false, boom), got (%v, %v, %v)", client, recoverable, err)
|
||||
}
|
||||
if got := atomic.LoadInt32(&calls); got != 1 {
|
||||
t.Fatalf("want 1 dial (no retry on a real error), got %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialDaemonWithRetry_ContextCancelConcedes(t *testing.T) {
|
||||
withFastDialRetry(t, 10*time.Second)
|
||||
dialDaemon = func(daemon.Handshake) (*daemon.Client, error) {
|
||||
return nil, fmt.Errorf("%w: connection refused", daemon.ErrDaemonUnavailable)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
client, recoverable, err := dialDaemonWithRetry(ctx, daemon.Handshake{})
|
||||
if client != nil || !recoverable {
|
||||
t.Fatalf("want (nil, true, err) on cancel, got (%v, %v, %v)", client, recoverable, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user