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 25a322dc8..901ab4b74 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 b437836c6..5d22b8c1c 100644 --- a/internal/purchase/slug_alias_drift_test.go +++ b/internal/purchase/slug_alias_drift_test.go @@ -43,6 +43,21 @@ 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) + } +} + // TestSavingsPlansSpellingsMatchSuppressionAliases checks that every per-type // Savings Plans spelling the purchase manager accepts canonicalises, for // suppressions, to the slug it buys, so a suppression stored under the