| new file mode 100644 |
| index 000000000..d2e098da2 |
| --- /dev/null |
| +++ b/internal/console/regionguess_test.go |
| @@ -0,0 +1,170 @@ |
| +package console |
| + |
| +import ( |
| + "bytes" |
| + "math/rand/v2" |
| + "net" |
| + "strings" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/geo" |
| +) |
| + |
| +type fakeLocator map[string]geo.Point |
| + |
| +func (f fakeLocator) Locate(ip net.IP) (geo.Point, bool) { |
| + p, ok := f[ip.String()] |
| + return p, ok |
| +} |
| + |
| +func allowAll(string) bool { return true } |
| + |
| +func cand(code, aws string, eligible bool) regionCandidate { |
| + return regionCandidate{Code: code, Home: code == "", AWSRegion: aws, |
| + Reachable: true, Hosting: true, KeyOK: eligible, Eligible: eligible} |
| +} |
| + |
| +var ( |
| + cHome = cand("", "eu-west-1", true) |
| + cUK = cand("uk", "eu-west-2", true) |
| + cAU = cand("au", "ap-southeast-2", true) |
| +) |
| + |
| +func TestGuessRegionOrder(t *testing.T) { |
| + loc := fakeLocator{"81.2.69.142": {Lat: -33.9, Lon: 151.2}} |
| + cands := []regionCandidate{cHome, cUK, cAU} |
| + cases := []struct { |
| + name string |
| + in guessInput |
| + want guessResult |
| + }{ |
| + {"timezone first", guessInput{TZ: "Europe/London", IP: net.ParseIP("81.2.69.142")}, guessResult{"uk", "timezone"}}, |
| + {"UTC falls to IP", guessInput{TZ: "Etc/UTC", IP: net.ParseIP("81.2.69.142")}, guessResult{"au", "ip"}}, |
| + {"nothing maps", guessInput{TZ: "Etc/UTC"}, guessResult{"", "default"}}, |
| + } |
| + for _, c := range cases { |
| + if got, ok := guessRegion(cands, allowAll, c.in, loc); !ok || got != c.want { |
| + t.Errorf("%s: got (%+v, %v), want %+v", c.name, got, ok, c.want) |
| + } |
| + } |
| + got, _ := guessRegion([]regionCandidate{cHome, cUK, cand("au", "ap-southeast-2", false)}, allowAll, guessInput{TZ: "Australia/Sydney"}, nil) |
| + if got.Code == "au" { |
| + t.Fatal("picked an ineligible region") |
| + } |
| +} |
| + |
| +// spec §6.4, codex R3-4. |
| +func TestGuessRegionNeverReturnsADisallowedHome(t *testing.T) { |
| + noPointHome := cand("", "", true) |
| + onlyUK := func(code string) bool { return instanceRegionAllowed([]string{"uk"}, code) } |
| + cands := []regionCandidate{noPointHome, cUK} |
| + if got, ok := guessRegion(cands, onlyUK, guessInput{TZ: "Europe/London"}, nil); !ok || got.Code != "uk" { |
| + t.Fatalf("mapped hint = (%+v, %v), want uk", got, ok) |
| + } |
| + if got, ok := guessRegion(cands, onlyUK, guessInput{TZ: "Etc/UTC"}, nil); ok { |
| + t.Fatalf("unmappable hint = %+v, want no guess", got) |
| + } |
| + if got, ok := guessRegion(homeOnlyCandidates(), onlyUK, guessInput{TZ: "Europe/London"}, nil); ok { |
| + t.Fatalf("edges-read failure = %+v, want no guess", got) |
| + } |
| +} |
| + |
| +// The invariant over generated MANIFESTS, using the door's own predicate |
| +// on the normalized code (spec §16, codex plan-R1 10): |
| +// |
| +// 1. every non-null guess passes instanceRegionAllowed and is an eligible |
| +// candidate; |
| +// 2. a guess exists EXACTLY when the order of §6.4 can reach one: the home |
| +// is a filtered candidate, or the timezone maps and some filtered |
| +// candidate has a point. |
| +func TestGuessRegionInvariantProperty(t *testing.T) { |
| + codes := []string{"", "uk", "au", "us", "jp"} |
| + listable := []string{"", "ie", "uk", "au", "us", "jp"} |
| + regions := []string{"eu-west-1", "eu-west-2", "ap-southeast-2", "us-east-1", "", "xx-nowhere-1"} |
| + zones := []string{"Europe/London", "Australia/Sydney", "America/New_York", "Etc/UTC", "", "Asia/Tokyo"} |
| + r := rand.New(rand.NewPCG(1, 2)) |
| + for i := 0; i < 20000; i++ { |
| + var cands []regionCandidate |
| + for _, code := range codes { |
| + if code != "" && r.IntN(3) == 0 { |
| + continue |
| + } |
| + cands = append(cands, cand(code, regions[r.IntN(len(regions))], code == "" || r.IntN(4) != 0)) |
| + } |
| + var list []string |
| + for _, c := range listable { |
| + if r.IntN(3) == 0 { |
| + list = append(list, c) |
| + } |
| + } |
| + allows := func(code string) bool { return instanceRegionAllowed(list, code) } |
| + tz := zones[r.IntN(len(zones))] |
| + got, ok := guessRegion(cands, allows, guessInput{TZ: tz}, nil) |
| + |
| + homeIn, anyPoint := false, false |
| + for _, c := range cands { |
| + if !c.Eligible || !allows(c.Code) { |
| + continue |
| + } |
| + homeIn = homeIn || c.Home |
| + if _, has := geo.LookupAWSRegion(c.AWSRegion); has { |
| + anyPoint = true |
| + } |
| + } |
| + _, tzMaps := geo.ZonePoint(tz) |
| + want := homeIn || (tzMaps && anyPoint) |
| + if ok != want { |
| + t.Fatalf("case %d: guess existence %v, want %v (list %v, tz %q, cands %+v)", i, ok, want, list, tz, cands) |
| + } |
| + if !ok { |
| + continue |
| + } |
| + if !allows(got.Code) { |
| + t.Fatalf("case %d: guess %+v fails the manifest %v", i, got, list) |
| + } |
| + if c, found := findCandidate(cands, got.Code); !found || !c.Eligible { |
| + t.Fatalf("case %d: guess %+v is not an eligible candidate", i, got) |
| + } |
| + } |
| +} |
| + |
| +// Review Focus 4's core: a malformed hint is absent. |
| +func TestGuessRegionToleratesGarbageHints(t *testing.T) { |
| + got, ok := guessRegion([]regionCandidate{cHome, cUK}, allowAll, guessInput{TZ: strings.Repeat("a", 10<<10)}, fakeLocator{}) |
| + if !ok || got != (guessResult{"", "default"}) { |
| + t.Fatalf("garbage tz = (%+v, %v), want the home default", got, ok) |
| + } |
| +} |
| + |
| +// codex plan-R3 4: a region whose boxes only collectively have every fact |
| +// is never guessed, however near the caller is (Task 10's splitCapability). |
| +func TestGuessRegionSkipsSplitCapabilityRegions(t *testing.T) { |
| + for name, boxes := range splitCapability { |
| + cands := regionCandidates(views(t, homeBox, boxes[0], boxes[1]), "ie", testKeyFP) |
| + got, ok := guessRegion(cands, allowAll, guessInput{TZ: "Europe/London"}, nil) |
| + if !ok || got.Code != "" { |
| + t.Errorf("%s: guess = %+v %v, want the home", name, got, ok) |
| + } |
| + } |
| +} |
| + |
| +// Controller ruling O7: the home has no "left out" diagnostic. A home |
| +// without a point yields "default" (spec §6.3), so the log line would |
| +// misdescribe it. A coded region without a point still logs. |
| +func TestRegionCandidatesHomeWithNoPointLogsNoO7(t *testing.T) { |
| + var buf bytes.Buffer |
| + restore := captureLog(&buf) |
| + defer restore() |
| + noPointSaid.Delete("") |
| + noPointSaid.Delete("odd") |
| + noRegionHome := edgeJSON("ie-box", "5.6.7.8", "", "", eligibleBox) |
| + odd := edgeJSON("odd-box", "3.3.3.3", "xx-nowhere-1", "odd", eligibleBox) |
| + regionCandidates(views(t, noRegionHome), "ie", testKeyFP) |
| + if buf.Len() != 0 { |
| + t.Fatalf("a home with no AWS region logged: %s", buf.String()) |
| + } |
| + regionCandidates(views(t, noRegionHome, odd), "ie", testKeyFP) |
| + if !strings.Contains(buf.String(), `region "odd"`) || strings.Contains(buf.String(), `region ""`) { |
| + t.Fatalf("log = %q, want the coded region only", buf.String()) |
| + } |
| +} |