cli: C13b is decided by the region source, not by comparing codes (nearest-region §9.6)
--region <home> against a switch-off console answers the home with source default, so equal codes read as C11. Any non-chosen source after an explicit --region now prints C13b. apps create's region line is asserted end to end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 files changed,
+15
−4
apps.go+3 −2apps_region.go+6 −1apps_region_test.go+2 −0fleets_cli_test.go+4 −1
diff --git a/apps.go b/apps.go| index c4c4e6540..10068422e 100644 |
| --- a/apps.go |
| +++ b/apps.go |
| @@ -495,8 +495,9 @@ func runAppsCreate(args []string) { |
| } |
| printUsing(stdout, sess) |
| acct := resolvedAccount(sess, acctRaw, acctIDRaw) |
| + region2 := strings.TrimSpace(*region) |
| res, err := sess.createApp(context.Background(), acct, strings.TrimSpace(*app), place2, *controlPlane, probe2, |
| - strings.TrimSpace(*region), detectTimezone()) |
| + region2, detectTimezone()) |
| if err != nil { |
| log.Fatalf("apps create: %v", err) |
| } |
| @@ -504,7 +505,7 @@ func runAppsCreate(args []string) { |
| fmt.Fprintf(stdout, "%s %s in account %s — it's yours\n", c.ok("created app"), res.App, res.Account) |
| fmt.Fprintf(stdout, " open: %s\n", c.link(res.URL)) |
| fmt.Fprintf(stdout, " ship: carlos ship --account %s --app %s ...\n", res.Account, res.App) |
| - printAppRegion(stdout, res, strings.TrimSpace(*region)) |
| + printAppRegion(stdout, res, region2) |
| // The app WAS created (everything above already printed and is true) — |
| // what's still unverified is whether --place actually stuck. The server |
| // echoes back what it really stored (handleAPICreateApp's "placement" |
diff --git a/apps_region.go b/apps_region.go| index a7c1d77a0..126d85141 100644 |
| --- a/apps_region.go |
| +++ b/apps_region.go |
| @@ -9,6 +9,11 @@ import ( |
| // a single-region deployment, C13a when an older console ignored --region, |
| // C13b when a console with the switch off did, else C9 to C12 by source. |
| // |
| +// C13b is decided by the source, not by comparing codes: a console that |
| +// applied --region answers source "chosen", so any other source after an |
| +// explicit --region means it was ignored. Comparing codes misses --region |
| +// <the home> against a switch-off console, which answers the home itself. |
| +// |
| // An empty region_source with a region present means the console could not |
| // read the stored region back (it then answers the home with no source). That |
| // says nothing about whether --region was applied, so it prints nothing: not |
| @@ -21,7 +26,7 @@ func printAppRegion(w io.Writer, res apiCreateAppResult, asked string) { |
| } |
| case res.RegionSource == "": |
| // Region read-back failed on the console. Say nothing. |
| - case asked != "" && res.Region != asked: |
| + case asked != "" && res.RegionSource != "chosen": |
| fmt.Fprintln(w, regionNotAppliedSwitchOff(res.RegionName)) |
| case res.RegionsAvailable >= 2: |
| fmt.Fprintln(w, regionCreatedLine(res.RegionName, res.RegionSource)) |
diff --git a/apps_region_test.go b/apps_region_test.go| index f8e31ce62..c2a3390bf 100644 |
| --- a/apps_region_test.go |
| +++ b/apps_region_test.go |
| @@ -82,6 +82,8 @@ func TestPrintAppRegion(t *testing.T) { |
| {"single region", apiCreateAppResult{Region: "ie", RegionName: "Ireland · Dublin", RegionSource: "default", RegionsAvailable: 1}, "", ""}, |
| {"old console asked", apiCreateAppResult{}, "uk", regionNotRecordedOldConsole + "\n"}, |
| {"switch off asked", apiCreateAppResult{Region: "ie", RegionName: "Ireland · Dublin", RegionSource: "default", RegionsAvailable: 2}, "uk", regionNotAppliedSwitchOff("Ireland · Dublin") + "\n"}, |
| + {"switch off, asked the home itself", apiCreateAppResult{Region: "ie", RegionName: "Ireland · Dublin", RegionSource: "default", RegionsAvailable: 1}, "ie", regionNotAppliedSwitchOff("Ireland · Dublin") + "\n"}, |
| + {"switch on, explicit region", apiCreateAppResult{Region: "uk", RegionName: "UK · London", RegionSource: "chosen", RegionsAvailable: 2}, "uk", regionCreatedLine("UK · London", "chosen") + "\n"}, |
| {"old console not asked", apiCreateAppResult{}, "", ""}, |
| } |
| for _, c := range cases { |
diff --git a/fleets_cli_test.go b/fleets_cli_test.go| index dfaba8325..ed43b284e 100644 |
| --- a/fleets_cli_test.go |
| +++ b/fleets_cli_test.go |
| @@ -779,11 +779,14 @@ func TestRunAppsCreatePlacementVerification(t *testing.T) { |
| }) |
| t.Run("happy path: echo matches", func(t *testing.T) { |
| - ts := fakeCreateEcho(t, `{"account":"bab","app":"titogo","url":"http://x/a/bab/apps/titogo","placement":"fleet/pi"}`) |
| + ts := fakeCreateEcho(t, `{"account":"bab","app":"titogo","url":"http://x/a/bab/apps/titogo","placement":"fleet/pi","region":"uk","region_name":"UK · London","region_source":"timezone","regions_available":2}`) |
| out, err := run(t, ts.URL) |
| if err != nil { |
| t.Fatalf("exited nonzero, want 0:\n%s", out) |
| } |
| + if !strings.Contains(out, "Region: UK · London (from your timezone). Use --region to choose another.\n") { |
| + t.Fatalf("output missing the region line:\n%s", out) |
| + } |
| if !strings.Contains(out, "placed on fleet/pi") { |
| t.Fatalf("output missing the placed confirmation:\n%s", out) |
| } |