test: use 1024-bit keys and parallelise ACM handler tests

This commit is contained in:
Julian Sommer
2026-10-14 13:54:15 +10:00
parent bec3a78167
commit a0b4d840e2
9 changed files with 134 additions and 19 deletions
+8 -2
View File
@@ -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)
@@ -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")
+16 -11
View File
@@ -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_<bits> or EC_<Go curve>;
// 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),
+21 -6
View File
@@ -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)
+17
View File
@@ -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{
@@ -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")
+15
View File
@@ -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"
+10
View File
@@ -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{
@@ -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,