From a0b4d840e2dde4e99f0e28309da230a744f25a7b Mon Sep 17 00:00:00 2001 From: Julian Sommer <115516918+juliansommer@users.noreply.github.com> Date: Wed, 14 Oct 2026 13:54:15 +1000 Subject: [PATCH] test: use 1024-bit keys and parallelise ACM handler tests --- spinifex/handlers/acm/certfields_test.go | 10 ++++-- spinifex/handlers/acm/getcertificate_test.go | 10 ++++++ spinifex/handlers/acm/keyalgorithm_test.go | 27 ++++++++------ spinifex/handlers/acm/privateca_test.go | 27 ++++++++++---- spinifex/handlers/acm/renewal_test.go | 17 +++++++++ spinifex/handlers/acm/service_impl_test.go | 35 +++++++++++++++++++ spinifex/handlers/acm/store_test.go | 15 ++++++++ spinifex/handlers/acm/tags_test.go | 10 ++++++ spinifex/handlers/acm/validation_mode_test.go | 2 ++ 9 files changed, 134 insertions(+), 19 deletions(-) diff --git a/spinifex/handlers/acm/certfields_test.go b/spinifex/handlers/acm/certfields_test.go index 13a283d0b..c4bf94cbc 100644 --- a/spinifex/handlers/acm/certfields_test.go +++ b/spinifex/handlers/acm/certfields_test.go @@ -31,10 +31,11 @@ const ( // genAuditShapedCert builds a certificate shaped like the one the audit // imported into real ACM (openssl req -x509 -newkey rsa:2048 -subj /CN=...): -// RSA-2048, SHA-256 with RSA, CN only, no SAN, key-usage or EKU extension. +// SHA-256 with RSA, CN only, no SAN, key-usage or EKU extension. The key is +// 1024 bits rather than the audit's 2048 to keep generation fast. func genAuditShapedCert(t *testing.T) (certPEM, keyPEM []byte) { t.Helper() - key, err := rsa.GenerateKey(rand.Reader, 2048) + key, err := rsa.GenerateKey(rand.Reader, 1024) require.NoError(t, err) serial, ok := new(big.Int).SetString(awsAuditSerialHex, 16) require.True(t, ok) @@ -82,6 +83,7 @@ func wireJSON(t *testing.T, v any) map[string]any { // Expected values are copied from real ACM's DescribeCertificate response for // the audit's certificate. Export is absent: aws-sdk-go v1 cannot carry it. func TestDescribeCertificate_ImportedMatchesAWSAuditResponse(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, keyPEM := genAuditShapedCert(t) d := describeCert(t, svc, importCert(t, svc, certPEM, keyPEM)) @@ -105,6 +107,7 @@ func TestDescribeCertificate_ImportedMatchesAWSAuditResponse(t *testing.T) { } func TestListCertificates_ImportedSummaryMatchesAWSAuditResponse(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, keyPEM := genAuditShapedCert(t) importCert(t, svc, certPEM, keyPEM) @@ -125,6 +128,7 @@ func TestListCertificates_ImportedSummaryMatchesAWSAuditResponse(t *testing.T) { } func TestImportCertificate_ReimportKeepsCreatedAtAndMovesImportedAt(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genAuditShapedCert(t) certArn := importCert(t, svc, c1, k1) @@ -144,6 +148,7 @@ func TestImportCertificate_ReimportKeepsCreatedAtAndMovesImportedAt(t *testing.T // A record stored before these fields existed holds the old Serial and Issuer // spellings, no SANs and no CreatedAt; it must still describe as AWS does. func TestDescribeCertificate_LegacyImportedRecordDerivedFromPEM(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, keyPEM := genAuditShapedCert(t) certArn := svc.mintCertificateArn(testAccountID) @@ -175,6 +180,7 @@ func TestDescribeCertificate_LegacyImportedRecordDerivedFromPEM(t *testing.T) { // a self-signed P-384 import carrying an O, SANs that omit the CN, and both // usage extensions. func TestDescribeCertificate_ImportedWithExtensionsMatchesAWS(t *testing.T) { + t.Parallel() svc := setupACMService(t) key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) require.NoError(t, err) diff --git a/spinifex/handlers/acm/getcertificate_test.go b/spinifex/handlers/acm/getcertificate_test.go index 23253eb38..4cd687b02 100644 --- a/spinifex/handlers/acm/getcertificate_test.go +++ b/spinifex/handlers/acm/getcertificate_test.go @@ -13,6 +13,7 @@ import ( ) func TestGetCertificate_ReturnsBodyAndChain(t *testing.T) { + t.Parallel() svc := setupACMService(t) leafPEM, keyPEM := genCert(t, "get.example.com") chainPEM, _ := genCert(t, "issuer.example.com") @@ -36,6 +37,7 @@ func TestGetCertificate_ReturnsBodyAndChain(t *testing.T) { // itself as the chain. The substitution is response-only: the stored record // keeps no chain, which is what ELBv2 builds its leaf+chain bundle from. func TestGetCertificate_AbsentChainReturnsCertificate(t *testing.T) { + t.Parallel() svc := setupACMService(t) leafPEM, keyPEM := genCert(t, "nochain.example.com") @@ -62,6 +64,7 @@ func TestGetCertificate_AbsentChainReturnsCertificate(t *testing.T) { // A re-import without a chain drops the chain the first import carried, so // GetCertificate falls back to the new certificate, not the stale chain. func TestGetCertificate_ReimportWithoutChainReturnsNewCertificate(t *testing.T) { + t.Parallel() svc := setupACMService(t) leafPEM, keyPEM := genCert(t, "reimport.example.com") chainPEM, _ := genCert(t, "issuer.example.com") @@ -90,6 +93,7 @@ func TestGetCertificate_ReimportWithoutChainReturnsNewCertificate(t *testing.T) } func TestGetCertificate_UnknownArnNotFound(t *testing.T) { + t.Parallel() svc := setupACMService(t) _, err := svc.GetCertificate(context.Background(), &acm.GetCertificateInput{ @@ -103,6 +107,7 @@ func TestGetCertificate_UnknownArnNotFound(t *testing.T) { // body is the material itself, so a cross-account read must not confirm the // certificate even exists. func TestGetCertificate_CrossAccountNotFound(t *testing.T) { + t.Parallel() svc := setupACMService(t) leafPEM, keyPEM := genCert(t, "other.example.com") @@ -123,6 +128,7 @@ func TestGetCertificate_CrossAccountNotFound(t *testing.T) { // and distinct from not-found, so a client polling for issuance can tell them // apart. func TestGetCertificate_PendingIssuanceIsRequestInProgress(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/pending-1" require.NoError(t, svc.store.PutCert(t.Context(), &CertRecord{ @@ -140,6 +146,7 @@ func TestGetCertificate_PendingIssuanceIsRequestInProgress(t *testing.T) { } func TestGetCertificate_EmptyArnRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) _, err := svc.GetCertificate(context.Background(), &acm.GetCertificateInput{}, testAccountID) @@ -170,6 +177,7 @@ func putUndecryptableCert(t *testing.T, store *Store, arn, domain string) { // than instrumentation: an undecryptable key is fatal to a decrypting read, so a // list that still returns the certificate cannot have decrypted it. func TestListPath_NeverTouchesKeyMaterial(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/nokeyread-1" putUndecryptableCert(t, svc.store, arn, "nokeyread.example.com") @@ -202,6 +210,7 @@ func TestListPath_NeverTouchesKeyMaterial(t *testing.T) { // caller cannot mistake ciphertext for usable key material in a field that is // plaintext PEM everywhere else. func TestMetadataReads_ClearPrivateKey(t *testing.T) { + t.Parallel() store := setupACMStore(t) arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/cleared-1" _, keyPEM := genCert(t, "cleared.example.com") @@ -230,6 +239,7 @@ func TestMetadataReads_ClearPrivateKey(t *testing.T) { } func TestGetCertMetadata_AbsentReturnsNil(t *testing.T) { + t.Parallel() store := setupACMStore(t) rec, err := store.GetCertMetadata(t.Context(), "arn:aws:acm:ap-southeast-2:000000000001:certificate/missing") diff --git a/spinifex/handlers/acm/keyalgorithm_test.go b/spinifex/handlers/acm/keyalgorithm_test.go index d38b04f42..30372cd19 100644 --- a/spinifex/handlers/acm/keyalgorithm_test.go +++ b/spinifex/handlers/acm/keyalgorithm_test.go @@ -20,9 +20,9 @@ import ( // genRSACert returns a self-signed RSA leaf with only a CN, the shape of the // certificate the AWS conformance audit imported. -func genRSACert(t *testing.T, cn string, bits int) (certPEM, keyPEM []byte) { +func genRSACert(t *testing.T, cn string) (certPEM, keyPEM []byte) { t.Helper() - key, err := rsa.GenerateKey(rand.Reader, bits) + key, err := rsa.GenerateKey(rand.Reader, 1024) require.NoError(t, err) tmpl := &x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixNano()), @@ -57,11 +57,12 @@ func describeAndListKeyAlgorithm(t *testing.T, svc *ACMServiceImpl, certArn stri return "", "" } -// AWS returns RSA-2048 with a hyphen from both Describe and List, although the -// API enum (and the ListCertificates keyTypes filter) spells it RSA_2048. +// AWS returns RSA-1024 with a hyphen from both Describe and List, although the +// API enum (and the ListCertificates keyTypes filter) spells it RSA_1024. func TestImportCertificate_RSAKeyAlgorithmIsHyphenated(t *testing.T) { + t.Parallel() svc := setupACMService(t) - certPEM, keyPEM := genRSACert(t, "rsa.example.com", 2048) + certPEM, keyPEM := genRSACert(t, "rsa.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{ Certificate: certPEM, @@ -70,18 +71,19 @@ func TestImportCertificate_RSAKeyAlgorithmIsHyphenated(t *testing.T) { require.NoError(t, err) described, listed := describeAndListKeyAlgorithm(t, svc, aws.StringValue(out.CertificateArn)) - assert.Equal(t, "RSA-2048", described) - assert.Equal(t, "RSA-2048", listed) + assert.Equal(t, "RSA-1024", described) + assert.Equal(t, "RSA-1024", listed) // The read path normalises, so check the stored spelling separately. raw, _, err := svc.store.store.Get(t.Context(), certKey(aws.StringValue(out.CertificateArn))) require.NoError(t, err) - assert.Equal(t, "RSA-2048", raw.KeyAlgorithm) + assert.Equal(t, "RSA-1024", raw.KeyAlgorithm) } // Records written before the spelling fix hold RSA_ or EC_; // they must read back in the AWS spelling without a migration. func TestKeyAlgorithm_StoredUnderscoreSpellingReadsBackHyphenated(t *testing.T) { + t.Parallel() cases := []struct { stored, want string }{ @@ -93,6 +95,7 @@ func TestKeyAlgorithm_StoredUnderscoreSpellingReadsBackHyphenated(t *testing.T) svc := setupACMService(t) for i, tc := range cases { t.Run(tc.stored, func(t *testing.T) { + t.Parallel() certArn := "arn:aws:acm:ap-southeast-2:" + testAccountID + ":certificate/legacy-" + string(rune('a'+i)) require.NoError(t, svc.store.PutCert(t.Context(), &CertRecord{ CertificateArn: certArn, @@ -111,8 +114,9 @@ func TestKeyAlgorithm_StoredUnderscoreSpellingReadsBackHyphenated(t *testing.T) // AWS refuses a re-import whose key type differs from the current one, naming // both in the API enum spelling. func TestImportCertificate_ReimportWithDifferentKeyTypeRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) - c1, k1 := genRSACert(t, "k.example.com", 2048) + c1, k1 := genRSACert(t, "k.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) require.NoError(t, err) @@ -123,12 +127,13 @@ func TestImportCertificate_ReimportWithDifferentKeyTypeRejected(t *testing.T) { code, msg, ok := awserrors.ResolveErrorDetail(err) require.True(t, ok) assert.Equal(t, awserrors.ErrorValidationException, code) - assert.Equal(t, "New certificate has a key of EC_prime256v1 which is different from RSA_2048 in the current certificate.", msg) + assert.Equal(t, "New certificate has a key of EC_prime256v1 which is different from RSA_1024 in the current certificate.", msg) } func TestImportCertificate_SerialZeroRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) - key, err := rsa.GenerateKey(rand.Reader, 2048) + key, err := rsa.GenerateKey(rand.Reader, 1024) require.NoError(t, err) tmpl := &x509.Certificate{ SerialNumber: big.NewInt(0), diff --git a/spinifex/handlers/acm/privateca_test.go b/spinifex/handlers/acm/privateca_test.go index 38a29e4ad..dd6331baa 100644 --- a/spinifex/handlers/acm/privateca_test.go +++ b/spinifex/handlers/acm/privateca_test.go @@ -17,13 +17,11 @@ import ( "github.com/stretchr/testify/require" ) -// TestMain lowers the RSA key sizes for the whole privateca test file. 2048 -// (CA) and 1024 (leaf) bits are still valid RSA keys and generate several -// times faster than the production defaults, and the tests here care about -// certificate structure, not cryptographic strength — mirrors -// admin.certKeyBits's test seam. +// TestMain lowers the RSA key sizes for the whole package to 1024 bits, the +// smallest crypto/rsa accepts; the tests care about certificate structure, +// not cryptographic strength — mirrors admin.certKeyBits's test seam. func TestMain(m *testing.M) { - caKeyBits = 2048 + caKeyBits = 1024 leafKeyBits = 1024 os.Exit(m.Run()) } @@ -35,6 +33,7 @@ func tenantCAPaths(t *testing.T) (certPath, keyPath string) { } func TestLoadOrCreateTenantCA_CreatesConstrainedRoot(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) @@ -79,6 +78,7 @@ func TestLoadOrCreateTenantCA_CreatesConstrainedRoot(t *testing.T) { } func TestLoadOrCreateTenantCA_RefusesEmptyPermittedDomains(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) _, err := LoadOrCreateTenantCA(certPath, keyPath, nil) @@ -88,6 +88,7 @@ func TestLoadOrCreateTenantCA_RefusesEmptyPermittedDomains(t *testing.T) { } func TestLoadOrCreateTenantCA_Idempotent(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) first, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) @@ -111,6 +112,7 @@ func TestLoadOrCreateTenantCA_Idempotent(t *testing.T) { } func TestLoadOrCreateTenantCA_DriftIsRejectedAndLeavesRootUntouched(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) _, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) @@ -135,6 +137,7 @@ func TestLoadOrCreateTenantCA_DriftIsRejectedAndLeavesRootUntouched(t *testing.T } func TestLoadOrCreateTenantCA_SubdomainRequestCoveredByExistingApex(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) _, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"example.com"}) @@ -147,6 +150,7 @@ func TestLoadOrCreateTenantCA_SubdomainRequestCoveredByExistingApex(t *testing.T } func TestLoadOrCreateTenantCA_IncompletePairIsRejected(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) _, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) @@ -161,6 +165,7 @@ func TestLoadOrCreateTenantCA_IncompletePairIsRejected(t *testing.T) { } func TestIssueLeaf_ChainsToRootAndCoversWildcard(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) require.NoError(t, err) @@ -193,6 +198,7 @@ func TestIssueLeaf_ChainsToRootAndCoversWildcard(t *testing.T) { } func TestIssueLeaf_RejectsDomainOutsidePermittedDomains(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) require.NoError(t, err) @@ -211,6 +217,7 @@ func TestIssueLeaf_RejectsDomainOutsidePermittedDomains(t *testing.T) { // calls out. If this leaf were to verify, the constraints baked into the // root would be decorative, not enforced. func TestLeafOutsidePermittedDomains_FailsVerify(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) require.NoError(t, err) @@ -240,6 +247,7 @@ func TestLeafOutsidePermittedDomains_FailsVerify(t *testing.T) { // the CA key so the test does not depend on IssueLeaf ever choosing to // offer an IP SAN option. func TestLeafWithIPSAN_FailsVerify(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) require.NoError(t, err) @@ -270,6 +278,7 @@ func TestLeafWithIPSAN_FailsVerify(t *testing.T) { // one), uses that intermediate to sign a leaf, and asserts the resulting // chain fails verification even though every individual signature is valid. func TestTenantRootCannotSignSubordinateCA(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) require.NoError(t, err) @@ -320,6 +329,7 @@ func TestTenantRootCannotSignSubordinateCA(t *testing.T) { } func TestAuthorized(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) require.NoError(t, err) @@ -342,12 +352,14 @@ func TestAuthorized(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { + t.Parallel() assert.Equal(t, tc.want, ca.Authorized(tc.domain)) }) } } func TestLoadTenantCA_MissingReturnsActionableError(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) _, err := LoadTenantCA(certPath, keyPath) @@ -356,6 +368,7 @@ func TestLoadTenantCA_MissingReturnsActionableError(t *testing.T) { } func TestLoadTenantCA_IncompletePairReturnsActionableError(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) _, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) @@ -369,6 +382,7 @@ func TestLoadTenantCA_IncompletePairReturnsActionableError(t *testing.T) { } func TestLoadTenantCA_RoundTripsCreatedCA(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) created, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com", "lab.example.org"}) @@ -391,6 +405,7 @@ func TestLoadTenantCA_RoundTripsCreatedCA(t *testing.T) { } func TestPermittedDomains_ReturnsDefensiveCopy(t *testing.T) { + t.Parallel() certPath, keyPath := tenantCAPaths(t) ca, err := LoadOrCreateTenantCA(certPath, keyPath, []string{"home.example.com"}) require.NoError(t, err) diff --git a/spinifex/handlers/acm/renewal_test.go b/spinifex/handlers/acm/renewal_test.go index 3f56e7f75..c93b8be49 100644 --- a/spinifex/handlers/acm/renewal_test.go +++ b/spinifex/handlers/acm/renewal_test.go @@ -21,6 +21,7 @@ import ( // exactly on renewalDueAt rather than depending on it landing on a whole // unit of time. func TestRenewable_ProportionalWindow(t *testing.T) { + t.Parallel() const arn = "arn:aws:acm:ap-southeast-2:1:certificate/window-test" jitter := renewalJitter(arn) now := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) @@ -38,6 +39,7 @@ func TestRenewable_ProportionalWindow(t *testing.T) { } t.Run("not yet due", func(t *testing.T) { + t.Parallel() lifetime := 90 * 24 * time.Hour window := renewalWindow(lifetime) rec := newRec(lifetime, window+jitter-time.Minute) @@ -45,6 +47,7 @@ func TestRenewable_ProportionalWindow(t *testing.T) { }) t.Run("exactly at threshold", func(t *testing.T) { + t.Parallel() lifetime := 90 * 24 * time.Hour window := renewalWindow(lifetime) rec := newRec(lifetime, window+jitter) @@ -52,6 +55,7 @@ func TestRenewable_ProportionalWindow(t *testing.T) { }) t.Run("overdue", func(t *testing.T) { + t.Parallel() lifetime := 90 * 24 * time.Hour window := renewalWindow(lifetime) rec := newRec(lifetime, window+jitter+24*time.Hour) @@ -59,6 +63,7 @@ func TestRenewable_ProportionalWindow(t *testing.T) { }) t.Run("very short lifetime hits the floor", func(t *testing.T) { + t.Parallel() lifetime := 10 * time.Minute window := renewalWindow(lifetime) require.Equal(t, minRenewalWindow, window, @@ -78,6 +83,7 @@ func TestRenewable_ProportionalWindow(t *testing.T) { // only a PRIVATE, ISSUED, ELIGIBLE certificate is ever renewable, regardless // of how overdue it is. func TestRenewable_GatesOnTypeStatusEligibility(t *testing.T) { + t.Parallel() now := time.Now() overdue := func() *CertRecord { return &CertRecord{ @@ -102,6 +108,7 @@ func TestRenewable_GatesOnTypeStatusEligibility(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() rec := overdue() tt.mutate(rec) assert.False(t, renewable(rec, now)) @@ -112,6 +119,7 @@ func TestRenewable_GatesOnTypeStatusEligibility(t *testing.T) { // --- Deterministic per-ARN jitter ------------------------------------------- func TestRenewalJitter_DeterministicPerARN(t *testing.T) { + t.Parallel() const arn = "arn:aws:acm:ap-southeast-2:1:certificate/jitter-test" first := renewalJitter(arn) second := renewalJitter(arn) @@ -121,6 +129,7 @@ func TestRenewalJitter_DeterministicPerARN(t *testing.T) { } func TestRenewalJitter_SpreadsAcrossDistinctARNs(t *testing.T) { + t.Parallel() offsets := make(map[time.Duration]bool) for i := range 8 { arn := fmt.Sprintf("arn:aws:acm:ap-southeast-2:1:certificate/cert-%d", i) @@ -145,6 +154,7 @@ func backdateToOverdue(t *testing.T, svc *ACMServiceImpl, certArn string) { // TestWorker_RenewOne_PreservesIdentityChangesKeyAndSerial is the core // renewal contract: same ARN, same domain and SANs, fresh key and serial. func TestWorker_RenewOne_PreservesIdentityChangesKeyAndSerial(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{ "renew.example.com": true, @@ -185,6 +195,7 @@ func TestWorker_RenewOne_PreservesIdentityChangesKeyAndSerial(t *testing.T) { // mandatory push described in the ACM design doc's ELBv2 propagation // section. func TestWorker_CertMaterialUpdated_InvokedExactlyOnceOnSuccess(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"fanout.example.com": true}} var calls int @@ -215,6 +226,7 @@ func TestWorker_CertMaterialUpdated_InvokedExactlyOnceOnSuccess(t *testing.T) { // scan over a mix of certificate shapes and asserts only the eligible, // overdue PRIVATE_CA certificate is renewed. func TestWorker_ScanOnce_SkipsIneligibleAndImported_RenewsEligible(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{ "eligible.example.com": true, @@ -267,6 +279,7 @@ func TestWorker_ScanOnce_SkipsIneligibleAndImported_RenewsEligible(t *testing.T) // RenewalSummary (the sole DescribeCertificate diagnostic surface) and must // not re-attempt on the very next scan. func TestWorker_ScanOnce_DomainOutsideConstraints_RecordsFailureAndBacksOff(t *testing.T) { + t.Parallel() svc := setupACMService(t) ca := &fakeCertAuthority{permitted: map[string]bool{"narrowed.example.com": true}} svc.TenantCA = ca @@ -305,6 +318,7 @@ func TestWorker_ScanOnce_DomainOutsideConstraints_RecordsFailureAndBacksOff(t *t // lease actually excludes a second worker: node-a's live lease must stop // node-b's worker from reissuing the same certificate underneath it. func TestWorker_LeaseContention_OnlyOneNodeRenews(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"contended.example.com": true}} @@ -338,6 +352,7 @@ func TestWorker_LeaseContention_OnlyOneNodeRenews(t *testing.T) { // path: a freshly issued certificate, nowhere near its renewal window, must // still reissue on demand. func TestForceRenewCertificate_BypassesRenewalWindow(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"force.example.com": true}} @@ -365,6 +380,7 @@ func TestForceRenewCertificate_BypassesRenewalWindow(t *testing.T) { // an imported certificate rather than silently no-op'ing or crashing — this // worker only ever drives PRIVATE_CA reissuance. func TestForceRenewCertificate_RejectsNonPrivateType(t *testing.T) { + t.Parallel() svc := setupACMService(t) c, k := genCert(t, "force-import.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{Certificate: c, PrivateKey: k}, testAccountID) @@ -379,6 +395,7 @@ func TestForceRenewCertificate_RejectsNonPrivateType(t *testing.T) { // ownership check: an ARN that does not exist, or belongs to another // account, must be rejected rather than disclosing anything about it. func TestForceRenewCertificate_UnknownArnRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) worker := NewWorker(svc, "node-1") _, err := worker.ForceRenewCertificate(context.Background(), &ForceRenewCertificateInput{ diff --git a/spinifex/handlers/acm/service_impl_test.go b/spinifex/handlers/acm/service_impl_test.go index 035292116..df7317e68 100644 --- a/spinifex/handlers/acm/service_impl_test.go +++ b/spinifex/handlers/acm/service_impl_test.go @@ -67,6 +67,7 @@ func genCert(t *testing.T, cn string, dnsNames ...string) (certPEM, keyPEM []byt } func TestImportCertificate_MintsArnAndParsesLeaf(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, keyPEM := genCert(t, "example.com", "example.com", "www.example.com") @@ -93,6 +94,7 @@ func TestImportCertificate_MintsArnAndParsesLeaf(t *testing.T) { } func TestImportCertificate_MismatchedKeyRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, _ := genCert(t, "a.example.com") _, otherKey := genCert(t, "b.example.com") @@ -106,6 +108,7 @@ func TestImportCertificate_MismatchedKeyRejected(t *testing.T) { } func TestImportCertificate_GarbagePEMRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) _, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{ Certificate: []byte("not a pem"), @@ -116,6 +119,7 @@ func TestImportCertificate_GarbagePEMRejected(t *testing.T) { } func TestListCertificates_ScopedToAccount(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "one.example.com") c2, k2 := genCert(t, "two.example.com") @@ -132,6 +136,7 @@ func TestListCertificates_ScopedToAccount(t *testing.T) { } func TestDescribeCertificate_CrossAccountHidden(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "secret.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) @@ -145,6 +150,7 @@ func TestDescribeCertificate_CrossAccountHidden(t *testing.T) { } func TestDeleteCertificate(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "del.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) @@ -165,6 +171,7 @@ func TestDeleteCertificate(t *testing.T) { } func TestImportCertificate_ReimportPreservesInUseBy(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "inuse.example.com") @@ -190,6 +197,7 @@ func TestImportCertificate_ReimportPreservesInUseBy(t *testing.T) { } func TestImportCertificate_ReimportInvokesFanoutHook(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "hook.example.com") @@ -215,6 +223,7 @@ func TestImportCertificate_ReimportInvokesFanoutHook(t *testing.T) { } func TestImportCertificate_ReimportZeroInUseBy_HookNotCalled(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "unused.example.com") @@ -240,6 +249,7 @@ func TestImportCertificate_ReimportZeroInUseBy_HookNotCalled(t *testing.T) { } func TestImportCertificate_FreshImportDoesNotInvokeHook(t *testing.T) { + t.Parallel() svc := setupACMService(t) called := false svc.CertMaterialUpdated = func(ctx context.Context, arn string) error { @@ -254,6 +264,7 @@ func TestImportCertificate_FreshImportDoesNotInvokeHook(t *testing.T) { } func TestImportCertificate_ReimportUnknownArnRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "re.example.com") _, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{ @@ -270,6 +281,7 @@ func TestImportCertificate_ReimportUnknownArnRejected(t *testing.T) { // contain the plaintext PEM key, and reading it back through the Store must // still recover the original plaintext. func TestImportCertificate_PrivateKeyEncryptedAtRest(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, keyPEM := genCert(t, "atrest.example.com") @@ -297,6 +309,7 @@ func TestImportCertificate_PrivateKeyEncryptedAtRest(t *testing.T) { // record is stored. Precedent: TestSensitiveDataNotLogged_MasterKey in // handlers/iam/service_impl_test.go. func TestDescribeCertificate_DoesNotLeakPrivateKey(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, keyPEM := genCert(t, "noleak.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{ @@ -370,6 +383,7 @@ func genLeafPEM(cn string, dnsNames []string) (certPEM, keyPEM string, err error } func TestRequestCertificate_ProviderAPI_PendingValidationNoResourceRecord(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.dnsProviderConfigured = true @@ -395,6 +409,7 @@ func TestRequestCertificate_ProviderAPI_PendingValidationNoResourceRecord(t *tes } func TestRequestCertificate_ManualTXT_EmitsTXTResourceRecordAndIneligible(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.NorthstarHostsZone = func(domain string) bool { return true } @@ -423,6 +438,7 @@ func TestRequestCertificate_ManualTXT_EmitsTXTResourceRecordAndIneligible(t *tes // PRIVATE_CA (automatic, offline, renewable), not MANUAL_TXT (which can never // complete against an internal zone a public CA cannot resolve). func TestRequestCertificate_PrivateCA_WinsOverNorthstarHostedZone(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"home.example.com": true}} svc.NorthstarHostsZone = func(domain string) bool { return domain == "home.example.com" } @@ -442,6 +458,7 @@ func TestRequestCertificate_PrivateCA_WinsOverNorthstarHostedZone(t *testing.T) } func TestRequestCertificate_PrivateCA_IssuesSynchronously(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"private.example.com": true}} @@ -470,6 +487,7 @@ func TestRequestCertificate_PrivateCA_IssuesSynchronously(t *testing.T) { // the daemon-wiring slice adds no coverage for the seam between // RequestCertificate and the real implementation without a test like this one. func TestRequestCertificate_PrivateCA_RealTenantCA_IssuesAndChains(t *testing.T) { + t.Parallel() svc := setupACMService(t) dir := t.TempDir() ca, err := LoadOrCreateTenantCA( @@ -509,6 +527,7 @@ func TestRequestCertificate_PrivateCA_RealTenantCA_IssuesAndChains(t *testing.T) } func TestRequestCertificate_PrivateCA_DomainOutsidePermittedRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"allowed.example.com": true}} @@ -520,6 +539,7 @@ func TestRequestCertificate_PrivateCA_DomainOutsidePermittedRejected(t *testing. } func TestRequestCertificate_PrivateCA_SANOutsidePermittedRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"allowed.example.com": true}} @@ -532,6 +552,7 @@ func TestRequestCertificate_PrivateCA_SANOutsidePermittedRejected(t *testing.T) } func TestRequestCertificate_PrivateCA_NoTenantCAWired_FailsLoudly(t *testing.T) { + t.Parallel() svc := setupACMService(t) // TenantCA deliberately left nil: PRIVATE_CA is derived (no dns provider, no // northstar zone) but nothing can drive it. @@ -542,6 +563,7 @@ func TestRequestCertificate_PrivateCA_NoTenantCAWired_FailsLoudly(t *testing.T) } func TestRequestCertificate_PrivateCA_IssueLeafErrorNotStored(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{ permitted: map[string]bool{"broken.example.com": true}, @@ -559,6 +581,7 @@ func TestRequestCertificate_PrivateCA_IssueLeafErrorNotStored(t *testing.T) { } func TestRequestCertificate_NoDomainName_Rejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) _, err := svc.RequestCertificate(context.Background(), &acm.RequestCertificateInput{}, testAccountID) require.Error(t, err) @@ -566,6 +589,7 @@ func TestRequestCertificate_NoDomainName_Rejected(t *testing.T) { } func TestRequestCertificate_PrivateCA_DescribeDoesNotLeakPrivateKey(t *testing.T) { + t.Parallel() svc := setupACMService(t) svc.TenantCA = &fakeCertAuthority{permitted: map[string]bool{"noleak-priv.example.com": true}} @@ -583,6 +607,7 @@ func TestRequestCertificate_PrivateCA_DescribeDoesNotLeakPrivateKey(t *testing.T } func TestImportCertificate_RenewalEligibilityIneligible(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "imported-ineligible.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) @@ -595,6 +620,7 @@ func TestImportCertificate_RenewalEligibilityIneligible(t *testing.T) { } func TestDeleteCertificate_RefusesWhenInUse(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "inuse-delete.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) @@ -612,6 +638,7 @@ func TestDeleteCertificate_RefusesWhenInUse(t *testing.T) { } func TestDeleteCertificate_SucceedsWhenNotInUse(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "notinuse-delete.example.com") out, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{Certificate: c1, PrivateKey: k1}, testAccountID) @@ -625,6 +652,8 @@ func TestDeleteCertificate_SucceedsWhenNotInUse(t *testing.T) { assert.Contains(t, err.Error(), awserrors.ErrorResourceNotFound) } +// Not parallel: it swaps the process-wide slog default, which parallel tests +// would write to concurrently. func TestSensitiveDataNotLogged_ACMPrivateKey(t *testing.T) { var buf strings.Builder logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})) @@ -654,6 +683,7 @@ func TestSensitiveDataNotLogged_ACMPrivateKey(t *testing.T) { // on one either: every spelling below names a real certificate under certKey's // old last-slash rule while resolving account-wide at the gate. func TestCertificateArnSpellingsTheGateCannotRead(t *testing.T) { + t.Parallel() svc := setupACMService(t) certPEM, keyPEM := genCert(t, "fenced.example.com") @@ -681,6 +711,7 @@ func TestCertificateArnSpellingsTheGateCannotRead(t *testing.T) { for _, tt := range spellings { t.Run(tt.certArn, func(t *testing.T) { + t.Parallel() _, err := svc.DescribeCertificate(context.Background(), &acm.DescribeCertificateInput{CertificateArn: aws.String(tt.certArn)}, testAccountID) require.Error(t, err) @@ -710,6 +741,7 @@ func TestCertificateArnSpellingsTheGateCannotRead(t *testing.T) { // The handler mints the ARN the gate builds, or every resource-scoped statement // names an object that does not exist. func TestMintedArnRoundTripsThroughTheGatesFormatter(t *testing.T) { + t.Parallel() svc := setupACMService(t) minted := svc.mintCertificateArn(testAccountID) @@ -722,6 +754,7 @@ func TestMintedArnRoundTripsThroughTheGatesFormatter(t *testing.T) { // empty: the SDK marshaller drops a nil slice, so a caller that ranges over the // result without a nil check breaks on every account's first state. func TestListCertificates_EmptyAccountReturnsEmptyList(t *testing.T) { + t.Parallel() svc := setupACMService(t) list, err := svc.ListCertificates(context.Background(), &acm.ListCertificatesInput{}, testAccountID) @@ -737,6 +770,7 @@ func TestListCertificates_EmptyAccountReturnsEmptyList(t *testing.T) { // The populated case must keep working: an empty slice that never fills is just // a different way of losing the list. func TestListCertificates_PopulatedAccountReturnsSummaries(t *testing.T) { + t.Parallel() svc := setupACMService(t) c1, k1 := genCert(t, "filled-one.example.com") c2, k2 := genCert(t, "filled-two.example.com") @@ -760,6 +794,7 @@ func TestListCertificates_PopulatedAccountReturnsSummaries(t *testing.T) { // fault. InternalErrorException is a retryable 500, so Terraform sat on // "Still creating..." instead of failing; this asserts the 4xx and the reason. func TestRequestCertificate_NoTenantCAIsAClientErrorNamingTheCause(t *testing.T) { + t.Parallel() svc := setupACMService(t) require.Nil(t, svc.TenantCA, "this test only means anything with no tenant CA wired") diff --git a/spinifex/handlers/acm/store_test.go b/spinifex/handlers/acm/store_test.go index cb427159c..40822b29d 100644 --- a/spinifex/handlers/acm/store_test.go +++ b/spinifex/handlers/acm/store_test.go @@ -39,6 +39,7 @@ func rawKV(t *testing.T, s *Store) jetstream.KeyValue { } func TestAddInUseBy_AddsAndDedupes(t *testing.T) { + t.Parallel() store := setupACMStore(t) rec := &CertRecord{CertificateArn: "arn:aws:acm:ap-southeast-2:000000000001:certificate/inuse-1", AccountID: testAccountID} require.NoError(t, store.PutCert(t.Context(), rec)) @@ -62,6 +63,7 @@ func TestAddInUseBy_AddsAndDedupes(t *testing.T) { } func TestAddInUseBy_NoopOnMissingCert(t *testing.T) { + t.Parallel() store := setupACMStore(t) // No cert exists under this ARN — must not error and must not create one. require.NoError(t, store.AddInUseBy(t.Context(), "arn:aws:acm:ap-southeast-2:000000000001:certificate/missing", "arn:lb/one")) @@ -74,6 +76,7 @@ func TestAddInUseBy_NoopOnMissingCert(t *testing.T) { // A certificate deleted after the ownership check must surface as not found // rather than a silent success, and must not be recreated by the tag write. func TestUpdateTags_MissingCertReturnsNotFound(t *testing.T) { + t.Parallel() store := setupACMStore(t) const certArn = "arn:aws:acm:ap-southeast-2:000000000001:certificate/missing" called := false @@ -90,6 +93,7 @@ func TestUpdateTags_MissingCertReturnsNotFound(t *testing.T) { } func TestRemoveInUseBy_RemovesEntry(t *testing.T) { + t.Parallel() store := setupACMStore(t) rec := &CertRecord{ CertificateArn: "arn:aws:acm:ap-southeast-2:000000000001:certificate/inuse-2", @@ -116,6 +120,7 @@ func TestRemoveInUseBy_RemovesEntry(t *testing.T) { // a healthy, renewed certificate. Only asserting the exact final set // catches that; asserting "no errors" does not. func TestAddInUseBy_ConcurrentDistinctResourcesAllSurvive(t *testing.T) { + t.Parallel() store := setupACMStore(t) certArn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/inuse-concurrent" require.NoError(t, store.PutCert(t.Context(), &CertRecord{CertificateArn: certArn, AccountID: testAccountID})) @@ -146,6 +151,7 @@ func TestAddInUseBy_ConcurrentDistinctResourcesAllSurvive(t *testing.T) { } func TestRemoveInUseBy_NoopWhenAbsentOrMissingCert(t *testing.T) { + t.Parallel() store := setupACMStore(t) rec := &CertRecord{ CertificateArn: "arn:aws:acm:ap-southeast-2:000000000001:certificate/inuse-3", @@ -165,6 +171,7 @@ func TestRemoveInUseBy_NoopWhenAbsentOrMissingCert(t *testing.T) { } func TestStore_PutCert_EncryptsPrivateKeyAtRest(t *testing.T) { + t.Parallel() store := setupACMStore(t) const plaintext = "-----BEGIN EC PRIVATE KEY-----\nZm9v\n-----END EC PRIVATE KEY-----\n" arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/enc" @@ -186,6 +193,7 @@ func TestStore_PutCert_EncryptsPrivateKeyAtRest(t *testing.T) { } func TestStore_GetCert_LegacyPlaintextPassthroughAndReencrypt(t *testing.T) { + t.Parallel() store := setupACMStore(t) const plaintext = "-----BEGIN RSA PRIVATE KEY-----\nYmFy\n-----END RSA PRIVATE KEY-----\n" arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/legacy" @@ -215,6 +223,7 @@ func TestStore_GetCert_LegacyPlaintextPassthroughAndReencrypt(t *testing.T) { // as a PEM private key block must be a hard error, never silently trusted as // legacy plaintext. func TestStore_GetCert_RejectsGarbageAsLegacyPlaintext(t *testing.T) { + t.Parallel() store := setupACMStore(t) arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/garbage" @@ -233,6 +242,7 @@ func TestStore_GetCert_RejectsGarbageAsLegacyPlaintext(t *testing.T) { // bucket would disagree silently, with the reader getting ciphertext where // it expects PEM. func TestNewStore_RequiresMasterKey(t *testing.T) { + t.Parallel() _, nc, _ := testutil.StartTestJetStream(t) _, err := NewStore(t.Context(), nc, nil) require.Error(t, err) @@ -243,6 +253,7 @@ func TestNewStore_RequiresMasterKey(t *testing.T) { // delegation token, and the resumable ACME/lease state — survive a // PutCert/GetCert round trip unchanged, alongside the encrypted PrivateKey. func TestStore_PutGetCert_RoundTripsManagedIssuanceFields(t *testing.T) { + t.Parallel() store := setupACMStore(t) arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/managed-1" rec := &CertRecord{ @@ -284,6 +295,7 @@ func TestStore_PutGetCert_RoundTripsManagedIssuanceFields(t *testing.T) { // the renewal predicate never reads PrivateKey and the scan runs on every // tick for every certificate in the store. func TestListAllCertMetadata_DoesNotDecryptPrivateKey(t *testing.T) { + t.Parallel() store := setupACMStore(t) const plaintext = "-----BEGIN EC PRIVATE KEY-----\nZm9v\n-----END EC PRIVATE KEY-----\n" arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/metadata-only" @@ -309,6 +321,7 @@ func TestListAllCertMetadata_DoesNotDecryptPrivateKey(t *testing.T) { // sees every account's certificates, unlike the account-scoped ListCerts used // by the ListCertificates API. func TestListAllCertMetadata_CrossesAccounts(t *testing.T) { + t.Parallel() store := setupACMStore(t) require.NoError(t, store.PutCert(t.Context(), &CertRecord{ CertificateArn: "arn:aws:acm:ap-southeast-2:1:certificate/acct-a", AccountID: "000000000001", @@ -328,6 +341,7 @@ func TestListAllCertMetadata_CrossesAccounts(t *testing.T) { // and that shape has already produced one silent-plaintext-write bug in this // package, so every write path over CertRecord gets this guard. func TestAcquireLease_LeavesPrivateKeyEncrypted(t *testing.T) { + t.Parallel() store := setupACMStore(t) const plaintext = "-----BEGIN EC PRIVATE KEY-----\nZm9v\n-----END EC PRIVATE KEY-----\n" arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/lease-encrypted" @@ -353,6 +367,7 @@ func TestAcquireLease_LeavesPrivateKeyEncrypted(t *testing.T) { // TestReleaseLease_LeavesPrivateKeyEncrypted is AcquireLease's counterpart // for the release path — same raw-CAS shape, same guard. func TestReleaseLease_LeavesPrivateKeyEncrypted(t *testing.T) { + t.Parallel() store := setupACMStore(t) const plaintext = "-----BEGIN EC PRIVATE KEY-----\nZm9v\n-----END EC PRIVATE KEY-----\n" arn := "arn:aws:acm:ap-southeast-2:000000000001:certificate/lease-release-encrypted" diff --git a/spinifex/handlers/acm/tags_test.go b/spinifex/handlers/acm/tags_test.go index dba98168a..223f766f0 100644 --- a/spinifex/handlers/acm/tags_test.go +++ b/spinifex/handlers/acm/tags_test.go @@ -24,6 +24,7 @@ func importTagged(t *testing.T, svc *ACMServiceImpl, tags ...*acm.Tag) string { } func TestListTagsForCertificate_ReturnsImportTags(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := importTagged(t, svc, &acm.Tag{Key: aws.String("Name"), Value: aws.String("ingress")}) @@ -37,6 +38,7 @@ func TestListTagsForCertificate_ReturnsImportTags(t *testing.T) { } func TestListTagsForCertificate_NoTagsReturnsEmpty(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := importTagged(t, svc) @@ -48,6 +50,7 @@ func TestListTagsForCertificate_NoTagsReturnsEmpty(t *testing.T) { } func TestAddTagsToCertificate_MergesAndPersists(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := importTagged(t, svc, &acm.Tag{Key: aws.String("Name"), Value: aws.String("ingress")}) @@ -72,6 +75,7 @@ func TestAddTagsToCertificate_MergesAndPersists(t *testing.T) { } func TestRemoveTagsFromCertificate_ByKeyAndByValue(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := importTagged(t, svc, &acm.Tag{Key: aws.String("Name"), Value: aws.String("ingress")}, @@ -100,6 +104,7 @@ func TestRemoveTagsFromCertificate_ByKeyAndByValue(t *testing.T) { } func TestTagOps_CrossAccountHidden(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := importTagged(t, svc, &acm.Tag{Key: aws.String("Name"), Value: aws.String("ingress")}) @@ -112,6 +117,7 @@ func TestTagOps_CrossAccountHidden(t *testing.T) { // Tag writes must change only the stored tag field: the encrypted key // survives byte-for-byte and still decrypts, and the tag change takes effect. func TestTagOps_PreserveStoredPrivateKey(t *testing.T) { + t.Parallel() cases := map[string]struct { write func(svc *ACMServiceImpl, certArn string) error wantTags map[string]string @@ -139,6 +145,7 @@ func TestTagOps_PreserveStoredPrivateKey(t *testing.T) { } for name, tc := range cases { t.Run(name, func(t *testing.T) { + t.Parallel() svc := setupACMService(t) certArn := importTagged(t, svc, &acm.Tag{Key: aws.String("Name"), Value: aws.String("ingress")}) @@ -189,6 +196,7 @@ func listTagMap(t *testing.T, svc *ACMServiceImpl, arn string) map[string]string // AWS answers a re-import carrying Tags with ValidationException and leaves // the stored certificate, material and tags alike, untouched. func TestImportCertificate_ReimportWithTagsRejected(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := importTagged(t, svc, &acm.Tag{Key: aws.String("Name"), Value: aws.String("ingress")}) @@ -216,6 +224,7 @@ func TestImportCertificate_ReimportWithTagsRejected(t *testing.T) { // A re-import without Tags keeps the tags already on the certificate rather // than clearing them, as InUseBy is kept. func TestImportCertificate_ReimportKeepsStoredTags(t *testing.T) { + t.Parallel() svc := setupACMService(t) arn := importTagged(t, svc, &acm.Tag{Key: aws.String("Name"), Value: aws.String("ingress")}) _, err := svc.AddTagsToCertificate(context.Background(), &acm.AddTagsToCertificateInput{ @@ -242,6 +251,7 @@ func TestImportCertificate_ReimportKeepsStoredTags(t *testing.T) { // AWS resolves the ARN before refusing the tags, so an unknown ARN with tags is // a ResourceNotFoundException, not the tagging ValidationException. func TestImportCertificate_ReimportWithTagsUnknownArnIsNotFound(t *testing.T) { + t.Parallel() svc := setupACMService(t) c, k := genCert(t, "x.example.com", "x.example.com") _, err := svc.ImportCertificate(context.Background(), &acm.ImportCertificateInput{ diff --git a/spinifex/handlers/acm/validation_mode_test.go b/spinifex/handlers/acm/validation_mode_test.go index a39cbaa98..3fc7c77ab 100644 --- a/spinifex/handlers/acm/validation_mode_test.go +++ b/spinifex/handlers/acm/validation_mode_test.go @@ -21,6 +21,7 @@ func hostsZoneFake(hosted ...string) func(string) bool { // deriveValidationMode, independent of RequestCertificate's surrounding // plumbing. func TestDeriveValidationMode(t *testing.T) { + t.Parallel() tenantCA := &fakeCertAuthority{permitted: map[string]bool{ "private.example.com": true, }} @@ -93,6 +94,7 @@ func TestDeriveValidationMode(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { + t.Parallel() svc := &ACMServiceImpl{ TenantCA: tc.tenantCA, dnsProviderConfigured: tc.dnsProviderConfigured,