Files
tunnel/internal/server/cooldown_test.go
T
TZGyn ebae420ab1 Add the native Go client and finish M3 hardening
The new cmd/tunnel client keeps the API URL and tunnel identity in
~/.config/tunnel/config.json (0600), provisions the key/CSR and
certificate, terminates TLS, proxies to local routes, reloads routes
without a restart, and runs as a background service. `tunnel import`
takes over an existing official CLI profile, preserving its tunnel ID,
token, key and certificate.

Server hardening: per-IP and per-tunnel rate limits for creates and
binds, a CA cool-down after repeated ACME failures, a startup sweep for
stale _acme-challenge records, admin metrics, a 60-second half-close
timeout, and separate prioritized control / ordered data bridge queues
(with `end` ordered after the final bytes). The dashboard shows the new
metrics.
2026-10-26 00:32:31 +08:00

65 lines
1.5 KiB
Go

package server
import (
"errors"
"fmt"
"testing"
"time"
"golang.org/x/crypto/acme"
)
func TestCooldown(t *testing.T) {
now := time.Now()
c := newCooldown()
c.now = func() time.Time { return now }
for i := 0; i < 4; i++ {
if d := c.failure(); d != 0 {
t.Fatalf("failure %d paused for %v, want no pause", i+1, d)
}
}
if d := c.failure(); d != time.Minute {
t.Fatalf("fifth failure pause = %v, want 1m", d)
}
if d := c.remaining(); d != time.Minute {
t.Fatalf("remaining = %v, want 1m", d)
}
if d := c.failure(); d != 2*time.Minute {
t.Fatalf("sixth failure pause = %v, want 2m", d)
}
for i := 0; i < 20; i++ {
c.failure()
}
if d := c.failure(); d != time.Hour {
t.Fatalf("pause cap = %v, want 1h", d)
}
now = now.Add(2 * time.Hour)
if d := c.remaining(); d != 0 {
t.Fatalf("remaining after expiry = %v", d)
}
c.success()
if c.failures != 0 {
t.Fatalf("success left failures = %d", c.failures)
}
}
func TestCAFailureClassification(t *testing.T) {
cases := []struct {
err error
want bool
}{
{errors.New("dns: create _acme-challenge: nope"), false},
{errors.New("acme: authorization for x is invalid"), false},
{errors.New("acme: authorize order: context deadline exceeded"), true},
{fmt.Errorf("acme: authorize order: %w", &acme.Error{StatusCode: 503}), true},
{fmt.Errorf("acme: authorize order: %w", &acme.Error{StatusCode: 429}), true},
}
for _, c := range cases {
if got := caFailure(c.err); got != c.want {
t.Errorf("caFailure(%v) = %v, want %v", c.err, got, c.want)
}
}
}