From dd398b3a933fe5c577d390eba088959c6ca37be1 Mon Sep 17 00:00:00 2001 From: Julian Sommer <115516918+juliansommer@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:39:16 +1000 Subject: [PATCH] test: run the rds suite in parallel (#806) --- spinifex/handlers/rds/agent_test.go | 18 ++++++++++ spinifex/handlers/rds/arn_parse_test.go | 1 + spinifex/handlers/rds/arn_test.go | 1 + spinifex/handlers/rds/backup_sweep_test.go | 17 +++++++++ spinifex/handlers/rds/backup_test.go | 36 ++++++++++++++++++++ spinifex/handlers/rds/bootstrap_test.go | 18 ++++++++++ spinifex/handlers/rds/commands_test.go | 5 +++ spinifex/handlers/rds/create_test.go | 17 +++++++++ spinifex/handlers/rds/delete_test.go | 11 ++++++ spinifex/handlers/rds/describe_test.go | 18 ++++++++++ spinifex/handlers/rds/engine_mariadb_test.go | 16 +++++++++ spinifex/handlers/rds/engine_test.go | 3 ++ spinifex/handlers/rds/events_test.go | 7 ++++ spinifex/handlers/rds/iam_test.go | 5 +++ spinifex/handlers/rds/launch_test.go | 14 ++++++++ spinifex/handlers/rds/lifecycle_test.go | 18 ++++++++++ spinifex/handlers/rds/modify_lease_test.go | 8 +++++ spinifex/handlers/rds/modify_test.go | 23 +++++++++++++ spinifex/handlers/rds/network_test.go | 2 ++ spinifex/handlers/rds/paramcatalog_test.go | 10 ++++++ spinifex/handlers/rds/parametergroup_test.go | 29 ++++++++++++++++ spinifex/handlers/rds/pending_test.go | 18 ++++++++++ spinifex/handlers/rds/reconciler_test.go | 10 ++++++ spinifex/handlers/rds/recovery_test.go | 13 +++++++ spinifex/handlers/rds/replace_test.go | 13 +++++++ spinifex/handlers/rds/restore_test.go | 15 ++++++++ spinifex/handlers/rds/sizing_test.go | 5 +++ spinifex/handlers/rds/snapshot_test.go | 26 ++++++++++++++ spinifex/handlers/rds/status_test.go | 3 ++ spinifex/handlers/rds/storage_test.go | 10 ++++++ spinifex/handlers/rds/store_test.go | 7 ++++ spinifex/handlers/rds/subnetgroup_test.go | 12 +++++++ spinifex/handlers/rds/tags_test.go | 12 +++++++ spinifex/handlers/rds/tlsenforcement_test.go | 5 +++ spinifex/handlers/rds/userdata_test.go | 3 ++ spinifex/handlers/rds/vpc_test.go | 5 +++ 36 files changed, 434 insertions(+) diff --git a/spinifex/handlers/rds/agent_test.go b/spinifex/handlers/rds/agent_test.go index 9938e27d5..c722b308c 100644 --- a/spinifex/handlers/rds/agent_test.go +++ b/spinifex/handlers/rds/agent_test.go @@ -125,6 +125,7 @@ func readRecord(t *testing.T, svc *Service) (DBInstanceRecord, string) { } func TestGetDBBootstrapConfig_ServesTheFullBootMaterial(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() rec.DataVolumeID = "vol-data-01" @@ -195,6 +196,7 @@ func TestGetDBBootstrapConfig_FormatGrantRequiresExactCurrentIdentity(t *testing } func TestGetDBBootstrapConfig_MintsFreshCertEachFetch(t *testing.T) { + t.Parallel() svc := newTestService(t) seedInstance(t, svc, defaultRecord()) ctx := context.Background() @@ -221,6 +223,7 @@ func TestGetDBBootstrapConfig_MintsFreshCertEachFetch(t *testing.T) { // The SAN set is what makes sslmode=verify-full work. The IP is the required // one, because the endpoint is a bare IP wherever DNS is not configured. func TestGetDBBootstrapConfig_CertSANsCoverIPAndHostname(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() seedInstance(t, svc, rec) @@ -237,6 +240,7 @@ func TestGetDBBootstrapConfig_CertSANsCoverIPAndHostname(t *testing.T) { } func TestGetDBBootstrapConfig_NoDNSNameStillMintsIPOnlyCert(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() rec.DNSName = "" @@ -255,6 +259,7 @@ func TestGetDBBootstrapConfig_NoDNSNameStillMintsIPOnlyCert(t *testing.T) { // is refused at binding rather than here — so the fetch must still serve a // config and boot a database. func TestGetDBBootstrapConfig_NoCAStillServesConfig(t *testing.T) { + t.Parallel() _, nc, _ := testutil.StartTestJetStream(t) svc := NewService(nc, testRegion).WithDeps(Deps{MasterKey: testMasterKey}) seedPending(t, svc, defaultRecord()) @@ -267,6 +272,7 @@ func TestGetDBBootstrapConfig_NoCAStillServesConfig(t *testing.T) { } func TestGetDBBootstrapConfig_ConfiguredCARequiresENIPrivateIP(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() rec.ENIPrivateIP = "" @@ -281,6 +287,7 @@ func TestGetDBBootstrapConfig_ConfiguredCARequiresENIPrivateIP(t *testing.T) { } func TestGetDBBootstrapConfig_PartialCAConfigurationFailsClosed(t *testing.T) { + t.Parallel() _, nc, _ := testutil.StartTestJetStream(t) svc := NewService(nc, testRegion).WithDeps(Deps{ CACertPath: "/etc/spinifex/ca.pem", @@ -300,6 +307,7 @@ func TestGetDBBootstrapConfig_PartialCAConfigurationFailsClosed(t *testing.T) { // ever read. The staged password is untouched, which is what makes the agent's // retry succeed once the CA is readable again. func TestGetDBBootstrapConfig_UnloadableCALeavesPasswordRecoverable(t *testing.T) { + t.Parallel() _, nc, _ := testutil.StartTestJetStream(t) loadErr := errors.New("read CA key /etc/spinifex/ca.key: permission denied") broken := true @@ -333,6 +341,7 @@ func TestGetDBBootstrapConfig_UnloadableCALeavesPasswordRecoverable(t *testing.T // the whole problem. A replay has no race to lose: every caller bound to the // generation is served the same password and none of them writes. func TestGetDBBootstrapConfig_ConcurrentFetchesAllReplayTheSamePassword(t *testing.T) { + t.Parallel() svc := newTestService(t) payloadID := seedPending(t, svc, defaultRecord()) _, beforeRaw := readRecord(t, svc) @@ -367,6 +376,7 @@ func TestGetDBBootstrapConfig_ConcurrentFetchesAllReplayTheSamePassword(t *testi } func TestGetDBBootstrapConfig_UnknownInstance(t *testing.T) { + t.Parallel() svc := newTestService(t) _, err := svc.GetDBBootstrapConfig(context.Background(), &GetDBBootstrapConfigInput{DBInstanceIdentifier: "ghost", InstanceID: testInstance}, testAccountID) @@ -385,6 +395,7 @@ func parseCertPEM(t *testing.T, pemStr string) *x509.Certificate { } func TestRegisterDBInstance_IdempotentAndRefreshesLastSeen(t *testing.T) { + t.Parallel() svc := newTestService(t) seedInstance(t, svc, defaultRecord()) ctx := context.Background() @@ -416,6 +427,7 @@ func TestRegisterDBInstance_IdempotentAndRefreshesLastSeen(t *testing.T) { // A replace mints a new instance ID, which is a new registration rather than a // continuation of the old VM's. func TestRegisterDBInstance_NewVMResetsRegisteredAt(t *testing.T) { + t.Parallel() svc := newTestService(t) seedInstance(t, svc, defaultRecord()) ctx := context.Background() @@ -434,6 +446,7 @@ func TestRegisterDBInstance_NewVMResetsRegisteredAt(t *testing.T) { } func TestRegisterDBInstance_UnknownInstance(t *testing.T) { + t.Parallel() svc := newTestService(t) _, err := svc.RegisterDBInstance(context.Background(), &RegisterDBInstanceInput{DBInstanceIdentifier: "ghost", InstanceID: testInstance}, testAccountID) @@ -444,6 +457,7 @@ func TestRegisterDBInstance_UnknownInstance(t *testing.T) { // Liveness is the hottest path in the service, so an unchanged beat must stay in // memory and only reach KV on the slower floor. func TestSubmitDBStateChange_PersistsOnChangeAndOnFloor(t *testing.T) { + t.Parallel() svc := newTestService(t) seedInstance(t, svc, defaultRecord()) ctx := context.Background() @@ -479,6 +493,7 @@ func TestSubmitDBStateChange_PersistsOnChangeAndOnFloor(t *testing.T) { } func TestSubmitDBStateChange_RecordsParameterRollback(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() rec.DBParameterGroupName = "customer-params" @@ -513,6 +528,7 @@ func TestSubmitDBStateChange_RecordsParameterRollback(t *testing.T) { // In-memory liveness is fresher than the record between persists, which is what // lets the reconciler judge staleness without reading KV every tick. func TestSubmitDBStateChange_LastSeenTracksUnpersistedBeats(t *testing.T) { + t.Parallel() svc := newTestService(t) seedInstance(t, svc, defaultRecord()) ctx := context.Background() @@ -541,6 +557,7 @@ func TestSubmitDBStateChange_LastSeenTracksUnpersistedBeats(t *testing.T) { } func TestSubmitDBStateChange_RejectsUnknownHealth(t *testing.T) { + t.Parallel() svc := newTestService(t) seedInstance(t, svc, defaultRecord()) _, err := svc.SubmitDBStateChange(context.Background(), &SubmitDBStateChangeInput{ @@ -551,6 +568,7 @@ func TestSubmitDBStateChange_RejectsUnknownHealth(t *testing.T) { } func TestInstanceIndex_RoundTrip(t *testing.T) { + t.Parallel() svc := newTestService(t) ctx := context.Background() diff --git a/spinifex/handlers/rds/arn_parse_test.go b/spinifex/handlers/rds/arn_parse_test.go index da14addf7..b249c5b24 100644 --- a/spinifex/handlers/rds/arn_parse_test.go +++ b/spinifex/handlers/rds/arn_parse_test.go @@ -74,6 +74,7 @@ func TestParseARN_RejectsMalformedAndForeignARNs(t *testing.T) { // The registry is what makes an unregistered kind a rejection rather than an // empty tag list, so every kind the parser accepts has to be accounted for. func TestTaggableResources_CoverEveryResourceThatExists(t *testing.T) { + t.Parallel() for _, kind := range []ResourceKind{ResourceKindDBInstance, ResourceKindDBSnapshot, ResourceKindDBSubnetGroup, ResourceKindDBParameterGroup} { _, ok := taggableResources[kind] diff --git a/spinifex/handlers/rds/arn_test.go b/spinifex/handlers/rds/arn_test.go index 9cfb39f98..1239eb826 100644 --- a/spinifex/handlers/rds/arn_test.go +++ b/spinifex/handlers/rds/arn_test.go @@ -12,6 +12,7 @@ const ( ) func TestARNBuilders(t *testing.T) { + t.Parallel() assert.Equal(t, "arn:aws:rds:ap-southeast-2:123456789012:db:orders-db", DBInstanceARN(testRegion, testAccountID, "orders-db")) assert.Equal(t, "arn:aws:rds:ap-southeast-2:123456789012:snapshot:orders-db-2026-07-24", diff --git a/spinifex/handlers/rds/backup_sweep_test.go b/spinifex/handlers/rds/backup_sweep_test.go index 58d77302b..fcb3b049f 100644 --- a/spinifex/handlers/rds/backup_sweep_test.go +++ b/spinifex/handlers/rds/backup_sweep_test.go @@ -78,6 +78,7 @@ func retainingRecord(days int64) DBInstanceRecord { // while KV is unhealthy, and cluster-wide scope is leader-gated by the framework // rather than by anything here. func TestBackupRetentionReaper_IsAClusterWideReaper(t *testing.T) { + t.Parallel() reaper := NewService(nil, testRegion).NewBackupRetentionReaper() assert.Equal(t, "rds-backup-retention", reaper.Class()) assert.Equal(t, vm.ScopeClusterWide, reaper.Scope()) @@ -88,6 +89,7 @@ func TestBackupRetentionReaper_IsAClusterWideReaper(t *testing.T) { } func TestSweep_RemovesOnlyTheBackupsPastRetention(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(7)) @@ -113,6 +115,7 @@ func TestSweep_RemovesOnlyTheBackupsPastRetention(t *testing.T) { // If backup creation has been failing for a week, strict retention would delete // the whole backup set at the moment a backup matters most. func TestSweep_NeverDeletesTheNewestBackupWhileBackupsAreOn(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(1)) @@ -132,6 +135,7 @@ func TestSweep_NeverDeletesTheNewestBackupWhileBackupsAreOn(t *testing.T) { // A snapshot still being cut is not something the customer could restore from, so // keeping it instead of the newest available one would leave them with nothing. func TestSweep_KeepsTheNewestAvailableRatherThanTheNewest(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(1)) @@ -147,6 +151,7 @@ func TestSweep_KeepsTheNewestAvailableRatherThanTheNewest(t *testing.T) { // The point of turning automated backups off is to make the data volume // GC-eligible again, so the last one goes too. func TestSweep_RemovesEverythingWhenBackupsAreOff(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(0)) @@ -163,6 +168,7 @@ func TestSweep_RemovesEverythingWhenBackupsAreOff(t *testing.T) { // restore an instance that no longer exists, and the alternative is a data volume // pinned for good. func TestSweep_RemovesEverythingWhenTheInstanceIsGone(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) newest := h.seedBackup(t, time.Hour) @@ -177,6 +183,7 @@ func TestSweep_RemovesEverythingWhenTheInstanceIsGone(t *testing.T) { // and the branch it would select deletes a live instance's whole backup set. The // bucket listing is the corroboration that stops it. func TestSweep_LeavesTheBackupSetWhenTheRecordCannotBeRead(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(7)) newest := h.seedBackup(t, time.Hour) @@ -214,6 +221,7 @@ func (m missingKey) Get(ctx context.Context, key string) (jetstream.KeyValueEntr // skip rather than a failure: the next pass retries, and the index entry stays so // the backup is not lost from it. func TestSweep_SkipsABackupStillInUse(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(0)) inUse := h.seedBackup(t, 2*oneDay) @@ -235,6 +243,7 @@ func TestSweep_SkipsABackupStillInUse(t *testing.T) { // A pass that under-collects is corrected by the next one two minutes later; a // pass that walks an unbounded number of snapshots is not. func TestSweep_IsBoundedPerPassAndResumes(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.svc = h.svc.WithDeps(Deps{ LoadCA: newTestCA(t), @@ -261,6 +270,7 @@ func TestSweep_IsBoundedPerPassAndResumes(t *testing.T) { // cleared so the index stops naming it, and nothing is counted as reaped because // no data went with it. func TestSweep_DropsAnIndexEntryWhoseSnapshotIsGone(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(7)) @@ -281,6 +291,7 @@ func TestSweep_DropsAnIndexEntryWhoseSnapshotIsGone(t *testing.T) { // Every tenant's retention is enforced on the same pass, so one account's backlog // cannot leave another's unenforced. func TestSweep_CoversEveryAccount(t *testing.T) { + t.Parallel() const otherAccount = "210987654321" h := newSnapshotHarness(t, false) @@ -301,6 +312,7 @@ func TestSweep_CoversEveryAccount(t *testing.T) { // A truncated listing must never read as "no accounts": that would leave every // tenant's retention unenforced while looking like a clean pass. func TestSweep_FailsWhenTheAccountBucketsCannotBeEnumerated(t *testing.T) { + t.Parallel() _, err := NewService(nil, testRegion).NewBackupRetentionReaper().Sweep(t.Context()) require.Error(t, err) } @@ -308,6 +320,7 @@ func TestSweep_FailsWhenTheAccountBucketsCannotBeEnumerated(t *testing.T) { // The backstop for a crash between the last DeleteDBSnapshot and the inline // volume delete that follows it: nothing else references the volume by then. func TestSweep_ReclaimsAnOrphanedRetainedVolume(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) kv, err := h.svc.bucket(t.Context(), testAccountID) require.NoError(t, err) @@ -331,6 +344,7 @@ func TestSweep_ReclaimsAnOrphanedRetainedVolume(t *testing.T) { // written with: a snapshot taken since would read as "nothing holds it", and one // deleted since would pin the volume for good. func TestSweep_ReChecksTheHoldersRatherThanTrustingTheRecord(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) kv, err := h.svc.bucket(t.Context(), testAccountID) require.NoError(t, err) @@ -356,6 +370,7 @@ func TestSweep_ReChecksTheHoldersRatherThanTrustingTheRecord(t *testing.T) { // "nobody". It is recorded and re-checked rather than failing the pass, so a // disagreement between the two indexes never wedges the sweep. func TestSweep_RetainsAVolumeTheVolumeStoreRefusesToRelease(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) kv, err := h.svc.bucket(t.Context(), testAccountID) require.NoError(t, err) @@ -378,6 +393,7 @@ func TestSweep_RetainsAVolumeTheVolumeStoreRefusesToRelease(t *testing.T) { // Turning automated backups off sweeps the set that exists rather than leaving it // to expire against a retention that is now zero. func TestModifyDBInstance_SweepsTheAutomatedBackupsWhenRetentionGoesToZero(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, retainingRecord(7)) swept := h.seedBackup(t, time.Hour) @@ -396,6 +412,7 @@ func TestModifyDBInstance_SweepsTheAutomatedBackupsWhenRetentionGoesToZero(t *te // An automated backup outliving its instance would pin the instance's data // volume for good, so teardown sweeps the set before releasing the volume. func TestDeleteDBInstance_SweepsTheAutomatedBackups(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) swept := seedAutomatedBackup(t, h.svc, h.snaps, testAccountID, testDBID, oneDay, SnapshotStatusAvailable) diff --git a/spinifex/handlers/rds/backup_test.go b/spinifex/handlers/rds/backup_test.go index a86d619c4..acb7477b1 100644 --- a/spinifex/handlers/rds/backup_test.go +++ b/spinifex/handlers/rds/backup_test.go @@ -117,6 +117,7 @@ func TestParseDailyWindow_RejectsWhatAWSRejects(t *testing.T) { } func TestParseDailyWindow_AcceptsAWindowThatWrapsMidnight(t *testing.T) { + t.Parallel() window, err := parseDailyWindow("PreferredBackupWindow", "23:45-00:15") require.NoError(t, err) assert.Equal(t, windowSlot, window.length()) @@ -155,6 +156,7 @@ func TestParseWeeklyWindow_RejectsWhatAWSRejects(t *testing.T) { } func TestParseWeeklyWindow_AcceptsAWindowThatWrapsTheWeek(t *testing.T) { + t.Parallel() window, err := parseWeeklyWindow("PreferredMaintenanceWindow", "sat:23:45-sun:00:15") require.NoError(t, err) assert.Equal(t, windowSlot, window.length()) @@ -171,6 +173,7 @@ func TestParseWeeklyWindow_AcceptsAWindowThatWrapsTheWeek(t *testing.T) { // AWS refuses the pair rather than the individual window, because the two would // collide the week the maintenance window's day comes round. func TestValidateWindows_RejectsAnOverlappingPair(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) _, _, err := svc.validateWindows(testDBID, "03:00-04:00", "mon:03:30-mon:04:30") @@ -187,6 +190,7 @@ func TestValidateWindows_RejectsAnOverlappingPair(t *testing.T) { // Canonical text, not the customer's: a describe has to read back the string a // later modify compares against, or every modify would look like a change. func TestValidateWindows_ReturnsTheCanonicalText(t *testing.T) { + t.Parallel() backup, maintenance, err := NewService(nil, testRegion). validateWindows(testDBID, "03:00-04:00", "SUN:05:00-sun:06:00") require.NoError(t, err) @@ -198,6 +202,7 @@ func TestValidateWindows_ReturnsTheCanonicalText(t *testing.T) { // window that moved whenever the record was rewritten would back up at an hour // the customer never saw. func TestValidateWindows_AssignsStableNonOverlappingWindows(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) block := svc.backupWindowBlock() maintenanceBlock := svc.maintenanceWindowBlock() @@ -237,6 +242,7 @@ func TestValidateWindows_AssignsStableNonOverlappingWindows(t *testing.T) { // second one on top of it would fail the same identifier deterministically, // forever. func TestValidateWindows_AssignsAroundACustomerNamedWindow(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) // Inside the other window's block, which is where a collision is possible at // all: an hour of the maintenance block covers two of its assignable slots. @@ -270,6 +276,7 @@ func TestValidateWindows_AssignsAroundACustomerNamedWindow(t *testing.T) { // A pair the customer named in full is still refused — the assignment moving out // of the way is for windows the platform chose, not for the ones it was given. func TestValidateWindows_StillRejectsAPairTheCustomerNamed(t *testing.T) { + t.Parallel() _, _, err := NewService(nil, testRegion). validateWindows(testDBID, "13:00-14:00", "wed:13:30-wed:14:30") require.Error(t, err) @@ -280,6 +287,7 @@ func TestValidateWindows_StillRejectsAPairTheCustomerNamed(t *testing.T) { // stored the other one derived it. Anything else would schedule the two passes // over each other on exactly the records that name one window. func TestResolvedWindows_AssignAroundTheStoredWindow(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) for i := range 200 { @@ -307,6 +315,7 @@ func assertWindowsDoNotOverlap(t *testing.T, id, backup, maintenance string) { // An operator's typo cannot be allowed to fail every create on the node, so the // built-in block is used and the fault is logged instead. func TestWindowBlock_FallsBackToTheBuiltInBlock(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion).WithDeps(Deps{Backup: BackupPolicy{ BackupWindowBlock: "not-a-window", MaintenanceWindowBlock: "19:00", @@ -322,6 +331,7 @@ func TestWindowBlock_FallsBackToTheBuiltInBlock(t *testing.T) { } func TestValidateRetentionPeriod_BoundsTheRetention(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) require.NoError(t, svc.validateRetentionPeriod(0)) require.NoError(t, svc.validateRetentionPeriod(defaultBackupRetentionCapDays)) @@ -345,6 +355,7 @@ func TestValidateRetentionPeriod_BoundsTheRetention(t *testing.T) { // window across leader churn and daemon restarts, and what stops a missed window // from being backfilled. func TestBackupDue_FiresOncePerWindowAndNeverBackfills(t *testing.T) { + t.Parallel() window, err := parseDailyWindow("PreferredBackupWindow", "03:00-03:30") require.NoError(t, err) svc := NewService(nil, testRegion) @@ -382,6 +393,7 @@ func TestBackupDue_FiresOncePerWindowAndNeverBackfills(t *testing.T) { // the backoff is what keeps a broken backup from re-quiescing the engine on every // two-minute pass. func TestBackupDue_PacesRetriesInsideTheWindow(t *testing.T) { + t.Parallel() window, err := parseDailyWindow("PreferredBackupWindow", "03:00-03:30") require.NoError(t, err) svc := NewService(nil, testRegion) @@ -409,6 +421,7 @@ func TestBackupDue_PacesRetriesInsideTheWindow(t *testing.T) { } func TestRunBackupWindow_TakesIndexesAndStampsTheBackup(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, backupReadyRecord()) @@ -451,6 +464,7 @@ func TestRunBackupWindow_TakesIndexesAndStampsTheBackup(t *testing.T) { // The window fires once however many passes run inside it, which is what a // two-minute reconciler and a leader handover both look like from here. func TestRunBackupWindow_FiresOncePerWindow(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, backupReadyRecord()) @@ -463,6 +477,7 @@ func TestRunBackupWindow_FiresOncePerWindow(t *testing.T) { } func TestRunBackupWindow_DoesNotFireOutsideTheWindow(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() rec.PreferredBackupWindow = closedBackupWindow(time.Now().UTC()) @@ -474,6 +489,7 @@ func TestRunBackupWindow_DoesNotFireOutsideTheWindow(t *testing.T) { } func TestRunBackupWindow_DoesNotFireWithBackupsDisabled(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() rec.BackupRetentionPeriod = 0 @@ -488,6 +504,7 @@ func TestRunBackupWindow_DoesNotFireWithBackupsDisabled(t *testing.T) { // the next window rather than this one. The skip is evented, because a customer // looking for last night's backup has to be able to see why there is none. func TestRunBackupWindow_SkipsAnInstanceItCannotBackUp(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() rec.Status = StatusRebooting @@ -514,6 +531,7 @@ func TestRunBackupWindow_SkipsAnInstanceItCannotBackUp(t *testing.T) { // A failed backup is a backup fault, never an instance fault. The // database stays available and the failure is counted so it is visible. func TestRunBackupWindow_CountsAFailureAndLeavesTheInstanceAvailable(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, backupReadyRecord()) h.snaps.createErr = assert.AnError @@ -534,6 +552,7 @@ func TestRunBackupWindow_CountsAFailureAndLeavesTheInstanceAvailable(t *testing. // A stored window this plane cannot parse is corruption rather than a schedule: // firing on a guessed window would back up at an hour nobody asked for. func TestRunBackupWindow_IgnoresAMalformedStoredWindow(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() rec.PreferredBackupWindow = "0300-0400" @@ -549,6 +568,7 @@ func TestRunBackupWindow_IgnoresAMalformedStoredWindow(t *testing.T) { // still carries none after it: reporting one back would show as drift in the next // configuration read for a request that never set it. func TestModifyDBInstance_DoesNotPersistTheWindowItWasNotGiven(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := retainingRecord(7) rec.PreferredMaintenanceWindow = "" @@ -569,6 +589,7 @@ func TestModifyDBInstance_DoesNotPersistTheWindowItWasNotGiven(t *testing.T) { // A record written before window fields existed carries no window at all, and still has to be // backed up — on the window a describe reports for it. func TestResolvedBackupWindow_DerivesAWindowForARecordWithoutOne(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) rec := &DBInstanceRecord{DBInstanceIdentifier: testDBID} @@ -591,6 +612,7 @@ func TestResolvedBackupWindow_DerivesAWindowForARecordWithoutOne(t *testing.T) { // rather than never. Only the transition happens here; the apply itself is the // reconciler's single drain through applyPendingModifications. func TestRunMaintenanceWindow_OpensADeferredModify(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() rec.FormatAuthorized = true @@ -647,6 +669,7 @@ func TestRunMaintenanceWindow_StandsDownWhenThereIsNothingToApply(t *testing.T) } func TestRunMaintenanceWindow_OpensOncePerWindow(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() rec.PreferredMaintenanceWindow = openMaintenanceWindow(time.Now().UTC()) @@ -664,6 +687,7 @@ func TestRunMaintenanceWindow_OpensOncePerWindow(t *testing.T) { } func TestAutomatedSnapshotIdentifier_TakesAWSsOwnName(t *testing.T) { + t.Parallel() at := time.Date(2026, 7, 30, 3, 25, 45, 0, time.UTC) assert.Equal(t, "rds:orders-db-2026-07-30-03-25", AutomatedSnapshotIdentifier(testDBID, at)) // Minute-precise, so a retry inside the same window never collides with the @@ -678,6 +702,7 @@ func TestAutomatedSnapshotIdentifier_TakesAWSsOwnName(t *testing.T) { // Automated backups own the rds: namespace, as in AWS, so a customer snapshot can // never collide with one the scheduler mints. func TestCreateDBSnapshot_RejectsTheAutomatedNamespace(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) @@ -692,6 +717,7 @@ func TestCreateDBSnapshot_RejectsTheAutomatedNamespace(t *testing.T) { } func TestDescribeDBInstanceAutomatedBackups_ReportsTheBackupSet(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, backupReadyRecord()) @@ -723,6 +749,7 @@ func TestDescribeDBInstanceAutomatedBackups_ReportsTheBackupSet(t *testing.T) { } func TestDescribeDBInstanceAutomatedBackups_SkipsAnInstanceWithBackupsOff(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() rec.BackupRetentionPeriod = 0 @@ -735,6 +762,7 @@ func TestDescribeDBInstanceAutomatedBackups_SkipsAnInstanceWithBackupsOff(t *tes } func TestDescribeDBInstanceAutomatedBackups_RejectsAnUnknownInstance(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) _, err := h.svc.DescribeDBInstanceAutomatedBackups(t.Context(), @@ -768,6 +796,7 @@ func TestDescribeDBInstanceAutomatedBackups_RejectsUnimplementedFilters(t *testi // The window pass has to be part of the leader's own account loop: a backup // nothing calls is a backup that is never taken. func TestReconciler_RunsTheBackupWindowOnItsAccountPass(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() now := time.Now().UTC() @@ -784,6 +813,7 @@ func TestReconciler_RunsTheBackupWindowOnItsAccountPass(t *testing.T) { // The GC framework's cluster-wide gate is the reconciler's own lease. Without it // the retention sweep would delete customer data from every node at once. func TestReconciler_ClusterLeaseFollowsLeadership(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) release, ok := h.rec.AcquireClusterLease() @@ -804,6 +834,7 @@ func TestReconciler_ClusterLeaseFollowsLeadership(t *testing.T) { // Automated backups are on by default at the full cap: a shorter default would // pay the whole storage cost of retaining a snapshot for a fraction of the cover. func TestValidateCreateRequest_DefaultsTheBackupSettings(t *testing.T) { + t.Parallel() svc := newCreateValidator() req, err := svc.validateCreateRequest(validCreateInput()) require.NoError(t, err) @@ -816,6 +847,7 @@ func TestValidateCreateRequest_DefaultsTheBackupSettings(t *testing.T) { } func TestValidateCreateRequest_AcceptsTheBackupSettings(t *testing.T) { + t.Parallel() input := validCreateInput() input.BackupRetentionPeriod = aws.Int64(3) input.PreferredBackupWindow = aws.String("03:00-04:00") @@ -866,6 +898,7 @@ func TestValidateCreateRequest_RejectsBadBackupSettings(t *testing.T) { // The window is persisted at create rather than derived on every read, so it // survives a change to the configured block the way AWS's assigned window does. func TestCreateDBInstance_PersistsTheBackupSettings(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") _, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) @@ -884,6 +917,7 @@ func TestCreateDBInstance_PersistsTheBackupSettings(t *testing.T) { // Retention 0 is an answer, not an absence: a client that sees no // BackupRetentionPeriod cannot tell "backups are off" from "not reported". func TestDescribeDBInstances_ReportsTheBackupSettingsIncludingZero(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") rec := seedCreated(t, h, testDBInstanceID) @@ -910,6 +944,7 @@ func TestDescribeDBInstances_ReportsTheBackupSettingsIncludingZero(t *testing.T) // Restoring from last night's backup is the whole point of keeping it, so the // namespace refused wherever a name is minted has to be accepted as a reference. func TestRestoreDBInstanceFromDBSnapshot_RestoresFromAnAutomatedBackup(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := backupReadyRecord() // Everything a restore reproduces from the snapshot alone. @@ -943,6 +978,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RestoresFromAnAutomatedBackup(t *testin // Only the retention sweep removes an automated backup, as in AWS: a customer // delete-db-snapshot against one is refused rather than silently accepted. func TestDeleteDBSnapshot_RefusesAnAutomatedBackup(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) seedInstance(t, h.svc, backupReadyRecord()) require.True(t, h.runBackupPass(t)) diff --git a/spinifex/handlers/rds/bootstrap_test.go b/spinifex/handlers/rds/bootstrap_test.go index 4d85fe1ee..f18d4fee2 100644 --- a/spinifex/handlers/rds/bootstrap_test.go +++ b/spinifex/handlers/rds/bootstrap_test.go @@ -55,6 +55,7 @@ func storedPayload(t *testing.T, svc *Service, id string) (*BootstrapPayloadEnve } func TestBootstrapPayload_RoundTripsAndCarriesNoCleartext(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() seedPending(t, svc, rec) @@ -76,6 +77,7 @@ func TestBootstrapPayload_RoundTripsAndCarriesNoCleartext(t *testing.T) { // The same password staged twice must not produce the same bytes, or a KV reader // could tell two instances share a password without decrypting either. func TestBootstrapPayload_CiphertextIsNotDeterministic(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() seedInstance(t, svc, rec) @@ -95,6 +97,7 @@ func TestBootstrapPayload_CiphertextIsNotDeterministic(t *testing.T) { } func TestBootstrapPayload_TamperedCiphertextIsRejected(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() seedPending(t, svc, rec) @@ -118,6 +121,7 @@ func TestBootstrapPayload_TamperedCiphertextIsRejected(t *testing.T) { // A payload written under another master key must say so rather than surfacing a // raw GCM authentication failure, which reads as tampering. func TestBootstrapPayload_WrongMasterKeyIsReportedLegibly(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() seedPending(t, svc, rec) @@ -176,6 +180,7 @@ func TestBootstrapPayload_ClaimsBindItToOneInstanceGeneration(t *testing.T) { // The whole point of the split: a fetch serves the password without writing // anything, so a lost reply is recovered by asking again. func TestGetDBBootstrapConfig_ReplaysUntilAcknowledged(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() payloadID := seedPending(t, svc, rec) @@ -295,6 +300,7 @@ func TestAcknowledgeDBBootstrap_Resolution(t *testing.T) { // The operation that destroys key material must not depend on being able to read // it, or a master.key change would strand a bootstrapped instance forever. func TestAcknowledgeDBBootstrap_NeverDecrypts(t *testing.T) { + t.Parallel() svc := newTestService(t) payloadID := seedPending(t, svc, defaultRecord()) @@ -314,6 +320,7 @@ func TestAcknowledgeDBBootstrap_NeverDecrypts(t *testing.T) { // The volume ID is an echo of what the fetch handed the guest rather than // independent proof, but a mismatch is a platform bug worth denying on. func TestAcknowledgeDBBootstrap_RejectsAForeignDataVolume(t *testing.T) { + t.Parallel() svc := newTestService(t) payloadID := seedPending(t, svc, defaultRecord()) @@ -334,6 +341,7 @@ func TestAcknowledgeDBBootstrap_RejectsAForeignDataVolume(t *testing.T) { // A beta record whose password was already spent keeps attaching: it is serving // real data and holds no secret at rest to fix. func TestGetDBBootstrapConfig_LegacyConsumedRecordAttaches(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() rec.Bootstrap = BootstrapState{Consumed: true, ResolvedParameters: rec.Bootstrap.ResolvedParameters} @@ -353,6 +361,7 @@ func TestGetDBBootstrapConfig_LegacyConsumedRecordAttaches(t *testing.T) { // datadir to save. The password is removed on sight rather than left waiting for // an operator who may never act. func TestGetDBBootstrapConfig_LegacyPlaintextIsScrubbedIdempotently(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() rec.Bootstrap.MasterUserPassword = "beta-plaintext-pw" @@ -375,6 +384,7 @@ func TestGetDBBootstrapConfig_LegacyPlaintextIsScrubbedIdempotently(t *testing.T // registers and beats and the operator sees a running-but-broken instance // instead of a VM retrying into silence. func TestGetDBBootstrapConfig_UnreadablePayloadAttachesAndReportsWhy(t *testing.T) { + t.Parallel() svc := newTestService(t) seedPending(t, svc, defaultRecord()) @@ -391,6 +401,7 @@ func TestGetDBBootstrapConfig_UnreadablePayloadAttachesAndReportsWhy(t *testing. } func TestCreateDBInstance_StagesThePasswordEncrypted(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") _, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) require.NoError(t, err) @@ -419,6 +430,7 @@ func TestCreateDBInstance_StagesThePasswordEncrypted(t *testing.T) { } func TestCreateDBInstance_WithoutAMasterKeyStagesNothing(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") h.svc = h.svc.WithDeps(Deps{ LoadCA: newTestCA(t), @@ -443,6 +455,7 @@ func TestCreateDBInstance_WithoutAMasterKeyStagesNothing(t *testing.T) { // is staged for it and an acknowledgement against it is denied rather than read // as a benign duplicate. func TestRestoredRecord_IsBornWithoutAStagedBootstrap(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() rec.Bootstrap.State = BootstrapStateNone @@ -459,6 +472,7 @@ func TestRestoredRecord_IsBornWithoutAStagedBootstrap(t *testing.T) { } func TestDeleteDBInstance_RemovesTheStagedPayload(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() seedPending(t, h.svc, rec) @@ -477,6 +491,7 @@ func TestDeleteDBInstance_RemovesTheStagedPayload(t *testing.T) { // The replacement would lose the format grant only the initial create can hold, // so it could never come up whatever the password did. func TestModifyDBInstance_RejectsADisruptiveChangeWhileTheBootstrapIsStaged(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifiableRecord() rec.Status = StatusFailed @@ -544,6 +559,7 @@ func TestReplaceInstanceVM_DiscardsAStagedBootstrap(t *testing.T) { // unusable: the create that follows owns the reservation, so the orphan it finds // is its own to overwrite. func TestCreateDBInstance_StagesOverAnOrphanedPayload(t *testing.T) { + t.Parallel() svc := newTestService(t) rec := defaultRecord() orphaned := seedPending(t, svc, rec) @@ -560,6 +576,7 @@ func TestCreateDBInstance_StagesOverAnOrphanedPayload(t *testing.T) { // the record's FailureReason: the status machine clears that on every // transition, which is exactly what a timed-out create does next. func TestMarkBootstrapUnrecoverable_ReasonSurvivesAStatusTransition(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifiableRecord() rec.Status = StatusCreating @@ -581,6 +598,7 @@ func TestMarkBootstrapUnrecoverable_ReasonSurvivesAStatusTransition(t *testing.T // Both reasons reach the customer, and an instance still holding staged // credentials while it serves is visible without a second call. func TestProjectDBInstance_ReportsTheBootstrapState(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) rec := defaultRecord() rec.Status = StatusAvailable diff --git a/spinifex/handlers/rds/commands_test.go b/spinifex/handlers/rds/commands_test.go index b5d32dd27..e23581eb9 100644 --- a/spinifex/handlers/rds/commands_test.go +++ b/spinifex/handlers/rds/commands_test.go @@ -10,6 +10,7 @@ import ( // The password is handed straight to the agent and never persisted, so the // command is the only place it exists between the request and the engine. func TestSetMasterPassword_CarriesTheCredentialsToTheAgent(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) require.NoError(t, h.svc.setMasterPassword(t.Context(), testAccountID, testDBID, "postgres", "n3w-pw")) @@ -27,6 +28,7 @@ func TestSetMasterPassword_CarriesTheCredentialsToTheAgent(t *testing.T) { // A rotation that could not be applied has to fail loudly: reporting success // would leave the customer with a password the engine does not accept. func TestSetMasterPassword_FailsWhenTheAgentRejectsIt(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, true) err := h.svc.setMasterPassword(t.Context(), testAccountID, testDBID, "postgres", "n3w-pw") @@ -37,6 +39,7 @@ func TestSetMasterPassword_FailsWhenTheAgentRejectsIt(t *testing.T) { // The settings the engine accepted but will not honour until it restarts // come back as the reply message, which is what a later reboot then clears. func TestApplyParameters_ReportsWhatIsPendingARestart(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.agent.replyWith("shared_buffers, max_connections") @@ -53,6 +56,7 @@ func TestApplyParameters_ReportsWhatIsPendingARestart(t *testing.T) { // An agent that reports nothing pending has applied everything live. func TestApplyParameters_ReportsNothingPendingOnAnEmptyReply(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) pending, err := h.svc.applyParameters(t.Context(), testAccountID, testDBID, @@ -64,6 +68,7 @@ func TestApplyParameters_ReportsNothingPendingOnAnEmptyReply(t *testing.T) { // The reply is matched on the command ID, so a stale answer to an earlier // issuer cannot be mistaken for the outcome of this command. func TestIssueCommand_CorrelatesTheReplyByCommandID(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) reply, err := h.svc.issueCommand(t.Context(), testAccountID, testDBID, diff --git a/spinifex/handlers/rds/create_test.go b/spinifex/handlers/rds/create_test.go index 51bdaf971..c706482b2 100644 --- a/spinifex/handlers/rds/create_test.go +++ b/spinifex/handlers/rds/create_test.go @@ -225,6 +225,7 @@ func validCreateInput() *rds.CreateDBInstanceInput { } func TestCreateDBInstance_ProvisionsAndRecordsTheInstance(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) out, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) @@ -290,6 +291,7 @@ func TestCreateDBInstance_ProvisionsAndRecordsTheInstance(t *testing.T) { } func TestCreateDBInstance_IAMFailurePrecedesReservationAndLaunch(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") iamErr := errors.New("IAM store unavailable") h.iam.PutRolePolicyErr = iamErr @@ -305,6 +307,7 @@ func TestCreateDBInstance_IAMFailurePrecedesReservationAndLaunch(t *testing.T) { } func TestCreateDBInstance_PublishesEndpointRecord(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) @@ -326,6 +329,7 @@ func TestCreateDBInstance_PublishesEndpointRecord(t *testing.T) { // Without northstar there is no name to resolve, so the endpoint has to be the // ENI IP itself rather than an empty host the client would dial. func TestCreateDBInstance_EndpointFallsBackToENIIPWithoutBaseDomain(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") out, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) @@ -346,6 +350,7 @@ func TestCreateDBInstance_EndpointFallsBackToENIIPWithoutBaseDomain(t *testing.T } func TestCreateDBInstance_DuplicateIdentifierIsRejected(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") _, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) @@ -365,6 +370,7 @@ func TestCreateDBInstance_DuplicateIdentifierIsRejected(t *testing.T) { // written must withdraw it — otherwise the name is permanently unusable while // naming an instance that was never provisioned. func TestCreateDBInstance_LaunchFailureWithdrawsTheReservation(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") h.launch.launcher.err = errors.New("no host has capacity") @@ -382,6 +388,7 @@ func TestCreateDBInstance_LaunchFailureWithdrawsTheReservation(t *testing.T) { // built. The deferred record delete removes the only thing naming the VM, its // two ENIs and the data volume, so anything left behind is unreclaimable. func TestCreateDBInstance_FailureAfterLaunchUnwindsEveryResource(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") // Dropping the reserved record while the launch is in flight fails the @@ -403,6 +410,7 @@ func TestCreateDBInstance_FailureAfterLaunchUnwindsEveryResource(t *testing.T) { } func TestCreateDBInstance_IndexFailureWithdrawsTheRecordedLaunch(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") // This invalid KV key makes the instance-index write fail after recordLaunch // has advanced the reservation revision. @@ -418,6 +426,7 @@ func TestCreateDBInstance_IndexFailureWithdrawsTheRecordedLaunch(t *testing.T) { } func TestCreateDBInstance_RecordLaunchDoesNotOverwriteAConcurrentReplacement(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") var replacement DBInstanceRecord h.launch.launcher.onLaunch = func() { @@ -431,6 +440,7 @@ func TestCreateDBInstance_RecordLaunchDoesNotOverwriteAConcurrentReplacement(t * } func TestCreateDBInstance_RollbackDoesNotDeleteAConcurrentReplacement(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") h.launch.launcher.instanceID = "invalid instance id" var replacement DBInstanceRecord @@ -447,6 +457,7 @@ func TestCreateDBInstance_RollbackDoesNotDeleteAConcurrentReplacement(t *testing } func TestCreateDBInstance_RollbackFollowsSameOwnerUpdates(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") h.launch.launcher.instanceID = "invalid instance id" h.launch.launcher.onTerminate = func() { @@ -470,6 +481,7 @@ func TestCreateDBInstance_RollbackFollowsSameOwnerUpdates(t *testing.T) { // The request may not be answered with an instance whose storage is // not actually encrypted, so an unencrypted volume fails the whole create. func TestCreateDBInstance_UnencryptedDataVolumeFailsTheCreate(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") h.launch.volumes.encrypted = false @@ -480,6 +492,7 @@ func TestCreateDBInstance_UnencryptedDataVolumeFailsTheCreate(t *testing.T) { } func TestCreateDBInstance_SecurityGroupsMustBeInThePlacementVPC(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") input := validCreateInput() @@ -500,6 +513,7 @@ func TestCreateDBInstance_SecurityGroupsMustBeInThePlacementVPC(t *testing.T) { } func TestCreateDBInstance_RequiresADefaultVPCWithASubnet(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") h.network.vpcs = nil @@ -560,6 +574,7 @@ func TestValidateCreateRequest_RejectsUnimplementedParameters(t *testing.T) { // DeletionProtection is honoured, so it has to be accepted // at create and carried onto the record DeleteDBInstance checks. func TestValidateCreateRequest_AcceptsDeletionProtection(t *testing.T) { + t.Parallel() input := validCreateInput() input.DeletionProtection = aws.Bool(true) @@ -613,6 +628,7 @@ func TestValidateCreateRequest_RejectsMalformedRequests(t *testing.T) { } func TestValidateCreateRequest_AcceptsTheImplicitDefaultParameterGroup(t *testing.T) { + t.Parallel() input := validCreateInput() input.DBParameterGroupName = aws.String("default.postgres18") input.Port = aws.Int64(6543) @@ -628,6 +644,7 @@ func TestValidateCreateRequest_AcceptsTheImplicitDefaultParameterGroup(t *testin // The inert parameters are accepted as no-ops rather than rejected: omitting // them creates no false guarantee, so a Terraform config carrying them works. func TestValidateCreateRequest_AcceptsInertParameters(t *testing.T) { + t.Parallel() input := validCreateInput() input.AutoMinorVersionUpgrade = aws.Bool(true) input.CopyTagsToSnapshot = aws.Bool(true) diff --git a/spinifex/handlers/rds/delete_test.go b/spinifex/handlers/rds/delete_test.go index 3dfdc0247..6bbb986f0 100644 --- a/spinifex/handlers/rds/delete_test.go +++ b/spinifex/handlers/rds/delete_test.go @@ -51,6 +51,7 @@ func skipFinalSnapshot() *rds.DeleteDBInstanceInput { // The whole teardown: the VM, the data volume, both NICs, the reverse index and // the record itself. func TestDeleteDBInstance_TearsDownEverythingItOwns(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) require.NoError(t, h.svc.PutInstanceIndex(t.Context(), testInstance, InstanceIndexEntry{ @@ -106,6 +107,7 @@ func TestDeleteDBInstance_RequiresAnExplicitSnapshotChoice(t *testing.T) { // The flag exists to stop exactly this call, so honouring it has to happen // before anything is torn down. func TestDeleteDBInstance_HonoursDeletionProtection(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() rec.DeletionProtection = true @@ -123,6 +125,7 @@ func TestDeleteDBInstance_HonoursDeletionProtection(t *testing.T) { // The snapshot is taken once the VM is gone, so it reads a sealed data // volume rather than one a live engine is still writing to. func TestDeleteDBInstance_TakesTheFinalSnapshotAfterTheEngineAndVMAreDown(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) @@ -152,6 +155,7 @@ func TestDeleteDBInstance_TakesTheFinalSnapshotAfterTheEngineAndVMAreDown(t *tes } func TestDeleteDBInstance_ReservesTheFinalSnapshotBeforeCuttingIt(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) @@ -173,6 +177,7 @@ func TestDeleteDBInstance_ReservesTheFinalSnapshotBeforeCuttingIt(t *testing.T) // creating. A retry adopts that exact account-scoped snapshot instead of // cutting another one and permanently pinning the source volume twice. func TestDeleteDBInstance_AdoptsAnInterruptedFinalSnapshot(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() rec.Status = StatusDeleting @@ -211,6 +216,7 @@ func TestDeleteDBInstance_AdoptsAnInterruptedFinalSnapshot(t *testing.T) { // be deleted while the final snapshot survives. It is retained and recorded // with the snapshots holding it. func TestDeleteDBInstance_RetainsTheVolumeAFinalSnapshotStillHolds(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) @@ -231,6 +237,7 @@ func TestDeleteDBInstance_RetainsTheVolumeAFinalSnapshotStillHolds(t *testing.T) // re-created under the same name must not have a stale snapshot of a previous // data volume accepted as its own — the volume would then be deleted unbacked. func TestDeleteDBInstance_RejectsAFinalSnapshotIdentifierAlreadyTaken(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) @@ -296,6 +303,7 @@ func TestDeleteDBInstance_RejectsARetryThatChangesTheSnapshotChoice(t *testing.T // reference the EC2 enumeration missed. Retaining converges; erroring would // wedge the delete on every retry until the bound marked it failed. func TestDeleteDBInstance_RetainsTheVolumeWhenTheVolumeStoreReportsItInUse(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.volumes.deleteErr = errors.New(awserrors.ErrorVolumeInUse) seedInstance(t, h.svc, availableRecord()) @@ -316,6 +324,7 @@ func TestDeleteDBInstance_RetainsTheVolumeWhenTheVolumeStoreReportsItInUse(t *te // A teardown that died partway through is replayed, so every step has to treat // a resource that is already gone as work it has done. func TestDeleteDBInstance_IsIdempotentAcrossARetriedTeardown(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() // The shape a delete leaves behind when its caller died mid-teardown. @@ -342,6 +351,7 @@ func TestDeleteDBInstance_IsIdempotentAcrossARetriedTeardown(t *testing.T) { // The reconciler owns a teardown whose caller never came back, so an // interrupted delete finishes without an operator. func TestReconciler_ResumesAnInterruptedDelete(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() rec.Status = StatusDeleting @@ -357,6 +367,7 @@ func TestReconciler_ResumesAnInterruptedDelete(t *testing.T) { // A stop whose caller died leaves the VM possibly still running, so the stop is // re-issued rather than assumed. func TestReconciler_ResumesAnInterruptedStop(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() rec.Status = StatusStopping diff --git a/spinifex/handlers/rds/describe_test.go b/spinifex/handlers/rds/describe_test.go index 6ec045682..048dc2bb5 100644 --- a/spinifex/handlers/rds/describe_test.go +++ b/spinifex/handlers/rds/describe_test.go @@ -23,6 +23,7 @@ func seedCreated(t *testing.T, h *createHarness, id string) DBInstanceRecord { } func TestDescribeDBInstances_ListsEveryInstanceSorted(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") for _, id := range []string{"zulu-db", "alpha-db", "mike-db"} { seedCreated(t, h, id) @@ -40,6 +41,7 @@ func TestDescribeDBInstances_ListsEveryInstanceSorted(t *testing.T) { } func TestDescribeDBInstances_EmptyAccountIsAnEmptyList(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") out, err := h.svc.DescribeDBInstances(t.Context(), nil, testAccountID) @@ -50,6 +52,7 @@ func TestDescribeDBInstances_EmptyAccountIsAnEmptyList(t *testing.T) { // A client polling a create must be able to tell "not ready" from "gone", so a // named instance that does not exist is an error rather than an empty list. func TestDescribeDBInstances_NamedMissingInstanceIsAnError(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -61,6 +64,7 @@ func TestDescribeDBInstances_NamedMissingInstanceIsAnError(t *testing.T) { } func TestDescribeDBInstances_NamedInstanceProjectsTheRecord(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) rec := seedCreated(t, h, testDBInstanceID) @@ -94,6 +98,7 @@ func TestDescribeDBInstances_NamedInstanceProjectsTheRecord(t *testing.T) { // identifier, so an instance created without one is unmanageable by the tool // this service is driven with — its post-create read finds nothing. func TestDescribeDBInstances_ReportsAStableResourceID(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") rec := seedCreated(t, h, testDBInstanceID) require.Regexp(t, `^db-[0-9a-f]{17}$`, rec.DbiResourceID) @@ -137,6 +142,7 @@ func TestDescribeDBInstances_FiltersOnResourceIDAndIdentifier(t *testing.T) { // false would leave every default configuration with a diff that no modify can // clear. func TestDescribeDBInstances_EchoesAutoMinorVersionUpgrade(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") unset := seedCreated(t, h, testDBInstanceID) @@ -157,6 +163,7 @@ func TestDescribeDBInstances_EchoesAutoMinorVersionUpgrade(t *testing.T) { } func TestDescribeDBInstances_EchoesAcceptedInertSettings(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") input := validCreateInput() input.CopyTagsToSnapshot = aws.Bool(true) @@ -182,6 +189,7 @@ func TestDescribeDBInstances_EchoesAcceptedInertSettings(t *testing.T) { } func TestDescribeDBInstances_EchoesOmittedInertSettingsAsDisabled(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -198,6 +206,7 @@ func TestDescribeDBInstances_EchoesOmittedInertSettingsAsDisabled(t *testing.T) // A filter nobody implemented is refused rather than dropped: dropping it // returns exactly the rows the caller asked to exclude. func TestDescribeDBInstances_RejectsAnUnrecognizedFilter(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -211,6 +220,7 @@ func TestDescribeDBInstances_RejectsAnUnrecognizedFilter(t *testing.T) { // A named instance and a filter that excludes it disagree; reporting the // instance anyway would answer a question the caller did not ask. func TestDescribeDBInstances_NamedInstanceMustSatisfyTheFilters(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -227,6 +237,7 @@ func TestDescribeDBInstances_NamedInstanceMustSatisfyTheFilters(t *testing.T) { // Instances live in per-account buckets, so one account's describe must not see // another's — the identifier is only unique within an account. func TestDescribeDBInstances_IsScopedToTheCallingAccount(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -238,6 +249,7 @@ func TestDescribeDBInstances_IsScopedToTheCallingAccount(t *testing.T) { // A DB instance with no endpoint yet has no Endpoint at all: an Endpoint with an // empty address would have a client dial an empty host. func TestProjectDBInstance_OmitsAnUnsettledEndpoint(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") instance := h.svc.projectDBInstance(&DBInstanceRecord{ @@ -253,6 +265,7 @@ func TestProjectDBInstance_OmitsAnUnsettledEndpoint(t *testing.T) { } func TestDesiredDNSChanges_CoversEveryTenantOrClaimsNoAuthority(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) rec := seedCreated(t, h, testDBInstanceID) @@ -267,6 +280,7 @@ func TestDesiredDNSChanges_CoversEveryTenantOrClaimsNoAuthority(t *testing.T) { // Without a base domain there are no managed RDS records, and claiming authority // would let the reconcile prune records this node cannot even name. func TestDesiredDNSChanges_ClaimsNoAuthorityWithoutABaseDomain(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -279,6 +293,7 @@ func TestDesiredDNSChanges_ClaimsNoAuthorityWithoutABaseDomain(t *testing.T) { // anything still holding an ENI IP keeps its name resolvable, including a failed // instance an operator is still investigating. func TestDesiredDNSChanges_SkipsDeletedButKeepsFailed(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) seedCreated(t, h, "gone-db") failed := seedCreated(t, h, "failed-db") @@ -301,6 +316,7 @@ func TestDesiredDNSChanges_SkipsDeletedButKeepsFailed(t *testing.T) { // A deferred change is what the customer polls for, and the Terraform provider // reads PendingModifiedValues to decide whether an apply has landed. func TestProjectDBInstance_ReportsTheDeferredChange(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") instance := h.svc.projectDBInstance(&DBInstanceRecord{ @@ -326,6 +342,7 @@ func TestProjectDBInstance_ReportsTheDeferredChange(t *testing.T) { // so reporting it as still pending would show Terraform drift on a change that // has landed. func TestProjectDBInstance_OmitsAPendingFilesystemGrow(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") instance := h.svc.projectDBInstance(&DBInstanceRecord{ @@ -340,6 +357,7 @@ func TestProjectDBInstance_OmitsAPendingFilesystemGrow(t *testing.T) { // AWS reports a parameter group's state on the membership rather than in // PendingModifiedValues, and the Terraform provider reads it there. func TestProjectDBInstance_ReportsTheParameterGroupApplyStatus(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") rec := &DBInstanceRecord{ DBInstanceIdentifier: testDBInstanceID, diff --git a/spinifex/handlers/rds/engine_mariadb_test.go b/spinifex/handlers/rds/engine_mariadb_test.go index c146859ab..f36310b59 100644 --- a/spinifex/handlers/rds/engine_mariadb_test.go +++ b/spinifex/handlers/rds/engine_mariadb_test.go @@ -18,6 +18,7 @@ import ( // they did, a create would have resolved an AMI nothing builds and left a volume // and an ENI behind for an instance that could never become available. func TestMariaDB_IsOffered(t *testing.T) { + t.Parallel() engine, err := LookupEngine(" MariaDB ") require.NoError(t, err, "the engine name is matched case-insensitively and trimmed") assert.Equal(t, engineMariaDB.Name, engine.Name) @@ -40,6 +41,7 @@ func TestMariaDB_IsOffered(t *testing.T) { // the MariaDB AMI carrying MariaDB's own port, family and guest configuration, // not PostgreSQL's defaults with a different name on them. func TestCreateDBInstance_LaunchesMariaDB(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) input := validCreateInput() input.Engine = aws.String("mariadb") @@ -75,6 +77,7 @@ func TestCreateDBInstance_LaunchesMariaDB(t *testing.T) { } func TestMariaDB_Identity(t *testing.T) { + t.Parallel() assert.Equal(t, "mariadb", engineMariaDB.Name) assert.Equal(t, int64(3306), engineMariaDB.DefaultPort) // The version series the API pins to, not an integer major: PostgreSQL's @@ -125,6 +128,7 @@ func TestMariaDB_ValidateMasterUsername(t *testing.T) { // The guest re-checks the name it is handed rather than trusting the control // plane, and it reaches the reserved half on its own. func TestMariaDB_ValidateUsernameNotReserved(t *testing.T) { + t.Parallel() for _, username := range []string{"mysql.session", "MySQL.infoschema", "mariadb.sys", "mariadb.anything"} { err := engineMariaDB.ValidateUsernameNotReserved(username) require.Error(t, err, "username %q", username) @@ -137,6 +141,7 @@ func TestMariaDB_ValidateUsernameNotReserved(t *testing.T) { // MariaDB maps a database onto a directory name and rds-init interpolates the // name into CREATE DATABASE from the shell, so this rule is the actual barrier. func TestMariaDB_ValidateDBName(t *testing.T) { + t.Parallel() for _, name := range []string{"", "orders", "Orders_2", strings.Repeat("d", 64)} { assert.NoError(t, engineMariaDB.ValidateDBName(name), "name %q", name) } @@ -154,6 +159,7 @@ func TestMariaDB_ValidateDBName(t *testing.T) { // The two catalogs are separate tables, not one table with an engine column, so // neither engine can be handed a setting the other's server would not parse. func TestMariaDBCatalog_IsSeparateFromPostgres(t *testing.T) { + t.Parallel() for _, name := range []string{"innodb_buffer_pool_size", "sql_mode", "key_buffer_size"} { _, ok := enginePostgres.LookupParameter(name) assert.False(t, ok, "PostgreSQL must not expose %s", name) @@ -173,6 +179,7 @@ func TestMariaDBCatalog_IsSeparateFromPostgres(t *testing.T) { // customer's to set: the endpoint, the agent's socket access, the serving // certificate and the snapshot recovery guarantee all depend on them. func TestMariaDBCatalog_OmitsPlatformOwnedSettings(t *testing.T) { + t.Parallel() owned := []string{ "port", "datadir", "socket", "bind_address", "ssl_ca", "ssl_cert", "ssl_key", @@ -189,6 +196,7 @@ func TestMariaDBCatalog_OmitsPlatformOwnedSettings(t *testing.T) { // The size-derived defaults have to move with the class, or the formulas are // decoration and one end of the supported range is mis-tuned. func TestMariaDBCatalog_SizeDerivedDefaultsScaleWithClassMemory(t *testing.T) { + t.Parallel() smallest, err := classMemoryMiB("db.t3.micro") require.NoError(t, err) largest, err := classMemoryMiB("db.m5.xlarge") @@ -212,6 +220,7 @@ func TestMariaDBCatalog_SizeDerivedDefaultsScaleWithClassMemory(t *testing.T) { // Three quarters of class memory is RDS's own default for this engine family, // and it is the setting whose being wrong stops the server from starting. func TestMariaDBCatalog_BufferPoolIsThreeQuartersOfClassMemory(t *testing.T) { + t.Parallel() memoryMiB, err := classMemoryMiB("db.m5.large") require.NoError(t, err) @@ -226,6 +235,7 @@ func TestMariaDBCatalog_BufferPoolIsThreeQuartersOfClassMemory(t *testing.T) { // max_connections = {DBInstanceClassMemory/12582880}, which is RDS's own formula. func TestMariaDBCatalog_MaxConnectionsFollowsTheRDSFormula(t *testing.T) { + t.Parallel() memoryMiB, err := classMemoryMiB("db.m5.xlarge") require.NoError(t, err) assert.Equal(t, strconv.FormatInt(memoryMiB*mibToBytes/12582880, 10), mariadbMaxConnectionsFor(memoryMiB)) @@ -377,6 +387,7 @@ func TestMariaDBCatalog_BoundsSizeDerivedOverridesToTheClass(t *testing.T) { // installed file. Getting one wrong is a permanent pending-reboot or a change // the customer is told took effect and did not. func TestMariaDBCatalog_ApplyTypesMatchTheEngine(t *testing.T) { + t.Parallel() static := []string{ "innodb_buffer_pool_size", "innodb_log_file_size", "innodb_flush_method", "innodb_read_io_threads", "innodb_write_io_threads", "innodb_purge_threads", @@ -401,6 +412,7 @@ func TestMariaDBCatalog_ApplyTypesMatchTheEngine(t *testing.T) { } func TestMariaDBCatalog_ResolvesEveryParameterAsALiteral(t *testing.T) { + t.Parallel() resolved, err := engineMariaDB.ResolveEffectiveParameters("db.m5.large", map[string]string{"max_connections": "300"}) require.NoError(t, err) require.Len(t, resolved, len(engineMariaDB.CatalogParameterNames())) @@ -426,6 +438,7 @@ func TestMariaDBCatalog_ResolvesEveryParameterAsALiteral(t *testing.T) { // time_zone startup option, so an option file naming it aborts the server before // it opens the datadir. func TestMariaDBCatalog_TimeZoneUsesItsStartupSpellingInTheOptionFile(t *testing.T) { + t.Parallel() assert.Equal(t, "default_time_zone", engineMariaDB.OptionFileName("time_zone")) // The customer-facing name is what the API reports and what a live SET GLOBAL @@ -437,6 +450,7 @@ func TestMariaDBCatalog_TimeZoneUsesItsStartupSpellingInTheOptionFile(t *testing } func TestMariaDBCatalog_EveryOtherParameterKeepsItsName(t *testing.T) { + t.Parallel() for _, name := range engineMariaDB.CatalogParameterNames() { if name == "time_zone" { continue @@ -449,6 +463,7 @@ func TestMariaDBCatalog_EveryOtherParameterKeepsItsName(t *testing.T) { // PostgreSQL sets every one of its settings under the name the customer knows, // so a mapping there would be a bug rather than a fix. func TestPostgresCatalog_NoParameterIsRenamedForTheOptionFile(t *testing.T) { + t.Parallel() for _, name := range enginePostgres.CatalogParameterNames() { assert.Equal(t, name, enginePostgres.OptionFileName(name)) } @@ -457,5 +472,6 @@ func TestPostgresCatalog_NoParameterIsRenamedForTheOptionFile(t *testing.T) { // A name the catalog does not carry is the customer's own, and the option file // takes it unchanged rather than silently dropping it. func TestMariaDBCatalog_OptionFileNamePassesAnUnknownNameThrough(t *testing.T) { + t.Parallel() assert.Equal(t, "not_a_parameter", engineMariaDB.OptionFileName("not_a_parameter")) } diff --git a/spinifex/handlers/rds/engine_test.go b/spinifex/handlers/rds/engine_test.go index 724a34b6a..3cbd7d030 100644 --- a/spinifex/handlers/rds/engine_test.go +++ b/spinifex/handlers/rds/engine_test.go @@ -10,6 +10,7 @@ import ( ) func TestLookupEngine(t *testing.T) { + t.Parallel() _, err := LookupEngine("PostgreSQL") require.Error(t, err, "the AWS engine identifier is 'postgres', not the product name") @@ -28,6 +29,7 @@ func TestLookupEngine(t *testing.T) { // A version other than the pinned one would be served by an image that is // not the one asked for, so it is rejected rather than quietly substituted. func TestEngineValidateVersion(t *testing.T) { + t.Parallel() engine, err := LookupEngine("postgres") require.NoError(t, err) @@ -109,6 +111,7 @@ func TestEngineValidateDBName(t *testing.T) { } func TestValidateMasterUserPassword(t *testing.T) { + t.Parallel() assert.NoError(t, ValidateMasterUserPassword("Sup3rSecret!")) assert.NoError(t, ValidateMasterUserPassword(strings.Repeat("x", maxMasterPasswordLen))) diff --git a/spinifex/handlers/rds/events_test.go b/spinifex/handlers/rds/events_test.go index 7cfeb75fe..0d7438d2e 100644 --- a/spinifex/handlers/rds/events_test.go +++ b/spinifex/handlers/rds/events_test.go @@ -41,6 +41,7 @@ func describeEvents(t *testing.T, svc *Service, input *rds.DescribeEventsInput) } func TestDescribeEvents_ReturnsARecordedEventWithItsSourceARN(t *testing.T) { + t.Parallel() svc := newTestService(t) svc.RecordEvent(context.Background(), testAccountID, EventSourceTypeDBInstance, testDBID, "DB instance stopped.", EventCategoryAvailability) @@ -57,6 +58,7 @@ func TestDescribeEvents_ReturnsARecordedEventWithItsSourceARN(t *testing.T) { // AWS's default window is one hour, so an event outside it is not returned even // though it is still inside the retention window. func TestDescribeEvents_ScopesToTheRequestedWindow(t *testing.T) { + t.Parallel() svc := newTestService(t) now := time.Now().UTC() seedEvent(t, svc, EventSourceTypeDBInstance, testDBID, now.Add(-10*time.Minute), "recent") @@ -69,6 +71,7 @@ func TestDescribeEvents_ScopesToTheRequestedWindow(t *testing.T) { } func TestDescribeEvents_FiltersByCategory(t *testing.T) { + t.Parallel() svc := newTestService(t) now := time.Now().UTC() seedEvent(t, svc, EventSourceTypeDBInstance, testDBID, now.Add(-time.Minute), "backed up", EventCategoryBackup) @@ -83,6 +86,7 @@ func TestDescribeEvents_FiltersByCategory(t *testing.T) { // An unfiltered read is account-wide, so a resource's history is reachable even // after the resource itself is gone. func TestDescribeEvents_ReadsAcrossResourcesAndScopesToOneWhenAsked(t *testing.T) { + t.Parallel() svc := newTestService(t) now := time.Now().UTC() seedEvent(t, svc, EventSourceTypeDBInstance, testDBID, now, "instance event") @@ -131,6 +135,7 @@ func TestDescribeEvents_RejectsMalformedRequests(t *testing.T) { // The ring is bounded, so a resource's whole history stays one KV value however // long it lives. func TestAppendEvent_BoundsTheRing(t *testing.T) { + t.Parallel() svc := newTestService(t) for range eventRingSize + 10 { require.NoError(t, svc.appendEvent(context.Background(), testAccountID, @@ -149,6 +154,7 @@ func TestAppendEvent_BoundsTheRing(t *testing.T) { // AWS's 14-day retention: anything older is dropped on the next append and // never returned by a read. func TestAppendEvent_DropsEventsPastTheRetentionWindow(t *testing.T) { + t.Parallel() svc := newTestService(t) seedEvent(t, svc, EventSourceTypeDBInstance, testDBID, time.Now().UTC().Add(-EventRetention-time.Hour), "ancient") @@ -163,6 +169,7 @@ func TestAppendEvent_DropsEventsPastTheRetentionWindow(t *testing.T) { // An event narrates work that already happened, so a failed append must not // turn a successful operation into a failed one. func TestRecordEvent_DoesNotPropagateAFailure(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) assert.NotPanics(t, func() { svc.RecordEvent(context.Background(), testAccountID, EventSourceTypeDBInstance, testDBID, "stopped") diff --git a/spinifex/handlers/rds/iam_test.go b/spinifex/handlers/rds/iam_test.go index 3f91d35ec..8af13819f 100644 --- a/spinifex/handlers/rds/iam_test.go +++ b/spinifex/handlers/rds/iam_test.go @@ -27,6 +27,7 @@ func rdsInstanceProfileARN(accountID string) string { // A Postgres RCE on a DB VM inherits this role, so the grant must be the four // internal actions and nothing wildcarded that could reach the customer surface. func TestInstanceRolePolicy_GrantsOnlyTheInternalActions(t *testing.T) { + t.Parallel() var doc handlers_iam.PolicyDocument require.NoError(t, json.Unmarshal([]byte(instanceRoleInlinePolicy), &doc)) require.Len(t, doc.Statement, 1) @@ -52,6 +53,7 @@ func TestInstanceRolePolicy_GrantsOnlyTheInternalActions(t *testing.T) { // under the VM's own account, so a role in the customer account would be // invisible to the agent and it would get no credentials at all. func TestEnsureInstanceProfile_CreatesInSystemAccount(t *testing.T) { + t.Parallel() f := iammock.New() arn, err := ensureInstanceProfile(func() handlers_iam.SystemInstanceRoleEnsurer { return f }, utils.GlobalAccountID) require.NoError(t, err) @@ -66,6 +68,7 @@ func TestEnsureInstanceProfile_CreatesInSystemAccount(t *testing.T) { // Every launch calls this, so a second call must converge on the existing role // rather than create a second one, and must re-assert the same grant. func TestEnsureInstanceProfile_IsIdempotent(t *testing.T) { + t.Parallel() f := iammock.New() provider := func() handlers_iam.SystemInstanceRoleEnsurer { return f } @@ -102,6 +105,7 @@ func TestEnsureInstanceProfile_RejectsUnavailableIAM(t *testing.T) { } func TestEnsureInstanceProfile_PreservesEnsureFailure(t *testing.T) { + t.Parallel() ensureErr := errors.New("IAM storage unavailable") f := iammock.New() f.PutRolePolicyErr = ensureErr @@ -116,6 +120,7 @@ func TestEnsureInstanceProfile_PreservesEnsureFailure(t *testing.T) { } func TestEnsureInstanceProfile_RejectsEmptyARN(t *testing.T) { + t.Parallel() f := iammock.New() f.EmptyInstanceProfileARN = true diff --git a/spinifex/handlers/rds/launch_test.go b/spinifex/handlers/rds/launch_test.go index 9226e2781..e68580973 100644 --- a/spinifex/handlers/rds/launch_test.go +++ b/spinifex/handlers/rds/launch_test.go @@ -417,6 +417,7 @@ func TestNATSVolumeAttacher_MapsNoResponderByInstanceState(t *testing.T) { } func TestNATSVolumeAttacher_StoppedLookupFailureIsInternal(t *testing.T) { + t.Parallel() _, nc := testutil.StartTestNATS(t) attacher := NewNATSVolumeAttacher(nc) @@ -426,6 +427,7 @@ func TestNATSVolumeAttacher_StoppedLookupFailureIsInternal(t *testing.T) { } func TestNATSVolumeAttacher_RejectsAnEmptyAttachment(t *testing.T) { + t.Parallel() _, nc := testutil.StartTestNATS(t) _, err := nc.Subscribe("ec2.cmd."+testInstance, func(msg *nats.Msg) { _ = msg.Respond([]byte(`{}`)) @@ -511,6 +513,7 @@ func tagOf(specs []*ec2.TagSpecification, key string) string { // --- Tests --- func TestLaunchDBInstanceVMWiresBothNICs(t *testing.T) { + t.Parallel() h := newLaunchHarness() out, err := LaunchDBInstanceVM(t.Context(), h.deps(), testLaunchInput()) @@ -564,6 +567,7 @@ func TestLaunchDBInstanceVMWiresBothNICs(t *testing.T) { } func TestLaunchDBInstanceVMAttachesTheDataVolume(t *testing.T) { + t.Parallel() h := newLaunchHarness() out, err := LaunchDBInstanceVM(t.Context(), h.deps(), testLaunchInput()) @@ -591,6 +595,7 @@ func TestLaunchDBInstanceVMAttachesTheDataVolume(t *testing.T) { } func TestLaunchDBInstanceVMReportsExistingVolumeIntentAndSerial(t *testing.T) { + t.Parallel() h := newLaunchHarness() in := testLaunchInput() in.ExistingDataVolume = "vol-existing-01" @@ -652,6 +657,7 @@ func TestLaunchDBInstanceVMRollsBackEverythingItCreated(t *testing.T) { } func TestLaunchDBInstanceVMTerminatesTheVMBeforeReleasingWhatIsAttachedToIt(t *testing.T) { + t.Parallel() h := newLaunchHarness() h.attacher.err = errors.New("no free hot-plug port") @@ -665,6 +671,7 @@ func TestLaunchDBInstanceVMTerminatesTheVMBeforeReleasingWhatIsAttachedToIt(t *t } func TestLaunchDBInstanceVMRollsBackAfterTheCallersContextIsDone(t *testing.T) { + t.Parallel() h := newLaunchHarness() ctx, cancel := context.WithCancel(t.Context()) @@ -705,6 +712,7 @@ func TestLaunchDBInstanceVMRejectsIncompleteInput(t *testing.T) { } func TestResolveEngineAMIMatchesOnManifestTags(t *testing.T) { + t.Parallel() h := newLaunchHarness() amiID, err := resolveEngineAMI(t.Context(), h.images, "postgres", "18") @@ -726,6 +734,7 @@ func TestResolveEngineAMIMatchesOnManifestTags(t *testing.T) { } func TestResolveEngineAMIOmitsTheVersionFilterWhenUnset(t *testing.T) { + t.Parallel() h := newLaunchHarness() _, err := resolveEngineAMI(t.Context(), h.images, "postgres", "") @@ -737,6 +746,7 @@ func TestResolveEngineAMIOmitsTheVersionFilterWhenUnset(t *testing.T) { } func TestResolveEngineAMIPicksTheNewestBuild(t *testing.T) { + t.Parallel() h := newLaunchHarness() h.images.images = []*ec2.Image{ {ImageId: aws.String("ami-old"), CreationDate: aws.String("2025-06-01T00:00:00Z")}, @@ -750,6 +760,7 @@ func TestResolveEngineAMIPicksTheNewestBuild(t *testing.T) { } func TestResolveEngineAMISkipsMalformedCatalogEntries(t *testing.T) { + t.Parallel() h := newLaunchHarness() h.images.images = []*ec2.Image{ nil, @@ -764,6 +775,7 @@ func TestResolveEngineAMISkipsMalformedCatalogEntries(t *testing.T) { } func TestResolveEngineAMIExcludesGPUTaggedBuilds(t *testing.T) { + t.Parallel() h := newLaunchHarness() h.images.images = []*ec2.Image{ {ImageId: aws.String(testEngineAMI), CreationDate: aws.String("2026-01-02T00:00:00Z")}, @@ -781,6 +793,7 @@ func TestResolveEngineAMIExcludesGPUTaggedBuilds(t *testing.T) { } func TestResolveEngineAMIFailsWhenOnlyGPUBuildsMatch(t *testing.T) { + t.Parallel() h := newLaunchHarness() h.images.images = []*ec2.Image{{ ImageId: aws.String("ami-rds-postgres-18-gpu"), @@ -794,6 +807,7 @@ func TestResolveEngineAMIFailsWhenOnlyGPUBuildsMatch(t *testing.T) { } func TestLaunchDBInstanceVMFailsWithoutAnEngineAMI(t *testing.T) { + t.Parallel() h := newLaunchHarness() h.images.images = nil diff --git a/spinifex/handlers/rds/lifecycle_test.go b/spinifex/handlers/rds/lifecycle_test.go index 9e2a972c5..e360976db 100644 --- a/spinifex/handlers/rds/lifecycle_test.go +++ b/spinifex/handlers/rds/lifecycle_test.go @@ -310,6 +310,7 @@ func (h *lifecycleHarness) events(t *testing.T) []Event { // The engine is asked to stop cleanly first, so the reboot is a restart of a // checkpointed cluster rather than a crash it has to replay. func TestRebootDBInstance_StopsTheEngineThenTheVM(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() rec.FormatAuthorized = true @@ -335,6 +336,7 @@ func TestRebootDBInstance_StopsTheEngineThenTheVM(t *testing.T) { // The static parameters are already in the engine's config, so the restart // is what applies them and the record stops advertising them. func TestRebootDBInstance_ClearsThePendingRebootParameters(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() rec.PendingRebootParameters = []string{"shared_buffers"} @@ -351,6 +353,7 @@ func TestRebootDBInstance_ClearsThePendingRebootParameters(t *testing.T) { // There is no standby to fail over to, so silently ignoring the flag would // report a failover that never happened. func TestRebootDBInstance_RejectsForceFailover(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) @@ -367,6 +370,7 @@ func TestRebootDBInstance_RejectsForceFailover(t *testing.T) { // Refusing to stop the VM over it would leave a customer unable to stop an // instance whose agent is wedged, so it is recorded and the stop continues. func TestStopDBInstance_RecordsAFailedGracefulStopAndContinues(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, true) seedInstance(t, h.svc, availableRecord()) @@ -391,6 +395,7 @@ func TestStopDBInstance_RecordsAFailedGracefulStopAndContinues(t *testing.T) { // way one that never held the VM does. Writing stopped on that alone would // report a VM still serving on the customer ENI as stopped. func TestStopDBInstance_FailsWhenNoNodeAnsweredButTheVMIsStillRunning(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.cmdr.stopNotOnNode = true h.vmState.state = instanceStateRunning @@ -408,6 +413,7 @@ func TestStopDBInstance_FailsWhenNoNodeAnsweredButTheVMIsStillRunning(t *testing // The same unanswered command is the normal shape of a VM that is genuinely // down, and that stop has to converge rather than fail. func TestStopDBInstance_CompletesWhenTheFleetConfirmsTheVMIsDown(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.cmdr.stopNotOnNode = true h.vmState.state = "stopped" @@ -424,6 +430,7 @@ func TestStopDBInstance_CompletesWhenTheFleetConfirmsTheVMIsDown(t *testing.T) { // drain and detach the data volume, so the first reading of the fleet has the VM // stopping. That is not down: the stop waits it out rather than returning on it. func TestStopDBInstance_WaitsForTheFleetToReportTheVMDown(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.vmState.detachReads = 1 seedInstance(t, h.svc, availableRecord()) @@ -439,6 +446,7 @@ func TestStopDBInstance_WaitsForTheFleetToReportTheVMDown(t *testing.T) { // The wait is bounded, and a VM the node accepted the stop for but never took // down has to end as a failure rather than as a stop that never returns. func TestStopDBInstance_FailsWhenTheVMNeverStops(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.cmdr.vm = nil seedInstance(t, h.svc, availableRecord()) @@ -456,6 +464,7 @@ func TestStopDBInstance_FailsWhenTheVMNeverStops(t *testing.T) { // The reconciler resumes a stop with no caller watching, so it needs the same // confirmation before it calls an unanswered command a completed stop. func TestReconciler_DoesNotCallAStillRunningVMStopped(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.cmdr.stopNotOnNode = true h.vmState.state = instanceStateRunning @@ -474,6 +483,7 @@ func TestReconciler_DoesNotCallAStillRunningVMStopped(t *testing.T) { // The data volume, the customer ENI and the DNS record are all retained, // so a start comes back on the same datadir at the same address. func TestStopDBInstance_RetainsTheEndpointAndTheVolume(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seed := availableRecord() seed.FormatAuthorized = true @@ -495,6 +505,7 @@ func TestStopDBInstance_RetainsTheEndpointAndTheVolume(t *testing.T) { // A pre-stop snapshot is a later phase's. Accepting it here would report a // snapshot the customer would then not find. func TestStopDBInstance_RejectsASnapshotIdentifier(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) seedInstance(t, h.svc, availableRecord()) @@ -508,6 +519,7 @@ func TestStopDBInstance_RejectsASnapshotIdentifier(t *testing.T) { } func TestStartDBInstance_StartsTheVMAndReportsStarting(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) rec := availableRecord() rec.Status = StatusStopped @@ -528,6 +540,7 @@ func TestStartDBInstance_StartsTheVMAndReportsStarting(t *testing.T) { // A node restart drops the VM from memory, so the start relaunches it from the // persisted stopped-instance record rather than failing. func TestStartDBInstance_FallsBackWhenNoNodeHoldsTheVM(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.cmdr.notOnNode = true rec := availableRecord() @@ -584,6 +597,7 @@ func TestLifecycleOps_RejectAnIllegalStartingState(t *testing.T) { // A failed transition must not be left looking like one still in progress: the // customer would wait on a reboot that is never coming. func TestRebootDBInstance_MarksFailedWhenTheVMCommandFails(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) h.cmdr.err = errors.New("no node answered") seedInstance(t, h.svc, availableRecord()) @@ -641,6 +655,7 @@ func TestReconciler_CompletesARestartOnAHeartbeatFromTheRestartedEngine(t *testi // is at most a persist floor behind the engine. Judging it by the raw stale // window left a database that came back cleanly rebooting until it timed out. func TestReconciler_CompletesARestartOnAPersistedHeartbeatInsideTheFloor(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) // Older than the stale window, younger than the window plus the floor, and // still after the transition it proves finished. @@ -656,6 +671,7 @@ func TestReconciler_CompletesARestartOnAPersistedHeartbeatInsideTheFloor(t *test // transition began would otherwise report the reboot as finished the instant it // started. func TestReconciler_IgnoresAHeartbeatPredatingTheRestart(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) now := time.Now().UTC() seedInstance(t, h.svc, restartingRecord(StatusRebooting, now, now.Add(-time.Minute))) @@ -668,6 +684,7 @@ func TestReconciler_IgnoresAHeartbeatPredatingTheRestart(t *testing.T) { // A restart that never comes back has to end somewhere: the customer sees a // broken instance either way, and failed is the state they can act on. func TestReconciler_MarksFailedWhenARestartOverrunsItsBound(t *testing.T) { + t.Parallel() h := newLifecycleHarness(t, false) now := time.Now().UTC() started := now.Add(-2 * transitionTimeout) @@ -684,6 +701,7 @@ func TestReconciler_MarksFailedWhenARestartOverrunsItsBound(t *testing.T) { // customer a write-ahead log will bring back an Aria or MyISAM table would be a // false assurance about exactly the data most at risk. func TestUncleanStopMessage_IsEngineAware(t *testing.T) { + t.Parallel() postgres := uncleanStopMessage(t.Context(), "postgres", "stopping the instance") assert.Contains(t, postgres, "could not be shut down cleanly before stopping the instance") assert.Contains(t, postgres, "write-ahead log") diff --git a/spinifex/handlers/rds/modify_lease_test.go b/spinifex/handlers/rds/modify_lease_test.go index bec708d8b..b20f81778 100644 --- a/spinifex/handlers/rds/modify_lease_test.go +++ b/spinifex/handlers/rds/modify_lease_test.go @@ -24,6 +24,7 @@ func heldLease(remaining time.Duration) *ModifyLease { // the sweep runs a second grow — and for a class change, a second // replaceInstanceVM against the same data volume and endpoint ENI. func TestModifyDBInstance_HoldsTheLeaseSoAReconcilePassCannotReEnterTheChange(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -50,6 +51,7 @@ func TestModifyDBInstance_HoldsTheLeaseSoAReconcilePassCannotReEnterTheChange(t // A pass that cannot take the lease leaves the instance alone entirely: the // holder is working on it, and the record already says what it is becoming. func TestReconciler_LeavesAModifyItsHolderIsStillInside(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) rec.ModifyLease = heldLease(modifyLeaseTTL) @@ -68,6 +70,7 @@ func TestReconciler_LeavesAModifyItsHolderIsStillInside(t *testing.T) { // the change over — which is what makes a leader that died mid-modify // recoverable rather than terminal. func TestReconciler_ResumesAModifyWhoseHolderIsGone(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) rec.ModifyLease = heldLease(-time.Second) @@ -107,6 +110,7 @@ func TestApplyPendingModifications_ReleasesTheLeaseWhateverTheOutcome(t *testing // modifying forever, since a pass that cannot take the lease does nothing. Past // the budget it is failed anyway, which is the state a retry is legal from. func TestReconciler_FailsAModifyWhoseHolderOverrunsTheBudget(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) rec.ModifyLease = heldLease(modifyLeaseTTL) @@ -125,6 +129,7 @@ func TestReconciler_FailsAModifyWhoseHolderOverrunsTheBudget(t *testing.T) { // A long-running apply has to keep its lease current, remain exclusive, and // stop as soon as another holder takes ownership. func TestWithModifyLease_RenewsAndCancelsTheApplyWhenTakenOver(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.svc.deps.ModifyLeaseTTL = 500 * time.Millisecond h.svc.deps.ModifyLeaseRefresh = 20 * time.Millisecond @@ -173,6 +178,7 @@ func TestWithModifyLease_RenewsAndCancelsTheApplyWhenTakenOver(t *testing.T) { } func TestModifyDBInstance_LeaseTakeoverLeavesTheTransitionRetryable(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.svc.deps.ModifyLeaseTTL = 500 * time.Millisecond h.svc.deps.ModifyLeaseRefresh = 10 * time.Millisecond @@ -199,6 +205,7 @@ func TestModifyDBInstance_LeaseTakeoverLeavesTheTransitionRetryable(t *testing.T } func TestWithModifyLease_CancelsWhenRenewalsFailUntilExpiry(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.svc.deps.ModifyLeaseTTL = 100 * time.Millisecond h.svc.deps.ModifyLeaseRefresh = 10 * time.Millisecond @@ -230,6 +237,7 @@ func TestWithModifyLease_CancelsWhenRenewalsFailUntilExpiry(t *testing.T) { // Re-taking a lease we already hold has to be allowed, or a caller that claims // twice deadlocks against itself. func TestClaimModifyLease_RefusesAnotherHolderButNotItsOwn(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) kv := h.kv(t) diff --git a/spinifex/handlers/rds/modify_test.go b/spinifex/handlers/rds/modify_test.go index 12f16ee6f..5d4f7ff53 100644 --- a/spinifex/handlers/rds/modify_test.go +++ b/spinifex/handlers/rds/modify_test.go @@ -130,6 +130,7 @@ func createLargeMemoryParameterGroup(t *testing.T, h *modifyHarness) { // None of these interrupts service, so AWS applies them as soon as possible and // so does this — ApplyImmediately is not what gates them. func TestModifyDBInstance_AppliesTheNonDisruptiveSettingsAtOnce(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -175,6 +176,7 @@ func TestModifyDBInstance_AppliesTheNonDisruptiveSettingsAtOnce(t *testing.T) { // Only the fact of the rotation is recorded. A password that reached KV // would be readable by anything that can read the bucket, forever. func TestModifyDBInstance_NeverPersistsTheRotatedPassword(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifiableRecord() rec.Bootstrap.MasterUserPassword = "" @@ -193,6 +195,7 @@ func TestModifyDBInstance_NeverPersistsTheRotatedPassword(t *testing.T) { // reported as applied but never reached the engine locks the customer out of // their own database with no signal. func TestModifyDBInstance_FailsWhenThePasswordCannotBeApplied(t *testing.T) { + t.Parallel() h := newModifyHarnessWithAgent(t, true) seedInstance(t, h.svc, modifiableRecord()) @@ -212,6 +215,7 @@ func TestModifyDBInstance_FailsWhenThePasswordCannotBeApplied(t *testing.T) { // Changing a database's ingress is a live ENI re-association: no replace, no // new address, so the endpoint clients resolve does not move. func TestModifyDBInstance_ReassociatesTheSecurityGroupsOnTheEndpointENI(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -231,6 +235,7 @@ func TestModifyDBInstance_ReassociatesTheSecurityGroupsOnTheEndpointENI(t *testi // An ENI cannot carry a group from another VPC, so the launch-time rule holds // at modify too — and rejecting it here keeps the failure ahead of every write. func TestModifyDBInstance_RejectsASecurityGroupFromAnotherVPC(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -247,6 +252,7 @@ func TestModifyDBInstance_RejectsASecurityGroupFromAnotherVPC(t *testing.T) { // Re-sending the groups already attached is what Terraform does on every apply. // It is not a change, so it must not reach the ENI at all. func TestModifyDBInstance_IgnoresTheSecurityGroupsAlreadyAttached(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -262,6 +268,7 @@ func TestModifyDBInstance_IgnoresTheSecurityGroupsAlreadyAttached(t *testing.T) // instance moves anywhere — a rejected request cannot cost a running database // its availability. func TestModifyDBInstance_RejectsAStorageShrinkBeforeAnythingMoves(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifiableRecord() rec.AllocatedStorage = 100 @@ -286,6 +293,7 @@ func TestModifyDBInstance_RejectsAStorageShrinkBeforeAnythingMoves(t *testing.T) // current one accompanies unrelated changes constantly. Failing it would fail // every one of them; it contributes nothing to the resulting configuration instead. func TestModifyDBInstance_TreatsTheCurrentStorageSizeAsNoChange(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -309,6 +317,7 @@ func TestModifyDBInstance_TreatsTheCurrentStorageSizeAsNoChange(t *testing.T) { // A request that changes nothing at all is answered with the instance as it // stands rather than an outage or an error. func TestModifyDBInstance_IsANoOpWhenNothingDiffers(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -326,6 +335,7 @@ func TestModifyDBInstance_IsANoOpWhenNothingDiffers(t *testing.T) { // The db.* classes are a facade over the platform's instance types, so a // class with nothing behind it is rejected with the set that has. func TestModifyDBInstance_RejectsAnUnmappedInstanceClass(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -342,6 +352,7 @@ func TestModifyDBInstance_RejectsAnUnmappedInstanceClass(t *testing.T) { // Until real groups are materialised the implicit default is the only name // that resolves, so any other one names a group that does not exist. func TestModifyDBInstance_RejectsAnUnknownParameterGroup(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -355,6 +366,7 @@ func TestModifyDBInstance_RejectsAnUnknownParameterGroup(t *testing.T) { } func TestModifyDBInstance_RejectsAnIncompatibleParameterGroupBeforeMutation(t *testing.T) { + t.Parallel() h := newModifyHarness(t) createLargeMemoryParameterGroup(t, h) seedInstance(t, h.svc, modifiableRecord()) @@ -376,6 +388,7 @@ func TestModifyDBInstance_RejectsAnIncompatibleParameterGroupBeforeMutation(t *t } func TestModifyDBInstance_ValidatesClassChangeAgainstCurrentGroup(t *testing.T) { + t.Parallel() h := newModifyHarness(t) createLargeMemoryParameterGroup(t, h) rec := modifiableRecord() @@ -398,6 +411,7 @@ func TestModifyDBInstance_ValidatesClassChangeAgainstCurrentGroup(t *testing.T) } func TestModifyDBInstance_ValidatesSimultaneousGroupAndClassAgainstTargets(t *testing.T) { + t.Parallel() h := newModifyHarness(t) createLargeMemoryParameterGroup(t, h) seedInstance(t, h.svc, modifiableRecord()) @@ -417,6 +431,7 @@ func TestModifyDBInstance_ValidatesSimultaneousGroupAndClassAgainstTargets(t *te // These fields are stored rather than acted on, but a value outside AWS's // range would fail in a maintenance window nobody is watching. func TestModifyDBInstance_RejectsAnOutOfRangeBackupRetention(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -472,6 +487,7 @@ func TestModifyDBInstance_RejectsTheUnimplementedParameters(t *testing.T) { // gp3 is the only type offered, so naming it is not a change and must not be // caught by the rejection that guards the types that are not offered. func TestModifyDBInstance_AcceptsTheStorageTypeItAlreadyHas(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -487,6 +503,7 @@ func TestModifyDBInstance_AcceptsTheStorageTypeItAlreadyHas(t *testing.T) { // A disruptive change without ApplyImmediately is recorded and left for the // maintenance window: the database keeps serving until then. func TestModifyDBInstance_DefersADisruptiveChangeToTheMaintenanceWindow(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) @@ -523,6 +540,7 @@ func TestModifyDBInstance_DefersADisruptiveChangeToTheMaintenanceWindow(t *testi // A disruptive change needs a live engine to stop cleanly and a live agent to // apply parameters, so it is legal only from a state that has both. func TestModifyDBInstance_RejectsADisruptiveChangeWhileNotAvailable(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifiableRecord() rec.Status = StatusStopped @@ -542,6 +560,7 @@ func TestModifyDBInstance_RejectsADisruptiveChangeWhileNotAvailable(t *testing.T // A failed instance is exactly the one a customer retries a change on, so the // same change is accepted from there. func TestModifyDBInstance_AcceptsARetryFromFailed(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifiableRecord() rec.Status = StatusFailed @@ -561,6 +580,7 @@ func TestModifyDBInstance_AcceptsARetryFromFailed(t *testing.T) { // the volume grows, the VM comes back, and the instance stays in modifying with // the in-guest filesystem grow still outstanding. func TestModifyDBInstance_AppliesAStorageGrowImmediately(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seed := modifiableRecord() seed.FormatAuthorized = true @@ -594,6 +614,7 @@ func TestModifyDBInstance_AppliesAStorageGrowImmediately(t *testing.T) { // and the request still recorded, so the reconciler or the customer can retry // it rather than having to reconstruct what was asked for. func TestModifyDBInstance_FailsTheInstanceAndKeepsThePendingValues(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seedInstance(t, h.svc, modifiableRecord()) h.storage.modifyErr = errors.New("the volume store rejected the resize") @@ -618,6 +639,7 @@ func TestModifyDBInstance_FailsTheInstanceAndKeepsThePendingValues(t *testing.T) } func TestModifyDBInstance_RequiresAnIdentifier(t *testing.T) { + t.Parallel() h := newModifyHarness(t) _, err := h.svc.ModifyDBInstance(t.Context(), &rds.ModifyDBInstanceInput{}, testAccountID) @@ -629,6 +651,7 @@ func TestModifyDBInstance_RequiresAnIdentifier(t *testing.T) { } func TestModifyDBInstance_RejectsAnUnknownInstance(t *testing.T) { + t.Parallel() h := newModifyHarness(t) _, err := h.svc.ModifyDBInstance(t.Context(), modifyInput(), testAccountID) diff --git a/spinifex/handlers/rds/network_test.go b/spinifex/handlers/rds/network_test.go index 5f513a405..d55c76500 100644 --- a/spinifex/handlers/rds/network_test.go +++ b/spinifex/handlers/rds/network_test.go @@ -15,6 +15,7 @@ import ( // InvalidParameterValue and every create without an explicit subnet group was // refused before it reached the launch path. func TestDefaultVPCID_SendsFilterNamesTheEC2SurfaceAccepts(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") _, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) @@ -31,6 +32,7 @@ func TestDefaultVPCID_SendsFilterNamesTheEC2SurfaceAccepts(t *testing.T) { // An explicit subnet group carries its own VPC, so the default lookup — and the // filter that goes with it — must not be issued at all. func TestResolvePlacement_SkipsTheDefaultLookupForAnExplicitSubnetGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") _, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput(testSubnetGroup, "subnet-alpha"), testAccountID) require.NoError(t, err) diff --git a/spinifex/handlers/rds/paramcatalog_test.go b/spinifex/handlers/rds/paramcatalog_test.go index 5312bf922..a1047e211 100644 --- a/spinifex/handlers/rds/paramcatalog_test.go +++ b/spinifex/handlers/rds/paramcatalog_test.go @@ -73,6 +73,7 @@ func TestParameterCatalog_EntriesAreWellFormed(t *testing.T) { // The size-derived defaults have to actually move with the class, or the // formulas are decoration and one end of the supported range is mis-tuned. func TestParameterCatalog_SizeDerivedDefaultsScaleWithClassMemory(t *testing.T) { + t.Parallel() smallest, err := classMemoryMiB("db.t3.micro") require.NoError(t, err) largest, err := classMemoryMiB("db.m5.xlarge") @@ -95,6 +96,7 @@ func TestParameterCatalog_SizeDerivedDefaultsScaleWithClassMemory(t *testing.T) // shared_buffers is a quarter of class memory on real RDS, and it is the setting // whose being wrong at the small end stops the engine from starting. func TestParameterCatalog_SharedBuffersIsAQuarterOfClassMemory(t *testing.T) { + t.Parallel() memoryMiB, err := classMemoryMiB("db.m5.large") require.NoError(t, err) @@ -169,6 +171,7 @@ func TestValidateParameterValue_AcceptsPostgresStringValues(t *testing.T) { } func TestValidateParameterValue_AcceptsEveryPostgresSpellingOfABoolean(t *testing.T) { + t.Parallel() for _, value := range []string{"on", "OFF", "true", "False", "yes", "no", "1", "0"} { _, err := enginePostgres.validateParameterValue("autovacuum", value) assert.NoError(t, err, "autovacuum rejected %q, which the engine accepts", value) @@ -327,6 +330,7 @@ func TestParameterCatalog_EachEngineExposesItsTLSEnforcementParameter(t *testing // A group's overrides are laid over the whole catalog, not merged into a subset: // every parameter is present, and one the group does not set carries its default. func TestResolveEffectiveParameters_OverlaysOverridesOnDefaults(t *testing.T) { + t.Parallel() resolved, err := enginePostgres.ResolveEffectiveParameters("db.t3.medium", map[string]string{"work_mem": "16384"}) require.NoError(t, err) require.Len(t, resolved, len(enginePostgres.CatalogParameterNames())) @@ -346,6 +350,7 @@ func TestResolveEffectiveParameters_OverlaysOverridesOnDefaults(t *testing.T) { // Sorted output is what lets a re-resolve that changed nothing produce a // byte-identical include, so an apply does not restart an engine for no reason. func TestResolveEffectiveParameters_IsSortedAndCarriesNoFormula(t *testing.T) { + t.Parallel() resolved, err := enginePostgres.ResolveEffectiveParameters("db.m5.xlarge", nil) require.NoError(t, err) @@ -361,6 +366,7 @@ func TestResolveEffectiveParameters_IsSortedAndCarriesNoFormula(t *testing.T) { // A stored value the catalog would now reject must not keep being handed to the // engine, so the resolve re-validates rather than trusting what was written. func TestResolveEffectiveParameters_RevalidatesStoredOverrides(t *testing.T) { + t.Parallel() _, err := enginePostgres.ResolveEffectiveParameters("db.t3.medium", map[string]string{"max_connections": "999999"}) require.Error(t, err) assert.Contains(t, err.Error(), "max_connections") @@ -427,6 +433,7 @@ func TestResolveEffectiveParameters_RejectsFatalCombinations(t *testing.T) { } func TestResolveEffectiveParameters_AcceptsMinimalWALWithoutReplication(t *testing.T) { + t.Parallel() _, err := enginePostgres.ResolveEffectiveParameters("db.t3.micro", map[string]string{ "wal_level": "minimal", "max_wal_senders": "0", "max_replication_slots": "0", }) @@ -434,6 +441,7 @@ func TestResolveEffectiveParameters_AcceptsMinimalWALWithoutReplication(t *testi } func TestParameterCatalog_Postgres18ApplyTypesAndBuiltCompressionMethods(t *testing.T) { + t.Parallel() autovacuumWorkers, _ := enginePostgres.LookupParameter("autovacuum_max_workers") assert.Equal(t, ApplyTypeDynamic, autovacuumWorkers.ApplyType) @@ -442,6 +450,7 @@ func TestParameterCatalog_Postgres18ApplyTypesAndBuiltCompressionMethods(t *test } func TestResolveEffectiveParameters_RejectsAnUnknownClass(t *testing.T) { + t.Parallel() _, err := enginePostgres.ResolveEffectiveParameters("db.x99.mega", nil) require.Error(t, err) assert.Equal(t, awserrors.ErrorInvalidParameterValue, awserrors.ValidErrorCodeFromError(err), @@ -451,6 +460,7 @@ func TestResolveEffectiveParameters_RejectsAnUnknownClass(t *testing.T) { // AllowedValues is what tells a customer what an integer means, so a unitful // parameter has to report its unit rather than a bare pair of numbers. func TestParameterSpec_AllowedValuesDescribesTheConstraint(t *testing.T) { + t.Parallel() shared, _ := enginePostgres.LookupParameter("shared_buffers") assert.Equal(t, "16-4194304 (8kB)", shared.AllowedValues()) diff --git a/spinifex/handlers/rds/parametergroup_test.go b/spinifex/handlers/rds/parametergroup_test.go index 9854177e6..15c3954a8 100644 --- a/spinifex/handlers/rds/parametergroup_test.go +++ b/spinifex/handlers/rds/parametergroup_test.go @@ -67,6 +67,7 @@ func describedParameters(t *testing.T, h *createHarness, group string) map[strin } func TestCreateDBParameterGroup_StoresAnEmptyGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) out, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) @@ -91,6 +92,7 @@ func TestCreateDBParameterGroup_StoresAnEmptyGroup(t *testing.T) { // An omitted family can only mean the one family this platform offers, so it // takes the pin rather than failing a Terraform config that leaves it out. func TestCreateDBParameterGroup_DefaultsTheFamilyAndRejectsAnother(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) input := parameterGroupInput(testParameterGroup) @@ -110,6 +112,7 @@ func TestCreateDBParameterGroup_DefaultsTheFamilyAndRejectsAnother(t *testing.T) // A customer group under the reserved prefix would be indistinguishable from the // implicit one, and would then be modifiable through a name that must not be. func TestCreateDBParameterGroup_RejectsTheReservedPrefix(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput("default.postgres18"), testAccountID) @@ -120,6 +123,7 @@ func TestCreateDBParameterGroup_RejectsTheReservedPrefix(t *testing.T) { } func TestCreateDBParameterGroup_RejectsADuplicateName(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -133,6 +137,7 @@ func TestCreateDBParameterGroup_RejectsADuplicateName(t *testing.T) { // The default group is what CreateDBInstance resolves when a request names none, // so it has to be listable and readable without anyone having created it. func TestDescribeDBParameterGroups_ReportsTheImplicitDefault(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) named, err := h.svc.DescribeDBParameterGroups(t.Context(), @@ -160,6 +165,7 @@ func parameterGroupNames(out *rds.DescribeDBParameterGroupsOutput) []string { // Synthesised rather than stored, so it must appear exactly once alongside the // customer's own groups rather than twice or not at all. func TestDescribeDBParameterGroups_ListsCustomerGroupsBesideTheDefault(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput("zeta"), testAccountID) require.NoError(t, err) @@ -172,6 +178,7 @@ func TestDescribeDBParameterGroups_ListsCustomerGroupsBesideTheDefault(t *testin } func TestDescribeDBParameterGroups_RejectsAnUnknownName(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.DescribeDBParameterGroups(t.Context(), @@ -190,6 +197,7 @@ func TestDescribeDBParameterGroups_RejectsAnUnknownName(t *testing.T) { } func TestModifyDBParameterGroup_StoresValidatedOverrides(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -213,6 +221,7 @@ func TestModifyDBParameterGroup_StoresValidatedOverrides(t *testing.T) { } func TestModifyDBParameterGroup_PropagatesDynamicParametersToEveryAttachedInstance(t *testing.T) { + t.Parallel() h := newModifyHarness(t) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -254,6 +263,7 @@ func TestModifyDBParameterGroup_PropagatesDynamicParametersToEveryAttachedInstan // leaves the group holding a value the engine never adopted. That has to reach // the instance's apply status rather than being reported as in-sync. func TestModifyDBParameterGroup_RecordsAFailedApplyOnTheInstance(t *testing.T) { + t.Parallel() h := newModifyHarnessWithAgent(t, true) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -278,6 +288,7 @@ func TestModifyDBParameterGroup_RecordsAFailedApplyOnTheInstance(t *testing.T) { // A later apply the engine accepts is what clears it, so the instance does not // keep reporting a failure it has recovered from. func TestModifyDBParameterGroup_ASuccessfulApplyClearsTheRecordedFailure(t *testing.T) { + t.Parallel() h := newModifyHarness(t) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -302,6 +313,7 @@ func TestModifyDBParameterGroup_ASuccessfulApplyClearsTheRecordedFailure(t *test // Propagation marks the instances that refused the set, not the group: an // instance that took the same values stays in-sync. func TestModifyDBParameterGroup_MarksOnlyTheInstancesWhoseApplyFailed(t *testing.T) { + t.Parallel() h := newModifyHarnessWithAgent(t, true) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -332,6 +344,7 @@ func TestModifyDBParameterGroup_MarksOnlyTheInstancesWhoseApplyFailed(t *testing } func TestModifyDBParameterGroup_RecordsStaticParametersPendingReboot(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.agent.replyWith("max_connections") _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) @@ -354,6 +367,7 @@ func TestModifyDBParameterGroup_RecordsStaticParametersPendingReboot(t *testing. } func TestModifyDBParameterGroup_DoesNotPropagateToAPendingAttachment(t *testing.T) { + t.Parallel() h := newModifyHarness(t) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -370,6 +384,7 @@ func TestModifyDBParameterGroup_DoesNotPropagateToAPendingAttachment(t *testing. } func TestModifyDBParameterGroup_ReturnsAPropagationFailure(t *testing.T) { + t.Parallel() h := newModifyHarnessWithAgent(t, true) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -395,6 +410,7 @@ func TestModifyDBParameterGroup_ReturnsAPropagationFailure(t *testing.T) { // A batch with one bad value must leave the group exactly as it was: a // half-applied modify would be a configuration nobody asked for. func TestModifyDBParameterGroup_WritesNothingWhenOneValueIsBad(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -449,6 +465,7 @@ func TestModifyDBParameterGroup_RejectsBadRequests(t *testing.T) { // AWS's own rule, and the one that keeps the default group a stable reference: // an instance that names it must get the catalog's values, not a tenant's edits. func TestModifyDBParameterGroup_RefusesTheDefaultGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.ModifyDBParameterGroup(t.Context(), @@ -460,6 +477,7 @@ func TestModifyDBParameterGroup_RefusesTheDefaultGroup(t *testing.T) { } func TestModifyDBParameterGroup_RejectsAnUnknownGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.ModifyDBParameterGroup(t.Context(), @@ -472,6 +490,7 @@ func TestModifyDBParameterGroup_RejectsAnUnknownGroup(t *testing.T) { // A computed default reaches the customer as the literal it evaluated to, // never as the formula that produced it. func TestDescribeDBParameters_ReportsComputedDefaultsAsLiterals(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) params := describedParameters(t, h, testDefaultPG) @@ -494,6 +513,7 @@ func TestDescribeDBParameters_ReportsComputedDefaultsAsLiterals(t *testing.T) { // a Terraform plan comparing what it wrote against what it reads would otherwise // show drift on every boolean, forever. func TestDescribeDBParameters_ReportsTheCustomersBooleanSpelling(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -516,6 +536,7 @@ func TestDescribeDBParameters_ReportsTheCustomersBooleanSpelling(t *testing.T) { // lower it. The refusal is the ordinary not-modifiable one, which names the // parameter rather than claiming the engine has no such setting. func TestDBParameterGroup_ReportsThePinnedTLSFloorAndRefusesToChangeIt(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -535,6 +556,7 @@ func TestDBParameterGroup_ReportsThePinnedTLSFloorAndRefusesToChangeIt(t *testin } func TestDescribeDBParameters_DerivesApplyMethodForLegacyOverrides(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -553,6 +575,7 @@ func TestDescribeDBParameters_DerivesApplyMethodForLegacyOverrides(t *testing.T) } func TestDescribeDBParameters_FiltersOnSource(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -577,6 +600,7 @@ func TestDescribeDBParameters_FiltersOnSource(t *testing.T) { } func TestDeleteDBParameterGroup_RemovesTheGroupAndItsValues(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -603,6 +627,7 @@ func TestDeleteDBParameterGroup_RemovesTheGroupAndItsValues(t *testing.T) { } func TestDeleteDBParameterGroup_RefusesTheDefaultGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.DeleteDBParameterGroup(t.Context(), @@ -645,6 +670,7 @@ func TestDeleteDBParameterGroup_RefusesWhileAnInstanceReferencesIt(t *testing.T) // The group is the thing bootstrap consumes, so a create has to resolve it into // the literals the agent installs rather than leaving the set empty. func TestCreateDBInstance_ResolvesTheNamedGroupIntoTheBootstrapSet(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -676,6 +702,7 @@ func TestCreateDBInstance_ResolvesTheNamedGroupIntoTheBootstrapSet(t *testing.T) // An unnamed group takes the implicit default, which resolves to the catalog // alone rather than to nothing. func TestCreateDBInstance_ResolvesTheDefaultGroupWhenNoneIsNamed(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBInstance(t.Context(), validCreateInput(), testAccountID) @@ -687,6 +714,7 @@ func TestCreateDBInstance_ResolvesTheDefaultGroupWhenNoneIsNamed(t *testing.T) { } func TestCreateDBInstance_RejectsAnUnknownParameterGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) input := validCreateInput() @@ -701,6 +729,7 @@ func TestCreateDBInstance_RejectsAnUnknownParameterGroup(t *testing.T) { // Both groups are taggable through the one ARN-addressed mutate path, so the // registry entries are what make an ARN a resource rather than a rejection. func TestTagActions_ReachBothGroupTypes(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput(testSubnetGroup, "subnet-alpha"), testAccountID) require.NoError(t, err) diff --git a/spinifex/handlers/rds/pending_test.go b/spinifex/handlers/rds/pending_test.go index bdd6e4a30..7f6fecef4 100644 --- a/spinifex/handlers/rds/pending_test.go +++ b/spinifex/handlers/rds/pending_test.go @@ -24,6 +24,7 @@ func modifyingRecord(pending *PendingModifiedValues) DBInstanceRecord { } func TestApplyPendingModifications_StopsBeforeDestructiveWorkWhenCancelled(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) seedInstance(t, h.svc, rec) @@ -40,6 +41,7 @@ func TestApplyPendingModifications_StopsBeforeDestructiveWorkWhenCancelled(t *te // A grow with no class change alongside it keeps the VM: it goes down, the // volume grows, and the same VM comes back up on the same datadir. func TestApplyPendingModifications_GrowsStorageOnTheSameVM(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) seedInstance(t, h.svc, rec) @@ -63,6 +65,7 @@ func TestApplyPendingModifications_GrowsStorageOnTheSameVM(t *testing.T) { // A class change is a VM replace, and a grow asked for alongside it rides // the same outage rather than opening a second one. func TestApplyPendingModifications_ClassChangeReplacesTheVMAndCarriesTheGrow(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{ DBInstanceClass: "db.m5.large", @@ -91,6 +94,7 @@ func TestApplyPendingModifications_ClassChangeReplacesTheVMAndCarriesTheGrow(t * // still the one running, so the restart that follows is what adopts the // statically-scoped ones. func TestApplyPendingModifications_AppliesTheParametersBeforeTheOutage(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.agent.replyWith("shared_buffers") rec := modifyingRecord(&PendingModifiedValues{ @@ -120,6 +124,7 @@ func TestApplyPendingModifications_AppliesTheParametersBeforeTheOutage(t *testin // stay pending until the customer reboots — the one path that still reports // pending-reboot. func TestApplyPendingModifications_KeepsThePendingRebootParametersWithoutAnOutage(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.agent.replyWith("shared_buffers") rec := modifyingRecord(&PendingModifiedValues{ @@ -140,6 +145,7 @@ func TestApplyPendingModifications_KeepsThePendingRebootParametersWithoutAnOutag // the customer would reboot a healthy database to clear it, and Terraform would // see it in every configuration read. func TestApplyPendingModifications_ClassChangeClearsThePendingRebootParameters(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.agent.replyWith("shared_buffers") rec := modifyingRecord(&PendingModifiedValues{ @@ -163,6 +169,7 @@ func TestApplyPendingModifications_ClassChangeClearsThePendingRebootParameters(t // customer asked for one change, and delivering half of it silently is the // failure mode this closes. func TestApplyPendingModifications_KeepsThePendingValuesWhenTheChangeFails(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.storage.modifyErr = errors.New("the volume store is unavailable") pending := &PendingModifiedValues{ @@ -188,6 +195,7 @@ func TestApplyPendingModifications_KeepsThePendingValuesWhenTheChangeFails(t *te // Nothing outstanding is not an error: a record whose values have already // landed is drained again by any resumed pass. func TestApplyPendingModifications_IsANoOpWithNothingOutstanding(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(nil) seedInstance(t, h.svc, rec) @@ -200,6 +208,7 @@ func TestApplyPendingModifications_IsANoOpWithNothingOutstanding(t *testing.T) { // The last step of a grow: the control plane has already grown the volume, and // this is what turns it into capacity the database can use. func TestFinishFilesystemGrow_ExtendsTheGuestAndClearsThePending(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{FilesystemGrowPending: true, RequestedAt: time.Now().UTC()}) rec.AllocatedStorage = 50 @@ -222,6 +231,7 @@ func TestFinishFilesystemGrow_ExtendsTheGuestAndClearsThePending(t *testing.T) { // volume is bigger and the database cannot use it, which is exactly the state // the reconciler has to keep retrying. func TestFinishFilesystemGrow_KeepsTheStepWhenTheGuestFails(t *testing.T) { + t.Parallel() h := newModifyHarnessWithAgent(t, true) rec := modifyingRecord(&PendingModifiedValues{FilesystemGrowPending: true, RequestedAt: time.Now().UTC()}) seedInstance(t, h.svc, rec) @@ -236,6 +246,7 @@ func TestFinishFilesystemGrow_KeepsTheStepWhenTheGuestFails(t *testing.T) { // A leader that died mid-modify leaves the change recorded and nothing else, so // the next pass re-runs it rather than waiting on a VM that was never touched. func TestReconciler_ResumesAnInterruptedModify(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) seedInstance(t, h.svc, rec) @@ -253,6 +264,7 @@ func TestReconciler_ResumesAnInterruptedModify(t *testing.T) { // The in-guest grow can only run once the agent is back, so the reconciler is // what issues it — a customer's modify finishes without them watching. func TestReconciler_FinishesTheFilesystemGrowOnceTheAgentIsBack(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(&PendingModifiedValues{FilesystemGrowPending: true, RequestedAt: time.Now().UTC()}) beat := time.Now().UTC() @@ -275,6 +287,7 @@ func TestReconciler_FinishesTheFilesystemGrowOnceTheAgentIsBack(t *testing.T) { // A modify whose VM never comes back cannot sit in modifying forever: the // instance is failed with the reason, which is a state a retry is legal from. func TestReconciler_FailsAModifyThatOverrunsItsBudget(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifyingRecord(nil) started := time.Now().UTC().Add(-2 * transitionTimeout) @@ -292,6 +305,7 @@ func TestReconciler_FailsAModifyThatOverrunsItsBudget(t *testing.T) { // A modify that cannot be applied at all is failed rather than retried // forever, and the request stays recorded so the retry has something to run. func TestReconciler_FailsAModifyItCannotApplyWithinTheBudget(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.storage.modifyErr = errors.New("the volume store is unavailable") rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) @@ -311,6 +325,7 @@ func TestReconciler_FailsAModifyItCannotApplyWithinTheBudget(t *testing.T) { // Inside the budget a failed attempt is retried on the next pass rather than // failing the instance on the first transient error. func TestReconciler_RetriesAFailedModifyInsideTheBudget(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.storage.modifyErr = errors.New("the volume store is briefly unavailable") rec := modifyingRecord(&PendingModifiedValues{AllocatedStorage: aws.Int64(50), RequestedAt: time.Now().UTC()}) @@ -328,6 +343,7 @@ func TestReconciler_RetriesAFailedModifyInsideTheBudget(t *testing.T) { // rather than carry the old set forward — a shared_buffers computed for the old // class is wrong at the new one in whichever direction the class moved. func TestApplyPendingModifications_ReResolvesTheParametersForTheNewClass(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.agent.replyWith("shared_buffers") rec := modifyingRecord(&PendingModifiedValues{ @@ -359,6 +375,7 @@ func TestApplyPendingModifications_ReResolvesTheParametersForTheNewClass(t *test // A re-resolve rather than a merge: a parameter the old group set and the new // one does not reverts to its catalog default instead of lingering. func TestApplyPendingModifications_ParameterGroupChangeRevertsTheOldGroupsValues(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.agent.replyWith("") @@ -396,6 +413,7 @@ func TestApplyPendingModifications_ParameterGroupChangeRevertsTheOldGroupsValues // without it outranking the outstanding request the instance would report // applying on every pass while the engine runs the set it already had. func TestApplyPendingModifications_AFailedParameterApplyIsRecordedOnTheInstance(t *testing.T) { + t.Parallel() h := newModifyHarnessWithAgent(t, true) rec := modifyingRecord(&PendingModifiedValues{ DBParameterGroupName: testDefaultGroup, diff --git a/spinifex/handlers/rds/reconciler_test.go b/spinifex/handlers/rds/reconciler_test.go index c3522cf68..bc35cc8d6 100644 --- a/spinifex/handlers/rds/reconciler_test.go +++ b/spinifex/handlers/rds/reconciler_test.go @@ -89,6 +89,7 @@ func (h *reconcileHarness) statusOf(t *testing.T, id string) (Status, string) { } func TestReconciler_MarksAvailableOnHealthyHeartbeatFromARunningVM(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) rec := healthyRecord() rec.FormatAuthorized = true @@ -119,6 +120,7 @@ func TestReconciler_MarksAvailableOnHealthyHeartbeatFromARunningVM(t *testing.T) // engine reported before the platform finished bringing the VM up, or the beat // is stale — either way the instance is not ready. func TestReconciler_HoldsCreatingWhenTheVMIsNotRunning(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) h.state.state = "pending" seedInstance(t, h.svc, healthyRecord()) @@ -169,6 +171,7 @@ func TestReconciler_HoldsCreatingUntilTheEngineIsHealthy(t *testing.T) { // floor; judging that beat by the raw stale window failed a live database at the // transition timeout. func TestReconciler_CompletesACreateOnAPersistedHeartbeatInsideTheFloor(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) rec := healthyRecord() // Older than the stale window, younger than the window plus the floor: what a @@ -186,6 +189,7 @@ func TestReconciler_CompletesACreateOnAPersistedHeartbeatInsideTheFloor(t *testi // A create that never comes up has to end somewhere: the customer sees a broken // instance either way, and failed is the state they can act on. func TestReconciler_MarksFailedWhenTheBootstrapTimesOut(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.BootstrapTimeout = time.Minute }) rec := healthyRecord() rec.Agent = AgentState{ @@ -206,6 +210,7 @@ func TestReconciler_MarksFailedWhenTheBootstrapTimesOut(t *testing.T) { // Inside the window a slow bootstrap is still a bootstrap: a false failed is // worse than a slow one. func TestReconciler_LeavesASlowBootstrapAloneInsideTheWindow(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.BootstrapTimeout = time.Hour }) rec := healthyRecord() rec.Agent = AgentState{} @@ -222,6 +227,7 @@ func TestReconciler_LeavesASlowBootstrapAloneInsideTheWindow(t *testing.T) { // owner. available and failed are the classifier's, and covered in // recovery_test.go. func TestReconciler_LeavesSettledInstancesAlone(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) rec := healthyRecord() rec.Status = StatusStopped @@ -238,6 +244,7 @@ func TestReconciler_LeavesSettledInstancesAlone(t *testing.T) { // A VM-state lookup that fails is not evidence the instance is unhealthy, so the // pass reports the error rather than silently holding — or worse, failing — it. func TestReconciler_SurfacesAVMStateLookupFailure(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) h.state.err = errors.New("no node answered the describe") seedInstance(t, h.svc, healthyRecord()) @@ -253,6 +260,7 @@ func TestReconciler_SurfacesAVMStateLookupFailure(t *testing.T) { // Without a VM-state resolver the heartbeat alone decides, so a node with EC2 // unwired still makes progress rather than stalling every create. func TestReconciler_FallsBackToTheHeartbeatWhenVMStateIsUnwired(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.InstanceState = nil }) seedInstance(t, h.svc, healthyRecord()) @@ -266,6 +274,7 @@ func TestReconciler_FallsBackToTheHeartbeatWhenVMStateIsUnwired(t *testing.T) { // claimant must not also believe it is leader, or two nodes would transition // the same records. func TestReconciler_ElectsASingleLeader(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) other := NewReconciler(h.svc, "node-b") @@ -283,6 +292,7 @@ func TestReconciler_ElectsASingleLeader(t *testing.T) { // A node that never won the lease must not delete the holder's key on shutdown. func TestReconciler_RelinquishOnlyReleasesItsOwnLease(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) other := NewReconciler(h.svc, "node-b") diff --git a/spinifex/handlers/rds/recovery_test.go b/spinifex/handlers/rds/recovery_test.go index f39151ce4..8a67d1bec 100644 --- a/spinifex/handlers/rds/recovery_test.go +++ b/spinifex/handlers/rds/recovery_test.go @@ -91,6 +91,7 @@ func (h *reconcileHarness) recordOf(t *testing.T, id string) DBInstanceRecord { // An instance that has been dark for a week must not keep reporting available: // the failure mode is silent for exactly as long as nobody tries to connect. func TestReconciler_MarksADarkInstanceFailed(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Nanosecond }) h.state.state = "stopped" seedInstance(t, h.svc, darkRecord()) @@ -108,6 +109,7 @@ func TestReconciler_MarksADarkInstanceFailed(t *testing.T) { // customer's monitoring can alert on is the whole point of detecting failure // when nothing repairs it. func TestReconciler_RecordsAnEventForADarkInstance(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Nanosecond }) h.state.state = "stopped" seedInstance(t, h.svc, darkRecord()) @@ -128,6 +130,7 @@ func TestReconciler_RecordsAnEventForADarkInstance(t *testing.T) { // fleet-wide fan-out, so issuing one per instance per pass would put the whole // fleet's liveness on the bus every 15 seconds. func TestReconciler_LeavesAHealthyInstanceUntouchedWithoutAVMLookup(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) seedInstance(t, h.svc, servingRecord()) @@ -181,6 +184,7 @@ func TestReconciler_HoldsAvailableOnHalfTheEvidence(t *testing.T) { // live database can be reported down, which is what a transient fleet describe // during a node restart cannot get past. func TestReconciler_StartsTheClockBeforeFailingWithinTheGrace(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Hour }) h.state.state = "stopped" seedInstance(t, h.svc, darkRecord()) @@ -196,6 +200,7 @@ func TestReconciler_StartsTheClockBeforeFailingWithinTheGrace(t *testing.T) { // The clock a previous leader stamped is what the grace is measured against, so // a leader change does not restart the window and defer detection indefinitely. func TestReconciler_FailsOnAClockStampedByAnEarlierLeader(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Minute }) h.state.state = "stopped" rec := darkRecord() @@ -218,6 +223,7 @@ func TestReconciler_FailsOnAClockStampedByAnEarlierLeader(t *testing.T) { // matters more without a recovery ladder than with one, since nothing else is // watching. func TestReconciler_TheClockResetsOnlyOnAHealthyHeartbeat(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Hour }) rec := darkRecord() stamped := time.Now().UTC().Add(-30 * time.Minute) @@ -250,6 +256,7 @@ func TestReconciler_TheClockResetsOnlyOnAHealthyHeartbeat(t *testing.T) { // allows failed → available, so an instance the AMI or EC2 brought back reports // itself recovered without an operator touching it. func TestReconciler_ClearsFailedOnARecoveredHeartbeat(t *testing.T) { + t.Parallel() h := newReconcileHarness(t) rec := servingRecord() rec.Status = StatusFailed @@ -278,6 +285,7 @@ func TestReconciler_ClearsFailedOnARecoveredHeartbeat(t *testing.T) { // Nothing retries a failed instance in v1.0, so a still-dark one is left where // it is rather than having its reason and clock rewritten every pass. func TestReconciler_LeavesAStillDarkFailedInstanceAlone(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Nanosecond }) h.state.state = "stopped" rec := darkRecord() @@ -298,6 +306,7 @@ func TestReconciler_LeavesAStillDarkFailedInstanceAlone(t *testing.T) { // floor. Judging a persisted beat by the raw stale window would report a steady // fleet as dark for most of every persist cycle. func TestReconciler_AllowsThePersistFloorOnAPersistedHeartbeat(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Nanosecond }) h.state.state = "stopped" rec := servingRecord() @@ -318,6 +327,7 @@ func TestReconciler_AllowsThePersistFloorOnAPersistedHeartbeat(t *testing.T) { // itself earns no slack, and a leader that took the last beat before the VM went // dark must call it failed on the stale window rather than on window plus floor. func TestReconciler_UsesTheTightBoundOnABeatThisNodeHandled(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Nanosecond }) h.state.state = instanceStateStopped rec := servingRecord() @@ -339,6 +349,7 @@ func TestReconciler_UsesTheTightBoundOnABeatThisNodeHandled(t *testing.T) { // gathered at all, so nothing is ever reported failed. Detection goes missing // rather than being declared on the heartbeat alone. func TestReconciler_NeverFailsAnInstanceWhenVMStateIsUnwired(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.InstanceState = nil d.FailureGrace = time.Nanosecond @@ -355,6 +366,7 @@ func TestReconciler_NeverFailsAnInstanceWhenVMStateIsUnwired(t *testing.T) { // A VM lookup that fails is not evidence the instance is down, so the pass // reports the error rather than starting a failure clock on a guess. func TestReconciler_SurfacesAVMLookupFailureWithoutFailingTheInstance(t *testing.T) { + t.Parallel() h := newReconcileHarness(t, func(d *Deps) { d.FailureGrace = time.Nanosecond }) h.state.err = errors.New("no node answered the describe") seedInstance(t, h.svc, darkRecord()) @@ -371,6 +383,7 @@ func TestReconciler_SurfacesAVMLookupFailureWithoutFailingTheInstance(t *testing // A failed instance has to be able to explain itself to the customer, not just // to the log. func TestProjectDBInstance_ReportsTheFailureReason(t *testing.T) { + t.Parallel() svc := NewService(nil, testRegion) rec := defaultRecord() rec.Status = StatusFailed diff --git a/spinifex/handlers/rds/replace_test.go b/spinifex/handlers/rds/replace_test.go index 86738aa9d..d47b1a440 100644 --- a/spinifex/handlers/rds/replace_test.go +++ b/spinifex/handlers/rds/replace_test.go @@ -30,6 +30,7 @@ func seedReplaceable(t *testing.T, h *modifyHarness, rec DBInstanceRecord) DBIns } func TestReplaceInstanceVM_StopsBeforeDestructiveWorkWhenCancelled(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) ctx, cancel := context.WithCancelCause(t.Context()) @@ -46,6 +47,7 @@ func TestReplaceInstanceVM_StopsBeforeDestructiveWorkWhenCancelled(t *testing.T) // both. Minting either would move the address clients resolve or hand the // customer an empty database. func TestReplaceInstanceVM_ReusesTheEndpointENIAndDataVolume(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) @@ -74,6 +76,7 @@ func TestReplaceInstanceVM_ReusesTheEndpointENIAndDataVolume(t *testing.T) { // The engine is checkpointed and the old VM is gone before the new one comes // up, or two VMs hold the same datadir. func TestReplaceInstanceVM_IAMFailureLeavesTheCurrentVMServing(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) iamErr := errors.New("IAM store unavailable") @@ -95,6 +98,7 @@ func TestReplaceInstanceVM_IAMFailureLeavesTheCurrentVMServing(t *testing.T) { } func TestReplaceInstanceVM_StopsTheEngineAndTerminatesTheOldVMFirst(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) @@ -118,6 +122,7 @@ func TestReplaceInstanceVM_StopsTheEngineAndTerminatesTheOldVMFirst(t *testing.T // While the old index entry exists the superseded agent's IMDS credentials // still resolve to this DB instance, so it goes before the new one lands. func TestReplaceInstanceVM_RewritesTheInstanceIndex(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) @@ -138,6 +143,7 @@ func TestReplaceInstanceVM_RewritesTheInstanceIndex(t *testing.T) { // The record names the new VM and forgets the old one's health, while every // field the endpoint is built from is left exactly as it was. func TestReplaceInstanceVM_RecordsTheNewVMAndKeepsTheEndpoint(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seed := modifiableRecord() seed.FormatAuthorized = true @@ -173,6 +179,7 @@ func TestReplaceInstanceVM_RecordsTheNewVMAndKeepsTheEndpoint(t *testing.T) { // A grow riding a class change takes the only window in which nothing holds the // volume: after the old VM is gone and before the new one exists. func TestReplaceInstanceVM_GrowsTheVolumeWhileNoVMHoldsIt(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) @@ -195,6 +202,7 @@ func TestReplaceInstanceVM_GrowsTheVolumeWhileNoVMHoldsIt(t *testing.T) { // An unmapped class has no instance type behind it, so the replace is refused // before the VM it would land on is torn down. func TestReplaceInstanceVM_RefusesAnUnmappedRecordedClass(t *testing.T) { + t.Parallel() h := newModifyHarness(t) seed := modifiableRecord() seed.DBInstanceClass = "db.r5.24xlarge" @@ -230,6 +238,7 @@ func TestReplaceInstanceVM_RefusesWithoutThePersistedIdentity(t *testing.T) { // a replacement: a new ENI would come up on a different address than the one // DNS and the serving certificate name. func TestReplaceInstanceVM_RefusesToMintAReplacementEndpoint(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) h.launch.enis.describeMissing = true @@ -285,6 +294,7 @@ func TestReplaceInstanceVM_LeavesTheRecordRecoverableOnEveryFailure(t *testing.T // A failed launch tears down what it created rather than leaving the DB // instance's own ENI and volume attached to a VM nobody will ever record. func TestReplaceInstanceVM_UnwindsAFailedLaunchWithoutTouchingTheEndpoint(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) h.launch.launcher.err = errors.New("no capacity on any node") @@ -298,6 +308,7 @@ func TestReplaceInstanceVM_UnwindsAFailedLaunchWithoutTouchingTheEndpoint(t *tes // Auto-recovery replaces onto the sizing the record already carries, so an // empty class in the request is not a class change. func TestReplaceInstanceVM_KeepsTheRecordedSizingWhenNoneIsRequested(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) @@ -312,6 +323,7 @@ func TestReplaceInstanceVM_KeepsTheRecordedSizingWhenNoneIsRequested(t *testing. // The customer's own event ring is where a replace becomes attributable to the // change or the failure that caused it. func TestReplaceInstanceVM_RecordsWhyTheVMWasReplaced(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) @@ -327,6 +339,7 @@ func TestReplaceInstanceVM_RecordsWhyTheVMWasReplaced(t *testing.T) { // initialize: the datadir is already there, and a second initdb would be // destructive. The user data is what carries the identity it fetches with. func TestReplaceInstanceVM_LaunchesWithTheInstancesOwnIdentity(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := seedReplaceable(t, h, modifiableRecord()) diff --git a/spinifex/handlers/rds/restore_test.go b/spinifex/handlers/rds/restore_test.go index 11dfe52f8..64c5f8a31 100644 --- a/spinifex/handlers/rds/restore_test.go +++ b/spinifex/handlers/rds/restore_test.go @@ -37,6 +37,7 @@ func restoreInput() *rds.RestoreDBInstanceFromDBSnapshotInput { } func TestRestoreDBInstanceFromDBSnapshot_BuildsANewInstanceOnTheSnapshotsData(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) snapshot := h.seedSnapshot(t) @@ -75,6 +76,7 @@ func TestRestoreDBInstanceFromDBSnapshot_BuildsANewInstanceOnTheSnapshotsData(t } func TestRestoreDBInstanceFromDBSnapshot_IAMFailurePrecedesReservationAndVolume(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) iamErr := errors.New("IAM store unavailable") @@ -93,6 +95,7 @@ func TestRestoreDBInstanceFromDBSnapshot_IAMFailurePrecedesReservationAndVolume( // Source-instance configuration comes from the snapshot when the restore // request does not override it; platform-owned settings use current defaults. func TestRestoreDBInstanceFromDBSnapshot_FallsBackToTheSnapshotsConfiguration(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) @@ -111,6 +114,7 @@ func TestRestoreDBInstanceFromDBSnapshot_FallsBackToTheSnapshotsConfiguration(t } func TestRestoreDBInstanceFromDBSnapshot_DefaultsRetentionAndTakesAnAutomatedBackup(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) now := time.Now().UTC() h.svc.deps.Backup = BackupPolicy{ @@ -154,6 +158,7 @@ func TestRestoreDBInstanceFromDBSnapshot_DefaultsRetentionAndTakesAnAutomatedBac } func TestRestoreDBInstanceFromDBSnapshot_HonoursTheRequestedOverrides(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) @@ -180,6 +185,7 @@ func TestRestoreDBInstanceFromDBSnapshot_HonoursTheRequestedOverrides(t *testing // CreateVolume refuses a size below the snapshot's, and a shrink has nowhere to // put the data the snapshot already holds. func TestRestoreDBInstanceFromDBSnapshot_RejectsStorageBelowTheSnapshots(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) @@ -196,6 +202,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RejectsStorageBelowTheSnapshots(t *test // The volume holds its source snapshot undeletable, so a restore that never // produced an instance must not leave one behind. func TestRestoreDBInstanceFromDBSnapshot_UnwindsTheVolumeAndTheRecordWhenTheLaunchFails(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) h.launch.launcher.err = errors.New("no node had capacity") @@ -212,6 +219,7 @@ func TestRestoreDBInstanceFromDBSnapshot_UnwindsTheVolumeAndTheRecordWhenTheLaun } func TestRestoreDBInstanceFromDBSnapshot_IndexFailureWithdrawsTheRecordedLaunch(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) // This invalid KV key makes the instance-index write fail after recordLaunch @@ -228,6 +236,7 @@ func TestRestoreDBInstanceFromDBSnapshot_IndexFailureWithdrawsTheRecordedLaunch( } func TestRestoreDBInstanceFromDBSnapshot_RecordLaunchDoesNotOverwriteAConcurrentReplacement(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) var replacement DBInstanceRecord @@ -242,6 +251,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RecordLaunchDoesNotOverwriteAConcurrent } func TestRestoreDBInstanceFromDBSnapshot_RollbackDoesNotDeleteAConcurrentReplacement(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) h.launch.launcher.instanceID = "invalid instance id" @@ -259,6 +269,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RollbackDoesNotDeleteAConcurrentReplace } func TestRestoreDBInstanceFromDBSnapshot_RejectsATakenIdentifier(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) @@ -272,6 +283,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RejectsATakenIdentifier(t *testing.T) { } func TestRestoreDBInstanceFromDBSnapshot_RejectsASnapshotThatDoesNotExist(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) _, err := h.svc.RestoreDBInstanceFromDBSnapshot(t.Context(), restoreInput(), testAccountID) @@ -281,6 +293,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RejectsASnapshotThatDoesNotExist(t *tes // A snapshot still being taken has no data to restore from. func TestRestoreDBInstanceFromDBSnapshot_RejectsASnapshotStillBeingTaken(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) kv, err := h.svc.bucket(t.Context(), testAccountID) require.NoError(t, err) @@ -332,6 +345,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RejectsUnimplementedParameters(t *testi // The datadir is written in one engine's on-disk format and no other can read // it, so a request naming a different engine is refused rather than honoured. func TestRestoreDBInstanceFromDBSnapshot_RejectsAnEngineChange(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) @@ -346,6 +360,7 @@ func TestRestoreDBInstanceFromDBSnapshot_RejectsAnEngineChange(t *testing.T) { // Read from the volume rather than echoed from the snapshot: a cluster whose // storage key has gone would otherwise report encryption it is not giving. func TestRestoreDBInstanceFromDBSnapshot_RefusesAnUnencryptedVolumeForAnEncryptedSnapshot(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) h.launch.volumes.encrypted = false diff --git a/spinifex/handlers/rds/sizing_test.go b/spinifex/handlers/rds/sizing_test.go index d64b817b5..5abd47980 100644 --- a/spinifex/handlers/rds/sizing_test.go +++ b/spinifex/handlers/rds/sizing_test.go @@ -10,6 +10,7 @@ import ( ) func TestInstanceTypeForClass_KnownClasses(t *testing.T) { + t.Parallel() tests := map[string]string{ "db.t3.micro": "t3.micro", "db.t3.small": "t3.small", @@ -26,6 +27,7 @@ func TestInstanceTypeForClass_KnownClasses(t *testing.T) { } func TestInstanceTypeForClass_UnmappedClassRejected(t *testing.T) { + t.Parallel() // Real AWS classes the platform does not offer, the bare EC2 type, and a // class-shaped string that is not one — all must be rejected rather than // guessed at by stripping the db. prefix. @@ -43,6 +45,7 @@ func TestInstanceTypeForClass_UnmappedClassRejected(t *testing.T) { // name an instance type that table actually defines. A typo here would surface // as a launch failure after the data volume and ENI already exist. func TestDBInstanceClasses_ResolveInSizingTable(t *testing.T) { + t.Parallel() for class, instanceType := range dbInstanceClasses { vcpus, ok := instancetypes.DefaultVCPUs(instanceType) assert.True(t, ok, "class %q maps to unknown instance type %q", class, instanceType) @@ -51,6 +54,7 @@ func TestDBInstanceClasses_ResolveInSizingTable(t *testing.T) { } func TestSupportedInstanceClasses_SortedAndComplete(t *testing.T) { + t.Parallel() assert.Equal(t, []string{ "db.m5.large", "db.m5.xlarge", "db.t3.large", "db.t3.medium", "db.t3.micro", "db.t3.small", @@ -62,6 +66,7 @@ func TestSupportedInstanceClasses_SortedAndComplete(t *testing.T) { // memory, so reading the head of the sorted list would advertise a shared_buffers // no small instance ever runs. func TestSmallestInstanceClass_IsTheLeastMemory(t *testing.T) { + t.Parallel() assert.Equal(t, "db.t3.micro", SmallestInstanceClass()) least, err := classMemoryMiB(SmallestInstanceClass()) diff --git a/spinifex/handlers/rds/snapshot_test.go b/spinifex/handlers/rds/snapshot_test.go index 102f51a35..a503df9aa 100644 --- a/spinifex/handlers/rds/snapshot_test.go +++ b/spinifex/handlers/rds/snapshot_test.go @@ -130,6 +130,7 @@ func snapshotInput() *rds.CreateDBSnapshotInput { } func TestCreateDBSnapshot_QuiescesTheEngineAndRecordsTheSnapshot(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) @@ -194,6 +195,7 @@ func TestCreateDBSnapshot_ReturnsTheInstanceToWhereItWasFound(t *testing.T) { // A stopped instance's datadir was sealed by the graceful stop, which is the // checkpoint a quiesce would be forcing — so there is no engine to hold. func TestCreateDBSnapshot_DoesNotQuiesceAStoppedInstance(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := availableRecord() rec.Status = StatusStopped @@ -210,6 +212,7 @@ func TestCreateDBSnapshot_DoesNotQuiesceAStoppedInstance(t *testing.T) { // A snapshot that could not be quiesced is still a restorable backup, so it // is taken and reported honestly rather than refused. func TestCreateDBSnapshot_FallsBackToCrashConsistentWhenTheQuiesceFails(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, true) h.seedSnapshotSource(t) @@ -232,6 +235,7 @@ func TestCreateDBSnapshot_FallsBackToCrashConsistentWhenTheQuiesceFails(t *testi // customer a write-ahead log will bring back an Aria or MyISAM table would be a // false assurance about exactly the data most at risk. func TestCrashConsistentSnapshotMessage_IsEngineAware(t *testing.T) { + t.Parallel() postgres := crashConsistentSnapshotMessage(t.Context(), "postgres") assert.Contains(t, postgres, "crash consistent") assert.Contains(t, postgres, "write-ahead log") @@ -251,6 +255,7 @@ func TestCrashConsistentSnapshotMessage_IsEngineAware(t *testing.T) { // The record only ever described a snapshot that now does not exist, so it goes // with it rather than holding the identifier for a reconciler to puzzle over. func TestCreateDBSnapshot_WithdrawsTheRecordWhenTheSnapshotFails(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) h.snaps.createErr = awserrors.Errorf(awserrors.ErrorInternalError, "the volume store refused the snapshot") @@ -264,6 +269,7 @@ func TestCreateDBSnapshot_WithdrawsTheRecordWhenTheSnapshotFails(t *testing.T) { } func TestCreateDBSnapshot_RejectsATakenIdentifier(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) _, err := h.svc.CreateDBSnapshot(t.Context(), snapshotInput(), testAccountID) @@ -279,6 +285,7 @@ func TestCreateDBSnapshot_RejectsATakenIdentifier(t *testing.T) { // One snapshot per instance at a time: the CAS into backing-up is the guard, so // an instance already there is refused rather than entering the same window. func TestCreateDBSnapshot_RefusesAnInstanceAlreadyBackingUp(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := availableRecord() rec.Status = StatusBackingUp @@ -296,6 +303,7 @@ func TestCreateDBSnapshot_RefusesAnInstanceAlreadyBackingUp(t *testing.T) { } func TestCreateDBSnapshot_RejectsAnUnusableIdentifier(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) @@ -311,6 +319,7 @@ func TestCreateDBSnapshot_RejectsAnUnusableIdentifier(t *testing.T) { } func TestDescribeDBSnapshots_FiltersByInstanceAndType(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) _, err := h.svc.CreateDBSnapshot(t.Context(), snapshotInput(), testAccountID) @@ -348,6 +357,7 @@ func TestDescribeDBSnapshots_FiltersByInstanceAndType(t *testing.T) { // A client polling a create would otherwise read "gone" for "not ready". func TestDescribeDBSnapshots_NamedSnapshotThatDoesNotExistIsAnError(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) _, err := h.svc.DescribeDBSnapshots(t.Context(), &rds.DescribeDBSnapshotsInput{ @@ -377,6 +387,7 @@ func TestDescribeDBSnapshots_RejectsUnhonouredScoping(t *testing.T) { } func TestDeleteDBSnapshot_RemovesTheSnapshotAndItsRecord(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) _, err := h.svc.CreateDBSnapshot(t.Context(), snapshotInput(), testAccountID) @@ -402,6 +413,7 @@ func TestDeleteDBSnapshot_RemovesTheSnapshotAndItsRecord(t *testing.T) { // A COW snapshot references its source volume's chunks, so a deleted instance's // volume is retained until the last snapshot holding it goes. func TestDeleteDBSnapshot_ReleasesARetainedVolumeWhenItWasTheLastHolder(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) _, err := h.svc.CreateDBSnapshot(t.Context(), snapshotInput(), testAccountID) @@ -433,6 +445,7 @@ func TestDeleteDBSnapshot_ReleasesARetainedVolumeWhenItWasTheLastHolder(t *testi // record was written with: another snapshot taken since would otherwise read as // "nothing holds it". func TestDeleteDBSnapshot_KeepsARetainedVolumeAnotherSnapshotStillHolds(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) _, err := h.svc.CreateDBSnapshot(t.Context(), snapshotInput(), testAccountID) @@ -467,6 +480,7 @@ func TestDeleteDBSnapshot_KeepsARetainedVolumeAnotherSnapshotStillHolds(t *testi // The raw EC2 code says nothing a customer can act on, so the fault names the // restored instance they have to remove first. func TestDeleteDBSnapshot_NamesTheRestoredInstanceStillReadingFromIt(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) _, err := h.svc.CreateDBSnapshot(t.Context(), snapshotInput(), testAccountID) @@ -492,6 +506,7 @@ func TestDeleteDBSnapshot_NamesTheRestoredInstanceStillReadingFromIt(t *testing. // A snapshot still being taken has no data to remove and an in-flight writer // that would recreate the record. func TestDeleteDBSnapshot_RefusesASnapshotStillBeingTaken(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) kv, err := h.svc.bucket(t.Context(), testAccountID) require.NoError(t, err) @@ -513,6 +528,7 @@ func TestDeleteDBSnapshot_RefusesASnapshotStillBeingTaken(t *testing.T) { // A snapshot still being taken has no PercentProgress, because reporting full // progress would have a client restore from something that does not exist. func TestProjectDBSnapshot_ReportsProgressOnlyWhenTheDataExists(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := DBSnapshotRecord{ DBSnapshotIdentifier: testSnapshotID, @@ -528,6 +544,7 @@ func TestProjectDBSnapshot_ReportsProgressOnlyWhenTheDataExists(t *testing.T) { // The EC2 snapshot is tagged with the DB snapshot identifier before the record // is flipped, so its presence is what says the data exists. func TestReconciler_AdoptsTheEC2SnapshotOfAnUnfinishedDBSnapshot(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := NewReconciler(h.svc, "node-a") @@ -560,6 +577,7 @@ func TestReconciler_AdoptsTheEC2SnapshotOfAnUnfinishedDBSnapshot(t *testing.T) { // A record left in creating would hold its name forever while naming nothing a // customer can restore. func TestReconciler_DoesNotAdoptAnotherAccountsSnapshot(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) reconciler := NewReconciler(h.svc, "node-a") @@ -593,6 +611,7 @@ func TestReconciler_DoesNotAdoptAnotherAccountsSnapshot(t *testing.T) { } func TestReconciler_KeepsCreatingSnapshotWhenEC2LookupIsIncomplete(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.snaps.describeErr = errors.New("snapshot metadata temporarily unavailable") reconciler := NewReconciler(h.svc, "node-a") @@ -617,6 +636,7 @@ func TestReconciler_KeepsCreatingSnapshotWhenEC2LookupIsIncomplete(t *testing.T) } func TestReconciler_DoesNotDeleteAConcurrentlyCompletedSnapshot(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) reconciler := NewReconciler(h.svc, "node-a") @@ -649,6 +669,7 @@ func TestReconciler_DoesNotDeleteAConcurrentlyCompletedSnapshot(t *testing.T) { } func TestReconciler_RejectsAmbiguousEC2Snapshots(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) reconciler := NewReconciler(h.svc, "node-a") @@ -683,6 +704,7 @@ func TestReconciler_RejectsAmbiguousEC2Snapshots(t *testing.T) { // A completed authoritative lookup is the only evidence that no EC2 snapshot // was cut. Once it succeeds with no match, the stale name can be released. func TestReconciler_WithdrawsADBSnapshotWhoseDataWasNeverCut(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := NewReconciler(h.svc, "node-a") @@ -707,6 +729,7 @@ func TestReconciler_WithdrawsADBSnapshotWhoseDataWasNeverCut(t *testing.T) { // Inside the window the record still belongs to a live worker, so touching it // would race the write that is about to land. func TestReconciler_LeavesAFreshCreatingSnapshotAlone(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) rec := NewReconciler(h.svc, "node-a") @@ -758,6 +781,7 @@ func TestReconciler_ReturnsAnInstanceStuckInBackingUp(t *testing.T) { // Inside the bound the snapshot is still running, and cutting it short would // leave the engine quiesced with nobody to release it. func TestReconciler_LeavesAnInFlightSnapshotAlone(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) reconciler := NewReconciler(h.svc, "node-a") @@ -782,6 +806,7 @@ func TestReconciler_LeavesAnInFlightSnapshotAlone(t *testing.T) { // The Terraform provider reads tags from the describe as well as from // ListTagsForResource, so the two have to agree. func TestDBSnapshotTags_ReadBackThroughBothPaths(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshotSource(t) @@ -811,6 +836,7 @@ func TestDBSnapshotTags_ReadBackThroughBothPaths(t *testing.T) { // The record has to survive a round trip through KV, because that is the only // copy a restore reads once the instance it came from is gone. func TestDBSnapshotRecord_SurvivesARoundTrip(t *testing.T) { + t.Parallel() updated := time.Now().UTC().Truncate(time.Second) rec := DBSnapshotRecord{ DBSnapshotIdentifier: testSnapshotID, diff --git a/spinifex/handlers/rds/status_test.go b/spinifex/handlers/rds/status_test.go index 1dda25ae2..1ececc09b 100644 --- a/spinifex/handlers/rds/status_test.go +++ b/spinifex/handlers/rds/status_test.go @@ -7,6 +7,7 @@ import ( ) func TestStatus_Valid(t *testing.T) { + t.Parallel() for _, s := range []Status{ StatusCreating, StatusAvailable, StatusModifying, StatusBackingUp, StatusRebooting, StatusStopping, StatusStarting, StatusStopped, @@ -22,6 +23,7 @@ func TestStatus_Valid(t *testing.T) { } func TestCanTransition_Lifecycle(t *testing.T) { + t.Parallel() tests := []struct { from, to Status want bool @@ -81,6 +83,7 @@ func TestCanTransition_Lifecycle(t *testing.T) { // Every state the machine can reach must itself be a state the machine knows, // or the reconciler could write a status it then refuses to read back. func TestTransitions_TargetsAreValidStates(t *testing.T) { + t.Parallel() for from, targets := range transitions { assert.True(t, from.Valid(), "source status %q should be valid", from) for _, to := range targets { diff --git a/spinifex/handlers/rds/storage_test.go b/spinifex/handlers/rds/storage_test.go index 6e7f11266..689f01bba 100644 --- a/spinifex/handlers/rds/storage_test.go +++ b/spinifex/handlers/rds/storage_test.go @@ -100,6 +100,7 @@ func TestValidateStorageGrow(t *testing.T) { // The volume store is grow-only, so a resumed grow that re-issued the modify // would be rejected as a shrink. It reads the volume first instead. func TestGrowDataVolume_SkipsAVolumeAlreadyAtTheTarget(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.storage.sizes[testDataVolume] = 50 @@ -110,6 +111,7 @@ func TestGrowDataVolume_SkipsAVolumeAlreadyAtTheTarget(t *testing.T) { // A volume the store cannot describe must not read as zero: zero is smaller // than every target, so the grow would go ahead against a volume that is gone. func TestGrowDataVolume_FailsWhenTheVolumeIsGone(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.storage.missing = true @@ -120,6 +122,7 @@ func TestGrowDataVolume_FailsWhenTheVolumeIsGone(t *testing.T) { } func TestGrowDataVolume_FailsWithoutAVolume(t *testing.T) { + t.Parallel() h := newModifyHarness(t) require.Error(t, h.svc.growDataVolume(t.Context(), "", 50)) @@ -128,6 +131,7 @@ func TestGrowDataVolume_FailsWithoutAVolume(t *testing.T) { // ModifyVolume refuses a volume a running VM holds, so the order is the // whole mechanism — engine down, VM down, grow, VM back. func TestGrowInstanceStorage_StopsTheEngineAndTheVMBeforeGrowing(t *testing.T) { + t.Parallel() h := newModifyHarness(t) rec := modifiableRecord() @@ -151,6 +155,7 @@ func TestGrowInstanceStorage_StopsTheEngineAndTheVMBeforeGrowing(t *testing.T) { // store refuses a resize until it has. The grow holds off until the fleet // reports the VM down rather than resizing on the accepted command. func TestGrowInstanceStorage_WaitsForTheDetachBeforeGrowing(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.vmState.detachReads = 1 rec := modifiableRecord() @@ -168,6 +173,7 @@ func TestGrowInstanceStorage_WaitsForTheDetachBeforeGrowing(t *testing.T) { // A wedged agent must not block a grow: the engine stop is a courtesy, and the // VM stop that follows is what actually releases the volume. func TestGrowInstanceStorage_ProceedsWhenTheEngineWillNotStop(t *testing.T) { + t.Parallel() h := newModifyHarnessWithAgent(t, true) rec := modifiableRecord() @@ -178,6 +184,7 @@ func TestGrowInstanceStorage_ProceedsWhenTheEngineWillNotStop(t *testing.T) { // A rejected grow must not turn a failed modification into an outage. The VM // restarts on its unchanged volume before the original error is returned. func TestGrowInstanceStorage_RestartsTheVMWhenTheGrowFails(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.storage.modifyErr = errors.New("the volume store is unavailable") rec := modifiableRecord() @@ -191,6 +198,7 @@ func TestGrowInstanceStorage_RestartsTheVMWhenTheGrowFails(t *testing.T) { // Both failures matter when the grow and its recovery fail: the first explains // the unchanged storage and the second explains the continuing outage. func TestGrowInstanceStorage_ReportsTheGrowAndRestartFailures(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.storage.modifyErr = errors.New("the volume store is unavailable") h.storage.onModify = func() { h.cmdr.err = errors.New("the node did not answer") } @@ -207,6 +215,7 @@ func TestGrowInstanceStorage_ReportsTheGrowAndRestartFailures(t *testing.T) { // Recovery belongs to the new lease holder after a takeover. The stale holder // must not restart the VM while its replacement may be retrying the grow. func TestGrowInstanceStorage_DoesNotRestartAfterLosingTheModifyLease(t *testing.T) { + t.Parallel() h := newModifyHarness(t) ctx, cancel := context.WithCancelCause(t.Context()) h.storage.modifyErr = errors.New("the volume store is unavailable") @@ -223,6 +232,7 @@ func TestGrowInstanceStorage_DoesNotRestartAfterLosingTheModifyLease(t *testing. // happen must not be followed by a resize that would be rejected — or worse, // accepted against a volume a live guest is writing to. func TestGrowInstanceStorage_FailsWhenTheVMWillNotStop(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.cmdr.err = errors.New("the node did not answer") rec := modifiableRecord() diff --git a/spinifex/handlers/rds/store_test.go b/spinifex/handlers/rds/store_test.go index a2959ca07..23c2131bd 100644 --- a/spinifex/handlers/rds/store_test.go +++ b/spinifex/handlers/rds/store_test.go @@ -10,10 +10,12 @@ import ( ) func TestAccountBucketName(t *testing.T) { + t.Parallel() assert.Equal(t, "rds-account-123456789012", AccountBucketName(testAccountID)) } func TestKeyPaths(t *testing.T) { + t.Parallel() assert.Equal(t, "db-instances/orders-db", DBInstanceKey("orders-db")) assert.Equal(t, "db-snapshots/orders-db-final", DBSnapshotKey("orders-db-final")) assert.Equal(t, "db-subnet-groups/prod-db-subnets", DBSubnetGroupKey("prod-db-subnets")) @@ -29,6 +31,7 @@ func TestKeyPaths(t *testing.T) { // Each key must sit under its own prefix, so an enumeration of one resource type // can never pick up another's records. func TestKeyPathsSitUnderTheirPrefix(t *testing.T) { + t.Parallel() assert.True(t, strings.HasPrefix(DBInstanceKey("x"), DBInstancesPrefix())) assert.True(t, strings.HasPrefix(DBSnapshotKey("x"), DBSnapshotsPrefix())) assert.True(t, strings.HasPrefix(DBSubnetGroupKey("x"), DBSubnetGroupsPrefix())) @@ -44,11 +47,13 @@ func TestKeyPathsSitUnderTheirPrefix(t *testing.T) { } func TestNewStore_NilConn(t *testing.T) { + t.Parallel() _, err := NewStore(nil) require.Error(t, err) } func TestNewStore_Valid(t *testing.T) { + t.Parallel() _, nc, _ := testutil.StartTestJetStream(t) s, err := NewStore(nc) require.NoError(t, err) @@ -56,6 +61,7 @@ func TestNewStore_Valid(t *testing.T) { } func TestGetOrCreateAccountBucket_Idempotent(t *testing.T) { + t.Parallel() _, nc, _ := testutil.StartTestJetStream(t) js := testutil.NewJetStream(t, nc) @@ -72,6 +78,7 @@ func TestGetOrCreateAccountBucket_Idempotent(t *testing.T) { } func TestGetOrCreateSystemBucket_Idempotent(t *testing.T) { + t.Parallel() _, nc, _ := testutil.StartTestJetStream(t) js := testutil.NewJetStream(t, nc) diff --git a/spinifex/handlers/rds/subnetgroup_test.go b/spinifex/handlers/rds/subnetgroup_test.go index 1413bbc6f..f344dd773 100644 --- a/spinifex/handlers/rds/subnetgroup_test.go +++ b/spinifex/handlers/rds/subnetgroup_test.go @@ -25,6 +25,7 @@ func subnetGroupInput(name string, subnetIDs ...string) *rds.CreateDBSubnetGroup // Terraform module against a platform that exposes one zone, and rejecting it // buys no safety. func TestCreateDBSubnetGroup_AcceptsASingleSubnet(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) out, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput(testSubnetGroup, "subnet-alpha"), testAccountID) @@ -47,6 +48,7 @@ func TestCreateDBSubnetGroup_AcceptsASingleSubnet(t *testing.T) { // Stored verbatim: the group keeps every subnet the customer supplied, so the // placement logic can change later without a record migration. func TestCreateDBSubnetGroup_StoresEverySubnetSupplied(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), @@ -65,6 +67,7 @@ func TestCreateDBSubnetGroup_StoresEverySubnetSupplied(t *testing.T) { } func TestCreateDBSubnetGroup_RejectsAMissingSubnet(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), @@ -78,6 +81,7 @@ func TestCreateDBSubnetGroup_RejectsAMissingSubnet(t *testing.T) { // The check that actually matters: a group spanning two VPCs cannot host an // instance at all, because the endpoint ENI lives in exactly one of them. func TestCreateDBSubnetGroup_RejectsACrossVPCSet(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) h.network.subnets = append(h.network.subnets, &ec2.Subnet{ SubnetId: aws.String("subnet-other"), @@ -96,6 +100,7 @@ func TestCreateDBSubnetGroup_RejectsACrossVPCSet(t *testing.T) { // The describe is issued as the caller, so another account's subnet does not // come back at all — it is reported as invalid rather than leaked or accepted. func TestCreateDBSubnetGroup_RejectsACrossAccountSubnet(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), @@ -143,6 +148,7 @@ func TestCreateDBSubnetGroup_RejectsMalformedRequests(t *testing.T) { // The name is the reservation, so a repeat is a conflict rather than a silent // overwrite of the first group's subnets. func TestCreateDBSubnetGroup_RejectsADuplicateName(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput(testSubnetGroup, "subnet-alpha"), testAccountID) require.NoError(t, err) @@ -154,6 +160,7 @@ func TestCreateDBSubnetGroup_RejectsADuplicateName(t *testing.T) { } func TestDescribeDBSubnetGroups_ListsAndNames(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput("zeta", "subnet-alpha"), testAccountID) require.NoError(t, err) @@ -176,6 +183,7 @@ func TestDescribeDBSubnetGroups_ListsAndNames(t *testing.T) { // A client polling a create would read an empty list as "gone" rather than // "not there", so a named group that does not exist is an error. func TestDescribeDBSubnetGroups_RejectsAnUnknownName(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.DescribeDBSubnetGroups(t.Context(), @@ -186,6 +194,7 @@ func TestDescribeDBSubnetGroups_RejectsAnUnknownName(t *testing.T) { } func TestDeleteDBSubnetGroup_RemovesAnUnusedGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput(testSubnetGroup, "subnet-alpha"), testAccountID) require.NoError(t, err) @@ -233,6 +242,7 @@ func TestDeleteDBSubnetGroup_RefusesWhileAnInstanceReferencesIt(t *testing.T) { // The group is what decides where the endpoint lands, so a create naming one has // to place the ENI in its subnet rather than in the account's default VPC. func TestCreateDBInstance_PlacesTheEndpointFromTheNamedSubnetGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput(testSubnetGroup, "subnet-zebra"), testAccountID) require.NoError(t, err) @@ -253,6 +263,7 @@ func TestCreateDBInstance_PlacesTheEndpointFromTheNamedSubnetGroup(t *testing.T) // was placed from is only useful to a client if it survives the projection, and // the Terraform provider reads db_subnet_group_name from exactly here. func TestDescribeDBInstances_ReportsTheSubnetGroupTheInstanceWasPlacedFrom(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) _, err := h.svc.CreateDBSubnetGroup(t.Context(), subnetGroupInput(testSubnetGroup, "subnet-zebra"), testAccountID) require.NoError(t, err) @@ -281,6 +292,7 @@ func TestDescribeDBInstances_ReportsTheSubnetGroupTheInstanceWasPlacedFrom(t *te // Placing the endpoint somewhere else would put it in a subnet nobody chose, so // a group that does not exist fails the create rather than falling back. func TestCreateDBInstance_RejectsAnUnknownSubnetGroup(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) input := validCreateInput() diff --git a/spinifex/handlers/rds/tags_test.go b/spinifex/handlers/rds/tags_test.go index 5ad480912..046709724 100644 --- a/spinifex/handlers/rds/tags_test.go +++ b/spinifex/handlers/rds/tags_test.go @@ -38,6 +38,7 @@ func listTags(t *testing.T, h *createHarness, id string) map[string]string { } func TestCreateDBInstance_TagsRoundTripThroughBothReadPaths(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") input := validCreateInput() input.Tags = awsTags("env", "prod", "team", "platform") @@ -61,6 +62,7 @@ func TestCreateDBInstance_TagsRoundTripThroughBothReadPaths(t *testing.T) { // The same rules apply at create as at AddTagsToResource, and they run before // the identifier is reserved so a rejected create leaves nothing behind. func TestCreateDBInstance_InvalidTagsLeaveNoRecord(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") input := validCreateInput() input.Tags = awsTags("aws:cloudformation:stack-name", "mine") @@ -73,6 +75,7 @@ func TestCreateDBInstance_InvalidTagsLeaveNoRecord(t *testing.T) { } func TestListTagsForResource_UntaggedInstanceIsAnEmptyList(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -82,6 +85,7 @@ func TestListTagsForResource_UntaggedInstanceIsAnEmptyList(t *testing.T) { // AddTagsToResource is a merge, and a repeated key overwrites rather than // duplicates, so a re-run of the same apply converges. func TestAddTagsToResource_MergesAndOverwrites(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) arn := aws.String(DBInstanceARN(testRegion, testAccountID, testDBInstanceID)) @@ -102,6 +106,7 @@ func TestAddTagsToResource_MergesAndOverwrites(t *testing.T) { // A key repeated inside one request keeps its last value rather than failing, // which is what AWS does. func TestAddTagsToResource_DuplicateKeyInOneRequestKeepsTheLastValue(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -115,6 +120,7 @@ func TestAddTagsToResource_DuplicateKeyInOneRequestKeepsTheLastValue(t *testing. } func TestRemoveTagsFromResource_RemovesNamedKeysAndIgnoresAbsentOnes(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) arn := aws.String(DBInstanceARN(testRegion, testAccountID, testDBInstanceID)) @@ -171,6 +177,7 @@ func TestTagActions_RejectInvalidTags(t *testing.T) { // The limit is a per-resource one, so two individually legal requests that // together exceed it are rejected on the second. func TestAddTagsToResource_LimitAppliesToTheMergedResult(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) arn := aws.String(DBInstanceARN(testRegion, testAccountID, testDBInstanceID)) @@ -200,6 +207,7 @@ func TestAddTagsToResource_LimitAppliesToTheMergedResult(t *testing.T) { // A well-formed ARN naming nothing is the resource's own fault, not an ARN // error: the two failures carry different messages on purpose. func TestTagActions_MissingResourceIsTheResourcesOwnFault(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") arn := aws.String(DBInstanceARN(testRegion, testAccountID, "no-such-db")) @@ -223,6 +231,7 @@ func TestTagActions_MissingResourceIsTheResourcesOwnFault(t *testing.T) { // Answering with an empty list would be indistinguishable from an untagged // snapshot and would let an apply appear to tag something that does not exist. func TestTagActions_MissingSnapshotIsRejected(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") _, err := h.svc.ListTagsForResource(t.Context(), &rds.ListTagsForResourceInput{ @@ -233,6 +242,7 @@ func TestTagActions_MissingSnapshotIsRejected(t *testing.T) { } func TestListTagsForResource_AcceptsAnAutomatedSnapshotARN(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") id := AutomatedSnapshotIdentifier(testDBInstanceID, time.Date(2026, 7, 24, 3, 4, 0, 0, time.UTC)) kv, err := h.svc.bucket(t.Context(), testAccountID) @@ -251,6 +261,7 @@ func TestListTagsForResource_AcceptsAnAutomatedSnapshotARN(t *testing.T) { } func TestTagActions_ForeignAccountARNIsRejected(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) @@ -264,6 +275,7 @@ func TestTagActions_ForeignAccountARNIsRejected(t *testing.T) { // Every tag write is a read-modify-write of the same record, so without CAS the // last writer would silently discard the others' keys. func TestTagWrites_ConcurrentAddsAndRemovesDoNotLoseEachOther(t *testing.T) { + t.Parallel() h := newCreateHarness(t, "") seedCreated(t, h, testDBInstanceID) arn := aws.String(DBInstanceARN(testRegion, testAccountID, testDBInstanceID)) diff --git a/spinifex/handlers/rds/tlsenforcement_test.go b/spinifex/handlers/rds/tlsenforcement_test.go index 36ee646eb..d31b45f9c 100644 --- a/spinifex/handlers/rds/tlsenforcement_test.go +++ b/spinifex/handlers/rds/tlsenforcement_test.go @@ -48,6 +48,7 @@ func requireNoTLSToEnforce(t *testing.T, err error, parameter, group string) { // The three states loadCA distinguishes, because the gate reads the middle one // as a refusal and must not read the last one as one. func TestTLSAvailable_ReportsWhetherTheDeploymentCanServeTLS(t *testing.T) { + t.Parallel() configured := NewService(nil, testRegion).WithDeps(Deps{LoadCA: newTestCA(t)}) available, err := configured.tlsAvailable() require.NoError(t, err) @@ -101,6 +102,7 @@ func TestCreateDBInstance_EnforcesTLSWithNoParameterGroupOfItsOwn(t *testing.T) // Enforcement the deployment cannot serve would launch an instance no client can // reach, so the binding is refused where the customer can see it. func TestCreateDBInstance_RefusesEnforcementWithNoClusterCA(t *testing.T) { + t.Parallel() h := newCreateHarness(t, testBaseDomain) withoutClusterCA(t, h.svc) @@ -145,6 +147,7 @@ func TestCreateDBInstance_AcceptsEnforcementTurnedOffWithNoClusterCA(t *testing. } func TestModifyDBInstance_RefusesEnforcementWithNoClusterCA(t *testing.T) { + t.Parallel() h := newModifyHarness(t) _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) require.NoError(t, err) @@ -163,6 +166,7 @@ func TestModifyDBInstance_RefusesEnforcementWithNoClusterCA(t *testing.T) { // The deferred half of a modify re-resolves at apply time, so the gate holds // there too rather than only at the request that recorded it. func TestApplyPendingModifications_RefusesEnforcementWithNoClusterCA(t *testing.T) { + t.Parallel() h := newModifyHarness(t) h.agent.replyWith("") _, err := h.svc.CreateDBParameterGroup(t.Context(), parameterGroupInput(testParameterGroup), testAccountID) @@ -182,6 +186,7 @@ func TestApplyPendingModifications_RefusesEnforcementWithNoClusterCA(t *testing. } func TestRestoreDBInstanceFromDBSnapshot_RefusesEnforcementWithNoClusterCA(t *testing.T) { + t.Parallel() h := newSnapshotHarness(t, false) h.seedSnapshot(t) withoutClusterCA(t, h.svc) diff --git a/spinifex/handlers/rds/userdata_test.go b/spinifex/handlers/rds/userdata_test.go index 4be4209d5..42e70710a 100644 --- a/spinifex/handlers/rds/userdata_test.go +++ b/spinifex/handlers/rds/userdata_test.go @@ -21,6 +21,7 @@ func testAgentUserDataInput() agentUserDataInput { } func TestBuildAgentUserData(t *testing.T) { + t.Parallel() out := buildAgentUserData(testAgentUserDataInput()) require.True(t, strings.HasPrefix(out, "#cloud-config\n"), "cloud-init only parses a document with the header") @@ -51,6 +52,7 @@ func TestBuildAgentUserData(t *testing.T) { // the agent's gateway credentials come from IMDS — so nothing secret is written // to a data source any process on the VM can read. func TestBuildAgentUserDataCarriesNoCredentials(t *testing.T) { + t.Parallel() in := testAgentUserDataInput() out := buildAgentUserData(in) @@ -63,6 +65,7 @@ func TestBuildAgentUserDataCarriesNoCredentials(t *testing.T) { // fails the pin in a way that looks like a certificate problem rather than a // missing configuration. func TestBuildAgentUserDataOmitsAnAbsentCA(t *testing.T) { + t.Parallel() in := testAgentUserDataInput() in.GatewayCACert = "" diff --git a/spinifex/handlers/rds/vpc_test.go b/spinifex/handlers/rds/vpc_test.go index ea917f091..98e240a99 100644 --- a/spinifex/handlers/rds/vpc_test.go +++ b/spinifex/handlers/rds/vpc_test.go @@ -12,12 +12,14 @@ import ( ) func TestSystemVPCNameIsPerRegion(t *testing.T) { + t.Parallel() assert.Equal(t, "rds-system-ap-southeast-2", SystemVPCName("ap-southeast-2")) assert.NotEqual(t, SystemVPCName("us-east-1"), SystemVPCName("ap-southeast-2"), "one system VPC per region, so two regions must not resolve to the same VPC") } func TestSystemVPCSpecDefaults(t *testing.T) { + t.Parallel() spec := SystemVPCSpec(nil, "ap-southeast-2") assert.Equal(t, handlers_systemvpc.Spec{ @@ -39,6 +41,7 @@ func TestSystemVPCSpecDefaults(t *testing.T) { } func TestSystemVPCSpecHonoursOperatorOverrides(t *testing.T) { + t.Parallel() spec := SystemVPCSpec(&config.RDSConfig{ SystemVPCSupernet: "172.16.0.0/14", SystemVPCPrivateSubnets: 3, @@ -50,6 +53,7 @@ func TestSystemVPCSpecHonoursOperatorOverrides(t *testing.T) { } func TestSystemVPCTagsAreRDSOwn(t *testing.T) { + t.Parallel() spec := SystemVPCSpec(nil, "ap-southeast-2") // The tag keys are the whole isolation mechanism: EKS's teardown and its @@ -62,6 +66,7 @@ func TestSystemVPCTagsAreRDSOwn(t *testing.T) { } func TestSystemVPCSupernetIsUsableAndDisjointFromEKS(t *testing.T) { + t.Parallel() supernet, err := netip.ParsePrefix(config.RDSDefaultSystemVPCSupernet) require.NoError(t, err) assert.Equal(t, supernet.Masked(), supernet, "a supernet with host bits set is rejected by the builder")