From 642456a89d74762eb70e74323905578403d45f5e Mon Sep 17 00:00:00 2001 From: Kush Date: Mon, 24 Aug 2026 14:48:53 -0400 Subject: [PATCH] chore: replace production-derived identifiers with placeholders This repository is public. The 10DLC test fixtures were captured from a live brand record and carried real values into it: - two Bandwidth employee email addresses, in brand fixtures - a real EIN, in four files - two real brand IDs (104 and 8 occurrences) - the internal display name of the acceptance-test brand - two references to an internal ticket key The email addresses are the reason this is worth a commit on its own. One belongs to a colleague who is not a contributor here and did not consent to having their work address published; the rest is internal-facing detail that a public repo has no reason to carry. Substitutions are identity-preserving. The two new brand-ID placeholders use unused BEXMPL slots rather than BEXMPL1/BEXMPL2, which already denote distinct brands -- reusing those would have merged two identities inside cmd/tendlc/brand_test.go, where both appear. Verified: build, full suite, gofmt and golangci-lint all clean, and a break-and-restore check confirms the renamed fixtures still discriminate (planting a wrong ID fails TestBrandUpdatePrintsAcceptanceReceiptWith- IDsAndLatencyNote) -- the rename did not neuter the assertions. Note this does not remove the values from git history, which is public and already fetched. It stops them propagating into the docsite, the next release, and search indexing. --- .agents/skills/release-cli/SKILL.md | 51 ++++++++++++++++++++++++ cmd/customerprofile/delete.go | 2 +- cmd/tendlc/brand_create.go | 2 +- cmd/tendlc/brand_create_test.go | 8 ++-- cmd/tendlc/brand_delete.go | 4 +- cmd/tendlc/brand_delete_test.go | 22 +++++----- cmd/tendlc/brand_get.go | 2 +- cmd/tendlc/brand_history.go | 4 +- cmd/tendlc/brand_identity.go | 4 +- cmd/tendlc/brand_identity_test.go | 24 +++++------ cmd/tendlc/brand_test.go | 12 +++--- cmd/tendlc/brand_update.go | 4 +- cmd/tendlc/brand_update_test.go | 34 ++++++++-------- cmd/tendlc/vetting.go | 12 +++--- cmd/tendlc/vetting_test.go | 42 +++++++++---------- internal/api/envelope_test.go | 8 ++-- internal/customerprofile/options.go | 2 +- internal/customerprofile/service_test.go | 4 +- internal/tendlc/brandoptions.go | 2 +- internal/tendlc/brandoptions_test.go | 8 ++-- internal/tendlc/brandupdate_test.go | 12 +++--- internal/tendlc/service_test.go | 6 +-- internal/tendlc/write_test.go | 30 +++++++------- 23 files changed, 175 insertions(+), 124 deletions(-) create mode 100644 .agents/skills/release-cli/SKILL.md diff --git a/.agents/skills/release-cli/SKILL.md b/.agents/skills/release-cli/SKILL.md new file mode 100644 index 0000000..92d3961 --- /dev/null +++ b/.agents/skills/release-cli/SKILL.md @@ -0,0 +1,51 @@ +--- +name: release-cli +description: Use when cutting a tagged release of the bw CLI from main, watching the goreleaser + bump-formula pipeline, and confirming the Homebrew tap PR opens and passes strict audit. +--- + +# Release the bw CLI + +Tag-driven release. Pushing `vX.Y.Z` triggers `.github/workflows/release.yml`, which runs three sequential jobs: + +1. `test` — runs `go test ./...` on linux/mac/windows +2. `release` — goreleaser builds binaries, creates the GitHub release, pushes Docker images +3. `bump-formula` — `mislav/bump-homebrew-formula-action@v3` opens a PR on `Bandwidth/homebrew-tap` updating `url` + `sha256` to the new tag's source archive + +The tap formula is **source-build**: `brew install` runs `go build` (~22s, vs. ~5s on the old pre-built tarball flow). The formula structure lives canonically in the tap repo — the action only bumps version/sha. + +## Pre-flight (on the cli repo) + +1. Confirm `gh auth status` is on the **kshahbw** account (Bandwidth writes need it). +2. On `main`, clean tree, pulled: `git checkout main && git pull && git status`. +3. Last release: `git describe --tags --abbrev=0`. +4. Decide next version (semver against the changes since that tag). + +## Cut the tag + +5. `git tag -a vX.Y.Z -m "Release vX.Y.Z"` +6. `git push origin vX.Y.Z` + +## Watch the cli release pipeline + +7. `gh run watch` (or `gh run list --workflow=release.yml --limit 1`) — wait for the workflow to finish. All three jobs (`test`, `release`, `bump-formula`) need to go green. +8. Confirm the GitHub release exists: `gh release view vX.Y.Z`. + +## Watch the homebrew-tap PR + +`bump-formula` opens a PR on `Bandwidth/homebrew-tap` with title `band vX.Y.Z`. The tap repo's `ci.yml` runs `brew audit --strict band` (the `CI / audit` check) on every PR — don't run it locally. + +9. Find it: `gh pr list --repo Bandwidth/homebrew-tap --search "band vX.Y.Z in:title" --state open`. +10. Wait for the `audit` check to go green: `gh pr checks --repo Bandwidth/homebrew-tap --watch`. +11. Merge it: `gh pr merge --repo Bandwidth/homebrew-tap --squash`. + +## Smoke test + +12. `brew update && brew upgrade band && band version` — confirm the new version installs and prints. First-time install on a clean machine takes ~22s (Go compile); upgrade of an existing install is faster. + +## If something fails + +- **Tests fail in release workflow** → the tag is already pushed; delete it (`git push --delete origin vX.Y.Z && git tag -d vX.Y.Z`), fix on a branch, re-tag. +- **`release` (goreleaser) fails** → check `gh run view --log-failed` on the release run; usually a missing secret or changelog filter issue. +- **`bump-formula` fails** → most often a `HOMEBREW_TAP_TOKEN` scope/expiry issue or a rate limit on the tap repo; the GitHub release still went out, so fix the token and re-run just that job (`gh run rerun --job bump-formula`). +- **Tap audit fails strict** → fix `Formula/band.rb` directly in the tap repo and merge that as a separate PR; the bump-formula PR can be rebased after. The cli repo no longer owns the formula structure, so don't try to fix it from there. +- **Repro audit locally** before re-tagging: clone the tap repo, then `brew audit --strict band`. diff --git a/cmd/customerprofile/delete.go b/cmd/customerprofile/delete.go index 1fc1dc0..886dd60 100644 --- a/cmd/customerprofile/delete.go +++ b/cmd/customerprofile/delete.go @@ -63,7 +63,7 @@ var restoreCmd = &cobra.Command{ Sends softDeleted: false. Note the published API docs describe restoring with {"deleted": false} — that form returns 404 "Customer profile not found" even -though the record is retrievable. Reported as MV-23429. +though the record is retrievable. Reported to the API team. No --confirm needed: restoring is not destructive.`, Example: ` band customer-profile restore 3IIzIFnRRQBE3AMzPpMTNo --plain`, diff --git a/cmd/tendlc/brand_create.go b/cmd/tendlc/brand_create.go index 1612e8a..00f2641 100644 --- a/cmd/tendlc/brand_create.go +++ b/cmd/tendlc/brand_create.go @@ -184,7 +184,7 @@ of a new registration. Sending any other key would turn it back into a create, so nothing else is added. Use it after making a change directly in TCR, or to pick up a brandId that was still null when the brand was created.`, - Example: ` band tendlc brand refresh BGJR2BA --plain`, + Example: ` band tendlc brand refresh BEXMPL6 --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { svc, err := service(cmd) diff --git a/cmd/tendlc/brand_create_test.go b/cmd/tendlc/brand_create_test.go index 83aedfe..3c37ca4 100644 --- a/cmd/tendlc/brand_create_test.go +++ b/cmd/tendlc/brand_create_test.go @@ -362,12 +362,12 @@ func TestBrandCreateRejectsStrayPositional(t *testing.T) { } } -// Test 10: refresh posts exactly {"brandId": "BGJR2BA"} — no other keys. A +// Test 10: refresh posts exactly {"brandId": "BEXMPL6"} — no other keys. A // refresh body carrying any extra key turns it back into a create. func TestBrandRefreshPostsExactBody(t *testing.T) { srv, bodies := stubBrandCreateCapturing(t, "WET8JUY8H0") - out, _, err := runBrandCmd(t, srv, "brand", "refresh", "BGJR2BA") + out, _, err := runBrandCmd(t, srv, "brand", "refresh", "BEXMPL6") if err != nil { t.Fatalf("brand refresh: %v", err) } @@ -378,8 +378,8 @@ func TestBrandRefreshPostsExactBody(t *testing.T) { if err := json.Unmarshal([]byte((*bodies)[0]), &sent); err != nil { t.Fatalf("request body is not JSON: %v", err) } - if len(sent) != 1 || sent["brandId"] != "BGJR2BA" { - t.Errorf("posted body = %v, want exactly {\"brandId\":\"BGJR2BA\"}", sent) + if len(sent) != 1 || sent["brandId"] != "BEXMPL6" { + t.Errorf("posted body = %v, want exactly {\"brandId\":\"BEXMPL6\"}", sent) } got := decodeStdout(t, out) if got["bandwidthId"] != "WET8JUY8H0" { diff --git a/cmd/tendlc/brand_delete.go b/cmd/tendlc/brand_delete.go index 186c288..4748528 100644 --- a/cmd/tendlc/brand_delete.go +++ b/cmd/tendlc/brand_delete.go @@ -48,8 +48,8 @@ IS success here, the only place in this command set where that is true — and only then does "deleted" flip to true. A --wait timeout (exit 5) prints the same unconfirmed receipt as no-wait; it never claims deleted:true merely because --wait gave up waiting.`, - Example: ` band tendlc brand delete BGJR2BA --confirm --plain - band tendlc brand delete BGJR2BA --confirm --wait --timeout 60 --plain`, + Example: ` band tendlc brand delete BEXMPL6 --confirm --plain + band tendlc brand delete BEXMPL6 --confirm --wait --timeout 60 --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if err := requireConfirm(brandDeleteConfirm, diff --git a/cmd/tendlc/brand_delete_test.go b/cmd/tendlc/brand_delete_test.go index deeef0b..b2ce347 100644 --- a/cmd/tendlc/brand_delete_test.go +++ b/cmd/tendlc/brand_delete_test.go @@ -43,7 +43,7 @@ func stubBrandDeleteStillExistsServer(t *testing.T, deleteStatus int) (*httptest return } w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(`{"data":{"brandId":"BGJR2BA","bandwidthId":"WET8JUY8H0"}}`)) + _, _ = w.Write([]byte(`{"data":{"brandId":"BEXMPL6","bandwidthId":"WET8JUY8H0"}}`)) }) return srv, &methods } @@ -53,7 +53,7 @@ func stubBrandDeleteStillExistsServer(t *testing.T, deleteStatus int) (*httptest // server, so this fails loudly (not silently) if the confirm gate ever moves // after the service/DELETE call. func TestBrandDeleteWithoutConfirmMakesNoRequests(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "brand", "delete", "BGJR2BA") + _, _, err := runBrandCmd(t, nil, "brand", "delete", "BEXMPL6") if err == nil { t.Fatal("want an error when --confirm is missing") } @@ -86,7 +86,7 @@ func TestBrandDeleteWithoutConfirmMakesNoRequests(t *testing.T) { func TestBrandDeleteWithConfirmIssuesDeleteAndPrintsReceipt(t *testing.T) { srv, methods := stubBrandDeleteServer(t, http.StatusNoContent) - out, _, err := runBrandCmd(t, srv, "brand", "delete", "BGJR2BA", "--confirm", "--plain") + out, _, err := runBrandCmd(t, srv, "brand", "delete", "BEXMPL6", "--confirm", "--plain") if err != nil { t.Fatalf("brand delete --confirm: %v", err) } @@ -94,8 +94,8 @@ func TestBrandDeleteWithConfirmIssuesDeleteAndPrintsReceipt(t *testing.T) { t.Fatalf("want exactly one DELETE, got %v", *methods) } got := decodeStdout(t, out) - if got["id"] != "BGJR2BA" { - t.Errorf("stdout = %v, want id BGJR2BA", got) + if got["id"] != "BEXMPL6" { + t.Errorf("stdout = %v, want id BEXMPL6", got) } if got["deleted"] != false { t.Errorf("stdout = %v, want deleted false — accepted is not confirmed, and there was no --wait to confirm it", got) @@ -116,7 +116,7 @@ func TestBrandDeleteWithConfirmIssuesDeleteAndPrintsReceipt(t *testing.T) { func TestBrandDeleteWaitTreats404AsSuccess(t *testing.T) { srv, methods := stubBrandDeleteServer(t, http.StatusNoContent) - out, _, err := runBrandCmd(t, srv, "brand", "delete", "BGJR2BA", "--confirm", "--wait", "--timeout", "5", "--plain") + out, _, err := runBrandCmd(t, srv, "brand", "delete", "BEXMPL6", "--confirm", "--wait", "--timeout", "5", "--plain") if err != nil { t.Fatalf("brand delete --confirm --wait: %v", err) } @@ -124,8 +124,8 @@ func TestBrandDeleteWaitTreats404AsSuccess(t *testing.T) { t.Fatalf("want a DELETE then at least one GET, got %v", *methods) } got := decodeStdout(t, out) - if got["id"] != "BGJR2BA" { - t.Errorf("stdout = %v, want id BGJR2BA", got) + if got["id"] != "BEXMPL6" { + t.Errorf("stdout = %v, want id BEXMPL6", got) } if got["deleted"] != true { t.Errorf("stdout = %v, want deleted true — the follow-up 404 confirmed it", got) @@ -142,7 +142,7 @@ func TestBrandDeleteWaitTreats404AsSuccess(t *testing.T) { func TestBrandDeleteWaitTimeoutKeepsReceiptHonest(t *testing.T) { srv, methods := stubBrandDeleteStillExistsServer(t, http.StatusNoContent) - out, _, err := runBrandCmd(t, srv, "brand", "delete", "BGJR2BA", "--confirm", "--wait", "--timeout", "0", "--plain") + out, _, err := runBrandCmd(t, srv, "brand", "delete", "BEXMPL6", "--confirm", "--wait", "--timeout", "0", "--plain") if err == nil { t.Fatal("want a timeout error") } @@ -153,8 +153,8 @@ func TestBrandDeleteWaitTimeoutKeepsReceiptHonest(t *testing.T) { t.Fatalf("want a DELETE then at least one GET, got %v", *methods) } got := decodeStdout(t, out) - if got["id"] != "BGJR2BA" { - t.Errorf("stdout = %v, want id BGJR2BA", got) + if got["id"] != "BEXMPL6" { + t.Errorf("stdout = %v, want id BEXMPL6", got) } if got["deleted"] != false { t.Errorf("stdout = %v, want deleted false — the timeout means completion was never confirmed, and exit 5 must not be paired with deleted:true", got) diff --git a/cmd/tendlc/brand_get.go b/cmd/tendlc/brand_get.go index fc75c4f..438c0a4 100644 --- a/cmd/tendlc/brand_get.go +++ b/cmd/tendlc/brand_get.go @@ -18,7 +18,7 @@ var brandGetCmd = &cobra.Command{ Brands have two IDs: bandwidthId exists immediately, while brandId is assigned by TCR and is null until registration completes. Either identifier works here — pass whichever one you have.`, - Example: ` band tendlc brand get BGJR2BA --plain + Example: ` band tendlc brand get BEXMPL6 --plain band tendlc brand get WET8JUY8H0 --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/tendlc/brand_history.go b/cmd/tendlc/brand_history.go index 211e722..c7d311e 100644 --- a/cmd/tendlc/brand_history.go +++ b/cmd/tendlc/brand_history.go @@ -32,8 +32,8 @@ Unlike customer profiles, brands have no versioned snapshots and no per-version fetch — this is the only history view for a brand. Either the TCR brandId or the Bandwidth bandwidthId works as the positional.`, - Example: ` band tendlc brand history BGJR2BA --plain - band tendlc brand history BGJR2BA --all --plain`, + Example: ` band tendlc brand history BEXMPL6 --plain + band tendlc brand history BEXMPL6 --all --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if brandHistoryAll && cmd.Flags().Changed("offset") { diff --git a/cmd/tendlc/brand_identity.go b/cmd/tendlc/brand_identity.go index e6ff7a0..1123135 100644 --- a/cmd/tendlc/brand_identity.go +++ b/cmd/tendlc/brand_identity.go @@ -28,7 +28,7 @@ endpoint returns 204 with no body — there is no ID or resource to poll, so there is no --wait here. The brand's own status is the signal. Requires --confirm.`, - Example: ` band tendlc brand reverify BGJR2BA --confirm --plain`, + Example: ` band tendlc brand reverify BEXMPL6 --confirm --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if err := requireConfirm(brandReverifyConfirm, @@ -68,7 +68,7 @@ verification before the brand goes UNVERIFIED. Re-sending an email is neither destructive nor billable, so unlike most writes in this command set, this does not require --confirm.`, - Example: ` band tendlc brand resend-2fa BGJR2BA --plain`, + Example: ` band tendlc brand resend-2fa BEXMPL6 --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { svc, err := service(cmd) diff --git a/cmd/tendlc/brand_identity_test.go b/cmd/tendlc/brand_identity_test.go index 3250f7b..ad2e527 100644 --- a/cmd/tendlc/brand_identity_test.go +++ b/cmd/tendlc/brand_identity_test.go @@ -27,7 +27,7 @@ func stubBrandIdentityServer(t *testing.T) (*httptest.Server, *[]string) { // server, so this fails loudly if the confirm gate ever moves after the // service/POST call. func TestBrandReverifyWithoutConfirmMakesNoRequests(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "brand", "reverify", "BGJR2BA") + _, _, err := runBrandCmd(t, nil, "brand", "reverify", "BEXMPL6") if err == nil { t.Fatal("want an error when --confirm is missing") } @@ -45,16 +45,16 @@ func TestBrandReverifyWithoutConfirmMakesNoRequests(t *testing.T) { func TestBrandReverifyWithConfirmPostsToIdentityReverify(t *testing.T) { srv, paths := stubBrandIdentityServer(t) - out, _, err := runBrandCmd(t, srv, "brand", "reverify", "BGJR2BA", "--confirm", "--plain") + out, _, err := runBrandCmd(t, srv, "brand", "reverify", "BEXMPL6", "--confirm", "--plain") if err != nil { t.Fatalf("brand reverify --confirm: %v", err) } - if len(*paths) != 1 || !strings.HasSuffix((*paths)[0], "/brands/BGJR2BA/identity/reverify") { + if len(*paths) != 1 || !strings.HasSuffix((*paths)[0], "/brands/BEXMPL6/identity/reverify") { t.Fatalf("paths = %v, want exactly one POST to .../identity/reverify", *paths) } got := decodeStdout(t, out) - if got["id"] != "BGJR2BA" { - t.Errorf("stdout = %v, want id BGJR2BA", got) + if got["id"] != "BEXMPL6" { + t.Errorf("stdout = %v, want id BEXMPL6", got) } if got["reverificationRequested"] != true { t.Errorf("stdout = %v, want reverificationRequested true", got) @@ -62,7 +62,7 @@ func TestBrandReverifyWithConfirmPostsToIdentityReverify(t *testing.T) { if got["status"] != "accepted" { t.Errorf("stdout = %v, want status accepted", got) } - if got["check"] != "band tendlc brand get BGJR2BA" { + if got["check"] != "band tendlc brand get BEXMPL6" { t.Errorf("stdout = %v, want check pointing at brand get", got) } } @@ -73,16 +73,16 @@ func TestBrandReverifyWithConfirmPostsToIdentityReverify(t *testing.T) { func TestBrandResend2FANeedsNoConfirm(t *testing.T) { srv, paths := stubBrandIdentityServer(t) - out, _, err := runBrandCmd(t, srv, "brand", "resend-2fa", "BGJR2BA", "--plain") + out, _, err := runBrandCmd(t, srv, "brand", "resend-2fa", "BEXMPL6", "--plain") if err != nil { t.Fatalf("brand resend-2fa: %v", err) } - if len(*paths) != 1 || !strings.HasSuffix((*paths)[0], "/brands/BGJR2BA/identity/resend2faEmail") { + if len(*paths) != 1 || !strings.HasSuffix((*paths)[0], "/brands/BEXMPL6/identity/resend2faEmail") { t.Fatalf("paths = %v, want exactly one POST to .../identity/resend2faEmail", *paths) } got := decodeStdout(t, out) - if got["id"] != "BGJR2BA" { - t.Errorf("stdout = %v, want id BGJR2BA", got) + if got["id"] != "BEXMPL6" { + t.Errorf("stdout = %v, want id BEXMPL6", got) } if got["emailResent"] != true { t.Errorf("stdout = %v, want emailResent true", got) @@ -119,8 +119,8 @@ func TestBrandIdentity403MapsToExitFour(t *testing.T) { name string args []string }{ - {"reverify", []string{"brand", "reverify", "BGJR2BA", "--confirm"}}, - {"resend-2fa", []string{"brand", "resend-2fa", "BGJR2BA"}}, + {"reverify", []string{"brand", "reverify", "BEXMPL6", "--confirm"}}, + {"resend-2fa", []string{"brand", "resend-2fa", "BEXMPL6"}}, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { diff --git a/cmd/tendlc/brand_test.go b/cmd/tendlc/brand_test.go index f0ee292..57b67af 100644 --- a/cmd/tendlc/brand_test.go +++ b/cmd/tendlc/brand_test.go @@ -75,7 +75,7 @@ func newBrandStub(t *testing.T, handler http.HandlerFunc) *httptest.Server { // page. Good enough for tests that just need brand list to succeed. func stubBrandList(t *testing.T) *httptest.Server { return newBrandStub(t, func(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte(`{"data":[{"bandwidthId":"WET8JUY8H0","brandId":"BGJR2BA"}],` + + _, _ = w.Write([]byte(`{"data":[{"bandwidthId":"WET8JUY8H0","brandId":"BEXMPL6"}],` + `"page":{"pageNumber":0,"pageSize":50,"totalElements":1,"totalPages":1}}`)) }) } @@ -106,7 +106,7 @@ func stubBrandGetCapturing(t *testing.T) (*httptest.Server, *[]string) { var paths []string srv := newBrandStub(t, func(w http.ResponseWriter, r *http.Request) { paths = append(paths, r.URL.Path) - _, _ = w.Write([]byte(`{"data":{"bandwidthId":"WET8JUY8H0","brandId":"BGJR2BA"}}`)) + _, _ = w.Write([]byte(`{"data":{"bandwidthId":"WET8JUY8H0","brandId":"BEXMPL6"}}`)) }) return srv, &paths } @@ -329,12 +329,12 @@ func TestBrandListAllWalksEveryPage(t *testing.T) { func TestBrandGetAcceptsEitherIdentifier(t *testing.T) { srv, paths := stubBrandGetCapturing(t) - for _, id := range []string{"BGJR2BA", "WET8JUY8H0"} { + for _, id := range []string{"BEXMPL6", "WET8JUY8H0"} { if _, _, err := runBrandCmd(t, srv, "brand", "get", id); err != nil { t.Fatalf("brand get %s: %v", id, err) } } - if len(*paths) != 2 || !strings.HasSuffix((*paths)[0], "/brands/BGJR2BA") || !strings.HasSuffix((*paths)[1], "/brands/WET8JUY8H0") { + if len(*paths) != 2 || !strings.HasSuffix((*paths)[0], "/brands/BEXMPL6") || !strings.HasSuffix((*paths)[1], "/brands/WET8JUY8H0") { t.Errorf("paths = %v; get must pass the ID through unchanged", *paths) } } @@ -361,7 +361,7 @@ func TestBrandCommandsRejectStrayPositionals(t *testing.T) { } func TestBrandHistoryReturnsMessageLog(t *testing.T) { - out, _, err := runBrandCmd(t, stubBrandHistory(t), "brand", "history", "BGJR2BA") + out, _, err := runBrandCmd(t, stubBrandHistory(t), "brand", "history", "BEXMPL6") if err != nil { t.Fatalf("brand history: %v", err) } @@ -373,7 +373,7 @@ func TestBrandHistoryReturnsMessageLog(t *testing.T) { // TestBrandHistoryAllWalksEveryPage is TestBrandListAllWalksEveryPage's twin // for `brand history --all`. func TestBrandHistoryAllWalksEveryPage(t *testing.T) { - out, errOut, err := runBrandCmd(t, stubBrandHistoryTwoPages(t), "brand", "history", "BGJR2BA", + out, errOut, err := runBrandCmd(t, stubBrandHistoryTwoPages(t), "brand", "history", "BEXMPL6", "--all", "--limit", "1", "--plain") if err != nil { t.Fatalf("brand history --all: %v", err) diff --git a/cmd/tendlc/brand_update.go b/cmd/tendlc/brand_update.go index 9995828..6c4072f 100644 --- a/cmd/tendlc/brand_update.go +++ b/cmd/tendlc/brand_update.go @@ -78,8 +78,8 @@ responds — and is rejected outright if the brand has an active campaign or an active Standard/Enhanced/Political vetting). Changing mobile-phone sets identity status to UNVERIFIED. Changing business-contact-email on a PUBLIC_PROFIT brand revokes Auth+ compliance.`, - Example: ` band tendlc brand update BGJR2BA --website "https://acme.example" --plain - band tendlc brand update BGJR2BA --company-name "Acme Corp 2" --confirm --plain`, + Example: ` band tendlc brand update BEXMPL6 --website "https://acme.example" --plain + band tendlc brand update BEXMPL6 --company-name "Acme Corp 2" --confirm --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { changed := map[string]bool{} diff --git a/cmd/tendlc/brand_update_test.go b/cmd/tendlc/brand_update_test.go index 71584ca..54cf227 100644 --- a/cmd/tendlc/brand_update_test.go +++ b/cmd/tendlc/brand_update_test.go @@ -20,19 +20,19 @@ import ( func liveBrandForUpdate(brandType, businessContactEmail string) map[string]any { return map[string]any{ "bandwidthId": "WET8JUY8H0", - "brandId": "BGJR2BA", + "brandId": "BEXMPL6", "brandIdentityStatus": "VERIFIED", "brandType": brandType, "companyName": "Bandwidth Inc", - "displayName": "Bandwidth Acceptance Test", + "displayName": "Example Brand", "street": "1000 Bandwidth Way", "city": "Raleigh", "state": "NC", "postalCode": "27606", "countryCodeA3": "USA", "phone": "+12025551234", - "email": "npatel@bandwidth.com", - "ein": "562242657", + "email": "ops@example.com", + "ein": "123456789", "einIssuingCountryCodeA3": "USA", "vertical": "PROFESSIONAL", "website": "https://bandwidth.com", @@ -91,7 +91,7 @@ func stubBrandUpdateServer(t *testing.T, getBody map[string]any, putStatus int) // cannot pass by accident even if the "nothing to update" check moved after // the service/GET call. func TestBrandUpdateNoFieldFlagsExitsSixWithZeroRequests(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "brand", "update", "BGJR2BA") + _, _, err := runBrandCmd(t, nil, "brand", "update", "BEXMPL6") if err == nil { t.Fatal("want an error when no field flags are passed") } @@ -111,7 +111,7 @@ func TestBrandUpdateNoFieldFlagsExitsSixWithZeroRequests(t *testing.T) { func TestBrandUpdateRenameOnlyPreservesEveryOtherField(t *testing.T) { srv, bodies := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "biz@acme.com"), http.StatusOK) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", "--display-name", "Renamed", "--plain") + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--display-name", "Renamed", "--plain") if err != nil { t.Fatalf("brand update: %v", err) } @@ -142,7 +142,7 @@ func TestBrandUpdateRenameOnlyPreservesEveryOtherField(t *testing.T) { func TestBrandUpdateCompanyNameWithoutConfirmRefuses(t *testing.T) { srv, bodies := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "biz@acme.com"), http.StatusOK) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", "--company-name", "New Co", "--plain") + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--company-name", "New Co", "--plain") if err == nil { t.Fatal("want an error when an identity field changes without --confirm") } @@ -164,7 +164,7 @@ func TestBrandUpdateCompanyNameWithoutConfirmRefuses(t *testing.T) { func TestBrandUpdateCompanyNameWithConfirmProceeds(t *testing.T) { srv, bodies := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "biz@acme.com"), http.StatusOK) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", "--company-name", "New Co", "--confirm", "--plain") + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--company-name", "New Co", "--confirm", "--plain") if err != nil { t.Fatalf("brand update --confirm: %v", err) } @@ -184,7 +184,7 @@ func TestBrandUpdateCompanyNameWithConfirmProceeds(t *testing.T) { func TestBrandUpdateWebsiteOnlyNeedsNoConfirm(t *testing.T) { srv, bodies := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "biz@acme.com"), http.StatusOK) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", "--website", "https://acme.example", "--plain") + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--website", "https://acme.example", "--plain") if err != nil { t.Fatalf("brand update --website: %v", err) } @@ -199,7 +199,7 @@ func TestBrandUpdateWebsiteOnlyNeedsNoConfirm(t *testing.T) { func TestBrandUpdateBusinessContactEmailConfirmDependsOnBrandType(t *testing.T) { t.Run("PRIVATE_PROFIT needs no confirm", func(t *testing.T) { srv, bodies := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "old@acme.com"), http.StatusOK) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--business-contact-email", "new@acme.com", "--plain") if err != nil { t.Fatalf("brand update: %v", err) @@ -211,7 +211,7 @@ func TestBrandUpdateBusinessContactEmailConfirmDependsOnBrandType(t *testing.T) t.Run("PUBLIC_PROFIT needs confirm", func(t *testing.T) { srv, bodies := stubBrandUpdateServer(t, liveBrandForUpdate("PUBLIC_PROFIT", "old@acme.com"), http.StatusOK) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--business-contact-email", "new@acme.com", "--plain") if err == nil { t.Fatal("want an error requiring --confirm on a PUBLIC_PROFIT brand") @@ -226,7 +226,7 @@ func TestBrandUpdateBusinessContactEmailConfirmDependsOnBrandType(t *testing.T) t.Errorf("want zero PUT requests, got %d", len(*bodies)) } - if _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", + if _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--business-contact-email", "new@acme.com", "--confirm", "--plain"); err != nil { t.Fatalf("brand update --confirm: %v", err) } @@ -242,7 +242,7 @@ func TestBrandUpdateBusinessContactEmailConfirmDependsOnBrandType(t *testing.T) func TestBrandUpdateClearingRequiredFieldExitsSixBeforePut(t *testing.T) { srv, bodies := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "biz@acme.com"), http.StatusOK) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", "--display-name", "", "--plain") + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--display-name", "", "--plain") if err == nil { t.Fatal("want an error when clearing a universally-required field") } @@ -268,7 +268,7 @@ func TestBrandUpdateHasNoWaitFlag(t *testing.T) { func TestBrandUpdateConflictOnPutMapsToTCRHint(t *testing.T) { srv, _ := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "biz@acme.com"), http.StatusConflict) - _, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", "--website", "https://acme.example", "--plain") + _, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--website", "https://acme.example", "--plain") if err == nil { t.Fatal("want an error on a 409 PUT") } @@ -297,7 +297,7 @@ func TestBrandUpdateConflictOnPutMapsToTCRHint(t *testing.T) { func TestBrandUpdatePrintsAcceptanceReceiptWithIDsAndLatencyNote(t *testing.T) { srv, _ := stubBrandUpdateServer(t, liveBrandForUpdate("PRIVATE_PROFIT", "biz@acme.com"), http.StatusOK) - out, _, err := runBrandCmd(t, srv, "brand", "update", "BGJR2BA", "--website", "https://acme.example", "--plain") + out, _, err := runBrandCmd(t, srv, "brand", "update", "BEXMPL6", "--website", "https://acme.example", "--plain") if err != nil { t.Fatalf("brand update: %v", err) } @@ -305,8 +305,8 @@ func TestBrandUpdatePrintsAcceptanceReceiptWithIDsAndLatencyNote(t *testing.T) { if got["bandwidthId"] != "WET8JUY8H0" { t.Errorf("stdout = %v, want bandwidthId WET8JUY8H0", got) } - if got["brandId"] != "BGJR2BA" { - t.Errorf("stdout = %v, want brandId BGJR2BA", got) + if got["brandId"] != "BEXMPL6" { + t.Errorf("stdout = %v, want brandId BEXMPL6", got) } if got["status"] != "accepted" { t.Errorf("stdout = %v, want status accepted", got) diff --git a/cmd/tendlc/vetting.go b/cmd/tendlc/vetting.go index 72652b7..ba3f0dd 100644 --- a/cmd/tendlc/vetting.go +++ b/cmd/tendlc/vetting.go @@ -236,8 +236,8 @@ var vettingListCmd = &cobra.Command{ The positional here is a BRAND ID. Vettings are brand-scoped; there is no campaign vetting endpoint.`, - Example: ` band tendlc vetting list BGJR2BA --plain - band tendlc vetting list BGJR2BA --all --plain`, + Example: ` band tendlc vetting list BEXMPL6 --plain + band tendlc vetting list BEXMPL6 --all --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if vettingListAll && cmd.Flags().Changed("offset") { @@ -311,8 +311,8 @@ class-level error. This places a billable order with an external vetting provider, so --confirm is required.`, - Example: ` band tendlc vetting request BGJR2BA --evp AEGIS --class STANDARD --confirm --plain - band tendlc vetting request BGJR2BA --evp AEGIS --class STANDARD --confirm --wait --plain`, + Example: ` band tendlc vetting request BEXMPL6 --evp AEGIS --class STANDARD --confirm --plain + band tendlc vetting request BEXMPL6 --evp AEGIS --class STANDARD --confirm --wait --plain`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if err := validateVettingRequest(vettingRequestEvp, vettingRequestClass); err != nil { @@ -384,8 +384,8 @@ assigned by the external provider. Recording an already-performed vetting is not billable, so unlike 'vetting request' this takes no --confirm.`, - Example: ` band tendlc vetting import BGJR2BA V123 --evp AEGIS --plain - band tendlc vetting import BGJR2BA V123 --evp AEGIS --vetting-token TOK123 --plain`, + Example: ` band tendlc vetting import BEXMPL6 V123 --evp AEGIS --plain + band tendlc vetting import BEXMPL6 V123 --evp AEGIS --vetting-token TOK123 --plain`, Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { if vettingImportEvp == "" { diff --git a/cmd/tendlc/vetting_test.go b/cmd/tendlc/vetting_test.go index cc37019..eb69e63 100644 --- a/cmd/tendlc/vetting_test.go +++ b/cmd/tendlc/vetting_test.go @@ -78,7 +78,7 @@ func stubVettingRequestThenPoll(t *testing.T, idKey, idValue, status string) *ht // both, and no request is made — runBrandCmd's `service` seam Fatals if // invoked with a nil server. func TestVettingRequestMissingRequiredFlagsAggregate(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "vetting", "request", "BGJR2BA", "--confirm") + _, _, err := runBrandCmd(t, nil, "vetting", "request", "BEXMPL6", "--confirm") if err == nil { t.Fatal("want an error for missing --evp and --class") } @@ -95,7 +95,7 @@ func TestVettingRequestMissingRequiredFlagsAggregate(t *testing.T) { // the published enumVettingClass, and this must not be "corrected" away. func TestVettingRequestAcceptsRCSClass(t *testing.T) { srv, bodies, _ := stubVettingRequestCapturing(t, "vettingBandwidthId", "V1") - _, _, err := runBrandCmd(t, srv, "vetting", "request", "BGJR2BA", + _, _, err := runBrandCmd(t, srv, "vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "RCS", "--confirm") if err != nil { t.Fatalf("vetting request --class RCS: %v", err) @@ -115,7 +115,7 @@ func TestVettingRequestAcceptsRCSClass(t *testing.T) { // Test 3: an invalid --class exits 6 listing the valid classes, with zero // requests made. func TestVettingRequestRejectsInvalidClass(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "vetting", "request", "BGJR2BA", + _, _, err := runBrandCmd(t, nil, "vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "NOT_A_CLASS", "--confirm") if err == nil { t.Fatal("want an error for an invalid --class") @@ -133,7 +133,7 @@ func TestVettingRequestRejectsInvalidClass(t *testing.T) { // Test 4: an invalid --evp exits 6 listing the valid providers, with zero // requests made. func TestVettingRequestRejectsInvalidEvp(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "vetting", "request", "BGJR2BA", + _, _, err := runBrandCmd(t, nil, "vetting", "request", "BEXMPL6", "--evp", "NOPE", "--class", "STANDARD", "--confirm") if err == nil { t.Fatal("want an error for an invalid --evp") @@ -154,7 +154,7 @@ func TestVettingRequestRejectsInvalidEvp(t *testing.T) { // shipped and both got a regression guard for; this was the one instance // left unguarded at the final whole-branch review (item C3). func TestVettingRequestAggregatesOneMissingOneInvalidFlag(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "vetting", "request", "BGJR2BA", + _, _, err := runBrandCmd(t, nil, "vetting", "request", "BEXMPL6", "--class", "NOT_A_CLASS", "--confirm") if err == nil { t.Fatal("want an error for a missing --evp combined with an invalid --class") @@ -173,7 +173,7 @@ func TestVettingRequestAggregatesOneMissingOneInvalidFlag(t *testing.T) { // Test 5: vetting request without --confirm exits 6, makes ZERO HTTP // requests, and the message mentions the order is billable. func TestVettingRequestWithoutConfirmMakesNoRequests(t *testing.T) { - _, _, err := runBrandCmd(t, nil, "vetting", "request", "BGJR2BA", "--evp", "AEGIS", "--class", "STANDARD") + _, _, err := runBrandCmd(t, nil, "vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "STANDARD") if err == nil { t.Fatal("want an error when --confirm is missing") } @@ -189,7 +189,7 @@ func TestVettingRequestWithoutConfirmMakesNoRequests(t *testing.T) { // prints the 202 receipt on stdout. func TestVettingRequestWithConfirmPostsBodyAndPrintsReceipt(t *testing.T) { srv, bodies, paths := stubVettingRequestCapturing(t, "vettingBandwidthId", "V1") - out, _, err := runBrandCmd(t, srv, "vetting", "request", "BGJR2BA", + out, _, err := runBrandCmd(t, srv, "vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "STANDARD", "--confirm") if err != nil { t.Fatalf("vetting request --confirm: %v", err) @@ -197,8 +197,8 @@ func TestVettingRequestWithConfirmPostsBodyAndPrintsReceipt(t *testing.T) { if len(*bodies) != 1 { t.Fatalf("want exactly one POST, got %d", len(*bodies)) } - if !strings.HasSuffix((*paths)[0], "/brands/BGJR2BA/vettings") { - t.Errorf("path = %q, want POST to .../brands/BGJR2BA/vettings", (*paths)[0]) + if !strings.HasSuffix((*paths)[0], "/brands/BEXMPL6/vettings") { + t.Errorf("path = %q, want POST to .../brands/BEXMPL6/vettings", (*paths)[0]) } var sent map[string]any if err := json.Unmarshal([]byte((*bodies)[0]), &sent); err != nil { @@ -221,15 +221,15 @@ func TestVettingRequestWithConfirmPostsBodyAndPrintsReceipt(t *testing.T) { func TestVettingImportPutsEvpAndOnlyIncludesTokenWhenPassed(t *testing.T) { t.Run("without token", func(t *testing.T) { srv, bodies, paths := stubVettingImportCapturing(t, "bandwidthId", "V1") - _, _, err := runBrandCmd(t, srv, "vetting", "import", "BGJR2BA", "V1", "--evp", "CV") + _, _, err := runBrandCmd(t, srv, "vetting", "import", "BEXMPL6", "V1", "--evp", "CV") if err != nil { t.Fatalf("vetting import: %v", err) } if len(*bodies) != 1 { t.Fatalf("want exactly one PUT, got %d", len(*bodies)) } - if !strings.HasSuffix((*paths)[0], "/brands/BGJR2BA/vettings/V1") { - t.Errorf("path = %q, want PUT to .../brands/BGJR2BA/vettings/V1", (*paths)[0]) + if !strings.HasSuffix((*paths)[0], "/brands/BEXMPL6/vettings/V1") { + t.Errorf("path = %q, want PUT to .../brands/BEXMPL6/vettings/V1", (*paths)[0]) } var sent map[string]any if err := json.Unmarshal([]byte((*bodies)[0]), &sent); err != nil { @@ -245,7 +245,7 @@ func TestVettingImportPutsEvpAndOnlyIncludesTokenWhenPassed(t *testing.T) { t.Run("with token", func(t *testing.T) { srv, bodies, _ := stubVettingImportCapturing(t, "bandwidthId", "V1") - _, _, err := runBrandCmd(t, srv, "vetting", "import", "BGJR2BA", "V1", + _, _, err := runBrandCmd(t, srv, "vetting", "import", "BEXMPL6", "V1", "--evp", "CV", "--vetting-token", "TOK123") if err != nil { t.Fatalf("vetting import --vetting-token: %v", err) @@ -273,7 +273,7 @@ func TestVettingImportHasNoConfirmFlag(t *testing.T) { // Test 8: vetting list on a brand with one ACTIVE vetting prints it, and the // truncation warning goes to stderr only. func TestVettingListPrintsEntryAndWarnsOnTruncationViaStderrOnly(t *testing.T) { - out, _, err := runBrandCmd(t, stubVettingList(t), "vetting", "list", "BGJR2BA") + out, _, err := runBrandCmd(t, stubVettingList(t), "vetting", "list", "BEXMPL6") if err != nil { t.Fatalf("vetting list: %v", err) } @@ -281,7 +281,7 @@ func TestVettingListPrintsEntryAndWarnsOnTruncationViaStderrOnly(t *testing.T) { t.Errorf("stdout = %q, want the ACTIVE vetting entry", out) } - out, errOut, err := runBrandCmd(t, stubVettingListTruncated(t), "vetting", "list", "BGJR2BA", "--limit", "1") + out, errOut, err := runBrandCmd(t, stubVettingListTruncated(t), "vetting", "list", "BEXMPL6", "--limit", "1") if err != nil { t.Fatalf("vetting list (truncated): %v", err) } @@ -322,7 +322,7 @@ func TestVettingCommandsRejectWrongPositionalCount(t *testing.T) { // field the API actually sent. func TestVettingRequestReceiptPreservesAPIFieldName(t *testing.T) { srv, _, _ := stubVettingRequestCapturing(t, "vettingBandwidthId", "V-XYZ") - out, _, err := runBrandCmd(t, srv, "vetting", "request", "BGJR2BA", + out, _, err := runBrandCmd(t, srv, "vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "STANDARD", "--confirm") if err != nil { t.Fatalf("vetting request: %v", err) @@ -344,7 +344,7 @@ func TestVettingRequestReceiptPreservesAPIFieldName(t *testing.T) { // keys and print the final ACTIVE resource. func TestVettingRequestWaitPollsToActive(t *testing.T) { srv := stubVettingRequestThenPoll(t, "vettingBandwidthId", "V1", "ACTIVE") - out, _, err := runBrandCmd(t, srv, "vetting", "request", "BGJR2BA", + out, _, err := runBrandCmd(t, srv, "vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "STANDARD", "--confirm", "--wait", "--timeout", "5") if err != nil { t.Fatalf("vetting request --wait: %v", err) @@ -366,9 +366,9 @@ func TestVettingRoleGate403MapsToExitFour(t *testing.T) { name string args []string }{ - {"list", []string{"vetting", "list", "BGJR2BA"}}, - {"request", []string{"vetting", "request", "BGJR2BA", "--evp", "AEGIS", "--class", "STANDARD", "--confirm"}}, - {"import", []string{"vetting", "import", "BGJR2BA", "V1", "--evp", "AEGIS"}}, + {"list", []string{"vetting", "list", "BEXMPL6"}}, + {"request", []string{"vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "STANDARD", "--confirm"}}, + {"import", []string{"vetting", "import", "BEXMPL6", "V1", "--evp", "AEGIS"}}, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { @@ -398,7 +398,7 @@ func TestVettingRequestNoVettingIDPrintsBodyWithKeyPreserved(t *testing.T) { _, _ = w.Write([]byte(`{"data":{"orders":[{"orderId":"O1"}]}}`)) }) - out, _, err := runBrandCmd(t, srv, "vetting", "request", "BGJR2BA", + out, _, err := runBrandCmd(t, srv, "vetting", "request", "BEXMPL6", "--evp", "AEGIS", "--class", "STANDARD", "--confirm", "--plain") if err == nil { t.Fatal("want an error when the response carries no vetting ID") diff --git a/internal/api/envelope_test.go b/internal/api/envelope_test.go index 04b1e49..d79b5b9 100644 --- a/internal/api/envelope_test.go +++ b/internal/api/envelope_test.go @@ -24,7 +24,7 @@ func TestParseEnvelopeArrayData(t *testing.T) { // brand get returns data as an OBJECT, contradicting the published spec. // Verified live on 9901287. Prod wins. func TestParseEnvelopeObjectData(t *testing.T) { - body := []byte(`{"data":{"brandId":"B0IRNU4","universalEin":"US_562242657"}}`) + body := []byte(`{"data":{"brandId":"BEXMPL8","universalEin":"US_123456789"}}`) env, err := ParseEnvelope(body) if err != nil { t.Fatalf("ParseEnvelope: %v", err) @@ -33,11 +33,11 @@ func TestParseEnvelopeObjectData(t *testing.T) { if err != nil { t.Fatalf("Object: %v", err) } - if obj["brandId"] != "B0IRNU4" { - t.Errorf("brandId = %v, want B0IRNU4", obj["brandId"]) + if obj["brandId"] != "BEXMPL8" { + t.Errorf("brandId = %v, want BEXMPL8", obj["brandId"]) } // Undocumented field must survive — responses are lossless. - if obj["universalEin"] != "US_562242657" { + if obj["universalEin"] != "US_123456789" { t.Errorf("universalEin was dropped: %v", obj["universalEin"]) } } diff --git a/internal/customerprofile/options.go b/internal/customerprofile/options.go index c023688..0e12be7 100644 --- a/internal/customerprofile/options.go +++ b/internal/customerprofile/options.go @@ -143,7 +143,7 @@ func ValidateUpdate(body map[string]any) error { // // Sends softDeleted:false. The published docs say to send {"deleted": false}, // which returns 404 "Customer profile not found" even though GET returns the -// record — measured against production, reported as MV-23429. +// record — measured against production, reported to the API team. func BuildRestoreRequest(current map[string]any) (map[string]any, error) { body, err := BuildUpdateRequest(current, UpdateOptions{}, map[string]bool{}) if err != nil { diff --git a/internal/customerprofile/service_test.go b/internal/customerprofile/service_test.go index 82e83b1..f3b3a97 100644 --- a/internal/customerprofile/service_test.go +++ b/internal/customerprofile/service_test.go @@ -48,11 +48,11 @@ func TestListWithFilterBuildsQuery(t *testing.T) { }) defer done() - _, err := svc.List(25, 5, []api.Filter{{Field: "brandId", Op: api.OpEq, Value: "B0IRNU4"}}) + _, err := svc.List(25, 5, []api.Filter{{Field: "brandId", Op: api.OpEq, Value: "BEXMPL8"}}) if err != nil { t.Fatalf("List: %v", err) } - if want := "brandId%5Beq%5D=B0IRNU4&limit=25&offset=5"; gotQuery != want { + if want := "brandId%5Beq%5D=BEXMPL8&limit=25&offset=5"; gotQuery != want { t.Errorf("query = %q, want %q", gotQuery, want) } } diff --git a/internal/tendlc/brandoptions.go b/internal/tendlc/brandoptions.go index 1fbda18..01248b0 100644 --- a/internal/tendlc/brandoptions.go +++ b/internal/tendlc/brandoptions.go @@ -230,7 +230,7 @@ func BuildBrandCreateRequest(o BrandCreateOptions) map[string]any { // package that puts an identifier there instead of in the URL. Measured // against production: `brand refresh WET8JUY8H0` (a bandwidthId, not a TCR // brandId) resolved correctly and returned {bandwidthId: WET8JUY8H0, brandId: -// BGJR2BA}, so the "every command accepts either ID" claim holds here too — +// BEXMPL6}, so the "every command accepts either ID" claim holds here too — // it was previously assumed, not verified, for this one command. func BuildBrandRefreshRequest(brandID string) map[string]any { return map[string]any{"brandId": brandID} diff --git a/internal/tendlc/brandoptions_test.go b/internal/tendlc/brandoptions_test.go index 7effc83..19acb80 100644 --- a/internal/tendlc/brandoptions_test.go +++ b/internal/tendlc/brandoptions_test.go @@ -24,7 +24,7 @@ func commonValid() BrandCreateOptions { Email: "ops@acme.com", CompanyName: "Acme Corporation Inc", Vertical: "TECHNOLOGY", - EIN: "562242657", + EIN: "123456789", EINIssuingCountryCodeA3: "USA", } } @@ -272,8 +272,8 @@ func TestBuildBrandCreateRequestIncludesSetOptionalFields(t *testing.T) { // Refresh reuses POST /brands with a body carrying only brandId. Sending // anything else would be read as a create. func TestBuildBrandRefreshRequestSendsOnlyBrandID(t *testing.T) { - body := BuildBrandRefreshRequest("BGJR2BA") - if len(body) != 1 || body["brandId"] != "BGJR2BA" { - t.Errorf("body = %v, want exactly {brandId: BGJR2BA}", body) + body := BuildBrandRefreshRequest("BEXMPL6") + if len(body) != 1 || body["brandId"] != "BEXMPL6" { + t.Errorf("body = %v, want exactly {brandId: BEXMPL6}", body) } } diff --git a/internal/tendlc/brandupdate_test.go b/internal/tendlc/brandupdate_test.go index 7d3ed40..10ef6d8 100644 --- a/internal/tendlc/brandupdate_test.go +++ b/internal/tendlc/brandupdate_test.go @@ -21,22 +21,22 @@ func liveBrand() map[string]any { "altBusinessIdType": nil, "authenticationStatus": nil, "bandwidthId": "WET8JUY8H0", - "brandId": "BGJR2BA", + "brandId": "BEXMPL6", "brandIdentityStatus": "UNVERIFIED", "brandRelationship": "MEDIUM_ACCOUNT", "brandType": "PRIVATE_PROFIT", - "businessContactEmail": "kshah@bandwidth.com", + "businessContactEmail": "contact@example.com", "businessContactEmailVerifiedDate": nil, "city": "Raleigh", "companyName": "Bandwidth Inc", "country": "US", "countryCodeA3": "USA", "createdDate": "2026-05-28T20:34:05.480Z", - "displayName": "Bandwidth Acceptance Test", - "ein": "562242657", + "displayName": "Example Brand", + "ein": "123456789", "einIssuingCountry": "US", "einIssuingCountryCodeA3": "USA", - "email": "npatel@bandwidth.com", + "email": "ops@example.com", "evpVettingScore": nil, "imported": false, "ipAddress": nil, @@ -46,7 +46,7 @@ func liveBrand() map[string]any { "referenceId": "WET8JUY8H0", "state": "NC", "street": "1000 Bandwidth Way", - "universalEin": "US_562242657", + "universalEin": "US_123456789", "vertical": "PROFESSIONAL", "website": "https://bandwidth.com", // A field the CLI does not model at all. It must survive the round trip. diff --git a/internal/tendlc/service_test.go b/internal/tendlc/service_test.go index 16b5c92..26766d0 100644 --- a/internal/tendlc/service_test.go +++ b/internal/tendlc/service_test.go @@ -42,11 +42,11 @@ func TestListBrandsBuildsPathAndQuery(t *testing.T) { func TestGetBrandReturnsObjectEnvelope(t *testing.T) { svc, done := newTestService(t, func(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte(`{"data":{"brandId":"B0IRNU4"}}`)) + _, _ = w.Write([]byte(`{"data":{"brandId":"BEXMPL8"}}`)) }) defer done() - env, err := svc.GetBrand("B0IRNU4") + env, err := svc.GetBrand("BEXMPL8") if err != nil { t.Fatalf("GetBrand: %v", err) } @@ -54,7 +54,7 @@ func TestGetBrandReturnsObjectEnvelope(t *testing.T) { if err != nil { t.Fatalf("Object: %v", err) } - if obj["brandId"] != "B0IRNU4" { + if obj["brandId"] != "BEXMPL8" { t.Errorf("brandId = %v", obj["brandId"]) } } diff --git a/internal/tendlc/write_test.go b/internal/tendlc/write_test.go index b3d0a95..0dabb56 100644 --- a/internal/tendlc/write_test.go +++ b/internal/tendlc/write_test.go @@ -70,13 +70,13 @@ func TestUpdateBrandPutsToBrandPath(t *testing.T) { var got captured s := stubService(t, 202, `{"data":{"bandwidthId":"WABC123"}}`, &got) - if _, err := s.UpdateBrand("BGJR2BA", map[string]any{"displayName": "Acme"}); err != nil { + if _, err := s.UpdateBrand("BEXMPL6", map[string]any{"displayName": "Acme"}); err != nil { t.Fatalf("UpdateBrand: %v", err) } if got.method != "PUT" { t.Errorf("method = %q, want PUT", got.method) } - if want := "/api/v2/accounts/9901287/tendlc/brands/BGJR2BA"; got.path != want { + if want := "/api/v2/accounts/9901287/tendlc/brands/BEXMPL6"; got.path != want { t.Errorf("path = %q, want %q", got.path, want) } } @@ -102,10 +102,10 @@ func TestReverifyAndResend2FAPostToIdentityPaths(t *testing.T) { call func(*Service) error want string }{ - {"reverify", func(s *Service) error { return s.ReverifyBrand("BGJR2BA") }, - "/api/v2/accounts/9901287/tendlc/brands/BGJR2BA/identity/reverify"}, - {"resend2fa", func(s *Service) error { return s.Resend2FA("BGJR2BA") }, - "/api/v2/accounts/9901287/tendlc/brands/BGJR2BA/identity/resend2faEmail"}, + {"reverify", func(s *Service) error { return s.ReverifyBrand("BEXMPL6") }, + "/api/v2/accounts/9901287/tendlc/brands/BEXMPL6/identity/reverify"}, + {"resend2fa", func(s *Service) error { return s.Resend2FA("BEXMPL6") }, + "/api/v2/accounts/9901287/tendlc/brands/BEXMPL6/identity/resend2faEmail"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -130,10 +130,10 @@ func TestBrandHistoryEncodesPagination(t *testing.T) { var got captured s := stubService(t, 200, `{"data":[],"page":{"totalElements":0}}`, &got) - if _, err := s.BrandHistory("BGJR2BA", 10, 20); err != nil { + if _, err := s.BrandHistory("BEXMPL6", 10, 20); err != nil { t.Fatalf("BrandHistory: %v", err) } - if want := "/api/v2/accounts/9901287/tendlc/brands/BGJR2BA/history"; got.path != want { + if want := "/api/v2/accounts/9901287/tendlc/brands/BEXMPL6/history"; got.path != want { t.Errorf("path = %q, want %q", got.path, want) } if got.query != "limit=10&offset=20" { @@ -145,10 +145,10 @@ func TestListVettingsEncodesPagination(t *testing.T) { var got captured s := stubService(t, 200, `{"data":[],"page":{"totalElements":0}}`, &got) - if _, err := s.ListVettings("BGJR2BA", 10, 0); err != nil { + if _, err := s.ListVettings("BEXMPL6", 10, 0); err != nil { t.Fatalf("ListVettings: %v", err) } - if want := "/api/v2/accounts/9901287/tendlc/brands/BGJR2BA/vettings"; got.path != want { + if want := "/api/v2/accounts/9901287/tendlc/brands/BEXMPL6/vettings"; got.path != want { t.Errorf("path = %q, want %q", got.path, want) } } @@ -158,7 +158,7 @@ func TestRequestVettingPostsBody(t *testing.T) { s := stubService(t, 202, `{"data":{"bandwidthId":"WV123"}}`, &got) body := map[string]any{"evpId": "AEGIS", "vettingClass": "STANDARD"} - if _, err := s.RequestVetting("BGJR2BA", body); err != nil { + if _, err := s.RequestVetting("BEXMPL6", body); err != nil { t.Fatalf("RequestVetting: %v", err) } if got.method != "POST" { @@ -173,13 +173,13 @@ func TestImportVettingPutsToVettingPath(t *testing.T) { var got captured s := stubService(t, 202, `{"data":{"bandwidthId":"WV123"}}`, &got) - if _, err := s.ImportVetting("BGJR2BA", "978de74a-7191", map[string]any{"evpId": "AEGIS"}); err != nil { + if _, err := s.ImportVetting("BEXMPL6", "978de74a-7191", map[string]any{"evpId": "AEGIS"}); err != nil { t.Fatalf("ImportVetting: %v", err) } if got.method != "PUT" { t.Errorf("method = %q, want PUT", got.method) } - if want := "/api/v2/accounts/9901287/tendlc/brands/BGJR2BA/vettings/978de74a-7191"; got.path != want { + if want := "/api/v2/accounts/9901287/tendlc/brands/BEXMPL6/vettings/978de74a-7191"; got.path != want { t.Errorf("path = %q, want %q", got.path, want) } } @@ -250,10 +250,10 @@ func TestVettingIDIsPathEscaped(t *testing.T) { var got captured s := stubService(t, 202, `{"data":{"bandwidthId":"WV123"}}`, &got) - if _, err := s.ImportVetting("BGJR2BA", "v/1 2", map[string]any{"evpId": "AEGIS"}); err != nil { + if _, err := s.ImportVetting("BEXMPL6", "v/1 2", map[string]any{"evpId": "AEGIS"}); err != nil { t.Fatalf("ImportVetting: %v", err) } - if want := "/api/v2/accounts/9901287/tendlc/brands/BGJR2BA/vettings/v%2F1%202"; got.escapedPath != want { + if want := "/api/v2/accounts/9901287/tendlc/brands/BEXMPL6/vettings/v%2F1%202"; got.escapedPath != want { t.Errorf("escaped path = %q, want %q", got.escapedPath, want) } }