From df15513bdb223b852db45b0905da4ae38b58c344 Mon Sep 17 00:00:00 2001 From: Astrid Gealer Date: Mon, 26 Oct 2026 09:41:13 +0000 Subject: [PATCH] fix(purchase): list Azure Cosmos DB's nosql slug in mapServiceSlug Settings writes Azure Cosmos DB service configs and overrides under "nosql", the slug Cosmos recommendations carry, but mapServiceSlug did not list it, so the settings test had to carry azure/nosql as a named exception. The Azure provider's GetServiceClient already accepts common.ServiceNoSQL, and mapServiceType already passed "nosql" through verbatim, so dispatch is unchanged; the slug table now states it. The frontend test drops the exception, and a purchase test pins every canonical reservation slug, nosql included. Co-Authored-By: Claude Opus 5.5 --- .../src/__tests__/settings-service-ids.test.ts | 7 +------ internal/purchase/coverage_extra_test.go | 1 + internal/purchase/execution.go | 13 ++++++++----- internal/purchase/redrive_guard_reach_test.go | 2 +- internal/purchase/slug_alias_drift_test.go | 15 +++++++++++++++ 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/frontend/src/__tests__/settings-service-ids.test.ts b/frontend/src/__tests__/settings-service-ids.test.ts index 317492498..68d9a21a9 100644 --- a/frontend/src/__tests__/settings-service-ids.test.ts +++ b/frontend/src/__tests__/settings-service-ids.test.ts @@ -28,11 +28,6 @@ jest.mock('../toast', () => ({ showToast: jest.fn(() => ({ dismiss: jest.fn() }) import * as api from '../api'; -// Cosmos DB recommendations carry common.ServiceNoSQL ("nosql"), which -// mapServiceSlug does not list yet (no Cosmos purchase path). Settings must -// still use that id so its config and overrides match those recommendations. -const NOT_IN_PURCHASE_SLUGS = ['azure/nosql']; - function backendServiceSlugs(): string[] { const src = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'internal', 'purchase', 'execution.go'), 'utf-8'); const block = /func mapServiceSlug\(service string\)[\s\S]*?slugs := map\[string\]common\.ServiceType\{([\s\S]*?)\n\t\}/.exec(src); @@ -122,7 +117,7 @@ describe('Settings Azure/GCP service ids (issue #613)', () => { expect(await overrideOptions(provider)).toEqual(SETTINGS_SERVICE_IDS[provider]); offered.push(...saved.map(s => `${provider}/${s}`)); } - expect(offered.filter(id => !slugs.includes(id.split('/')[1]!))).toEqual(NOT_IN_PURCHASE_SLUGS); + expect(offered.filter(id => !slugs.includes(id.split('/')[1]!))).toEqual([]); }); it('shows rows stored under the old ids on their cards and saves them under the current ids', async () => { diff --git a/internal/purchase/coverage_extra_test.go b/internal/purchase/coverage_extra_test.go index 1285bf4ca..50283fa9c 100644 --- a/internal/purchase/coverage_extra_test.go +++ b/internal/purchase/coverage_extra_test.go @@ -43,6 +43,7 @@ func TestMapServiceType_AllBranches(t *testing.T) { // canonical-only. {"compute", common.ServiceCompute}, {"relational-db", common.ServiceRelationalDB}, + {"nosql", common.ServiceNoSQL}, {"cache", common.ServiceCache}, {"search", common.ServiceSearch}, {"data-warehouse", common.ServiceDataWarehouse}, diff --git a/internal/purchase/execution.go b/internal/purchase/execution.go index 16679db4b..578126257 100644 --- a/internal/purchase/execution.go +++ b/internal/purchase/execution.go @@ -1275,11 +1275,13 @@ func resolveEC2OfferingClass(s string) (ec2types.OfferingClassType, error) { // mapServiceSlug returns the common.ServiceType for a canonical or legacy // service slug. // -// Canonical slugs (compute, relational-db, cache, search, data-warehouse) -// map to the canonical ServiceType constants. Legacy AWS-only slugs (ec2, -// rds, elasticache, opensearch, redshift, memorydb) map to the AWS-legacy -// constants for backward compat with rec rows persisted before the -// canonical normalisation. +// Canonical slugs (compute, relational-db, nosql, cache, search, +// data-warehouse) map to the canonical ServiceType constants. Legacy +// AWS-only slugs (ec2, rds, elasticache, opensearch, redshift, memorydb) +// map to the AWS-legacy constants for backward compat with rec rows +// persisted before the canonical normalisation. nosql is Azure Cosmos DB +// (#613); it used to reach the Azure client only through mapServiceType's +// verbatim fallback. // // The split matters because Azure and GCP providers' GetServiceClient // switches accept canonical-only; passing the AWS-legacy constants for a @@ -1296,6 +1298,7 @@ func mapServiceSlug(service string) (common.ServiceType, bool) { // Canonical slugs. "compute": common.ServiceCompute, "relational-db": common.ServiceRelationalDB, + "nosql": common.ServiceNoSQL, "cache": common.ServiceCache, "search": common.ServiceSearch, "data-warehouse": common.ServiceDataWarehouse, diff --git a/internal/purchase/redrive_guard_reach_test.go b/internal/purchase/redrive_guard_reach_test.go index 09212bf5e..4fb23775a 100644 --- a/internal/purchase/redrive_guard_reach_test.go +++ b/internal/purchase/redrive_guard_reach_test.go @@ -52,7 +52,7 @@ func TestRedriveGuardReachMatchesDispatchReach(t *testing.T) { "savings-plans-database", "savingsplans-database", // Every key of mapServiceSlug (execution.go). - "compute", "relational-db", "cache", "search", "data-warehouse", + "compute", "relational-db", "nosql", "cache", "search", "data-warehouse", "ec2", "rds", "elasticache", "opensearch", "redshift", "memorydb", // The literal value of all 20 common.ServiceType constants, so a value diff --git a/internal/purchase/slug_alias_drift_test.go b/internal/purchase/slug_alias_drift_test.go index 739088705..e06aba15b 100644 --- a/internal/purchase/slug_alias_drift_test.go +++ b/internal/purchase/slug_alias_drift_test.go @@ -42,3 +42,18 @@ func TestLegacyAWSSlugsMatchSuppressionAliases(t *testing.T) { }) } } + +// TestMapServiceSlugAcceptsEveryCanonicalReservationSlug pins the canonical +// slugs, including Azure Cosmos DB's "nosql" (#613). The Settings page writes +// service configs and overrides under these ids, and its test reads this +// table, so a slug missing here shows up there as a slug the backend rejects. +func TestMapServiceSlugAcceptsEveryCanonicalReservationSlug(t *testing.T) { + for _, want := range []common.ServiceType{ + common.ServiceCompute, common.ServiceRelationalDB, common.ServiceNoSQL, + common.ServiceCache, common.ServiceSearch, common.ServiceDataWarehouse, + } { + got, ok := mapServiceSlug(string(want)) + require.True(t, ok, "mapServiceSlug rejects canonical slug %q", want) + assert.Equal(t, want, got) + } +}