carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "net/http"
6 "testing"
7 "time"
8)
9
10// The GeoIP publisher keeps the Releases handle it was built with. After
11// ConfigureClaimRuntime that must be the mutation boundary, never the raw
12// bucket, or every publish, off, sweep, expiry and feed write skips it.
13func TestClaimGeoipPublisherUsesTheBoundary(t *testing.T) {
14 cx := newClaimCase(t)
15 s := cx.srv
16 if s.geoip == nil {
17 t.Fatal("the enabled console has no GeoIP publisher")
18 }
19 if any(s.geoip.store) == any(cx.rawStore) {
20 t.Fatal("the GeoIP publisher writes to the raw bucket")
21 }
22 if any(s.geoip.store) != any(s.cfg.Releases) {
23 t.Fatalf("the GeoIP publisher holds %T, not the installed Config.Releases %T", s.geoip.store, s.cfg.Releases)
24 }
25}
26
27// The three operator routes and the app route, each through the real mux with
28// a real bearer, so a fixture's nonApp or read reason is checked against a
29// request that reached its handler (an unauthenticated 401 reaches nothing).
30func TestClaimGeoipRoutesReachTheirHandlers(t *testing.T) {
31 cx := newClaimCase(t)
32 for _, c := range []struct {
33 method, path, body string
34 want []int
35 }{
36 {http.MethodGet, "/api/cli/system/geoip", "", []int{http.StatusOK}},
37 {http.MethodDelete, "/api/cli/system/geoip", "", []int{http.StatusOK}},
38 // Not a database: staged, then refused by verification (the job is
39 // asynchronous), or refused outright. Either way the handler ran.
40 {http.MethodPut, "/api/cli/system/geoip", "not a database", []int{http.StatusAccepted, http.StatusUnprocessableEntity}},
41 } {
42 code, _, _ := rawReq(t, c.method, cx.ts.URL+c.path, "", c.body)
43 if code != http.StatusUnauthorized {
44 t.Errorf("%s %s without a token: %d, want 401", c.method, c.path, code)
45 }
46 code, _, out := rawReq(t, c.method, cx.ts.URL+c.path, cx.bearer, c.body)
47 ok := false
48 for _, w := range c.want {
49 ok = ok || code == w
50 }
51 if !ok {
52 t.Errorf("%s %s as the owner: %d %s, want one of %v", c.method, c.path, code, out, c.want)
53 }
54 }
55 cx.quiesce()
56 // The app route needs an instance-family caller, not an operator token
57 // alone, but it must still be a registered, authenticated route.
58 code, _, _ := rawReq(t, http.MethodGet, cx.ts.URL+"/api/cli/apps/bab/titogo/geo?ip=8.8.8.8", "", "")
59 if code != http.StatusUnauthorized {
60 t.Errorf("GET apps geo without a token: %d, want 401", code)
61 }
62 code, _, out := rawReq(t, http.MethodGet, cx.ts.URL+"/api/cli/apps/bab/titogo/geo?ip=8.8.8.8", cx.bearer, "")
63 if code == http.StatusUnauthorized || code == http.StatusNotFound {
64 t.Errorf("GET apps geo as the owner: %d %s, want the handler's own answer", code, out)
65 }
66}
67
68// StartGeoipJobs is a recorded worker: it blocks until its context ends and
69// joins every goroutine it started, so a bounded call leaves nothing running.
70func TestClaimGeoipWorkerJoinsItsGoroutines(t *testing.T) {
71 cx := newClaimCase(t)
72 ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
73 defer cancel()
74 done := make(chan struct{})
75 go func() {
76 cx.srv.RunClaimWorker(ctx, "StartGeoipJobs")
77 close(done)
78 }()
79 select {
80 case <-done:
81 case <-time.After(10 * time.Second):
82 t.Fatal("StartGeoipJobs did not return after its context ended")
83 }
84 cx.quiesce()
85}
86
87// No bucket, no publisher: GeoIP is off and the worker returns at once.
88func TestClaimGeoipWorkerOffReturnsImmediately(t *testing.T) {
89 cx := newClaimCase(t)
90 cx.srv.geoip = nil
91 done := make(chan struct{})
92 go func() {
93 cx.srv.RunClaimWorker(context.Background(), "StartGeoipJobs")
94 close(done)
95 }()
96 select {
97 case <-done:
98 case <-time.After(5 * time.Second):
99 t.Fatal("StartGeoipJobs with no publisher blocked")
100 }
101}
102