repos
/ orchard main

orchard

mirror

Every site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.

blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics

2.0 KB · 67 lines · Go Raw History
 1package tools
 2
 3import (
 4	"context"
 5	"net/http"
 6	"net/http/httptest"
 7	"sync/atomic"
 8	"testing"
 9	"time"
10)
11
12// The point of the pool: once it is spent, no request leaves. A limit that
13// still sends the call and throws the answer away is not a limit.
14func TestSpentPoolSendsNothing(t *testing.T) {
15	var hits int32
16	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
17		atomic.AddInt32(&hits, 1)
18		_, _ = w.Write([]byte("ok"))
19	}))
20	defer srv.Close()
21
22	d := &Deps{HTTP: srv.Client(), Public: srv.Client(), Now: time.Now,
23		Guard: NewGuard(time.Minute), Budgets: NewBudgets()}
24	// A tiny pool on this test's host.
25	host := hostOf(srv.URL)
26	budgets[host] = budget{gap: 0, minute: 2, hour: 2, day: 2}
27	defer delete(budgets, host)
28
29	for i := 0; i < 2; i++ {
30		if _, err := get(context.Background(), d, srv.URL, ""); err != nil {
31			t.Fatalf("call %d refused early: %v", i+1, err)
32		}
33	}
34	before := atomic.LoadInt32(&hits)
35	for i := 0; i < 5; i++ {
36		if _, err := get(context.Background(), d, srv.URL, ""); err == nil {
37			t.Fatal("a spent pool still allowed a call")
38		}
39	}
40	if got := atomic.LoadInt32(&hits); got != before {
41		t.Errorf("%d requests went out after the pool was spent", got-before)
42	}
43}
44
45// And a host that refused is not called again either, which is the poking.
46func TestBoxedHostSendsNothing(t *testing.T) {
47	var hits int32
48	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
49		atomic.AddInt32(&hits, 1)
50		w.WriteHeader(http.StatusAccepted)
51	}))
52	defer srv.Close()
53
54	d := &Deps{HTTP: srv.Client(), Public: srv.Client(), Now: time.Now,
55		Guard: NewGuard(time.Minute), Budgets: NewBudgets()}
56	if _, err := get(context.Background(), d, srv.URL, ""); err == nil {
57		t.Fatal("a 202 was not treated as a refusal")
58	}
59	after := atomic.LoadInt32(&hits)
60	for i := 0; i < 5; i++ {
61		_, _ = get(context.Background(), d, srv.URL, "")
62	}
63	if got := atomic.LoadInt32(&hits); got != after {
64		t.Errorf("%d requests were sent to a host that already refused", got-after)
65	}
66}