package main import ( "errors" "net" "path/filepath" "strings" "testing" "time" "unicode/utf8" "github.com/carlosframework/platform/internal/geoip" ) func TestOpenGeoIPIsOptionalAndNeverFatal(t *testing.T) { if openGeoIP("") != nil { t.Fatal("unset CONSOLE_GEOIP_DB must mean no locator") } if openGeoIP(filepath.Join(t.TempDir(), "missing.mmdb")) != nil { t.Fatal("an unopenable file must mean no locator, not a crash") } } func TestAppRegionsSwitchParses(t *testing.T) { for v, want := range map[string]bool{"on": true, "ON": true, " on ": true, "": false, "off": false, "true": false} { if got := appRegionsSwitch(v); got != want { t.Errorf("appRegionsSwitch(%q) = %v, want %v", v, got, want) } } } // OpenMMDB's error can carry decoded database contents over many lines, so // the console logs only its first line, capped at 200 bytes. func TestFirstLineCapped(t *testing.T) { cases := []struct { name string err error want string }{ {"single line", errors.New("open x: no such file"), "open x: no such file"}, {"multi-line keeps the first", errors.New("bad metadata\nsecret decoded body\nmore"), "bad metadata"}, {"crlf", errors.New("first\r\nsecond"), "first"}, {"leading newline is an empty first line", errors.New("\nbody"), ""}, {"exactly 200 bytes", errors.New(strings.Repeat("a", 200)), strings.Repeat("a", 200)}, {"long line is cut at 200 bytes", errors.New(strings.Repeat("a", 500)), strings.Repeat("a", 200)}, {"long first line with more lines", errors.New(strings.Repeat("b", 300) + "\nrest"), strings.Repeat("b", 200)}, {"nil", nil, ""}, } for _, c := range cases { if got := firstLineCapped(c.err); got != c.want { t.Errorf("%s: firstLineCapped = %q, want %q", c.name, got, c.want) } } // A cut must not leave half of a multi-byte rune. got := firstLineCapped(errors.New(strings.Repeat("é", 150))) if len(got) > 200 || !utf8.ValidString(got) { t.Errorf("cut through a rune: len %d valid %v", len(got), utf8.ValidString(got)) } } // Codex R10-1: the override obeys D1. The repo's fixtures are GeoIP2 (they // never expire), so the tick is driven with a clock far in the future and // checked to leave them alone, and the Expired rule itself is geoip's own, // tested there. func TestGeoIPOverrideExpiryTick(t *testing.T) { sw := openGeoIPAt("../../internal/geo/testdata/GeoIP2-City-Test.mmdb", time.Now()) if sw == nil { t.Fatal("fixture did not open") } if geoIPExpiryTick(sw, "x", time.Now().Add(400*24*time.Hour)) { t.Fatal("a GeoIP2 database must never expire") } if _, ok := sw.Locate(net.ParseIP("81.2.69.142")); !ok { t.Fatal("the override stopped answering") } sw.Swap(nil) if !geoIPExpiryTick(sw, "x", time.Now()) { t.Fatal("an empty reader leaves nothing to watch") } } func TestGeoIPOverrideIsClosedAtExpiryAndRefusedWhenExpired(t *testing.T) { const fx = "../../internal/geo/testdata/GeoIP2-City-Test.mmdb" sw := openGeoIPAt(fx, time.Now()) geoipExpired = func(string, time.Time, time.Time) bool { return true } t.Cleanup(func() { geoipExpired = geoip.Expired }) if !geoIPExpiryTick(sw, fx, time.Now()) { t.Fatal("an expired override was not closed") } if _, ok := sw.Locate(net.ParseIP("81.2.69.142")); ok { t.Fatal("region guesses still use an expired override") } if openGeoIPAt(fx, time.Now()) != nil { t.Fatal("an already-expired override was opened at startup") } } func TestConsoleGeoReaderNeverYieldsATypedNil(t *testing.T) { sw := consoleGeoReader(filepath.Join(t.TempDir(), "missing.mmdb")) if sw != nil { t.Fatal("an unusable override must give no reader") } if geoLocator(sw) != nil { t.Fatal("a nil reader must be an untyped nil Locator") } }