carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "errors"
5 "net"
6 "path/filepath"
7 "strings"
8 "testing"
9 "time"
10 "unicode/utf8"
11
12 "github.com/carlosframework/platform/internal/geoip"
13)
14
15func TestOpenGeoIPIsOptionalAndNeverFatal(t *testing.T) {
16 if openGeoIP("") != nil {
17 t.Fatal("unset CONSOLE_GEOIP_DB must mean no locator")
18 }
19 if openGeoIP(filepath.Join(t.TempDir(), "missing.mmdb")) != nil {
20 t.Fatal("an unopenable file must mean no locator, not a crash")
21 }
22}
23
24func TestAppRegionsSwitchParses(t *testing.T) {
25 for v, want := range map[string]bool{"on": true, "ON": true, " on ": true, "": false, "off": false, "true": false} {
26 if got := appRegionsSwitch(v); got != want {
27 t.Errorf("appRegionsSwitch(%q) = %v, want %v", v, got, want)
28 }
29 }
30}
31
32// OpenMMDB's error can carry decoded database contents over many lines, so
33// the console logs only its first line, capped at 200 bytes.
34func TestFirstLineCapped(t *testing.T) {
35 cases := []struct {
36 name string
37 err error
38 want string
39 }{
40 {"single line", errors.New("open x: no such file"), "open x: no such file"},
41 {"multi-line keeps the first", errors.New("bad metadata\nsecret decoded body\nmore"), "bad metadata"},
42 {"crlf", errors.New("first\r\nsecond"), "first"},
43 {"leading newline is an empty first line", errors.New("\nbody"), ""},
44 {"exactly 200 bytes", errors.New(strings.Repeat("a", 200)), strings.Repeat("a", 200)},
45 {"long line is cut at 200 bytes", errors.New(strings.Repeat("a", 500)), strings.Repeat("a", 200)},
46 {"long first line with more lines", errors.New(strings.Repeat("b", 300) + "\nrest"), strings.Repeat("b", 200)},
47 {"nil", nil, ""},
48 }
49 for _, c := range cases {
50 if got := firstLineCapped(c.err); got != c.want {
51 t.Errorf("%s: firstLineCapped = %q, want %q", c.name, got, c.want)
52 }
53 }
54 // A cut must not leave half of a multi-byte rune.
55 got := firstLineCapped(errors.New(strings.Repeat("é", 150)))
56 if len(got) > 200 || !utf8.ValidString(got) {
57 t.Errorf("cut through a rune: len %d valid %v", len(got), utf8.ValidString(got))
58 }
59}
60
61// Codex R10-1: the override obeys D1. The repo's fixtures are GeoIP2 (they
62// never expire), so the tick is driven with a clock far in the future and
63// checked to leave them alone, and the Expired rule itself is geoip's own,
64// tested there.
65func TestGeoIPOverrideExpiryTick(t *testing.T) {
66 sw := openGeoIPAt("../../internal/geo/testdata/GeoIP2-City-Test.mmdb", time.Now())
67 if sw == nil {
68 t.Fatal("fixture did not open")
69 }
70 if geoIPExpiryTick(sw, "x", time.Now().Add(400*24*time.Hour)) {
71 t.Fatal("a GeoIP2 database must never expire")
72 }
73 if _, ok := sw.Locate(net.ParseIP("81.2.69.142")); !ok {
74 t.Fatal("the override stopped answering")
75 }
76 sw.Swap(nil)
77 if !geoIPExpiryTick(sw, "x", time.Now()) {
78 t.Fatal("an empty reader leaves nothing to watch")
79 }
80}
81
82func TestGeoIPOverrideIsClosedAtExpiryAndRefusedWhenExpired(t *testing.T) {
83 const fx = "../../internal/geo/testdata/GeoIP2-City-Test.mmdb"
84 sw := openGeoIPAt(fx, time.Now())
85 geoipExpired = func(string, time.Time, time.Time) bool { return true }
86 t.Cleanup(func() { geoipExpired = geoip.Expired })
87 if !geoIPExpiryTick(sw, fx, time.Now()) {
88 t.Fatal("an expired override was not closed")
89 }
90 if _, ok := sw.Locate(net.ParseIP("81.2.69.142")); ok {
91 t.Fatal("region guesses still use an expired override")
92 }
93 if openGeoIPAt(fx, time.Now()) != nil {
94 t.Fatal("an already-expired override was opened at startup")
95 }
96}
97
98func TestConsoleGeoReaderNeverYieldsATypedNil(t *testing.T) {
99 sw := consoleGeoReader(filepath.Join(t.TempDir(), "missing.mmdb"))
100 if sw != nil {
101 t.Fatal("an unusable override must give no reader")
102 }
103 if geoLocator(sw) != nil {
104 t.Fatal("a nil reader must be an untyped nil Locator")
105 }
106}
107