mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
fix: run the vpc, elbv2 and iam handler tests in parallel
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestEnsureDefaultVPC_PublicIPMappingDisabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
svc.SetDefaultPublicIPMapping(false)
|
||||
|
||||
@@ -24,6 +25,7 @@ func TestEnsureDefaultVPC_PublicIPMappingDisabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnsureDefaultVPC_PublicIPMappingDefaultOn(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.EnsureDefaultVPC(testAccountID)
|
||||
|
||||
@@ -35,6 +35,7 @@ func createAutoENI(t *testing.T, svc *VPCServiceImpl, subnetID, instanceID strin
|
||||
}
|
||||
|
||||
func TestListAbandonedInstanceENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -69,6 +70,7 @@ func TestListAbandonedInstanceENIs(t *testing.T) {
|
||||
// records that do name an instance, which is where a zombie hides. The two
|
||||
// listings must not overlap, or a sweep would act on the same record twice.
|
||||
func TestListAttachedInstanceENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -102,6 +104,7 @@ func TestListAttachedInstanceENIs(t *testing.T) {
|
||||
// A record written before CreatedAt existed carries no age, and age is the only
|
||||
// thing standing between this listing and an ENI a launch is still using.
|
||||
func TestListAttachedInstanceENIsIgnoresZeroCreatedAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -122,6 +125,7 @@ func TestListAttachedInstanceENIsIgnoresZeroCreatedAt(t *testing.T) {
|
||||
// worth sweeping: the SG dependency check counts an abandoned ENI, so the group
|
||||
// stays undeletable for as long as the record survives.
|
||||
func TestAbandonedENIBlocksSecurityGroupDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestGetENIRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -25,12 +26,14 @@ func TestGetENIRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetENIRecord_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.GetENIRecord(testAccountID, "eni-nonexistent")
|
||||
assert.ErrorContains(t, err, "InvalidNetworkInterfaceID.NotFound")
|
||||
}
|
||||
|
||||
func TestUpdateENI_PatchesFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -51,6 +54,7 @@ func TestUpdateENI_PatchesFields(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateENI_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
err := svc.UpdateENI(testAccountID, "eni-nonexistent", func(r *ENIRecord) {
|
||||
r.AttachmentStatus = "attached"
|
||||
@@ -59,6 +63,7 @@ func TestUpdateENI_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateENI_NoopPatchPersists(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -73,6 +78,7 @@ func TestUpdateENI_NoopPatchPersists(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFindENIByAttachment(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -89,12 +95,14 @@ func TestFindENIByAttachment(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFindENIByAttachment_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.FindENIByAttachment(testAccountID, "eni-attach-missing")
|
||||
assert.ErrorContains(t, err, "InvalidAttachmentID.NotFound")
|
||||
}
|
||||
|
||||
func TestFindENIByAttachment_AccountScoped(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -111,6 +119,7 @@ func TestFindENIByAttachment_AccountScoped(t *testing.T) {
|
||||
// ENIRecord so a struct-field rename would surface as a test failure rather
|
||||
// than a silent JSON-tag drift.
|
||||
func TestUpdateENI_RoundTripsAllNewFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -136,6 +145,7 @@ func TestUpdateENI_RoundTripsAllNewFields(t *testing.T) {
|
||||
// TestUpdateENI_PreservesUnrelatedFields ensures a partial patch does not
|
||||
// clobber pre-existing record state.
|
||||
func TestUpdateENI_PreservesUnrelatedFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
|
||||
@@ -33,6 +33,7 @@ func stubAddNAT(t *testing.T, nc *nats.Conn, fail string) *[]string {
|
||||
}
|
||||
|
||||
func TestRebindENIPublicIP_MovesRecordAndNAT(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
seen := stubAddNAT(t, nc, "")
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
@@ -56,6 +57,7 @@ func TestRebindENIPublicIP_MovesRecordAndNAT(t *testing.T) {
|
||||
// A NAT commit failure leaves the instance with no working ingress, so the
|
||||
// record must not be moved to advertise an address that does not route.
|
||||
func TestRebindENIPublicIP_NATFailureLeavesRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
stubAddNAT(t, nc, "ovn unreachable")
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
@@ -72,6 +74,7 @@ func TestRebindENIPublicIP_NATFailureLeavesRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRebindENIPublicIP_IsIdempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
stubAddNAT(t, nc, "")
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
@@ -84,6 +87,7 @@ func TestRebindENIPublicIP_IsIdempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRebindENIPublicIP_RejectsMismatchAndUnknown(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
stubAddNAT(t, nc, "")
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -9,18 +9,21 @@ import (
|
||||
)
|
||||
|
||||
func TestGenerateENIMac_Deterministic(t *testing.T) {
|
||||
t.Parallel()
|
||||
mac1 := generateENIMac("eni-abc123")
|
||||
mac2 := generateENIMac("eni-abc123")
|
||||
assert.Equal(t, mac1, mac2, "same input must produce same MAC")
|
||||
}
|
||||
|
||||
func TestGenerateENIMac_DifferentInputs(t *testing.T) {
|
||||
t.Parallel()
|
||||
mac1 := generateENIMac("eni-aaa")
|
||||
mac2 := generateENIMac("eni-bbb")
|
||||
assert.NotEqual(t, mac1, mac2, "different inputs should produce different MACs")
|
||||
}
|
||||
|
||||
func TestGenerateENIMac_LocallyAdministered(t *testing.T) {
|
||||
t.Parallel()
|
||||
mac := generateENIMac("eni-test123")
|
||||
hw, err := net.ParseMAC(mac)
|
||||
require.NoError(t, err)
|
||||
@@ -29,6 +32,7 @@ func TestGenerateENIMac_LocallyAdministered(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateENIMac_EmptyString(t *testing.T) {
|
||||
t.Parallel()
|
||||
mac := generateENIMac("")
|
||||
hw, err := net.ParseMAC(mac)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -29,6 +29,7 @@ func createTestENI(t *testing.T, svc *VPCServiceImpl, subnetId string) string {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -49,6 +50,7 @@ func TestCreateNetworkInterface(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_SequentialIPs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -67,12 +69,14 @@ func TestCreateNetworkInterface_SequentialIPs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_MissingSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateNetworkInterface(context.Background(), &ec2.CreateNetworkInterfaceInput{}, testAccountID)
|
||||
assert.ErrorContains(t, err, "MissingParameter")
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_InvalidSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateNetworkInterface(context.Background(), &ec2.CreateNetworkInterfaceInput{
|
||||
SubnetId: aws.String("subnet-nonexistent"),
|
||||
@@ -81,6 +85,7 @@ func TestCreateNetworkInterface_InvalidSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_WithTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -109,6 +114,7 @@ func TestCreateNetworkInterface_WithTags(t *testing.T) {
|
||||
// marks the ENIRecord SuppressDHCP, threads it into the vpc.create-port event
|
||||
// for vpcd, and never persists as a customer-visible tag.
|
||||
func TestCreateNetworkInterface_DHCPDisabledTag_SuppressesDHCP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -155,6 +161,7 @@ func TestCreateNetworkInterface_DHCPDisabledTag_SuppressesDHCP(t *testing.T) {
|
||||
// ordinary ENI (no internal tag) is unaffected: SuppressDHCP stays false and
|
||||
// the wire event carries no suppress_dhcp marker.
|
||||
func TestCreateNetworkInterface_NoDHCPDisabledTag_LeavesDHCPEnabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -185,6 +192,7 @@ func TestCreateNetworkInterface_NoDHCPDisabledTag_LeavesDHCPEnabled(t *testing.T
|
||||
}
|
||||
|
||||
func TestDeleteNetworkInterface(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -203,6 +211,7 @@ func TestDeleteNetworkInterface(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteNetworkInterface_InUse(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -220,6 +229,7 @@ func TestDeleteNetworkInterface_InUse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteNetworkInterface_ReleasesIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -245,6 +255,7 @@ func TestDeleteNetworkInterface_ReleasesIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_All(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -258,6 +269,7 @@ func TestDescribeNetworkInterfaces_All(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_ByID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -274,6 +286,7 @@ func TestDescribeNetworkInterfaces_ByID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterBySubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetA := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -293,6 +306,7 @@ func TestDescribeNetworkInterfaces_FilterBySubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.DescribeNetworkInterfaces(context.Background(), &ec2.DescribeNetworkInterfacesInput{
|
||||
NetworkInterfaceIds: []*string{aws.String("eni-nonexistent")},
|
||||
@@ -301,6 +315,7 @@ func TestDescribeNetworkInterfaces_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -323,6 +338,7 @@ func TestAttachENI(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachENI_AlreadyAttached(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -337,6 +353,7 @@ func TestAttachENI_AlreadyAttached(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -379,6 +396,7 @@ func (k *failNTimesDeleteKV) Delete(ctx context.Context, key string, opts ...jet
|
||||
// from a fresh read rather than giving up, so a lagging replica never causes
|
||||
// a leaked ENI.
|
||||
func TestDetachAndDeleteENI_RetriesOnDeleteCASConflict(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -402,6 +420,7 @@ func TestDetachAndDeleteENI_RetriesOnDeleteCASConflict(t *testing.T) {
|
||||
// bounded: a Delete that never stops conflicting must surface an error
|
||||
// rather than retry forever or silently drop the ENI.
|
||||
func TestDetachAndDeleteENI_ExhaustsRetriesReturnsError(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -419,6 +438,7 @@ func TestDetachAndDeleteENI_ExhaustsRetriesReturnsError(t *testing.T) {
|
||||
// another ENI in between — so a delete that never succeeds must leave the
|
||||
// allocation exactly as it found it.
|
||||
func TestDetachAndDeleteENI_HoldsIPUntilDeleteSucceeds(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -440,6 +460,7 @@ func TestDetachAndDeleteENI_HoldsIPUntilDeleteSucceeds(t *testing.T) {
|
||||
// the public-facing guard is unchanged by the new atomic path: a genuinely
|
||||
// attached ENI without force must still return InvalidNetworkInterface.InUse.
|
||||
func TestDetachAndDeleteENI_ForceFalseOnLiveAttachment_StaysAWSFaithful(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -463,6 +484,7 @@ func TestDetachAndDeleteENI_ForceFalseOnLiveAttachment_StaysAWSFaithful(t *testi
|
||||
// with no separate re-read between the two — the mechanism that closes the
|
||||
// stale-replica window DetachENI+DeleteNetworkInterface used to open.
|
||||
func TestDetachAndDeleteENI_ForceTrueDeletesLiveAttachment(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -485,6 +507,7 @@ func TestDetachAndDeleteENI_ForceTrueDeletesLiveAttachment(t *testing.T) {
|
||||
// stops the caller logging a false "Deleted ENI on termination" for an ENI
|
||||
// it never touched.
|
||||
func TestDetachAndDeleteENI_AbsentForceTrue_ReturnsNotDeletedNoError(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
deleted, err := svc.DetachAndDeleteENI(context.Background(), testAccountID, "eni-never-existed", true)
|
||||
@@ -495,6 +518,7 @@ func TestDetachAndDeleteENI_AbsentForceTrue_ReturnsNotDeletedNoError(t *testing.
|
||||
// TestDetachAndDeleteENI_AbsentForceFalse_ReturnsNotFound asserts the public
|
||||
// (non-force) path stays AWS-faithful on an absent ENI.
|
||||
func TestDetachAndDeleteENI_AbsentForceFalse_ReturnsNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.DetachAndDeleteENI(context.Background(), testAccountID, "eni-never-existed", false)
|
||||
@@ -502,6 +526,7 @@ func TestDetachAndDeleteENI_AbsentForceFalse_ReturnsNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateENIMac(t *testing.T) {
|
||||
t.Parallel()
|
||||
mac := generateENIMac("eni-test123")
|
||||
hw, err := net.ParseMAC(mac)
|
||||
require.NoError(t, err)
|
||||
@@ -517,6 +542,7 @@ func TestGenerateENIMac(t *testing.T) {
|
||||
// --- Filter tests ---
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpc1 := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpc2 := createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -537,6 +563,7 @@ func TestDescribeNetworkInterfaces_FilterByVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByAttachmentInstanceId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -559,6 +586,7 @@ func TestDescribeNetworkInterfaces_FilterByAttachmentInstanceId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByDescription(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -588,6 +616,7 @@ func TestDescribeNetworkInterfaces_FilterByDescription(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_IPExhaustion(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
// /28 subnet: 16 IPs total, 4 reserved at start + 1 broadcast = 11 usable
|
||||
@@ -611,6 +640,7 @@ func TestCreateNetworkInterface_IPExhaustion(t *testing.T) {
|
||||
// --- NATS event tests ---
|
||||
|
||||
func TestCreateNetworkInterface_PublishesEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -638,6 +668,7 @@ func TestCreateNetworkInterface_PublishesEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteNetworkInterface_PublishesEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -668,6 +699,7 @@ func TestDeleteNetworkInterface_PublishesEvent(t *testing.T) {
|
||||
// is already gone by the time this runs, so a failed teardown must still let
|
||||
// the caller (instance terminate, appliance teardown) converge.
|
||||
func TestDeleteNetworkInterface_VpcdDeletePortError_NonFatal(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -689,6 +721,7 @@ func TestDeleteNetworkInterface_VpcdDeletePortError_NonFatal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_SecurityGroups(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -713,6 +746,7 @@ func TestModifyNetworkInterfaceAttribute_SecurityGroups(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_PublishesUpdatePortSGs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -744,6 +778,7 @@ func TestModifyNetworkInterfaceAttribute_PublishesUpdatePortSGs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_DescriptionOnly_NoSGEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -770,6 +805,7 @@ func TestModifyNetworkInterfaceAttribute_DescriptionOnly_NoSGEvent(t *testing.T)
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_PublishesEventCarriesSGs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -798,6 +834,7 @@ func TestCreateNetworkInterface_PublishesEventCarriesSGs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_Description(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -818,6 +855,7 @@ func TestModifyNetworkInterfaceAttribute_Description(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_SourceDestCheckOnly(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -855,6 +893,7 @@ func TestModifyNetworkInterfaceAttribute_SourceDestCheckOnly(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_SourceDestCheckOnly_NoSGEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -881,6 +920,7 @@ func TestModifyNetworkInterfaceAttribute_SourceDestCheckOnly_NoSGEvent(t *testin
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_NoAttributes(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -893,6 +933,7 @@ func TestModifyNetworkInterfaceAttribute_NoAttributes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.ModifyNetworkInterfaceAttribute(context.Background(), &ec2.ModifyNetworkInterfaceAttributeInput{
|
||||
@@ -903,6 +944,7 @@ func TestModifyNetworkInterfaceAttribute_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyNetworkInterfaceAttribute_MissingID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.ModifyNetworkInterfaceAttribute(context.Background(), &ec2.ModifyNetworkInterfaceAttributeInput{
|
||||
@@ -912,6 +954,7 @@ func TestModifyNetworkInterfaceAttribute_MissingID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_WithSecurityGroups(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -929,6 +972,7 @@ func TestCreateNetworkInterface_WithSecurityGroups(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_FallsBackToDefaultSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -943,6 +987,7 @@ func TestCreateNetworkInterface_FallsBackToDefaultSG(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByNetworkInterfaceId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -970,6 +1015,7 @@ func TestDescribeNetworkInterfaces_FilterByNetworkInterfaceId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -999,6 +1045,7 @@ func TestDescribeNetworkInterfaces_FilterByStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByPrivateIpAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -1035,6 +1082,7 @@ func TestDescribeNetworkInterfaces_FilterByPrivateIpAddress(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByAvailabilityZone(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
// Create subnet with explicit AZ
|
||||
@@ -1066,6 +1114,7 @@ func TestDescribeNetworkInterfaces_FilterByAvailabilityZone(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByGroupId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -1103,6 +1152,7 @@ func TestDescribeNetworkInterfaces_FilterByGroupId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByMacAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -1131,6 +1181,7 @@ func TestDescribeNetworkInterfaces_FilterByMacAddress(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByAttachmentId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -1152,6 +1203,7 @@ func TestDescribeNetworkInterfaces_FilterByAttachmentId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeNetworkInterfaces_FilterByAttachmentStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -1212,6 +1264,7 @@ func putEIPRecord(t *testing.T, kv jetstream.KeyValue, key, eniID string) {
|
||||
}
|
||||
|
||||
func TestIsEIPOwned_NilKV(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := &VPCServiceImpl{eipKV: nil}
|
||||
|
||||
owned, err := svc.isEIPOwned(context.Background(), "eni-111", testAccountID)
|
||||
@@ -1220,6 +1273,7 @@ func TestIsEIPOwned_NilKV(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEIPOwned_NoKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _ := setupEIPTestService(t)
|
||||
|
||||
owned, err := svc.isEIPOwned(context.Background(), "eni-111", testAccountID)
|
||||
@@ -1228,6 +1282,7 @@ func TestIsEIPOwned_NoKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEIPOwned_MatchingENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, kv := setupEIPTestService(t)
|
||||
putEIPRecord(t, kv, testAccountID+".eipalloc-001", "eni-111")
|
||||
|
||||
@@ -1237,6 +1292,7 @@ func TestIsEIPOwned_MatchingENI(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEIPOwned_NonMatchingENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, kv := setupEIPTestService(t)
|
||||
putEIPRecord(t, kv, testAccountID+".eipalloc-001", "eni-222")
|
||||
|
||||
@@ -1246,6 +1302,7 @@ func TestIsEIPOwned_NonMatchingENI(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEIPOwned_WrongAccountPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, kv := setupEIPTestService(t)
|
||||
putEIPRecord(t, kv, "999999999999.eipalloc-001", "eni-111")
|
||||
|
||||
@@ -1255,6 +1312,7 @@ func TestIsEIPOwned_WrongAccountPrefix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEIPOwned_MalformedJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, kv := setupEIPTestService(t)
|
||||
_, err := kv.Put(t.Context(), testAccountID+".eipalloc-001", []byte("not json"))
|
||||
require.NoError(t, err)
|
||||
@@ -1265,6 +1323,7 @@ func TestIsEIPOwned_MalformedJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEIPOwned_MultipleRecords_OneMatches(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, kv := setupEIPTestService(t)
|
||||
putEIPRecord(t, kv, testAccountID+".eipalloc-001", "eni-aaa")
|
||||
putEIPRecord(t, kv, testAccountID+".eipalloc-002", "eni-target")
|
||||
@@ -1278,6 +1337,7 @@ func TestIsEIPOwned_MultipleRecords_OneMatches(t *testing.T) {
|
||||
// --- validateSGAttachment ---
|
||||
|
||||
func TestValidateSGAttachment_OK(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sg := createTestSG(t, svc, vpcID, "ok")
|
||||
@@ -1287,6 +1347,7 @@ func TestValidateSGAttachment_OK(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateSGAttachment_EmptyList(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1295,6 +1356,7 @@ func TestValidateSGAttachment_EmptyList(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateSGAttachment_TooMany(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgs := make([]string, 6)
|
||||
@@ -1307,12 +1369,14 @@ func TestValidateSGAttachment_TooMany(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateSGAttachment_UnknownVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
err := svc.validateSGAttachment(t.Context(), testAccountID, []string{"sg-aaa"}, "vpc-missing")
|
||||
assert.ErrorContains(t, err, "InvalidVpcID.NotFound")
|
||||
}
|
||||
|
||||
func TestValidateSGAttachment_SGNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1321,6 +1385,7 @@ func TestValidateSGAttachment_SGNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateSGAttachment_CrossVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcA := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpcB := createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -1334,6 +1399,7 @@ func TestValidateSGAttachment_CrossVPC(t *testing.T) {
|
||||
// caller knows OVN port-group reconciliation didn't happen. The KV record
|
||||
// already changed by this point — that's intentional (reconciler converges).
|
||||
func TestModifyNetworkInterfaceAttribute_VpcdError_Propagated(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -1357,6 +1423,7 @@ func TestModifyNetworkInterfaceAttribute_VpcdError_Propagated(t *testing.T) {
|
||||
// the ENI record so DescribeNetworkInterfaces shows the auto-assigned public
|
||||
// IP after RunInstances and EIP associate-address.
|
||||
func TestUpdateENIPublicIP_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -1374,6 +1441,7 @@ func TestUpdateENIPublicIP_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateENIPublicIP_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
err := svc.UpdateENIPublicIP(testAccountID, "eni-missing", "203.0.113.8", "amazon")
|
||||
|
||||
@@ -33,6 +33,7 @@ func testPool() external.ExternalPoolConfig {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_AllocateSequential(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
ipam := setupTestExternalIPAM(t, []external.ExternalPoolConfig{pool})
|
||||
|
||||
@@ -52,6 +53,7 @@ func TestExternalIPAM_AllocateSequential(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_GatewayIPReserved(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
ipam := setupTestExternalIPAM(t, []external.ExternalPoolConfig{pool})
|
||||
|
||||
@@ -69,6 +71,7 @@ func TestExternalIPAM_GatewayIPReserved(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_ExplicitGatewayIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
pool.GatewayIP = "192.168.1.155"
|
||||
ipam := setupTestExternalIPAM(t, []external.ExternalPoolConfig{pool})
|
||||
@@ -89,6 +92,7 @@ func TestExternalIPAM_ExplicitGatewayIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_Release(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
ipam := setupTestExternalIPAM(t, []external.ExternalPoolConfig{pool})
|
||||
|
||||
@@ -112,6 +116,7 @@ func TestExternalIPAM_Release(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_Exhaustion(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := external.ExternalPoolConfig{
|
||||
Name: "tiny",
|
||||
RangeStart: "10.0.0.1",
|
||||
@@ -136,6 +141,7 @@ func TestExternalIPAM_Exhaustion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_CASConflict(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
ipam := setupTestExternalIPAM(t, []external.ExternalPoolConfig{pool})
|
||||
|
||||
@@ -169,6 +175,7 @@ func TestExternalIPAM_CASConflict(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_MultiPool(t *testing.T) {
|
||||
t.Parallel()
|
||||
pools := []external.ExternalPoolConfig{
|
||||
{
|
||||
Name: "us-east",
|
||||
@@ -205,6 +212,7 @@ func TestExternalIPAM_MultiPool(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_PoolFallback(t *testing.T) {
|
||||
t.Parallel()
|
||||
pools := []external.ExternalPoolConfig{
|
||||
{
|
||||
Name: "az-pool",
|
||||
@@ -253,6 +261,7 @@ func TestExternalIPAM_PoolFallback(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_SpecificPool(t *testing.T) {
|
||||
t.Parallel()
|
||||
pools := []external.ExternalPoolConfig{
|
||||
{
|
||||
Name: "pool-a",
|
||||
@@ -278,6 +287,7 @@ func TestExternalIPAM_SpecificPool(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_RangeValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
pool external.ExternalPoolConfig
|
||||
@@ -337,6 +347,7 @@ func TestExternalIPAM_RangeValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_InitFromConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
// Create IPAM twice — second init should be idempotent
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
@@ -362,6 +373,7 @@ func TestExternalIPAM_InitFromConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_ReleaseNotAllocated(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
ipam := setupTestExternalIPAM(t, []external.ExternalPoolConfig{pool})
|
||||
|
||||
@@ -370,6 +382,7 @@ func TestExternalIPAM_ReleaseNotAllocated(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_NoPoolAvailable(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := external.ExternalPoolConfig{
|
||||
Name: "us-only",
|
||||
RangeStart: "10.0.0.1",
|
||||
@@ -390,6 +403,7 @@ func TestExternalIPAM_NoPoolAvailable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExternalIPAM_FindPoolByName_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
pool := testPool()
|
||||
ipam := setupTestExternalIPAM(t, []external.ExternalPoolConfig{pool})
|
||||
|
||||
@@ -402,6 +416,7 @@ func TestExternalIPAM_FindPoolByName_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePoolConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
base := func() external.ExternalPoolConfig {
|
||||
return external.ExternalPoolConfig{
|
||||
Name: "wan",
|
||||
|
||||
@@ -22,6 +22,7 @@ func setupTestIPAM(t *testing.T) *IPAM {
|
||||
}
|
||||
|
||||
func TestIPAM_AllocateFirst(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
ip, err := ipam.AllocateIP(t.Context(), "subnet-1", "10.0.1.0/24", PurposeENIPrimary, "eni-1")
|
||||
@@ -31,6 +32,7 @@ func TestIPAM_AllocateFirst(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_AllocateSequential(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
ip1, err := ipam.AllocateIP(t.Context(), "subnet-seq", "10.0.2.0/24", PurposeENIPrimary, "eni-seq")
|
||||
@@ -47,6 +49,7 @@ func TestIPAM_AllocateSequential(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_Release(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
ip1, err := ipam.AllocateIP(t.Context(), "subnet-rel", "10.0.3.0/24", PurposeENIPrimary, "eni-rel")
|
||||
@@ -68,6 +71,7 @@ func TestIPAM_Release(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_ReleaseNotAllocated(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
_, err := ipam.AllocateIP(t.Context(), "subnet-rna", "10.0.4.0/24", PurposeENIPrimary, "eni-rna")
|
||||
@@ -78,6 +82,7 @@ func TestIPAM_ReleaseNotAllocated(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_ReleaseNoRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
err := ipam.ReleaseIP(t.Context(), "subnet-nonexistent", "10.0.0.4")
|
||||
@@ -85,6 +90,7 @@ func TestIPAM_ReleaseNoRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_Exhaustion(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
// /28 = 16 IPs total. Reserved: .0, .1, .2, .3, .15 = 5. Available: 11
|
||||
@@ -108,6 +114,7 @@ func TestIPAM_Exhaustion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_ReservedIPs(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
cidr := "10.0.6.0/24"
|
||||
@@ -123,6 +130,7 @@ func TestIPAM_ReservedIPs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_AllocatedIPs(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
// No allocations yet
|
||||
@@ -144,6 +152,7 @@ func TestIPAM_AllocatedIPs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_MultipleSubnets(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
ip1, err := ipam.AllocateIP(t.Context(), "subnet-a", "10.0.10.0/24", PurposeENIPrimary, "eni-a")
|
||||
@@ -161,6 +170,7 @@ func TestIPAM_MultipleSubnets(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_LargerSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
|
||||
// /20 subnet — first allocable is still .4
|
||||
@@ -170,6 +180,7 @@ func TestIPAM_LargerSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_NewIPAMWithKV(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
js := testutil.NewJetStream(t, nc)
|
||||
|
||||
@@ -188,6 +199,7 @@ func TestIPAM_NewIPAMWithKV(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIPAM_InvalidCIDR(t *testing.T) {
|
||||
t.Parallel()
|
||||
ipam := setupTestIPAM(t)
|
||||
_, err := ipam.AllocateIP(t.Context(), "subnet-bad", "not-a-cidr", PurposeENIPrimary, "eni-bad")
|
||||
assert.Error(t, err)
|
||||
@@ -195,6 +207,7 @@ func TestIPAM_InvalidCIDR(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCompareIPs_NilSortsFirst(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Negative(t, compareIPs(nil, net.ParseIP("10.0.0.1")))
|
||||
assert.Zero(t, compareIPs(net.ParseIP("10.0.0.1"), net.ParseIP("10.0.0.1")))
|
||||
assert.Positive(t, compareIPs(net.ParseIP("10.0.0.2"), net.ParseIP("10.0.0.1")))
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestVPCExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -22,6 +23,7 @@ func TestVPCExists(t *testing.T) {
|
||||
// A gw-lrp lease outliving its VPC is exactly the leak the reaper is for, so a
|
||||
// deleted VPC has to read as absent.
|
||||
func TestVPCExistsFalseAfterDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
_, err := svc.DeleteVpc(context.Background(), &ec2.DeleteVpcInput{VpcId: aws.String(vpcID)}, testAccountID)
|
||||
@@ -33,6 +35,7 @@ func TestVPCExistsFalseAfterDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVPCExistsUnknownID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
exists, err := svc.VPCExists(t.Context(), "vpc-missing")
|
||||
@@ -41,6 +44,7 @@ func TestVPCExistsUnknownID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestENIExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -52,6 +56,7 @@ func TestENIExists(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestENIExistsUnknownID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
exists, err := svc.ENIExists(t.Context(), "eni-missing")
|
||||
@@ -60,6 +65,7 @@ func TestENIExistsUnknownID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLeaseOwnerLookupsRejectEmptyIDs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.VPCExists(t.Context(), "")
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
// is why unit tests on each operation individually all passed while it
|
||||
// existed — this asserts the end state across the ordering that matters.
|
||||
func TestRLC5_NoOrphanedRouteTableAfterSubnetThenVpcTeardown(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.70.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.70.1.0/24")
|
||||
@@ -51,6 +52,7 @@ func TestRLC5_NoOrphanedRouteTableAfterSubnetThenVpcTeardown(t *testing.T) {
|
||||
// compatible so SDK callers and account-scoping checks see the real error.
|
||||
// Every VPC-family delete endpoint must have a case here.
|
||||
func TestRLC1_VPCDeleteNotFoundOnAbsent(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
wantErr string
|
||||
@@ -88,6 +90,7 @@ func TestRLC1_VPCDeleteNotFoundOnAbsent(t *testing.T) {
|
||||
// and the in-use guard then blocks delete forever, so the instance can never
|
||||
// finish terminating.
|
||||
func TestRLC_ENINonDeadlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetId := createTestSubnet(t, svc, vpcId, "10.0.1.0/24")
|
||||
@@ -125,6 +128,7 @@ func TestRLC_ENINonDeadlock(t *testing.T) {
|
||||
// detached/available (orphan) child must never pin its parent undeletable —
|
||||
// otherwise tofu destroy deadlocks on a child the GC backstop already reaps.
|
||||
func TestRLC3_VPCFamilyDeleteGuardsLiveDependents(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("DeleteSubnet blocked by an attached ENI", func(t *testing.T) {
|
||||
svc := setupTestVPCService(t)
|
||||
vpcId := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -30,6 +30,7 @@ func createTestSG(t *testing.T, svc *VPCServiceImpl, vpcID, name string) string
|
||||
// --- CreateSecurityGroup ---
|
||||
|
||||
func TestCreateSecurityGroup_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -43,6 +44,7 @@ func TestCreateSecurityGroup_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSecurityGroup_MissingGroupName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.CreateSecurityGroup(context.Background(), &ec2.CreateSecurityGroupInput{
|
||||
@@ -52,6 +54,7 @@ func TestCreateSecurityGroup_MissingGroupName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSecurityGroup_MissingVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.CreateSecurityGroup(context.Background(), &ec2.CreateSecurityGroupInput{
|
||||
@@ -61,6 +64,7 @@ func TestCreateSecurityGroup_MissingVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSecurityGroup_InvalidVpc(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.CreateSecurityGroup(context.Background(), &ec2.CreateSecurityGroupInput{
|
||||
@@ -72,6 +76,7 @@ func TestCreateSecurityGroup_InvalidVpc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSecurityGroup_DuplicateName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -92,6 +97,7 @@ func TestCreateSecurityGroup_DuplicateName(t *testing.T) {
|
||||
// --- DeleteSecurityGroup ---
|
||||
|
||||
func TestDeleteSecurityGroup_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "delete-me")
|
||||
@@ -110,6 +116,7 @@ func TestDeleteSecurityGroup_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSecurityGroup_MissingGroupId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.DeleteSecurityGroup(context.Background(), &ec2.DeleteSecurityGroupInput{}, testAccountID)
|
||||
@@ -119,6 +126,7 @@ func TestDeleteSecurityGroup_MissingGroupId(t *testing.T) {
|
||||
// --- DescribeSecurityGroups ---
|
||||
|
||||
func TestDescribeSecurityGroups_All(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "sg-a")
|
||||
@@ -131,6 +139,7 @@ func TestDescribeSecurityGroups_All(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_ByGroupId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "target-sg")
|
||||
@@ -145,6 +154,7 @@ func TestDescribeSecurityGroups_ByGroupId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_ByGroupName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "find-me")
|
||||
@@ -161,6 +171,7 @@ func TestDescribeSecurityGroups_ByGroupName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.DescribeSecurityGroups(context.Background(), &ec2.DescribeSecurityGroupsInput{
|
||||
@@ -173,6 +184,7 @@ func TestDescribeSecurityGroups_NotFound(t *testing.T) {
|
||||
// --- AuthorizeSecurityGroupIngress ---
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "ingress-sg")
|
||||
@@ -200,6 +212,7 @@ func TestAuthorizeSecurityGroupIngress_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.AuthorizeSecurityGroupIngress(context.Background(), &ec2.AuthorizeSecurityGroupIngressInput{
|
||||
@@ -210,6 +223,7 @@ func TestAuthorizeSecurityGroupIngress_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_MissingGroupId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.AuthorizeSecurityGroupIngress(context.Background(), &ec2.AuthorizeSecurityGroupIngressInput{}, testAccountID)
|
||||
@@ -219,6 +233,7 @@ func TestAuthorizeSecurityGroupIngress_MissingGroupId(t *testing.T) {
|
||||
// --- AuthorizeSecurityGroupEgress ---
|
||||
|
||||
func TestAuthorizeSecurityGroupEgress_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "egress-sg")
|
||||
@@ -239,6 +254,7 @@ func TestAuthorizeSecurityGroupEgress_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupEgress_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.AuthorizeSecurityGroupEgress(context.Background(), &ec2.AuthorizeSecurityGroupEgressInput{
|
||||
@@ -250,6 +266,7 @@ func TestAuthorizeSecurityGroupEgress_NotFound(t *testing.T) {
|
||||
// The AWS provider reads SecurityGroupRules[0].SecurityGroupRuleId on create;
|
||||
// an empty ruleset panics the plugin. Authorize must echo the created rules.
|
||||
func TestAuthorizeSecurityGroupIngress_ReturnsRuleSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "ingress-ruleset-sg")
|
||||
@@ -277,6 +294,7 @@ func TestAuthorizeSecurityGroupIngress_ReturnsRuleSet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupEgress_ReturnsRuleSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "egress-ruleset-sg")
|
||||
@@ -301,6 +319,7 @@ func TestAuthorizeSecurityGroupEgress_ReturnsRuleSet(t *testing.T) {
|
||||
// --- RevokeSecurityGroupIngress ---
|
||||
|
||||
func TestRevokeSecurityGroupIngress_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "revoke-ingress-sg")
|
||||
@@ -329,6 +348,7 @@ func TestRevokeSecurityGroupIngress_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupIngress_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.RevokeSecurityGroupIngress(context.Background(), &ec2.RevokeSecurityGroupIngressInput{
|
||||
@@ -341,6 +361,7 @@ func TestRevokeSecurityGroupIngress_NotFound(t *testing.T) {
|
||||
// rule returns InvalidPermission.NotFound, matching AWS non-idempotent
|
||||
// revoke behavior.
|
||||
func TestRevokeSecurityGroupIngress_RuleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "rule-notfound-ingress")
|
||||
@@ -362,6 +383,7 @@ func TestRevokeSecurityGroupIngress_RuleNotFound(t *testing.T) {
|
||||
// --- RevokeSecurityGroupEgress ---
|
||||
|
||||
func TestRevokeSecurityGroupEgress_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "revoke-egress-sg")
|
||||
@@ -381,6 +403,7 @@ func TestRevokeSecurityGroupEgress_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupEgress_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.RevokeSecurityGroupEgress(context.Background(), &ec2.RevokeSecurityGroupEgressInput{
|
||||
@@ -393,6 +416,7 @@ func TestRevokeSecurityGroupEgress_NotFound(t *testing.T) {
|
||||
// IPv6-only permission succeeds as a no-op; Terraform auto-revokes ::/0 IPv6
|
||||
// egress and must not abort when spinifex stores no IPv6 rules.
|
||||
func TestRevokeSecurityGroupEgress_IPv6OnlyIsNoOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "ipv6-revoke-noop")
|
||||
@@ -413,6 +437,7 @@ func TestRevokeSecurityGroupEgress_IPv6OnlyIsNoOp(t *testing.T) {
|
||||
// TestRevokeSecurityGroupIngress_IPv6OnlyIsNoOp mirrors the egress no-op test
|
||||
// — the ingress path shares the same parsing code and must behave the same.
|
||||
func TestRevokeSecurityGroupIngress_IPv6OnlyIsNoOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "ipv6-revoke-ingress-noop")
|
||||
@@ -433,6 +458,7 @@ func TestRevokeSecurityGroupIngress_IPv6OnlyIsNoOp(t *testing.T) {
|
||||
// TestAuthorizeSecurityGroupEgress_IPv6Rejected asserts that authorizing
|
||||
// IPv6 sources is rejected; spinifex cannot program IPv6 OVN ACLs.
|
||||
func TestAuthorizeSecurityGroupEgress_IPv6Rejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "ipv6-authorize-rejected")
|
||||
@@ -452,6 +478,7 @@ func TestAuthorizeSecurityGroupEgress_IPv6Rejected(t *testing.T) {
|
||||
// TestRevokeSecurityGroupEgress_RuleNotFound: egress counterpart — revoking a
|
||||
// non-existent egress rule returns InvalidPermission.NotFound.
|
||||
func TestRevokeSecurityGroupEgress_RuleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "rule-notfound-egress")
|
||||
@@ -507,6 +534,7 @@ func ingressRuleIDReferencing(t *testing.T, svc *VPCServiceImpl, sgID, srcSG str
|
||||
// with empty IpPermissions; if the handler ignores SecurityGroupRuleIds the
|
||||
// cross-ref rule survives and DeleteSecurityGroup deadlocks on DependencyViolation.
|
||||
func TestRevokeSecurityGroupIngress_ByRuleID_BreaksMutualReferenceTeardown(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgA := createTestSG(t, svc, vpcID, "tf-sg-a")
|
||||
@@ -537,6 +565,7 @@ func TestRevokeSecurityGroupIngress_ByRuleID_BreaksMutualReferenceTeardown(t *te
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupEgress_ByRuleID_RemovesRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "tf-egress-byid")
|
||||
@@ -569,6 +598,7 @@ func TestRevokeSecurityGroupEgress_ByRuleID_RemovesRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupIngress_ByRuleID_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "tf-byid-notfound")
|
||||
@@ -581,6 +611,7 @@ func TestRevokeSecurityGroupIngress_ByRuleID_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupIngress_ByRuleID_Malformed(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "tf-byid-malformed")
|
||||
@@ -595,6 +626,7 @@ func TestRevokeSecurityGroupIngress_ByRuleID_Malformed(t *testing.T) {
|
||||
// --- Helper function tests ---
|
||||
|
||||
func TestIpPermissionsToSGRules_TCP(t *testing.T) {
|
||||
t.Parallel()
|
||||
proto := "tcp"
|
||||
perms := []*ec2.IpPermission{
|
||||
{
|
||||
@@ -615,6 +647,7 @@ func TestIpPermissionsToSGRules_TCP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIpPermissionsToSGRules_AllTraffic(t *testing.T) {
|
||||
t.Parallel()
|
||||
allProto := "-1"
|
||||
perms := []*ec2.IpPermission{
|
||||
{
|
||||
@@ -630,6 +663,7 @@ func TestIpPermissionsToSGRules_AllTraffic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIpPermissionsToSGRules_SourceSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
proto := "-1"
|
||||
srcSG := "sg-0123456789abcdef0"
|
||||
perms := []*ec2.IpPermission{
|
||||
@@ -648,6 +682,7 @@ func TestIpPermissionsToSGRules_SourceSG(t *testing.T) {
|
||||
// TestIpPermissionsToSGRules_IPv6OnlyAuthorize rejects authorize calls whose
|
||||
// only source is Ipv6Ranges (OVN ACL builder is IPv4-only).
|
||||
func TestIpPermissionsToSGRules_IPv6OnlyAuthorize(t *testing.T) {
|
||||
t.Parallel()
|
||||
allProto := "-1"
|
||||
perms := []*ec2.IpPermission{
|
||||
{
|
||||
@@ -665,6 +700,7 @@ func TestIpPermissionsToSGRules_IPv6OnlyAuthorize(t *testing.T) {
|
||||
// revoke (returns zero rules, no error), matching Terraform's auto-revoke of
|
||||
// the default ::/0 IPv6 egress rule.
|
||||
func TestIpPermissionsToSGRules_IPv6OnlyRevoke(t *testing.T) {
|
||||
t.Parallel()
|
||||
allProto := "-1"
|
||||
perms := []*ec2.IpPermission{
|
||||
{
|
||||
@@ -681,6 +717,7 @@ func TestIpPermissionsToSGRules_IPv6OnlyRevoke(t *testing.T) {
|
||||
// TestIpPermissionsToSGRules_MixedV4V6Revoke keeps the v4 rule and drops the
|
||||
// v6 part on a mixed-source permission on revoke.
|
||||
func TestIpPermissionsToSGRules_MixedV4V6Revoke(t *testing.T) {
|
||||
t.Parallel()
|
||||
proto := "tcp"
|
||||
perms := []*ec2.IpPermission{
|
||||
{
|
||||
@@ -699,6 +736,7 @@ func TestIpPermissionsToSGRules_MixedV4V6Revoke(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSGRulesToIpPermissions_Conversion(t *testing.T) {
|
||||
t.Parallel()
|
||||
rules := []SGRule{
|
||||
{IpProtocol: "tcp", FromPort: 443, ToPort: 443, CidrIp: "0.0.0.0/0"},
|
||||
{IpProtocol: "-1", SourceSG: "sg-other"},
|
||||
@@ -728,6 +766,7 @@ func TestSGRulesToIpPermissions_Conversion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSGRuleKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
rule := SGRule{IpProtocol: "tcp", FromPort: 80, ToPort: 80, CidrIp: "10.0.0.0/8"}
|
||||
key := sgRuleKey(rule)
|
||||
assert.NotEmpty(t, key)
|
||||
@@ -743,6 +782,7 @@ func TestSGRuleKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSGRecordToEC2_Basic(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
record := &SecurityGroupRecord{
|
||||
@@ -770,6 +810,7 @@ func TestSGRecordToEC2_Basic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveSGRules_MatchingRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
existing := []SGRule{
|
||||
{IpProtocol: "tcp", FromPort: 22, ToPort: 22, CidrIp: "10.0.0.0/8"},
|
||||
{IpProtocol: "tcp", FromPort: 80, ToPort: 80, CidrIp: "0.0.0.0/0"},
|
||||
@@ -784,6 +825,7 @@ func TestRemoveSGRules_MatchingRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveSGRules_NoMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
existing := []SGRule{
|
||||
{IpProtocol: "tcp", FromPort: 80, ToPort: 80, CidrIp: "0.0.0.0/0"},
|
||||
}
|
||||
@@ -798,6 +840,7 @@ func TestRemoveSGRules_NoMatch(t *testing.T) {
|
||||
// --- SG-rule input validation (OVN ACL match-expression injection) ---
|
||||
|
||||
func TestValidateSGRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
rule SGRule
|
||||
@@ -843,6 +886,7 @@ func TestValidateSGRule(t *testing.T) {
|
||||
// validateSGRule is the last gate before the OVN ACL builder, which emits no L4
|
||||
// predicate for a protocol it does not recognise. Only canonical names pass.
|
||||
func TestValidateSGRule_Protocol(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, proto := range []string{"tcp", "udp", "icmp", "-1"} {
|
||||
assert.NoError(t, validateSGRule(SGRule{IpProtocol: proto, CidrIp: "10.0.0.0/8"}), "protocol %q", proto)
|
||||
}
|
||||
@@ -852,6 +896,7 @@ func TestValidateSGRule_Protocol(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNormalizeIPProtocol(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := map[string]string{
|
||||
"": "-1",
|
||||
"-1": "-1",
|
||||
@@ -880,6 +925,7 @@ func TestNormalizeIPProtocol(t *testing.T) {
|
||||
// full payload table lives in TestValidateSGRule above.
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_RejectsInvalidRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "inj-sg")
|
||||
@@ -899,6 +945,7 @@ func TestAuthorizeSecurityGroupIngress_RejectsInvalidRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupEgress_RejectsInvalidRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "inj-sg-egress")
|
||||
@@ -916,6 +963,7 @@ func TestAuthorizeSecurityGroupEgress_RejectsInvalidRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupIngress_RejectsInvalidRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "inj-sg-revoke")
|
||||
@@ -935,6 +983,7 @@ func TestRevokeSecurityGroupIngress_RejectsInvalidRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_AcceptsValidSourceSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "valid-src-sg")
|
||||
@@ -954,6 +1003,7 @@ func TestAuthorizeSecurityGroupIngress_AcceptsValidSourceSG(t *testing.T) {
|
||||
// --protocol 6 must be stored as tcp, so it builds the same ACL as
|
||||
// --protocol tcp instead of an L4-less match that allows every IP protocol.
|
||||
func TestAuthorizeSecurityGroupIngress_NormalizesNumericProtocol(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "numeric-proto-sg")
|
||||
@@ -998,6 +1048,7 @@ func TestAuthorizeSecurityGroupIngress_NormalizesNumericProtocol(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_RejectsUnsupportedProtocol(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "bad-proto-sg")
|
||||
@@ -1018,6 +1069,7 @@ func TestAuthorizeSecurityGroupIngress_RejectsUnsupportedProtocol(t *testing.T)
|
||||
// --- DescribeSecurityGroups filter tests ---
|
||||
|
||||
func TestDescribeSecurityGroups_FilterByGroupId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "target-sg")
|
||||
@@ -1034,6 +1086,7 @@ func TestDescribeSecurityGroups_FilterByGroupId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterByDescription(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1062,6 +1115,7 @@ func TestDescribeSecurityGroups_FilterByDescription(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterByVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpc1 := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpc2 := createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1082,6 +1136,7 @@ func TestDescribeSecurityGroups_FilterByVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterByIpPermissionCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "cidr-sg")
|
||||
@@ -1114,6 +1169,7 @@ func TestDescribeSecurityGroups_FilterByIpPermissionCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterMultipleValues_OR(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "sg-alpha")
|
||||
@@ -1130,6 +1186,7 @@ func TestDescribeSecurityGroups_FilterMultipleValues_OR(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterMultipleFilters_AND(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpc1 := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpc2 := createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1148,6 +1205,7 @@ func TestDescribeSecurityGroups_FilterMultipleFilters_AND(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterUnknownName_Error(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.DescribeSecurityGroups(context.Background(), &ec2.DescribeSecurityGroupsInput{
|
||||
@@ -1159,6 +1217,7 @@ func TestDescribeSecurityGroups_FilterUnknownName_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterWildcard(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "prod-web")
|
||||
@@ -1175,6 +1234,7 @@ func TestDescribeSecurityGroups_FilterWildcard(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterByTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1204,6 +1264,7 @@ func TestDescribeSecurityGroups_FilterByTag(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroups_FilterNoResults(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "my-sg")
|
||||
@@ -1243,6 +1304,7 @@ func findDefaultSGInVPC(t *testing.T, svc *VPCServiceImpl, vpcID string) string
|
||||
}
|
||||
|
||||
func TestCreateVpc_ProvisionsDefaultSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1264,6 +1326,7 @@ func TestCreateVpc_ProvisionsDefaultSG(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSecurityGroup_RejectsReservedDefaultName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1277,6 +1340,7 @@ func TestCreateSecurityGroup_RejectsReservedDefaultName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSecurityGroup_RejectsDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := findDefaultSGInVPC(t, svc, vpcID)
|
||||
@@ -1289,6 +1353,7 @@ func TestDeleteSecurityGroup_RejectsDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSecurityGroup_RejectsAttachedToENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1308,6 +1373,7 @@ func TestDeleteSecurityGroup_RejectsAttachedToENI(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSecurityGroup_RejectsReferencedFromOtherSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
srcSG := createTestSG(t, svc, vpcID, "src-sg")
|
||||
@@ -1331,6 +1397,7 @@ func TestDeleteSecurityGroup_RejectsReferencedFromOtherSG(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteVpc_BlocksOnNonDefaultSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "user-sg")
|
||||
@@ -1344,6 +1411,7 @@ func TestDeleteVpc_BlocksOnNonDefaultSG(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteVpc_CascadesDefaultSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
defaultSG := findDefaultSGInVPC(t, svc, vpcID)
|
||||
@@ -1360,6 +1428,7 @@ func TestDeleteVpc_CascadesDefaultSG(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_RuleLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "rule-limit-sg")
|
||||
@@ -1392,6 +1461,7 @@ func TestAuthorizeSecurityGroupIngress_RuleLimit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnsureDefaultVPC_CreatesDefaultSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
info, err := svc.EnsureDefaultVPC(testAccountID)
|
||||
require.NoError(t, err)
|
||||
@@ -1410,6 +1480,7 @@ func TestEnsureDefaultVPC_CreatesDefaultSG(t *testing.T) {
|
||||
// --- SourceSG existence + same-VPC validation on Authorize ---
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_SourceSG_SameVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
ownerSG := createTestSG(t, svc, vpcID, "owner-sg")
|
||||
@@ -1427,6 +1498,7 @@ func TestAuthorizeSecurityGroupIngress_SourceSG_SameVPC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_SourceSG_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
ownerSG := createTestSG(t, svc, vpcID, "owner-sg-nf")
|
||||
@@ -1445,6 +1517,7 @@ func TestAuthorizeSecurityGroupIngress_SourceSG_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_SourceSG_CrossVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcA := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpcB := createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -1465,6 +1538,7 @@ func TestAuthorizeSecurityGroupIngress_SourceSG_CrossVPC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupEgress_SourceSG_CrossVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcA := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpcB := createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -1486,6 +1560,7 @@ func TestAuthorizeSecurityGroupEgress_SourceSG_CrossVPC(t *testing.T) {
|
||||
// --- Egress dependency check (mirror of ingress coverage) ---
|
||||
|
||||
func TestDeleteSecurityGroup_RejectsReferencedFromOtherSGEgress(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
srcSG := createTestSG(t, svc, vpcID, "egress-src-sg")
|
||||
@@ -1511,6 +1586,7 @@ func TestDeleteSecurityGroup_RejectsReferencedFromOtherSGEgress(t *testing.T) {
|
||||
// --- Egress rule-count limit (mirror of ingress) ---
|
||||
|
||||
func TestAuthorizeSecurityGroupEgress_RuleLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "egress-rule-limit-sg")
|
||||
@@ -1550,6 +1626,7 @@ func TestAuthorizeSecurityGroupEgress_RuleLimit(t *testing.T) {
|
||||
// persists on vpcd error — the orphan-PG reconciler is the safety net. This
|
||||
// asserts both the error is surfaced AND the KV-first contract is held.
|
||||
func TestCreateSecurityGroup_VpcdError_RecordPersists(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _ := setupTestVPCServiceWithFailingVpcd(t, "forced-create-sg-error")
|
||||
|
||||
// CreateVpc also publishes vpc.create-sg and would fail; seed the VPC
|
||||
@@ -1575,6 +1652,7 @@ func TestCreateSecurityGroup_VpcdError_RecordPersists(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSecurityGroup_VpcdError_Propagated(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Build the SG using a temporary success stub, then swap to failing for
|
||||
// the delete call.
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
@@ -1601,6 +1679,7 @@ func TestDeleteSecurityGroup_VpcdError_Propagated(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_VpcdError_Propagated(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "to-authorize")
|
||||
@@ -1630,6 +1709,7 @@ func TestAuthorizeSecurityGroupIngress_VpcdError_Propagated(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupIngress_VpcdError_Propagated(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "to-revoke")
|
||||
@@ -1660,6 +1740,7 @@ func TestRevokeSecurityGroupIngress_VpcdError_Propagated(t *testing.T) {
|
||||
// TestAuthorizeSecurityGroupEgress_VpcdError_Propagated asserts that vpcd
|
||||
// failures on the egress path are surfaced to the caller.
|
||||
func TestAuthorizeSecurityGroupEgress_VpcdError_Propagated(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "egress-vpcd-fail")
|
||||
@@ -1681,6 +1762,7 @@ func TestAuthorizeSecurityGroupEgress_VpcdError_Propagated(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRevokeSecurityGroupEgress_VpcdError_Propagated(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "egress-revoke-fail")
|
||||
@@ -1704,6 +1786,7 @@ func TestRevokeSecurityGroupEgress_VpcdError_Propagated(t *testing.T) {
|
||||
// TestFindDefaultSGForVPC_NoMatch asserts that a lookup for vpcB's default SG
|
||||
// returns "" (not an error) when only vpcA has SGs.
|
||||
func TestFindDefaultSGForVPC_NoMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcA := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
_ = createTestSG(t, svc, vpcA, "non-default-a")
|
||||
@@ -1716,6 +1799,7 @@ func TestFindDefaultSGForVPC_NoMatch(t *testing.T) {
|
||||
// TestFindDefaultSGForVPC_SkipsMalformedRecord asserts that a corrupt SG entry
|
||||
// is skipped rather than aborting the scan.
|
||||
func TestFindDefaultSGForVPC_SkipsMalformedRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1732,6 +1816,7 @@ func TestFindDefaultSGForVPC_SkipsMalformedRecord(t *testing.T) {
|
||||
// --- Fix #1: DeleteVpc cascade failure must leave VPC record intact ---
|
||||
|
||||
func TestDeleteVpc_VpcdError_VPCNotDeleted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1753,6 +1838,7 @@ func TestDeleteVpc_VpcdError_VPCNotDeleted(t *testing.T) {
|
||||
// --- Fix #3: checkSGDependencies fails closed on KV read error ---
|
||||
|
||||
func TestDeleteSecurityGroup_FailsClosedOnCorruptENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "to-delete-corrupt")
|
||||
@@ -1831,6 +1917,7 @@ func authorizeIngressTCP(t *testing.T, svc *VPCServiceImpl, sgID string, port in
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_All(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgA := createTestSG(t, svc, vpcID, "rules-a")
|
||||
@@ -1866,6 +1953,7 @@ func TestDescribeSecurityGroupRules_All(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_FilterByGroupID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgA := createTestSG(t, svc, vpcID, "filter-a")
|
||||
@@ -1884,6 +1972,7 @@ func TestDescribeSecurityGroupRules_FilterByGroupID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_FilterByRuleID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "by-rule-id")
|
||||
@@ -1898,6 +1987,7 @@ func TestDescribeSecurityGroupRules_FilterByRuleID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_ByRuleIDsExplicit(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "explicit-ids")
|
||||
@@ -1912,6 +2002,7 @@ func TestDescribeSecurityGroupRules_ByRuleIDsExplicit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_RuleIDNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "any")
|
||||
@@ -1924,6 +2015,7 @@ func TestDescribeSecurityGroupRules_RuleIDNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_NilInput(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "nil-input")
|
||||
@@ -1934,6 +2026,7 @@ func TestDescribeSecurityGroupRules_NilInput(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_TagFiltersReturnNothing(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "tag-filter")
|
||||
@@ -1947,6 +2040,7 @@ func TestDescribeSecurityGroupRules_TagFiltersReturnNothing(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_ReferencedGroupInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
srcSG := createTestSG(t, svc, vpcID, "source")
|
||||
@@ -1986,6 +2080,7 @@ func TestDescribeSecurityGroupRules_ReferencedGroupInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeSecurityGroupIngress_AssignsRuleIDs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "assigns-ids")
|
||||
@@ -2015,6 +2110,7 @@ func TestAuthorizeSecurityGroupIngress_AssignsRuleIDs(t *testing.T) {
|
||||
// TestAuthorizeSecurityGroupIngress_RejectsContentDuplicate verifies key-based
|
||||
// dedup catches duplicate-content rules regardless of generated RuleId values.
|
||||
func TestAuthorizeSecurityGroupIngress_RejectsContentDuplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "dup-rule")
|
||||
@@ -2041,6 +2137,7 @@ func TestAuthorizeSecurityGroupIngress_RejectsContentDuplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSecurityGroupRules_InvalidFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSG(t, svc, vpcID, "invalid-filter")
|
||||
@@ -2059,6 +2156,7 @@ func TestDescribeSecurityGroupRules_InvalidFilter(t *testing.T) {
|
||||
// cannot match its rules, never revokes them, and the backend SG is pinned by
|
||||
// DependencyViolation — hanging tofu destroy with orphaned security groups.
|
||||
func TestSecurityGroupRule_DescriptionRoundTrips(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
nodeSG := createTestSG(t, svc, vpcID, "node-sg")
|
||||
|
||||
@@ -67,6 +67,7 @@ func createTestSubnet(t *testing.T, svc *VPCServiceImpl, vpcID, cidr string) str
|
||||
// --- VPC Tests ---
|
||||
|
||||
func TestCreateVpc(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
out, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String("10.0.0.0/16"),
|
||||
@@ -80,12 +81,14 @@ func TestCreateVpc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_MissingCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{}, testAccountID)
|
||||
assert.ErrorContains(t, err, "MissingParameter")
|
||||
}
|
||||
|
||||
func TestCreateVpc_EmptyCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String(""),
|
||||
@@ -94,6 +97,7 @@ func TestCreateVpc_EmptyCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_InvalidCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String("not-a-cidr"),
|
||||
@@ -102,6 +106,7 @@ func TestCreateVpc_InvalidCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_CidrTooLarge(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String("10.0.0.0/8"),
|
||||
@@ -110,6 +115,7 @@ func TestCreateVpc_CidrTooLarge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_CidrTooSmall(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String("10.0.0.0/29"),
|
||||
@@ -118,6 +124,7 @@ func TestCreateVpc_CidrTooSmall(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_WithTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
out, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String("10.0.0.0/16"),
|
||||
@@ -144,6 +151,7 @@ func TestCreateVpc_WithTags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_TagsWrongResourceType(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
out, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String("10.0.0.0/16"),
|
||||
@@ -161,6 +169,7 @@ func TestCreateVpc_TagsWrongResourceType(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_VNIIncrement(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
// Create two VPCs and verify they get different VNIs
|
||||
@@ -174,6 +183,7 @@ func TestCreateVpc_VNIIncrement(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteVpc(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -191,12 +201,14 @@ func TestDeleteVpc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteVpc_MissingID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.DeleteVpc(context.Background(), &ec2.DeleteVpcInput{}, testAccountID)
|
||||
assert.ErrorContains(t, err, "MissingParameter")
|
||||
}
|
||||
|
||||
func TestDeleteVpc_WithSubnets(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -212,6 +224,7 @@ func TestDeleteVpc_WithSubnets(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_All(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -222,6 +235,7 @@ func TestDescribeVpcs_All(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_ByID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -235,6 +249,7 @@ func TestDescribeVpcs_ByID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
desc, err := svc.DescribeVpcs(context.Background(), &ec2.DescribeVpcsInput{}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
@@ -242,6 +257,7 @@ func TestDescribeVpcs_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.DescribeVpcs(context.Background(), &ec2.DescribeVpcsInput{
|
||||
VpcIds: []*string{aws.String("vpc-nonexistent")},
|
||||
@@ -252,6 +268,7 @@ func TestDescribeVpcs_NotFound(t *testing.T) {
|
||||
// --- Subnet Tests ---
|
||||
|
||||
func TestCreateSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -270,6 +287,7 @@ func TestCreateSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_MissingVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateSubnet(context.Background(), &ec2.CreateSubnetInput{
|
||||
CidrBlock: aws.String("10.0.1.0/24"),
|
||||
@@ -278,6 +296,7 @@ func TestCreateSubnet_MissingVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_MissingCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
_, err := svc.CreateSubnet(context.Background(), &ec2.CreateSubnetInput{
|
||||
@@ -287,6 +306,7 @@ func TestCreateSubnet_MissingCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_InvalidVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.CreateSubnet(context.Background(), &ec2.CreateSubnetInput{
|
||||
VpcId: aws.String("vpc-nonexistent"),
|
||||
@@ -296,6 +316,7 @@ func TestCreateSubnet_InvalidVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_InvalidCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
_, err := svc.CreateSubnet(context.Background(), &ec2.CreateSubnetInput{
|
||||
@@ -306,6 +327,7 @@ func TestCreateSubnet_InvalidCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_OutsideVpcCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
_, err := svc.CreateSubnet(context.Background(), &ec2.CreateSubnetInput{
|
||||
@@ -316,6 +338,7 @@ func TestCreateSubnet_OutsideVpcCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_ConflictingCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -329,6 +352,7 @@ func TestCreateSubnet_ConflictingCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_WithTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -349,6 +373,7 @@ func TestCreateSubnet_WithTags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_WithAZ(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -362,6 +387,7 @@ func TestCreateSubnet_WithAZ(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -380,12 +406,14 @@ func TestDeleteSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSubnet_MissingID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.DeleteSubnet(context.Background(), &ec2.DeleteSubnetInput{}, testAccountID)
|
||||
assert.ErrorContains(t, err, "MissingParameter")
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_All(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -397,6 +425,7 @@ func TestDescribeSubnets_All(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_ByID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -411,6 +440,7 @@ func TestDescribeSubnets_ByID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_ByVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpc1 := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpc2 := createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -431,6 +461,7 @@ func TestDescribeSubnets_ByVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
desc, err := svc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
require.NoError(t, err)
|
||||
@@ -438,6 +469,7 @@ func TestDescribeSubnets_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{
|
||||
SubnetIds: []*string{aws.String("subnet-nonexistent")},
|
||||
@@ -446,6 +478,7 @@ func TestDescribeSubnets_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateMultipleSubnetsInVpc(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -467,6 +500,7 @@ func TestCreateMultipleSubnetsInVpc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteVpcAfterSubnetsDeleted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -485,6 +519,7 @@ func TestDeleteVpcAfterSubnetsDeleted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_CidrRanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -509,6 +544,7 @@ func TestCreateSubnet_CidrRanges(t *testing.T) {
|
||||
// --- Default VPC Tests ---
|
||||
|
||||
func TestEnsureDefaultVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
info, err := svc.EnsureDefaultVPC(testAccountID)
|
||||
@@ -536,6 +572,7 @@ func TestEnsureDefaultVPC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnsureDefaultVPC_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
// Call twice — should be idempotent
|
||||
@@ -555,6 +592,7 @@ func TestEnsureDefaultVPC_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnsureDefaultVPC_SkipsWhenDefaultExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
// Create default VPC first
|
||||
@@ -586,6 +624,7 @@ func TestEnsureDefaultVPC_SkipsWhenDefaultExists(t *testing.T) {
|
||||
// call for the same VPC is a no-op. Concurrent calls otherwise create duplicate
|
||||
// IsMain=true records, corrupting route-table resolution.
|
||||
func TestCreateMainRouteTable_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.99.0.0/16") // CreateVpc auto-calls createMainRouteTable
|
||||
|
||||
@@ -608,6 +647,7 @@ func TestCreateMainRouteTable_Idempotent(t *testing.T) {
|
||||
// (AWS-faithful), so without DeleteVpc reaping it the rtbKV bucket leaks one
|
||||
// orphaned main RT per deleted VPC.
|
||||
func TestDeleteVpc_ReapsMainRouteTable(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.77.0.0/16") // auto-creates the main RT
|
||||
|
||||
@@ -720,6 +760,7 @@ func getTestRouteTableAssociations(t *testing.T, svc *VPCServiceImpl, accountID,
|
||||
// is removed out from under the route table, orphaning it permanently since
|
||||
// DeleteRouteTable can never resolve a VpcId that no longer exists.
|
||||
func TestDeleteVpc_RejectsNonMainRouteTable(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.60.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.60.1.0/24")
|
||||
@@ -743,6 +784,7 @@ func TestDeleteVpc_RejectsNonMainRouteTable(t *testing.T) {
|
||||
// strips the only id that can detach it, leaving a gateway that can never be
|
||||
// detached or deleted and an account teardown that can never finish.
|
||||
func TestDeleteVpc_RejectsAttachedInternetGateway(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.64.0.0/16")
|
||||
|
||||
@@ -762,6 +804,7 @@ func TestDeleteVpc_RejectsAttachedInternetGateway(t *testing.T) {
|
||||
|
||||
// A gateway attached to some other VPC must not block this one.
|
||||
func TestDeleteVpc_IgnoresInternetGatewayOnAnotherVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.65.0.0/16")
|
||||
|
||||
@@ -778,6 +821,7 @@ func TestDeleteVpc_IgnoresInternetGatewayOnAnotherVPC(t *testing.T) {
|
||||
// a transient/corrupt read must never let DeleteVpc orphan a route table it
|
||||
// could not evaluate.
|
||||
func TestDeleteVpc_RouteTableCheckFailsClosedOnCorruptRTB(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.61.0.0/16")
|
||||
|
||||
@@ -793,6 +837,7 @@ func TestDeleteVpc_RouteTableCheckFailsClosedOnCorruptRTB(t *testing.T) {
|
||||
// every association naming the deleted subnet, so the route table it leaves
|
||||
// behind never carries a reference to a subnet that no longer exists.
|
||||
func TestDeleteSubnet_ClearsRouteTableAssociations(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.62.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.62.1.0/24")
|
||||
@@ -810,6 +855,7 @@ func TestDeleteSubnet_ClearsRouteTableAssociations(t *testing.T) {
|
||||
// it cannot be typed here; a mirror struct would silently erase every field
|
||||
// added to RouteTableRecord that the mirror had not caught up with.
|
||||
func TestDeleteSubnet_PreservesUnknownRouteTableFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.63.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.63.1.0/24")
|
||||
@@ -845,6 +891,7 @@ func TestDeleteSubnet_PreservesUnknownRouteTableFields(t *testing.T) {
|
||||
// associations for one subnet does not touch a route table association
|
||||
// belonging to a different subnet/VPC.
|
||||
func TestDeleteSubnet_LeavesOtherVPCsAssociationsAlone(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
vpcA := createTestVPC(t, svc, "10.63.0.0/16")
|
||||
@@ -868,6 +915,7 @@ func TestDeleteSubnet_LeavesOtherVPCsAssociationsAlone(t *testing.T) {
|
||||
// operation retryable, rather than deleting the subnet with a stale
|
||||
// association strand behind it unseen.
|
||||
func TestDeleteSubnet_RouteTableCheckFailsClosedOnCorruptRTB(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.65.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.65.1.0/24")
|
||||
@@ -888,6 +936,7 @@ func TestDeleteSubnet_RouteTableCheckFailsClosedOnCorruptRTB(t *testing.T) {
|
||||
// EnsureDefaultVPC runs before vpcd has subscribed. The SG step is best-effort,
|
||||
// so subnet and RTB must still land in KV when vpcd is absent.
|
||||
func TestEnsureDefaultVPC_NoVpcdResponder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
svc, err := NewVPCServiceImplWithNATS(t.Context(), nil, nc)
|
||||
require.NoError(t, err)
|
||||
@@ -940,6 +989,7 @@ func TestEnsureDefaultVPC_NoVpcdResponder(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetDefaultSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
// No default subnet yet
|
||||
@@ -957,6 +1007,7 @@ func TestGetDefaultSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetDefaultSubnet_NotConfusedByNonDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
// Create a non-default VPC + subnet
|
||||
@@ -976,6 +1027,7 @@ func TestGetDefaultSubnet_NotConfusedByNonDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_CidrTooSmall(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
_, err := svc.CreateSubnet(context.Background(), &ec2.CreateSubnetInput{
|
||||
@@ -986,6 +1038,7 @@ func TestCreateSubnet_CidrTooSmall(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpcCidrBlockAssociation(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
out, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
CidrBlock: aws.String("10.0.0.0/16"),
|
||||
@@ -999,6 +1052,7 @@ func TestVpcCidrBlockAssociation(t *testing.T) {
|
||||
// --- Filter tests ---
|
||||
|
||||
func TestDescribeVpcs_NilFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1012,6 +1066,7 @@ func TestDescribeVpcs_NilFields(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterByVpcId_NoMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1032,6 +1087,7 @@ func TestDescribeSubnets_FilterByVpcId_NoMatch(t *testing.T) {
|
||||
// --- NATS event tests ---
|
||||
|
||||
func TestCreateVpc_PublishesEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
|
||||
eventCh := make(chan *nats.Msg, 1)
|
||||
@@ -1057,6 +1113,7 @@ func TestCreateVpc_PublishesEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteVpc_PublishesEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1081,6 +1138,7 @@ func TestDeleteVpc_PublishesEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateSubnet_PublishesEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1108,6 +1166,7 @@ func TestCreateSubnet_PublishesEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteSubnet_PublishesEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1136,6 +1195,7 @@ func TestDeleteSubnet_PublishesEvent(t *testing.T) {
|
||||
// --- Additional coverage tests ---
|
||||
|
||||
func TestEnsureDefaultVPC_WithConfigAZ(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Create a service with custom config that has AZ set
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
testutil.StubVpcdSGResponder(t, nc)
|
||||
@@ -1155,6 +1215,7 @@ func TestEnsureDefaultVPC_WithConfigAZ(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateVpc_NormalizesNetworkCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
// Pass a CIDR with host bits set — should be normalized to network address
|
||||
out, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
@@ -1166,6 +1227,7 @@ func TestCreateVpc_NormalizesNetworkCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteVpc_WithENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1188,6 +1250,7 @@ func TestDeleteVpc_WithENIs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateNetworkInterface_WithExplicitIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1201,12 +1264,14 @@ func TestCreateNetworkInterface_WithExplicitIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachENI_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.AttachENI(testAccountID, "eni-nonexistent", "i-test", 0)
|
||||
assert.ErrorContains(t, err, "InvalidNetworkInterfaceID.NotFound")
|
||||
}
|
||||
|
||||
func TestDetachENI_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
err := svc.DetachENI(context.Background(), testAccountID, "eni-nonexistent")
|
||||
assert.ErrorContains(t, err, "InvalidNetworkInterfaceID.NotFound")
|
||||
@@ -1215,6 +1280,7 @@ func TestDetachENI_NotFound(t *testing.T) {
|
||||
// --- Per-account isolation tests ---
|
||||
|
||||
func TestEnsureDefaultVPC_PerAccountIsolation(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
accountA := "111111111111"
|
||||
accountB := "222222222222"
|
||||
@@ -1240,6 +1306,7 @@ func TestEnsureDefaultVPC_PerAccountIsolation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnsureDefaultVPC_IndependentVNIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
accountA := "111111111111"
|
||||
accountB := "222222222222"
|
||||
@@ -1275,6 +1342,7 @@ func TestEnsureDefaultVPC_IndependentVNIs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_NoGlobalSharing(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
globalAccount := "000000000000"
|
||||
otherAccount := "111111111111"
|
||||
@@ -1290,6 +1358,7 @@ func TestDescribeVpcs_NoGlobalSharing(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetDefaultSubnet_PerAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
accountA := "111111111111"
|
||||
accountB := "222222222222"
|
||||
@@ -1312,6 +1381,7 @@ func TestGetDefaultSubnet_PerAccount(t *testing.T) {
|
||||
// --- EnsureDefaultVPC event test ---
|
||||
|
||||
func TestEnsureDefaultVPC_PublishesEvents(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, nc := setupTestVPCServiceWithNC(t)
|
||||
|
||||
vpcCh := make(chan *nats.Msg, 1)
|
||||
@@ -1346,6 +1416,7 @@ func TestEnsureDefaultVPC_PublishesEvents(t *testing.T) {
|
||||
// --- MapPublicIpOnLaunch tests ---
|
||||
|
||||
func TestSubnet_MapPublicIpOnLaunch(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1360,6 +1431,7 @@ func TestSubnet_MapPublicIpOnLaunch(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSubnet_ModifyAttribute(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1385,6 +1457,7 @@ func TestSubnet_ModifyAttribute(t *testing.T) {
|
||||
// --- VPC Attribute Tests ---
|
||||
|
||||
func TestVpc_DescribeVpcAttribute_Defaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1415,6 +1488,7 @@ func TestVpc_DescribeVpcAttribute_Defaults(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_ModifyVpcAttribute_EnableDnsHostnames(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1435,6 +1509,7 @@ func TestVpc_ModifyVpcAttribute_EnableDnsHostnames(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_ModifyVpcAttribute_EnableDnsSupport(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1455,6 +1530,7 @@ func TestVpc_ModifyVpcAttribute_EnableDnsSupport(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_ModifyVpcAttribute_IndependentFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1474,6 +1550,7 @@ func TestVpc_ModifyVpcAttribute_IndependentFields(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_DescribeVpcAttribute_InvalidVpcID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.DescribeVpcAttribute(context.Background(), &ec2.DescribeVpcAttributeInput{
|
||||
@@ -1484,6 +1561,7 @@ func TestVpc_DescribeVpcAttribute_InvalidVpcID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_DescribeVpcAttribute_InvalidAttribute(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1495,6 +1573,7 @@ func TestVpc_DescribeVpcAttribute_InvalidAttribute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_DescribeVpcAttribute_MissingAttribute(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1505,6 +1584,7 @@ func TestVpc_DescribeVpcAttribute_MissingAttribute(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_ModifyVpcAttribute_EnableNetworkAddressUsageMetrics(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1523,6 +1603,7 @@ func TestVpc_ModifyVpcAttribute_EnableNetworkAddressUsageMetrics(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_ModifyVpcAttribute_NoAttributes(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1533,6 +1614,7 @@ func TestVpc_ModifyVpcAttribute_NoAttributes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVpc_ModifyVpcAttribute_InvalidVpcID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.ModifyVpcAttribute(context.Background(), &ec2.ModifyVpcAttributeInput{
|
||||
@@ -1545,6 +1627,7 @@ func TestVpc_ModifyVpcAttribute_InvalidVpcID(t *testing.T) {
|
||||
// --- DescribeVpcs filter tests ---
|
||||
|
||||
func TestDescribeVpcs_FilterByCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1560,6 +1643,7 @@ func TestDescribeVpcs_FilterByCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterByState(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1582,6 +1666,7 @@ func TestDescribeVpcs_FilterByState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterByIsDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1603,6 +1688,7 @@ func TestDescribeVpcs_FilterByIsDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterMultipleValues_OR(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1618,6 +1704,7 @@ func TestDescribeVpcs_FilterMultipleValues_OR(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterMultipleFilters_AND(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1643,6 +1730,7 @@ func TestDescribeVpcs_FilterMultipleFilters_AND(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterUnknownName_Error(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
_, err := svc.DescribeVpcs(context.Background(), &ec2.DescribeVpcsInput{
|
||||
Filters: []*ec2.Filter{
|
||||
@@ -1653,6 +1741,7 @@ func TestDescribeVpcs_FilterUnknownName_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterWildcard(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestVPC(t, svc, "10.1.0.0/16")
|
||||
@@ -1668,6 +1757,7 @@ func TestDescribeVpcs_FilterWildcard(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterNoResults(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1681,6 +1771,7 @@ func TestDescribeVpcs_FilterNoResults(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterNoFilters(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1691,6 +1782,7 @@ func TestDescribeVpcs_FilterNoFilters(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterByTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
out, err := svc.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
@@ -1720,6 +1812,7 @@ func TestDescribeVpcs_FilterByTag(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterByVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
id1 := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1735,6 +1828,7 @@ func TestDescribeVpcs_FilterByVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeVpcs_FilterByOwnerId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1758,6 +1852,7 @@ func TestDescribeVpcs_FilterByOwnerId(t *testing.T) {
|
||||
// --- DescribeSubnets filter tests ---
|
||||
|
||||
func TestDescribeSubnets_FilterByVpcId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpc1 := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpc2 := createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1775,6 +1870,7 @@ func TestDescribeSubnets_FilterByVpcId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterByCidr(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1791,6 +1887,7 @@ func TestDescribeSubnets_FilterByCidr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterByState(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1813,6 +1910,7 @@ func TestDescribeSubnets_FilterByState(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterBySubnetId(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1829,6 +1927,7 @@ func TestDescribeSubnets_FilterBySubnetId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterByDefaultForAz(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1843,6 +1942,7 @@ func TestDescribeSubnets_FilterByDefaultForAz(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterMultipleValues_OR(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1859,6 +1959,7 @@ func TestDescribeSubnets_FilterMultipleValues_OR(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterMultipleFilters_AND(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpc1 := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
vpc2 := createTestVPC(t, svc, "172.16.0.0/16")
|
||||
@@ -1887,6 +1988,7 @@ func TestDescribeSubnets_FilterMultipleFilters_AND(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterUnknownName_Error(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{
|
||||
@@ -1898,6 +2000,7 @@ func TestDescribeSubnets_FilterUnknownName_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterWildcard(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1913,6 +2016,7 @@ func TestDescribeSubnets_FilterWildcard(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterNoResults(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1927,6 +2031,7 @@ func TestDescribeSubnets_FilterNoResults(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSubnets_FilterByTag(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
|
||||
@@ -1959,6 +2064,7 @@ func TestDescribeSubnets_FilterByTag(t *testing.T) {
|
||||
// --- SetExternalIPAM / GetSubnet ---
|
||||
|
||||
func TestGetSubnet_Success(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -1971,6 +2077,7 @@ func TestGetSubnet_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetSubnet_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
|
||||
_, err := svc.GetSubnet(testAccountID, "subnet-missing")
|
||||
@@ -1981,6 +2088,7 @@ func TestGetSubnet_NotFound(t *testing.T) {
|
||||
// --- CreateTags write-through ---
|
||||
|
||||
func TestApplyRecordTags_SubnetTagFilteredDescribe(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.0.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.0.1.0/24")
|
||||
@@ -2013,6 +2121,7 @@ func TestApplyRecordTags_SubnetTagFilteredDescribe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplyRecordTags_VpcMergePreservesRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.1.0.0/16")
|
||||
|
||||
@@ -2033,6 +2142,7 @@ func TestApplyRecordTags_VpcMergePreservesRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveRecordTags_Subnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.2.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.2.1.0/24")
|
||||
@@ -2064,6 +2174,7 @@ func TestRemoveRecordTags_Subnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplyRecordTags_SecurityGroupTagFilteredDescribe(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.3.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "mirror-sg")
|
||||
@@ -2086,6 +2197,7 @@ func TestApplyRecordTags_SecurityGroupTagFilteredDescribe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveRecordTags_SecurityGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.4.0.0/16")
|
||||
sgID := createTestSG(t, svc, vpcID, "mirror-sg-del")
|
||||
@@ -2116,6 +2228,7 @@ func TestRemoveRecordTags_SecurityGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplyRecordTags_ENITagFilteredDescribe(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
vpcID := createTestVPC(t, svc, "10.5.0.0/16")
|
||||
subnetID := createTestSubnet(t, svc, vpcID, "10.5.1.0/24")
|
||||
@@ -2139,6 +2252,7 @@ func TestApplyRecordTags_ENITagFilteredDescribe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestApplyRecordTags_UnknownResourceNoError(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestVPCService(t)
|
||||
// Absent subnet + non-VPC-owned resource id: both skipped without error.
|
||||
err := svc.ApplyRecordTags(&ec2.CreateTagsInput{
|
||||
|
||||
@@ -63,6 +63,7 @@ func handleNATSMsg[In any, Out any](msg *nats.Msg, fn func(context.Context, *In,
|
||||
}
|
||||
|
||||
func TestNATSVPCService_CreateVpc(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _, _ := setupNATSVPCServiceTest(t)
|
||||
|
||||
out, err := client.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
@@ -75,6 +76,7 @@ func TestNATSVPCService_CreateVpc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSVPCService_DescribeVpcs(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _, _ := setupNATSVPCServiceTest(t)
|
||||
|
||||
// Create a VPC first
|
||||
@@ -98,6 +100,7 @@ func TestNATSVPCService_DescribeVpcs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSVPCService_DeleteVpc(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _, _ := setupNATSVPCServiceTest(t)
|
||||
|
||||
createOut, err := client.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
@@ -112,6 +115,7 @@ func TestNATSVPCService_DeleteVpc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSVPCService_CreateAndDeleteSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _, _ := setupNATSVPCServiceTest(t)
|
||||
|
||||
vpcOut, err := client.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
@@ -134,6 +138,7 @@ func TestNATSVPCService_CreateAndDeleteSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSVPCService_DescribeSubnets(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _, _ := setupNATSVPCServiceTest(t)
|
||||
|
||||
vpcOut, err := client.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
@@ -153,6 +158,7 @@ func TestNATSVPCService_DescribeSubnets(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSVPCService_CreateAndDeleteENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _, _ := setupNATSVPCServiceTest(t)
|
||||
|
||||
vpcOut, err := client.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
@@ -179,6 +185,7 @@ func TestNATSVPCService_CreateAndDeleteENI(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSVPCService_DescribeNetworkInterfaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _, _ := setupNATSVPCServiceTest(t)
|
||||
|
||||
vpcOut, err := client.CreateVpc(context.Background(), &ec2.CreateVpcInput{
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
// TestToHealthReport verifies the LBAgentHeartbeatInput.toHealthReport conversion,
|
||||
// in particular that aws.StringValue handles nil pointers without panicking.
|
||||
func TestToHealthReport(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
in *LBAgentHeartbeatInput
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
// TestBuildLBAgentEnv verifies that buildLBAgentEnv produces a KEY=value blob
|
||||
// containing all five env vars with the correct values.
|
||||
func TestBuildLBAgentEnv(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := &ELBv2ServiceImpl{
|
||||
GatewayURL: "https://10.0.0.1:9999",
|
||||
SystemAccessKey: "AKIAIOSFODNN7EXAMPLE",
|
||||
@@ -47,6 +48,7 @@ func TestBuildLBAgentEnv(t *testing.T) {
|
||||
// (staticCreds=false) the static keys are dropped so the lb-agent signs with
|
||||
// IMDS instance-role credentials via the SDK chain.
|
||||
func TestBuildLBAgentEnv_IMDSModeOmitsKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := &ELBv2ServiceImpl{
|
||||
GatewayURL: "https://10.0.0.1:9999",
|
||||
SystemAccessKey: "AKIAIOSFODNN7EXAMPLE",
|
||||
@@ -64,6 +66,7 @@ func TestBuildLBAgentEnv_IMDSModeOmitsKeys(t *testing.T) {
|
||||
// system) heartbeats over the on-link mgmt-bridge URL, not the WAN GatewayURL,
|
||||
// so the control-plane heartbeat survives a reboot that strands the data plane.
|
||||
func TestBuildLBAgentEnv_UsesMgmtGateway(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := &ELBv2ServiceImpl{
|
||||
GatewayURL: "https://10.0.0.1:9999",
|
||||
MgmtBridgeIP: "192.168.50.1",
|
||||
@@ -82,12 +85,14 @@ func TestBuildLBAgentEnv_UsesMgmtGateway(t *testing.T) {
|
||||
|
||||
// TestSubnetCIDRForIP and TestSubnetGatewayIP cover the CIDR helpers.
|
||||
func TestSubnetCIDRForIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, "10.0.1.5/24", subnetCIDRForIP("10.0.1.5", "10.0.1.0/24"))
|
||||
assert.Equal(t, "172.16.2.10/20", subnetCIDRForIP("172.16.2.10", "172.16.0.0/20"))
|
||||
assert.Empty(t, subnetCIDRForIP("10.0.1.5", "bad-cidr"))
|
||||
}
|
||||
|
||||
func TestSubnetGatewayIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, "10.0.1.1", subnetGatewayIP("10.0.1.0/24"))
|
||||
assert.Equal(t, "172.16.0.1", subnetGatewayIP("172.16.0.0/20"))
|
||||
assert.Empty(t, subnetGatewayIP("not-a-cidr"))
|
||||
@@ -139,6 +144,7 @@ func setupMicrovmTestService(t *testing.T) (*ELBv2ServiceImpl, *handlers_ec2_vpc
|
||||
// TestCreateLoadBalancer_DeliversCACert asserts the CACert plumbed into the
|
||||
// service is forwarded to the launcher for fw_cfg delivery.
|
||||
func TestCreateLoadBalancer_DeliversCACert(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _, subnetID := setupMicrovmTestService(t)
|
||||
|
||||
mock := &mockSystemInstanceLauncher{
|
||||
@@ -164,6 +170,7 @@ func TestCreateLoadBalancer_DeliversCACert(t *testing.T) {
|
||||
// TestCreateLoadBalancer_Microvm_LBAgentEnv asserts all five lb-agent env vars
|
||||
// appear in LBAgentEnv with correct values.
|
||||
func TestCreateLoadBalancer_Microvm_LBAgentEnv(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _, subnetID := setupMicrovmTestService(t)
|
||||
|
||||
mock := &mockSystemInstanceLauncher{
|
||||
@@ -192,6 +199,7 @@ func TestCreateLoadBalancer_Microvm_LBAgentEnv(t *testing.T) {
|
||||
// TestCreateLoadBalancer_Microvm_NICs asserts NIC[0].IsDefault=true and
|
||||
// NIC[1].IsDefault=false, and that CIDR/Gateway are derived from the subnet.
|
||||
func TestCreateLoadBalancer_Microvm_NICs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _, subnetID := setupMicrovmTestService(t)
|
||||
|
||||
mock := &mockSystemInstanceLauncher{
|
||||
@@ -238,6 +246,7 @@ func parseEnvBlob(t *testing.T, blob string) map[string]string {
|
||||
// TestBuildMicrovmNICs_NilVPC verifies buildMicrovmNICs works when VPCService
|
||||
// is nil — CIDR and gateway remain empty, NIC structure is still correct.
|
||||
func TestBuildMicrovmNICs_NilVPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := &ELBv2ServiceImpl{VPCService: nil}
|
||||
nics := svc.buildMicrovmNICs("10.0.1.5", "02:aa:bb:cc:dd:01", "subnet-abc", "eni-abc", SchemeInternal, nil, testAccountID)
|
||||
require.Len(t, nics, 2, "primary ENI NIC + mgmt NIC")
|
||||
@@ -249,6 +258,7 @@ func TestBuildMicrovmNICs_NilVPC(t *testing.T) {
|
||||
|
||||
// TestBuildMicrovmNICs_ExtraENIs verifies NIC[2+] are appended for multi-subnet ALBs.
|
||||
func TestBuildMicrovmNICs_ExtraENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := &ELBv2ServiceImpl{VPCService: nil}
|
||||
extras := []ExtraENIInput{
|
||||
{ENIID: "eni-extra1", ENIMac: "02:aa:bb:cc:dd:02", ENIIP: "10.0.2.5", SubnetID: "subnet-extra1"},
|
||||
@@ -263,6 +273,7 @@ func TestBuildMicrovmNICs_ExtraENIs(t *testing.T) {
|
||||
// internet-facing single-node must not emit a /32 via mgmt to AdvertiseIP, as that
|
||||
// would steal the host's WAN return path and break same-chassis ingress.
|
||||
func TestBuildMicrovmNICs_MgmtRoute(t *testing.T) {
|
||||
t.Parallel()
|
||||
mk := func() *ELBv2ServiceImpl {
|
||||
return &ELBv2ServiceImpl{
|
||||
VPCService: nil,
|
||||
|
||||
@@ -50,6 +50,7 @@ func httpsListenerInput(lbArn, certArn string, port int64) *elbv2.CreateListener
|
||||
}
|
||||
|
||||
func TestCreateListener_AddsToInUseByIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-idx1", "idx-lb1")
|
||||
|
||||
@@ -62,6 +63,7 @@ func TestCreateListener_AddsToInUseByIndex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateListener_TwoListenersSameCert_SingleInUseByEntry(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-idx2", "idx-lb2")
|
||||
|
||||
@@ -77,6 +79,7 @@ func TestCreateListener_TwoListenersSameCert_SingleInUseByEntry(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteListener_RemovesFromInUseByIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-idx3", "idx-lb3")
|
||||
|
||||
@@ -94,6 +97,7 @@ func TestDeleteListener_RemovesFromInUseByIndex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteListener_KeepsInUseByWhenAnotherListenerSharesCert(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-idx4", "idx-lb4")
|
||||
|
||||
@@ -113,6 +117,7 @@ func TestDeleteListener_KeepsInUseByWhenAnotherListenerSharesCert(t *testing.T)
|
||||
}
|
||||
|
||||
func TestModifyListener_SwitchesCertInInUseByIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-idx5", "idx-lb5")
|
||||
|
||||
@@ -135,6 +140,7 @@ func TestModifyListener_SwitchesCertInInUseByIndex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteLoadBalancer_RemovesFromInUseByIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-idx6", "idx-lb6")
|
||||
|
||||
@@ -152,6 +158,7 @@ func TestDeleteLoadBalancer_RemovesFromInUseByIndex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddRemoveListenerCertificates_MaintainInUseByIndex(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-idx7", "idx-lb7")
|
||||
|
||||
@@ -186,11 +193,13 @@ func TestAddRemoveListenerCertificates_MaintainInUseByIndex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateStoredConfigForCert_ZeroInUseBy_NoOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
require.NoError(t, svc.UpdateStoredConfigForCert(context.Background(), testCertArn))
|
||||
}
|
||||
|
||||
func TestUpdateStoredConfigForCert_UnknownCert_NoOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
require.NoError(t, svc.UpdateStoredConfigForCert(context.Background(), "arn:aws:acm:ap-southeast-2:000000000001:certificate/does-not-exist"))
|
||||
}
|
||||
@@ -200,6 +209,7 @@ func TestUpdateStoredConfigForCert_UnknownCert_NoOp(t *testing.T) {
|
||||
// LB was deleted out from under the index, and one whose LB exists but never
|
||||
// went active (empty InstanceID, so updateStoredConfig itself no-ops).
|
||||
func TestUpdateStoredConfigForCert_SkipsMissingOrInactiveLB(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
require.NoError(t, svc.acmStore.AddInUseBy(context.Background(), testCertArn,
|
||||
@@ -233,6 +243,7 @@ func clearInUseBy(t *testing.T, svc *ELBv2ServiceImpl, certArn, lbArn string) {
|
||||
}
|
||||
|
||||
func TestReconcileCertInUseIndex_BackfillsPreExistingListener(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-rec1", "rec-lb1")
|
||||
|
||||
@@ -249,6 +260,7 @@ func TestReconcileCertInUseIndex_BackfillsPreExistingListener(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReconcileCertInUseIndex_IsIdempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-rec2", "rec-lb2")
|
||||
|
||||
@@ -265,6 +277,7 @@ func TestReconcileCertInUseIndex_IsIdempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReconcileCertInUseIndex_RemovesEntryWithNoListener(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-rec3", "rec-lb3")
|
||||
|
||||
@@ -279,6 +292,7 @@ func TestReconcileCertInUseIndex_RemovesEntryWithNoListener(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReconcileCertInUseIndex_RemovesEntryForDeletedLB(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
require.NoError(t, svc.acmStore.AddInUseBy(context.Background(), testCertArn,
|
||||
@@ -292,6 +306,7 @@ func TestReconcileCertInUseIndex_RemovesEntryForDeletedLB(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReconcileCertInUseIndex_TwoListenersSameLB_SingleEntry(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb := activeLB(t, svc, "lb-rec4", "rec-lb4")
|
||||
|
||||
@@ -311,6 +326,7 @@ func TestReconcileCertInUseIndex_TwoListenersSameLB_SingleEntry(t *testing.T) {
|
||||
// A nil acmStore means HTTPS is unavailable, not that the index is broken, so
|
||||
// the reconcile has to no-op rather than panic on daemon startup.
|
||||
func TestReconcileCertInUseIndex_NilACMStore_NoOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.NoError(t, (&ELBv2ServiceImpl{}).ReconcileCertInUseIndex(context.Background()))
|
||||
}
|
||||
|
||||
@@ -319,6 +335,7 @@ func TestReconcileCertInUseIndex_NilACMStore_NoOp(t *testing.T) {
|
||||
// re-import reaches no load balancer and the served leaf goes stale silently.
|
||||
// After a reconcile the same re-import must fan out again.
|
||||
func TestReconcileCertInUseIndex_RestoresRenewalFanOut(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
|
||||
masterKey, err := handlers_iam.GenerateMasterKey()
|
||||
@@ -416,6 +433,7 @@ func genLeafCertPEM(t *testing.T, cn string, dnsNames ...string) (certPEM, keyPE
|
||||
// CreateListener path (which builds the InUseBy index), and the real
|
||||
// UpdateStoredConfigForCert fan-out.
|
||||
func TestACMReimport_FansOutToInUseLoadBalancer(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
|
||||
// Both services read the same bucket, so they must share one key or the
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
// --- DescribeLoadBalancers: ARN filter (service_impl_test.go covers FilterByName) ---
|
||||
|
||||
func TestDescribeLoadBalancers_FilterByArn(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
out1, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("lb-arn-a")}, testAccountID)
|
||||
@@ -32,6 +33,7 @@ func TestDescribeLoadBalancers_FilterByArn(t *testing.T) {
|
||||
// --- DescribeTargetGroups: by name and by ARN (service_impl_test.go only covers FilterByLBArn) ---
|
||||
|
||||
func TestDescribeTargetGroups_FilterByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
_, _ = svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{Name: aws.String("tg-alpha")}, testAccountID)
|
||||
@@ -46,6 +48,7 @@ func TestDescribeTargetGroups_FilterByName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeTargetGroups_FilterByArn(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
out1, _ := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{Name: aws.String("tg-arn-a")}, testAccountID)
|
||||
@@ -62,6 +65,7 @@ func TestDescribeTargetGroups_FilterByArn(t *testing.T) {
|
||||
// --- DescribeListeners: filter by listener ARN (service_impl_test.go covers FilterByLBArn) ---
|
||||
|
||||
func TestDescribeListeners_FilterByListenerArn(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
lb, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("lb-larn")}, testAccountID)
|
||||
@@ -89,6 +93,7 @@ func TestDescribeListeners_FilterByListenerArn(t *testing.T) {
|
||||
// --- RegisterTargets edge cases ---
|
||||
|
||||
func TestRegisterTargets_NilTargetSkipped(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
tg, _ := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{Name: aws.String("tg-nil"), Port: aws.Int64(80)}, testAccountID)
|
||||
@@ -110,6 +115,7 @@ func TestRegisterTargets_NilTargetSkipped(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRegisterTargets_TGNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
_, err := svc.RegisterTargets(context.Background(), &elbv2.RegisterTargetsInput{
|
||||
TargetGroupArn: aws.String("arn:nonexistent"),
|
||||
@@ -119,6 +125,7 @@ func TestRegisterTargets_TGNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeregisterTargets_TGNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
_, err := svc.DeregisterTargets(context.Background(), &elbv2.DeregisterTargetsInput{
|
||||
TargetGroupArn: aws.String("arn:nonexistent"),
|
||||
@@ -129,6 +136,7 @@ func TestDeregisterTargets_TGNotFound(t *testing.T) {
|
||||
// --- Missing-ARN error paths across the service surface ---
|
||||
|
||||
func TestMissingArnReturnsError(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
tests := []struct {
|
||||
@@ -198,6 +206,7 @@ func TestMissingArnReturnsError(t *testing.T) {
|
||||
// --- CreateListener: missing actions (separate from missing-ARN path) ---
|
||||
|
||||
func TestCreateListener_MissingActions(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lb, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("lb-noact")}, testAccountID)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestLBFrontendIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
// internet-facing → the AZ-0 public IP.
|
||||
inet := &LoadBalancerRecord{
|
||||
Scheme: SchemeInternetFacing,
|
||||
@@ -35,6 +36,7 @@ func TestLBFrontendIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPublishLBDNS_NoopWhenDisabled(t *testing.T) {
|
||||
t.Parallel()
|
||||
rec := &LoadBalancerRecord{
|
||||
Scheme: SchemeInternal,
|
||||
DNSName: "internal-web-abc.ap-southeast-2.elb.spx3.net",
|
||||
@@ -48,6 +50,7 @@ func TestPublishLBDNS_NoopWhenDisabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDesiredDNSChanges_IncludesEndpointReadyProvisioningLoadBalancers(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
svc.dnsBaseDomain = "spx3.net"
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ func httpsListener() *ListenerRecord {
|
||||
}
|
||||
|
||||
func TestGenerateHAProxyConfigWithCerts_HTTPSRendersSslCrt(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-abc123", Name: "my-alb"}
|
||||
listeners := []*ListenerRecord{httpsListener()}
|
||||
certPEM := "CERTPEM\nKEYPEM\n"
|
||||
@@ -56,6 +57,7 @@ func TestGenerateHAProxyConfigWithCerts_HTTPSRendersSslCrt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateHAProxyConfigWithCerts_HTTPHasNoSslCrt(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-abc123", Name: "my-alb"}
|
||||
listeners := []*ListenerRecord{{
|
||||
ListenerArn: "arn:aws:elasticloadbalancing:us-east-1:123:listener/app/my-alb/lb-abc123/lst-http",
|
||||
@@ -75,6 +77,7 @@ func TestGenerateHAProxyConfigWithCerts_HTTPHasNoSslCrt(t *testing.T) {
|
||||
// A secure listener whose cert ARN is unresolved renders without ssl crt — the
|
||||
// gap surfaces at HAProxy bind time rather than silently serving cleartext.
|
||||
func TestGenerateHAProxyConfigWithCerts_MissingCertRendersPlain(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-abc123", Name: "my-alb"}
|
||||
config, certFiles, err := GenerateHAProxyConfigWithCerts(
|
||||
lb, []*ListenerRecord{httpsListener()}, nil, nil, "0.0.0.0", nil,
|
||||
@@ -85,6 +88,7 @@ func TestGenerateHAProxyConfigWithCerts_MissingCertRendersPlain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigCertHash_ChangesOnRotation(t *testing.T) {
|
||||
t.Parallel()
|
||||
const cfg = "frontend ft\n bind *:443 ssl crt /etc/haproxy/certs/x.pem\n"
|
||||
path := "/etc/haproxy/certs/x.pem"
|
||||
|
||||
@@ -112,6 +116,7 @@ func putTestCert(t *testing.T, svc *ELBv2ServiceImpl, arn, account, leaf, chain,
|
||||
}
|
||||
|
||||
func TestResolveCertPEM_ConcatOrderAndOwnership(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
require.NotNil(t, svc.acmStore)
|
||||
putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "CHAIN", "KEY")
|
||||
@@ -139,6 +144,7 @@ func TestResolveCertPEM_ConcatOrderAndOwnership(t *testing.T) {
|
||||
// ACMServiceImpl and ELBv2ServiceImpl each open their own Store over the
|
||||
// same JetStream bucket and must agree on the same key.
|
||||
func TestResolveCertPEM_CrossesACMServiceKeyBoundary(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
|
||||
masterKey, err := handlers_iam.GenerateMasterKey()
|
||||
@@ -175,6 +181,7 @@ func TestResolveCertPEM_CrossesACMServiceKeyBoundary(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateListenerCerts_RejectsUnknownAndWrongAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "", "KEY")
|
||||
|
||||
@@ -199,6 +206,7 @@ func TestValidateListenerCerts_RejectsUnknownAndWrongAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveCertPEM_NoChain(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "", "KEY")
|
||||
|
||||
@@ -208,6 +216,7 @@ func TestResolveCertPEM_NoChain(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveListenerCerts_DedupAndResolve(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
putTestCert(t, svc, certTestArn, testAccountID, "LEAF", "", "KEY")
|
||||
|
||||
@@ -224,6 +233,7 @@ func TestResolveListenerCerts_DedupAndResolve(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetLBConfig_DeliversCertFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
rec := &LoadBalancerRecord{
|
||||
LoadBalancerID: "lb-deliver1",
|
||||
@@ -258,6 +268,7 @@ func TestGetLBConfig_DeliversCertFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCertFilesToSDK_SortedAndNilSafe(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Nil(t, certFilesToSDK(nil))
|
||||
|
||||
out := certFilesToSDK(map[string]string{"/b.pem": "B", "/a.pem": "A"})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestGenerateHAProxyConfig_SingleListenerAndBackend(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{
|
||||
LoadBalancerID: "lb-abc123",
|
||||
Name: "my-alb",
|
||||
@@ -68,6 +69,7 @@ func TestGenerateHAProxyConfig_SingleListenerAndBackend(t *testing.T) {
|
||||
// own check and proxied traffic both speak TLS. Without it the check fails and
|
||||
// the server is marked DOWN, returning 503.
|
||||
func TestGenerateHAProxyConfig_HTTPSBackendReencrypts(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-tls"}
|
||||
tgArn := "arn:aws:elasticloadbalancing:us-east-1:123:targetgroup/tls-tg/tg-tls"
|
||||
listeners := []*ListenerRecord{
|
||||
@@ -98,6 +100,7 @@ func TestGenerateHAProxyConfig_HTTPSBackendReencrypts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateHAProxyConfig_MultipleListeners(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-multi"}
|
||||
|
||||
tgArn1 := "arn:tg1"
|
||||
@@ -141,6 +144,7 @@ func TestGenerateHAProxyConfig_MultipleListeners(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateHAProxyConfig_SkipsDrainingTargets(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-drain"}
|
||||
tgArn := "arn:tg-drain"
|
||||
|
||||
@@ -172,6 +176,7 @@ func TestGenerateHAProxyConfig_SkipsDrainingTargets(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateHAProxyConfig_SharedTargetGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-shared"}
|
||||
tgArn := "arn:shared-tg"
|
||||
|
||||
@@ -199,6 +204,7 @@ func TestGenerateHAProxyConfig_SharedTargetGroup(t *testing.T) {
|
||||
// with host-header rules forwarding to target groups. The generator must synthesize a
|
||||
// default backend; a dangling `default_backend bk_` makes HAProxy fail to start.
|
||||
func TestGenerateHAProxyConfig_FixedResponseDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-ingress"}
|
||||
listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/wd-ingress/lb-ingress/lst-616760baf6a3031c3"
|
||||
appTG := "arn:aws:elasticloadbalancing:ap-southeast-2:1:targetgroup/wd-identity/tg-app1"
|
||||
@@ -255,6 +261,7 @@ func TestGenerateHAProxyConfig_FixedResponseDefault(t *testing.T) {
|
||||
// TestGenerateHAProxyConfig_FixedResponseRejectsUnsafeBody falls back to a bare
|
||||
// status when the body or content-type contains injection bytes.
|
||||
func TestGenerateHAProxyConfig_FixedResponseRejectsUnsafeBody(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-evil"}
|
||||
listeners := []*ListenerRecord{
|
||||
{
|
||||
@@ -282,6 +289,7 @@ func TestGenerateHAProxyConfig_FixedResponseRejectsUnsafeBody(t *testing.T) {
|
||||
// HTTP→HTTPS redirect (scheme-only change, all other fields default) as a
|
||||
// `redirect scheme https` directive so HAProxy preserves host/path/query.
|
||||
func TestGenerateHAProxyConfig_RedirectSchemeDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-redir"}
|
||||
listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-redir/lst-aa"
|
||||
listeners := []*ListenerRecord{
|
||||
@@ -312,6 +320,7 @@ func TestGenerateHAProxyConfig_RedirectSchemeDefault(t *testing.T) {
|
||||
// TestGenerateHAProxyConfig_RedirectLocation renders a redirect that changes
|
||||
// the host as a rebuilt `location` with HAProxy placeholders preserved.
|
||||
func TestGenerateHAProxyConfig_RedirectLocation(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-redir2"}
|
||||
listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-redir2/lst-bb"
|
||||
listeners := []*ListenerRecord{
|
||||
@@ -341,6 +350,7 @@ func TestGenerateHAProxyConfig_RedirectLocation(t *testing.T) {
|
||||
// a placeholder protocol (resolved from the listener), an explicit port, and a
|
||||
// custom literal path + query.
|
||||
func TestGenerateHAProxyConfig_RedirectCustomAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-redir3"}
|
||||
listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-redir3/lst-dd"
|
||||
listeners := []*ListenerRecord{
|
||||
@@ -370,6 +380,7 @@ func TestGenerateHAProxyConfig_RedirectCustomAll(t *testing.T) {
|
||||
// TestGenerateHAProxyConfig_RedirectRule renders a per-rule redirect action via
|
||||
// a synthetic backend reached by use_backend.
|
||||
func TestGenerateHAProxyConfig_RedirectRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-rr"}
|
||||
listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:1:listener/app/r/lb-rr/lst-cc"
|
||||
appTG := "arn:aws:elasticloadbalancing:ap-southeast-2:1:targetgroup/app/tg-app"
|
||||
@@ -412,6 +423,7 @@ func TestGenerateHAProxyConfig_RedirectRule(t *testing.T) {
|
||||
// TestGenerateHAProxyConfig_RedirectRejectsInjection fails the render when a
|
||||
// redirect field carries HAProxy meta-characters that survived to the store.
|
||||
func TestGenerateHAProxyConfig_RedirectRejectsInjection(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-evil2"}
|
||||
listeners := []*ListenerRecord{
|
||||
{
|
||||
@@ -432,6 +444,7 @@ func TestGenerateHAProxyConfig_RedirectRejectsInjection(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateHAProxyConfig_NoListeners(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-empty"}
|
||||
config, _, err := GenerateHAProxyConfigWithCerts(lb, nil, nil, nil, "0.0.0.0", nil)
|
||||
require.NoError(t, err)
|
||||
@@ -444,6 +457,7 @@ func TestGenerateHAProxyConfig_NoListeners(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSanitizeName(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
prefix string
|
||||
input string
|
||||
@@ -464,6 +478,7 @@ func TestSanitizeName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateALBConfig_StillWorks(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Regression: ALB (no Type set) should still generate HTTP-mode config
|
||||
lb := &LoadBalancerRecord{
|
||||
LoadBalancerID: "lb-alb-regression",
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
// --- evaluateHealth ---
|
||||
|
||||
func TestEvaluateHealth_InitialToHealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveHealthy: 1}
|
||||
|
||||
@@ -22,6 +23,7 @@ func TestEvaluateHealth_InitialToHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_InitialToUnhealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveUnhealthy: cfg.UnhealthyThreshold}
|
||||
|
||||
@@ -30,6 +32,7 @@ func TestEvaluateHealth_InitialToUnhealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_InitialStaysInitial(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveUnhealthy: 1} // below threshold
|
||||
|
||||
@@ -38,6 +41,7 @@ func TestEvaluateHealth_InitialStaysInitial(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_HealthyToUnhealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveUnhealthy: cfg.UnhealthyThreshold}
|
||||
|
||||
@@ -46,6 +50,7 @@ func TestEvaluateHealth_HealthyToUnhealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_HealthyStaysHealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveUnhealthy: 1} // below threshold
|
||||
|
||||
@@ -54,6 +59,7 @@ func TestEvaluateHealth_HealthyStaysHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_UnhealthyToHealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveHealthy: cfg.HealthyThreshold}
|
||||
|
||||
@@ -62,6 +68,7 @@ func TestEvaluateHealth_UnhealthyToHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_UnhealthyStaysUnhealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveHealthy: cfg.HealthyThreshold - 1}
|
||||
|
||||
@@ -70,6 +77,7 @@ func TestEvaluateHealth_UnhealthyStaysUnhealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_DrainingUnchanged(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultHealthCheck()
|
||||
ctr := &targetCounter{consecutiveHealthy: 100}
|
||||
|
||||
@@ -111,6 +119,7 @@ func setupLBWithTG(t *testing.T, store *Store, lbID string, tg *TargetGroupRecor
|
||||
}
|
||||
|
||||
func TestHandleHealthReport_TransitionsInitialToHealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
|
||||
hc := newHealthChecker(store)
|
||||
@@ -141,6 +150,7 @@ func TestHandleHealthReport_TransitionsInitialToHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleHealthReport_UnhealthyAfterThreshold(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
|
||||
hc := newHealthChecker(store)
|
||||
@@ -179,6 +189,7 @@ func TestHandleHealthReport_UnhealthyAfterThreshold(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleHealthReport_SkipsDrainingTargets(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
|
||||
hc := newHealthChecker(store)
|
||||
@@ -209,6 +220,7 @@ func TestHandleHealthReport_SkipsDrainingTargets(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveTarget(t *testing.T) {
|
||||
t.Parallel()
|
||||
hc := newHealthChecker(nil)
|
||||
|
||||
hc.mu.Lock()
|
||||
@@ -224,6 +236,7 @@ func TestRemoveTarget(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleHealthReport_TargetPortZeroUsesTGPort(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
hc := newHealthChecker(store)
|
||||
|
||||
@@ -259,6 +272,7 @@ func TestHandleHealthReport_TargetPortZeroUsesTGPort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleHealthReportDirect_TransitionsInitialToHealthy(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
hc := newHealthChecker(store)
|
||||
|
||||
@@ -288,6 +302,7 @@ func TestHandleHealthReportDirect_TransitionsInitialToHealthy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleHealthReport_OnlyProcessesTGsForReportingLB(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
hc := newHealthChecker(store)
|
||||
|
||||
@@ -335,6 +350,7 @@ func TestHandleHealthReport_OnlyProcessesTGsForReportingLB(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHandleHealthReport_FallbackListTargetGroups(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
hc := newHealthChecker(store)
|
||||
|
||||
@@ -366,6 +382,7 @@ func TestHandleHealthReport_FallbackListTargetGroups(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEvaluateHealth_ZeroThresholdsUsesDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := HealthCheckConfig{} // all zeros
|
||||
|
||||
// Unhealthy→healthy should require DefaultHealthyThreshold consecutive healthy
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
// the lazy IAMProvider when the eager IAM field is unset — the daemon wires only
|
||||
// the provider so the IAM build cannot race the NATS KV backend at startup.
|
||||
func TestEnsureLBInstanceProfile_UsesLazyProvider(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := iammock.New()
|
||||
s := &ELBv2ServiceImpl{IAMProvider: func() handlers_iam.SystemInstanceRoleEnsurer { return f }}
|
||||
|
||||
@@ -25,6 +26,7 @@ func TestEnsureLBInstanceProfile_UsesLazyProvider(t *testing.T) {
|
||||
// resolves the profile under the instance's account, so a role created in the LB
|
||||
// owner account would be invisible to it and the lb-agent would get no creds.
|
||||
func TestEnsureLBInstanceProfile_CreatesInSystemAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := iammock.New()
|
||||
s := &ELBv2ServiceImpl{IAM: f}
|
||||
|
||||
@@ -37,6 +39,7 @@ func TestEnsureLBInstanceProfile_CreatesInSystemAccount(t *testing.T) {
|
||||
// provider returns nil because the KV backend is not up yet) yields "" so the LB
|
||||
// VM falls back to baked static creds and retries on the next launch.
|
||||
func TestEnsureLBInstanceProfile_NotReadyFallsBack(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := &ELBv2ServiceImpl{IAMProvider: func() handlers_iam.SystemInstanceRoleEnsurer { return nil }}
|
||||
assert.Empty(t, s.ensureLBInstanceProfile(utils.GlobalAccountID))
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
// deleting an absent ELBv2 resource is success, not NotFound. Every delete endpoint
|
||||
// must have a case here — a missing case is an idempotency gap.
|
||||
func TestRLC1_ELBv2DeleteIdempotentOnAbsent(t *testing.T) {
|
||||
t.Parallel()
|
||||
const arnBase = "arn:aws:elasticloadbalancing:us-east-1:123456789012:"
|
||||
|
||||
cases := []struct {
|
||||
@@ -59,6 +60,7 @@ func TestRLC1_ELBv2DeleteIdempotentOnAbsent(t *testing.T) {
|
||||
// after DeleteLoadBalancer no listener or rule owned by the LB may remain. Two
|
||||
// listeners are used so a single-listener cascade can't pass by accident.
|
||||
func TestRLC2_ELBv2NoOrphanAfterDeleteLB(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
lbOut, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("rlc2-lb")}, testAccountID)
|
||||
@@ -103,6 +105,7 @@ func TestRLC2_ELBv2NoOrphanAfterDeleteLB(t *testing.T) {
|
||||
// cascade: DeleteLoadBalancer must use deleteListenerCascade, not store.DeleteListener
|
||||
// directly (a direct call orphans rules). Enforced structurally against source.
|
||||
func TestRLC3_ELBv2DeleteLBDoesNotBypassListenerCascade(t *testing.T) {
|
||||
t.Parallel()
|
||||
src, err := os.ReadFile("service_impl.go")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -145,6 +148,7 @@ func stripComments(src string) string {
|
||||
// after LB+listener+rule teardown, the target group must not remain pinned as
|
||||
// ResourceInUse by an orphaned rule or stale listener default action.
|
||||
func TestRLC4_ELBv2TGDeletableAfterLBTeardown(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
lbOut, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("rlc4-lb")}, testAccountID)
|
||||
@@ -185,6 +189,7 @@ func TestRLC4_ELBv2TGDeletableAfterLBTeardown(t *testing.T) {
|
||||
// trap this guard prevents. Locks the liveLB/liveListener skip both ways so a
|
||||
// maintainer cannot regress the in-use scan back to a global rule sweep.
|
||||
func TestRLC3_ELBv2TGInUseGuardGatesOnLiveRefsOnly(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
// Orphan-rule TG: forwarded to by a rule whose owning LB/listener is gone.
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
// one success and one DuplicateLoadBalancerName. The atomic name claim is the
|
||||
// barrier; under -race a double-claim would surface as a launcher/store race.
|
||||
func TestCreateLoadBalancer_ConcurrentSameNameSingleOwner(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
const n = 2
|
||||
@@ -55,6 +56,7 @@ func TestCreateLoadBalancer_ConcurrentSameNameSingleOwner(t *testing.T) {
|
||||
// window: a claim with no persisted LB record yet — the gap a real create
|
||||
// leaves open — must not be stealable by a second claimer within the TTL.
|
||||
func TestClaimLBName_PendingClaimBlocksSecondClaimer(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
ok1, dup1, err := svc.store.ClaimLBName(t.Context(), "pending-lb", testAccountID, "lb-first")
|
||||
@@ -74,6 +76,7 @@ func TestClaimLBName_PendingClaimBlocksSecondClaimer(t *testing.T) {
|
||||
// crashed prior create. A fresh CreateLoadBalancer must reclaim it and succeed.
|
||||
// The injectable clock ages the claim so the test never sleeps.
|
||||
func TestCreateLoadBalancer_ReclaimsCrashOrphanNameClaim(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
// Seed a name claim pointing at a non-existent LB (crashed create left no
|
||||
@@ -97,6 +100,7 @@ func TestCreateLoadBalancer_ReclaimsCrashOrphanNameClaim(t *testing.T) {
|
||||
// Deleting a load balancer releases its name claim so the name is immediately
|
||||
// reusable.
|
||||
func TestDeleteLoadBalancer_ReleasesNameForReuse(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
out, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
@@ -121,6 +125,7 @@ func TestDeleteLoadBalancer_ReleasesNameForReuse(t *testing.T) {
|
||||
// provisioning on a nonexistent subnet) must release the claim on its way out, so
|
||||
// a retry with the same name is not permanently locked out by the failed attempt.
|
||||
func TestCreateLoadBalancer_FailureAfterClaimReleasesNameForRetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _ := setupTestServiceWithVPC(t)
|
||||
|
||||
_, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
|
||||
@@ -42,6 +42,7 @@ func nlbTG(arn string, targets ...Target) *TargetGroupRecord {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_TCP(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-tcp", Type: LoadBalancerTypeNetwork}
|
||||
tgArn := "arn:aws:elasticloadbalancing:us-east-1:1:targetgroup/db/tg-db"
|
||||
listeners := []*ListenerRecord{tcpListener("arn:lst-tcp", "lst-tcp", ProtocolTCP, 5432, tgArn)}
|
||||
@@ -67,6 +68,7 @@ func TestGenerateNLBStream_TCP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_UDP(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-udp", Type: LoadBalancerTypeNetwork}
|
||||
tgArn := "arn:tg-dns"
|
||||
listeners := []*ListenerRecord{tcpListener("arn:lst-udp", "lst-udp", ProtocolUDP, 53, tgArn)}
|
||||
@@ -83,6 +85,7 @@ func TestGenerateNLBStream_UDP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_TLS(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-tls", Type: LoadBalancerTypeNetwork}
|
||||
tgArn := "arn:tg-tls"
|
||||
lst := tcpListener("arn:lst-tls", "lst-tls", ProtocolTLS, 443, tgArn)
|
||||
@@ -104,6 +107,7 @@ func TestGenerateNLBStream_TLS(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_TCPUDP(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-both", Type: LoadBalancerTypeNetwork}
|
||||
tgArn := "arn:tg-both"
|
||||
listeners := []*ListenerRecord{tcpListener("arn:lst-both", "lst-both", ProtocolTCPUDP, 5000, tgArn)}
|
||||
@@ -121,6 +125,7 @@ func TestGenerateNLBStream_TCPUDP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_SkipsDrainingAndNoIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-drain", Type: LoadBalancerTypeNetwork}
|
||||
tgArn := "arn:tg-drain"
|
||||
listeners := []*ListenerRecord{tcpListener("arn:lst-drain", "lst-drain", ProtocolTCP, 3306, tgArn)}
|
||||
@@ -140,6 +145,7 @@ func TestGenerateNLBStream_SkipsDrainingAndNoIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_EmptyUpstreamPlaceholder(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb-empty", Type: LoadBalancerTypeNetwork}
|
||||
tgArn := "arn:tg-empty"
|
||||
listeners := []*ListenerRecord{tcpListener("arn:lst-empty", "lst-empty", ProtocolTCP, 80, tgArn)}
|
||||
@@ -153,6 +159,7 @@ func TestGenerateNLBStream_EmptyUpstreamPlaceholder(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildNLBHealthTargets_Resolution(t *testing.T) {
|
||||
t.Parallel()
|
||||
tgArn := "arn:aws:elasticloadbalancing:us-east-1:1:targetgroup/db/tg-db"
|
||||
tgByArn := map[string]*TargetGroupRecord{
|
||||
tgArn: nlbTG(tgArn,
|
||||
@@ -174,6 +181,7 @@ func TestBuildNLBHealthTargets_Resolution(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildNLBHealthTargets_ExplicitHCPortAndHTTP(t *testing.T) {
|
||||
t.Parallel()
|
||||
tgArn := "arn:tg-http"
|
||||
tg := nlbTG(tgArn, Target{Id: "i-1", Port: 8080, PrivateIP: "10.0.0.9", HealthState: TargetHealthHealthy})
|
||||
tg.HealthCheck.Protocol = ProtocolHTTP
|
||||
@@ -215,6 +223,7 @@ func combinedPEM(t *testing.T, cn string, dnsNames ...string) string {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_SNICertGetsItsOwnMapEntry(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb, lst, tgByArn := nlbTLSFixture("lst-sni", "arn:cert-1", "arn:cert-2")
|
||||
certPEM := map[string]string{
|
||||
"arn:cert-1": combinedPEM(t, "default.example.com", "default.example.com"),
|
||||
@@ -244,6 +253,7 @@ func TestGenerateNLBStream_SNICertGetsItsOwnMapEntry(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateNLBStream_WildcardSANBecomesMapKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb, lst, tgByArn := nlbTLSFixture("lst-wild", "arn:cert-1", "arn:cert-2")
|
||||
certPEM := map[string]string{
|
||||
"arn:cert-1": combinedPEM(t, "example.com", "example.com"),
|
||||
@@ -259,6 +269,7 @@ func TestGenerateNLBStream_WildcardSANBecomesMapKey(t *testing.T) {
|
||||
// A single certificate must keep rendering a literal path: a variable makes nginx
|
||||
// re-read the PEM on every handshake.
|
||||
func TestGenerateNLBStream_SingleCertRendersLiteralPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb, lst, tgByArn := nlbTLSFixture("lst-one", "arn:cert-1")
|
||||
certPEM := map[string]string{"arn:cert-1": combinedPEM(t, "example.com", "example.com")}
|
||||
|
||||
@@ -272,6 +283,7 @@ func TestGenerateNLBStream_SingleCertRendersLiteralPath(t *testing.T) {
|
||||
// The default certificate already answers unmatched names, so a second cert that
|
||||
// only repeats them cannot be selected — and its key must not be shipped.
|
||||
func TestGenerateNLBStream_UnselectableSNICertNotDelivered(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb, lst, tgByArn := nlbTLSFixture("lst-dup", "arn:cert-1", "arn:cert-2")
|
||||
certPEM := map[string]string{
|
||||
"arn:cert-1": combinedPEM(t, "example.com", "example.com"),
|
||||
@@ -289,6 +301,7 @@ func TestGenerateNLBStream_UnselectableSNICertNotDelivered(t *testing.T) {
|
||||
// A subject is caller-supplied on import, so a name that could break the config
|
||||
// must never reach a map key.
|
||||
func TestCertDNSNames_DropsNamesThatAreNotHostnames(t *testing.T) {
|
||||
t.Parallel()
|
||||
pemText := combinedPEM(t, "ok.example.com", "ok.example.com", "bad name; }", "-leading-hyphen.com", "UPPER.example.com")
|
||||
|
||||
assert.Equal(t, []string{"ok.example.com", "upper.example.com"}, certDNSNames(pemText))
|
||||
|
||||
@@ -23,6 +23,7 @@ func testLBRecord(id, state string, createdAt, lastHeartbeat time.Time) *LoadBal
|
||||
}
|
||||
|
||||
func TestReapStuckProvisioningLBs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
now := time.Now().UTC()
|
||||
|
||||
@@ -61,6 +62,7 @@ func TestReapStuckProvisioningLBs(t *testing.T) {
|
||||
// TestStartLifecycleReaper drives the ticker goroutine with a tiny interval and
|
||||
// confirms it reaps a stuck LB, then cancels to exercise the ctx.Done exit.
|
||||
func TestStartLifecycleReaper(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
svc.reaperInterval = 5 * time.Millisecond
|
||||
|
||||
@@ -74,7 +76,7 @@ func TestStartLifecycleReaper(t *testing.T) {
|
||||
require.Eventually(t, func() bool {
|
||||
got, err := svc.store.GetLoadBalancer(t.Context(), stuck.LoadBalancerID)
|
||||
return err == nil && got != nil && got.State == StateFailed
|
||||
}, time.Second, 10*time.Millisecond, "reaper goroutine should mark the stuck LB failed")
|
||||
}, 3*time.Second, 10*time.Millisecond, "reaper goroutine should mark the stuck LB failed")
|
||||
|
||||
cancel()
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ func setupTestServiceWithInstance(t *testing.T, instanceID, instanceIP string) (
|
||||
}
|
||||
|
||||
func TestRegisterTargets_ResolvesPrivateIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _, _ := setupTestServiceWithInstance(t, "i-web001", "10.0.1.50")
|
||||
|
||||
// Create target group
|
||||
@@ -91,6 +92,7 @@ func TestRegisterTargets_ResolvesPrivateIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRegisterTargets_UnresolvableIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _, _ := setupTestServiceWithInstance(t, "i-web001", "10.0.1.50")
|
||||
|
||||
tgOut, _ := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
@@ -112,6 +114,7 @@ func TestRegisterTargets_UnresolvableIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRegisterTargets_WithoutVPCService(t *testing.T) {
|
||||
t.Parallel()
|
||||
// When VPC service is nil, IP resolution is skipped gracefully
|
||||
svc := setupTestService(t)
|
||||
|
||||
@@ -132,6 +135,7 @@ func TestRegisterTargets_WithoutVPCService(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRegisterTargets_MultipleInstances(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, _ := setupTestServiceWithInstance(t, "i-web001", "10.0.1.50")
|
||||
|
||||
// Create a second instance ENI
|
||||
@@ -169,6 +173,7 @@ func TestRegisterTargets_MultipleInstances(t *testing.T) {
|
||||
// startup resets non-draining targets to "initial", preventing stale "healthy"
|
||||
// claims before the lb-agent has posted a fresh report after a daemon restart.
|
||||
func TestResetTargetHealthOnStartup_TransitionsAllNonDrainingToInitial(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestNATS(t)
|
||||
svc := &ELBv2ServiceImpl{store: store}
|
||||
|
||||
@@ -204,6 +209,7 @@ func TestResetTargetHealthOnStartup_TransitionsAllNonDrainingToInitial(t *testin
|
||||
}
|
||||
|
||||
func TestResetTargetHealthOnStartup_NilSafe(t *testing.T) {
|
||||
t.Parallel()
|
||||
var svc *ELBv2ServiceImpl
|
||||
assert.NoError(t, svc.ResetTargetHealthOnStartup(t.Context()))
|
||||
|
||||
@@ -214,6 +220,7 @@ func TestResetTargetHealthOnStartup_NilSafe(t *testing.T) {
|
||||
// A target group not forwarded to by any listener serves no traffic, so its
|
||||
// targets report "unused" (Target.NotInUse) instead of stalling in "initial".
|
||||
func TestDescribeTargetHealth_UnusedWhenNoListener(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _, _ := setupTestServiceWithInstance(t, "i-web001", "10.0.1.50")
|
||||
|
||||
tgOut, err := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
@@ -239,6 +246,7 @@ func TestDescribeTargetHealth_UnusedWhenNoListener(t *testing.T) {
|
||||
// Once a listener forwards to the target group it is in use, so DescribeTargetHealth
|
||||
// reports the live health state ("initial" until a health report lands), not "unused".
|
||||
func TestDescribeTargetHealth_InUseWhenListenerForwards(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _, _ := setupTestServiceWithInstance(t, "i-web001", "10.0.1.50")
|
||||
|
||||
tgOut, err := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
|
||||
@@ -35,6 +35,7 @@ func describeLB(t *testing.T, svc *ELBv2ServiceImpl, arn string) *elbv2.LoadBala
|
||||
}
|
||||
|
||||
func TestSetIpAddressType_IPv4Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "ipt-lb")
|
||||
|
||||
@@ -48,6 +49,7 @@ func TestSetIpAddressType_IPv4Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetIpAddressType_DualstackRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "ipt-ds")
|
||||
|
||||
@@ -60,6 +62,7 @@ func TestSetIpAddressType_DualstackRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetIpAddressType_MissingParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "ipt-mp")
|
||||
|
||||
@@ -77,6 +80,7 @@ func TestSetIpAddressType_MissingParams(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetIpAddressType_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
_, err := svc.SetIpAddressType(context.Background(), &elbv2.SetIpAddressTypeInput{
|
||||
LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-deadbeef"),
|
||||
@@ -87,6 +91,7 @@ func TestSetIpAddressType_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSecurityGroups_UpdatesRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "sg-lb")
|
||||
|
||||
@@ -100,6 +105,7 @@ func TestSetSecurityGroups_UpdatesRecord(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSecurityGroups_EmptyRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "sg-empty")
|
||||
|
||||
@@ -111,6 +117,7 @@ func TestSetSecurityGroups_EmptyRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSecurityGroups_NLBRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
out, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
Name: aws.String("sg-nlb"),
|
||||
@@ -128,6 +135,7 @@ func TestSetSecurityGroups_NLBRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSecurityGroups_NLBWithSGs_Replaces(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
out, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
Name: aws.String("sg-nlb-repl"),
|
||||
@@ -150,6 +158,7 @@ func TestSetSecurityGroups_NLBWithSGs_Replaces(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSecurityGroups_TooManyRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "sg-too-many")
|
||||
|
||||
@@ -162,6 +171,7 @@ func TestSetSecurityGroups_TooManyRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSecurityGroups_CrossAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "sg-xacct")
|
||||
|
||||
@@ -187,6 +197,7 @@ func frontendPublicIP(lb *elbv2.LoadBalancer) string {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancerSync_ReturnsFrontendIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
mock.launchResult.PublicIP = "203.0.113.9"
|
||||
vid := vpcID(t, vpcSvc)
|
||||
@@ -207,6 +218,7 @@ func TestCreateLoadBalancerSync_ReturnsFrontendIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_AsyncDefersFrontendIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
mock.launchResult.PublicIP = "203.0.113.9"
|
||||
vid := vpcID(t, vpcSvc)
|
||||
@@ -265,6 +277,7 @@ func vpcID(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl) string {
|
||||
}
|
||||
|
||||
func TestSetSubnets_AddSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
vid := vpcID(t, vpcSvc)
|
||||
sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.20.0/24", "us-east-1a")
|
||||
@@ -298,6 +311,7 @@ func TestSetSubnets_AddSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_RemoveSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
vid := vpcID(t, vpcSvc)
|
||||
sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.22.0/24", "us-east-1a")
|
||||
@@ -332,6 +346,7 @@ func TestSetSubnets_RemoveSubnet(t *testing.T) {
|
||||
// yet caught up with the detach saw the ENI in use, logged, and left it behind.
|
||||
// An ENI still marked attached at delete time is that state, made deterministic.
|
||||
func TestSetSubnets_RemovedENIStillAttachedIsDeleted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, _ := setupSubnetTestService(t)
|
||||
vid := vpcID(t, vpcSvc)
|
||||
sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.30.0/24", "us-east-1a")
|
||||
@@ -370,6 +385,7 @@ func TestSetSubnets_RemovedENIStillAttachedIsDeleted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_Replace(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, _ := setupSubnetTestService(t)
|
||||
vid := vpcID(t, vpcSvc)
|
||||
sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.24.0/24", "us-east-1a")
|
||||
@@ -396,6 +412,7 @@ func TestSetSubnets_Replace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_TerminateFailureRollsBackNewENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
vid := vpcID(t, vpcSvc)
|
||||
sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.26.0/24", "us-east-1a")
|
||||
@@ -428,6 +445,7 @@ func TestSetSubnets_TerminateFailureRollsBackNewENIs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
vid := vpcID(t, vpcSvc)
|
||||
sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.26.0/24", "us-east-1a")
|
||||
@@ -453,6 +471,7 @@ func TestSetSubnets_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_SubnetMappings(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, _ := setupSubnetTestService(t)
|
||||
vid := vpcID(t, vpcSvc)
|
||||
sub1 := getTestSubnetID(t, vpcSvc, vid, "10.0.27.0/24", "us-east-1a")
|
||||
@@ -478,6 +497,7 @@ func TestSetSubnets_SubnetMappings(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_WithoutVPCService(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "novpc-subnets")
|
||||
|
||||
@@ -491,6 +511,7 @@ func TestSetSubnets_WithoutVPCService(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_MissingParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "subnets-mp")
|
||||
|
||||
@@ -508,6 +529,7 @@ func TestSetSubnets_MissingParams(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
_, err := svc.SetSubnets(context.Background(), &elbv2.SetSubnetsInput{
|
||||
LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-deadbeef"),
|
||||
@@ -518,6 +540,7 @@ func TestSetSubnets_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetSubnets_CrossAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createLBArn(t, svc, "subnets-xacct")
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ func createHTTPSListener(t *testing.T, svc *ELBv2ServiceImpl) string {
|
||||
}
|
||||
|
||||
func TestCreateListener_HTTPSRequiresCert(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbArn := createLBArn(t, svc, "https-nocert")
|
||||
|
||||
@@ -54,6 +55,7 @@ func TestCreateListener_HTTPSRequiresCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateListener_HTTPSDefaultsSslPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbArn := createLBArn(t, svc, "https-default-policy")
|
||||
|
||||
@@ -83,6 +85,7 @@ func TestCreateListener_HTTPSDefaultsSslPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateListener_HTTPRejectsCert(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbArn := createLBArn(t, svc, "http-withcert")
|
||||
|
||||
@@ -98,6 +101,7 @@ func TestCreateListener_HTTPRejectsCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateListener_UnknownSslPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbArn := createLBArn(t, svc, "https-badpolicy")
|
||||
|
||||
@@ -114,6 +118,7 @@ func TestCreateListener_UnknownSslPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyListener_SwitchToHTTPSRequiresCert(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbArn := createLBArn(t, svc, "switch-lb")
|
||||
lst, err := svc.CreateListener(context.Background(), &elbv2.CreateListenerInput{
|
||||
@@ -145,6 +150,7 @@ func TestModifyListener_SwitchToHTTPSRequiresCert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyListener_SwitchAwayClearsCerts(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createHTTPSListener(t, svc)
|
||||
|
||||
@@ -158,6 +164,7 @@ func TestModifyListener_SwitchAwayClearsCerts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListenerCertificates_AddRemoveDescribe(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createHTTPSListener(t, svc)
|
||||
|
||||
@@ -208,6 +215,7 @@ func TestListenerCertificates_AddRemoveDescribe(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddListenerCertificates_HTTPRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbArn := createLBArn(t, svc, "http-addcert")
|
||||
lst, err := svc.CreateListener(context.Background(), &elbv2.CreateListenerInput{
|
||||
@@ -227,6 +235,7 @@ func TestAddListenerCertificates_HTTPRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListenerCertificates_ValidationAndNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
arn := createHTTPSListener(t, svc)
|
||||
const otherAccount = "999999999999"
|
||||
@@ -285,6 +294,7 @@ func TestListenerCertificates_ValidationAndNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSSLPolicies_EmptyName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
_, err := svc.DescribeSSLPolicies(context.Background(), &elbv2.DescribeSSLPoliciesInput{
|
||||
Names: []*string{aws.String("")},
|
||||
@@ -293,6 +303,7 @@ func TestDescribeSSLPolicies_EmptyName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeSSLPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
// No filter → full catalog.
|
||||
@@ -331,6 +342,7 @@ func attachedCertListener(t *testing.T, svc *ELBv2ServiceImpl, id, name string)
|
||||
}
|
||||
|
||||
func TestAddListenerCertificates_RerendersConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbID, listenerArn := attachedCertListener(t, svc, "lb-rerender1", "rerender-lb1")
|
||||
|
||||
@@ -358,6 +370,7 @@ func TestAddListenerCertificates_RerendersConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveListenerCertificates_RerendersConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbID, listenerArn := attachedCertListener(t, svc, "lb-rerender2", "rerender-lb2")
|
||||
|
||||
@@ -392,6 +405,7 @@ func TestRemoveListenerCertificates_RerendersConfig(t *testing.T) {
|
||||
// move an existing deployment's PEM, and it must render first so a client that
|
||||
// sends no SNI still gets it.
|
||||
func TestListenerCerts_DefaultCertRendersFirstAndKeepsItsPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbID, listenerArn := attachedCertListener(t, svc, "lb-rerender3", "rerender-lb3")
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ func createTCPListener(t *testing.T, svc *ELBv2ServiceImpl, lbArn *string, port
|
||||
// dedicated internal SG (not the VPC default) attached to its ENI, recorded in
|
||||
// NLBManagedSGID, while DescribeLoadBalancers still reports no customer SGs.
|
||||
func TestCreateNLB_MintsManagedSGAttachedToENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, vpcID := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -152,6 +153,7 @@ func TestCreateNLB_MintsManagedSGAttachedToENI(t *testing.T) {
|
||||
// customer SGs over the managed SG, falls back to the managed SG without them, and
|
||||
// an ALB always uses its customer SGs.
|
||||
func TestLBENIGroups_Precedence(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, []string{"sg-a"}, lbENIGroups(&LoadBalancerRecord{
|
||||
Type: LoadBalancerTypeNetwork, SecurityGroups: []string{"sg-a"}, NLBManagedSGID: "sg-mgd",
|
||||
}))
|
||||
@@ -167,6 +169,7 @@ func TestLBENIGroups_Precedence(t *testing.T) {
|
||||
// with customer SGs joins those SGs on its ENI and skips the managed SG entirely;
|
||||
// the caller then owns the listener-port ingress rules.
|
||||
func TestCreateNLB_WithCustomerSGs_AttachesThemNoManagedSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, vpcID := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -200,6 +203,7 @@ func TestCreateNLB_WithCustomerSGs_AttachesThemNoManagedSG(t *testing.T) {
|
||||
// TestCreateALB_NoManagedSG verifies the managed-SG behavior is NLB-only: an ALB
|
||||
// keeps the existing customer-SG / default-SG semantics.
|
||||
func TestCreateALB_NoManagedSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -217,6 +221,7 @@ func TestCreateALB_NoManagedSG(t *testing.T) {
|
||||
// TestCreateListener_Internal_OpensPortFromVPCCIDR verifies an internal NLB's
|
||||
// listener port is authorized on the managed SG from the VPC CIDR.
|
||||
func TestCreateListener_Internal_OpensPortFromVPCCIDR(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -234,6 +239,7 @@ func TestCreateListener_Internal_OpensPortFromVPCCIDR(t *testing.T) {
|
||||
// TestCreateListener_InternetFacing_OpensPortFromAnywhere verifies an
|
||||
// internet-facing NLB's listener port is authorized from 0.0.0.0/0.
|
||||
func TestCreateListener_InternetFacing_OpensPortFromAnywhere(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -250,6 +256,7 @@ func TestCreateListener_InternetFacing_OpensPortFromAnywhere(t *testing.T) {
|
||||
// TestCreateListener_TCPUDP_OpensBothProtocols verifies a TCP_UDP listener opens
|
||||
// both tcp and udp on the listener port.
|
||||
func TestCreateListener_TCPUDP_OpensBothProtocols(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -279,6 +286,7 @@ func TestCreateListener_TCPUDP_OpensBothProtocols(t *testing.T) {
|
||||
// swaps the scheme-default rule for the supplied CIDRs across existing listeners
|
||||
// and is idempotent on re-invocation.
|
||||
func TestSetLoadBalancerIngressCIDRs_RewritesListenerRules(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -310,6 +318,7 @@ func TestSetLoadBalancerIngressCIDRs_RewritesListenerRules(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetLoadBalancerIngressCIDRs_RejectsBadInput(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -330,6 +339,7 @@ func TestSetLoadBalancerIngressCIDRs_RejectsBadInput(t *testing.T) {
|
||||
// TestDeleteListener_RevokesPort verifies removing a listener closes its port on
|
||||
// the managed SG.
|
||||
func TestDeleteListener_RevokesPort(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -354,6 +364,7 @@ func TestDeleteListener_RevokesPort(t *testing.T) {
|
||||
// TestDeleteNLB_DeletesManagedSG verifies the managed SG is removed on LB
|
||||
// teardown.
|
||||
func TestDeleteNLB_DeletesManagedSG(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -380,6 +391,7 @@ func TestDeleteNLB_DeletesManagedSG(t *testing.T) {
|
||||
// record, no managed ENI, no managed SG, and the name claim released. The
|
||||
// leaked managed SG is what pinned the EKS control-plane VPC against DeleteVpc.
|
||||
func TestCreateLoadBalancerSync_LaunchFailure_RollsBackEverything(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -407,6 +419,7 @@ func TestCreateLoadBalancerSync_LaunchFailure_RollsBackEverything(t *testing.T)
|
||||
// code, while an unrecognised cause stays ServerInternal rather than inventing a
|
||||
// client error.
|
||||
func TestCreateLoadBalancerSync_LaunchFailure_SurfacesCause(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -428,6 +441,7 @@ func TestCreateLoadBalancerSync_LaunchFailure_SurfacesCause(t *testing.T) {
|
||||
// launching, so its failed record and managed SG are kept — the caller can
|
||||
// reclaim them via DeleteLoadBalancer. Only the sync path rolls back fully.
|
||||
func TestCreateLoadBalancer_AsyncLaunchFailure_KeepsFailedRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc, mock := setupSubnetTestService(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
mock.launchErr = errors.New("allocate public IP: " + awserrors.ErrorInsufficientAddressCapacity)
|
||||
|
||||
@@ -42,6 +42,7 @@ func storedTargets(t *testing.T, svc *ELBv2ServiceImpl, tgArn string) []Target {
|
||||
}
|
||||
|
||||
func TestRegisterTargets_CrossAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
tgArn := ownedTargetGroup(t, svc, "reg-xacct-tg")
|
||||
|
||||
@@ -65,6 +66,7 @@ func TestRegisterTargets_CrossAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeregisterTargets_CrossAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
tgArn := ownedTargetGroup(t, svc, "dereg-xacct-tg")
|
||||
|
||||
@@ -92,6 +94,7 @@ func TestDeregisterTargets_CrossAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeTargetHealth_CrossAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
tgArn := ownedTargetGroup(t, svc, "health-xacct-tg")
|
||||
|
||||
@@ -115,6 +118,7 @@ func TestDescribeTargetHealth_CrossAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateListener_CrossAccountLoadBalancer(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbArn := ownedLoadBalancer(t, svc, "lst-xacct-lb", testAccountID)
|
||||
|
||||
@@ -152,6 +156,7 @@ func TestCreateListener_CrossAccountLoadBalancer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateListener_CrossAccountTargetGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
victimTGArn := ownedTargetGroup(t, svc, "lst-xacct-victim-tg")
|
||||
attackerLBArn := ownedLoadBalancer(t, svc, "lst-xacct-att-lb", attackerAccountID)
|
||||
@@ -173,6 +178,7 @@ func TestCreateListener_CrossAccountTargetGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyListener_CrossAccountTargetGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
victimTGArn := ownedTargetGroup(t, svc, "mod-xacct-victim-tg")
|
||||
attackerLBArn := ownedLoadBalancer(t, svc, "mod-xacct-att-lb", attackerAccountID)
|
||||
@@ -207,6 +213,7 @@ func TestModifyListener_CrossAccountTargetGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_CrossAccountTargetGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
victimTGArn := ownedTargetGroup(t, svc, "rule-xacct-victim-tg")
|
||||
attackerLBArn := ownedLoadBalancer(t, svc, "rule-xacct-att-lb", attackerAccountID)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
// controller always sends ProtocolVersion and recreates the TG (DuplicateTargetGroupName
|
||||
// loop) if Describe reads it back empty.
|
||||
func TestCreateTargetGroup_ProtocolVersionDefaultsAndRoundTrips(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
out, err := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
@@ -38,6 +39,7 @@ func TestCreateTargetGroup_ProtocolVersionDefaultsAndRoundTrips(t *testing.T) {
|
||||
|
||||
// An explicit ProtocolVersion is preserved, not overwritten by the default.
|
||||
func TestCreateTargetGroup_ProtocolVersionExplicitPreserved(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
out, err := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
@@ -53,6 +55,7 @@ func TestCreateTargetGroup_ProtocolVersionExplicitPreserved(t *testing.T) {
|
||||
|
||||
// An unknown ProtocolVersion is rejected.
|
||||
func TestCreateTargetGroup_ProtocolVersionInvalidRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
_, err := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
@@ -66,6 +69,7 @@ func TestCreateTargetGroup_ProtocolVersionInvalidRejected(t *testing.T) {
|
||||
// NLB (TCP/UDP/TLS) target groups have no ProtocolVersion; AWS omits it and so
|
||||
// must Spinifex, otherwise the controller sees spurious drift on NLB TGs.
|
||||
func TestCreateTargetGroup_ProtocolVersionOmittedForNLB(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
out, err := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
|
||||
@@ -30,6 +30,7 @@ func managedENIIDs(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl) []stri
|
||||
// PutLoadBalancer-failure path runs leaves no orphan: ENIs deleted, managed SG
|
||||
// deleted, name claim released (so the name is immediately reclaimable).
|
||||
func TestRollbackLBInfra_RemovesAllInfra(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -69,6 +70,7 @@ func TestRollbackLBInfra_RemovesAllInfra(t *testing.T) {
|
||||
// updateStoredConfig-failure path: the listener port was already authorized, so
|
||||
// rollback must revoke it AND remove the persisted listener record.
|
||||
func TestRollbackListener_RevokesPortAndDeletesRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
@@ -100,6 +102,7 @@ func TestRollbackListener_RevokesPortAndDeletesRecord(t *testing.T) {
|
||||
// the port was never successfully opened, so rollback deletes the listener
|
||||
// record but must not touch SG authorizations (authorizedCIDRs is nil).
|
||||
func TestRollbackListener_NilCIDRsSkipsRevoke(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
subnetID, _ := firstSubnet(t, vpcSvc)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateRule_InputValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-val")
|
||||
|
||||
cases := []struct {
|
||||
@@ -33,6 +34,7 @@ func TestCreateRule_InputValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_WrongAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-acct")
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
ListenerArn: aws.String(env.listenerArn),
|
||||
@@ -45,6 +47,7 @@ func TestCreateRule_WrongAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyRule_InputValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "mod-val")
|
||||
_, err := env.svc.ModifyRule(context.Background(), nil, testAccountID)
|
||||
require.Error(t, err)
|
||||
@@ -60,6 +63,7 @@ func TestModifyRule_InputValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyRule_RewriteActions(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "mod-act")
|
||||
cr, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
ListenerArn: aws.String(env.listenerArn),
|
||||
@@ -78,6 +82,7 @@ func TestModifyRule_RewriteActions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRule_InputValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "del-val")
|
||||
_, err := env.svc.DeleteRule(context.Background(), nil, testAccountID)
|
||||
require.Error(t, err)
|
||||
@@ -94,6 +99,7 @@ func TestDeleteRule_InputValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeRules_InputValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "desc-val")
|
||||
_, err := env.svc.DescribeRules(context.Background(), nil, testAccountID)
|
||||
require.Error(t, err)
|
||||
@@ -105,6 +111,7 @@ func TestDescribeRules_InputValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeRules_ByRuleArns(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "desc-arns")
|
||||
cr, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
ListenerArn: aws.String(env.listenerArn),
|
||||
@@ -129,6 +136,7 @@ func TestDescribeRules_ByRuleArns(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetRulePriorities_InputValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "spri-val")
|
||||
_, err := env.svc.SetRulePriorities(context.Background(), nil, testAccountID)
|
||||
require.Error(t, err)
|
||||
@@ -146,6 +154,7 @@ func TestSetRulePriorities_InputValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetRulePriorities_InvalidPriority(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "spri-bad")
|
||||
cr, _ := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
ListenerArn: aws.String(env.listenerArn),
|
||||
@@ -172,6 +181,7 @@ func TestSetRulePriorities_InvalidPriority(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateConditions_TooMany(t *testing.T) {
|
||||
t.Parallel()
|
||||
in := make([]*elbv2.RuleCondition, MaxConditionsPerRule+1)
|
||||
for i := range in {
|
||||
in[i] = &elbv2.RuleCondition{Field: aws.String("path-pattern"), Values: aws.StringSlice([]string{"/a"})}
|
||||
@@ -182,6 +192,7 @@ func TestValidateConditions_TooMany(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateConditions_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := validateAndConvertConditions(nil)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "MissingParameter")
|
||||
@@ -192,6 +203,7 @@ func TestValidateConditions_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateConditions_InvalidField(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := validateAndConvertConditions([]*elbv2.RuleCondition{
|
||||
{Field: aws.String("unknown-field"), Values: aws.StringSlice([]string{"x"})},
|
||||
})
|
||||
@@ -200,6 +212,7 @@ func TestValidateConditions_InvalidField(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateConditions_HTTPHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Missing HttpHeaderConfig.
|
||||
_, err := validateAndConvertConditions([]*elbv2.RuleCondition{
|
||||
{Field: aws.String("http-header")},
|
||||
@@ -228,6 +241,7 @@ func TestValidateConditions_HTTPHeader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateConditions_Method(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := validateAndConvertConditions([]*elbv2.RuleCondition{
|
||||
{Field: aws.String("http-request-method")},
|
||||
})
|
||||
@@ -246,6 +260,7 @@ func TestValidateConditions_Method(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateConditions_QueryString(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := validateAndConvertConditions([]*elbv2.RuleCondition{
|
||||
{Field: aws.String("query-string")},
|
||||
})
|
||||
@@ -270,6 +285,7 @@ func TestValidateConditions_QueryString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateConditions_SourceIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := validateAndConvertConditions([]*elbv2.RuleCondition{
|
||||
{Field: aws.String("source-ip")},
|
||||
})
|
||||
@@ -288,6 +304,7 @@ func TestValidateConditions_SourceIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWildcardMatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
in string
|
||||
wantFlag string
|
||||
@@ -310,6 +327,7 @@ func TestWildcardMatch(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateRenderSafe(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.NoError(t, validateRenderSafe("/api/v1"))
|
||||
for _, bad := range []string{"", "a b", "a\tb", "a\nb", "a\rb", "a#b", `a"b`} {
|
||||
err := validateRenderSafe(bad)
|
||||
@@ -321,6 +339,7 @@ func TestValidateRenderSafe(t *testing.T) {
|
||||
// ACL emission path in one render. Each rule uses a distinct backend so the
|
||||
// match expressions can be located in the generated config.
|
||||
func TestHAProxyRender_AllConditionFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb1", LoadBalancerArn: "arn:lb1", Type: LoadBalancerTypeApplication}
|
||||
listener := &ListenerRecord{
|
||||
ListenerArn: "arn:lst1", ListenerID: "lst1", LoadBalancerArn: "arn:lb1",
|
||||
@@ -374,11 +393,13 @@ func TestHAProxyRender_AllConditionFields(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHAProxyRender_UnsupportedField(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := buildHAProxyACLExprs(RuleCondition{Field: "nonsense", Values: []string{"x"}})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestBuildHAProxyRule_RoutesToBackend(t *testing.T) {
|
||||
t.Parallel()
|
||||
rule, err := buildHAProxyRule(&RuleRecord{
|
||||
RuleID: "r1",
|
||||
Conditions: []RuleCondition{{Field: RuleFieldPathPattern, Values: []string{"/a"}}},
|
||||
@@ -388,6 +409,7 @@ func TestBuildHAProxyRule_RoutesToBackend(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRegisterRuleBackend_RejectsUnknownAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
noopBackend := func(string) {}
|
||||
noopFixed := func(string, *FixedResponseAction) {}
|
||||
noopRedirect := func(string, *HAProxyRedirect) {}
|
||||
@@ -402,6 +424,7 @@ func TestRegisterRuleBackend_RejectsUnknownAction(t *testing.T) {
|
||||
// without rule-action enumeration, the health checker never resolves rule-only TGs
|
||||
// and they time out at 0/1 healthy (TargetGroupsForLB walked only DefaultActions).
|
||||
func TestTargetGroupsForLB_IncludesRuleTGs(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "tgs-rule")
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
ListenerArn: aws.String(env.listenerArn),
|
||||
|
||||
@@ -53,6 +53,7 @@ func newRuleTestEnv(t *testing.T, namePrefix string) ruleTestEnv {
|
||||
}
|
||||
|
||||
func TestCreateRule_PathPattern(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-path")
|
||||
|
||||
out, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -76,6 +77,7 @@ func TestCreateRule_PathPattern(t *testing.T) {
|
||||
// Controller emits) was rejected with MissingParameter because only the flat
|
||||
// TargetGroupArn field was read.
|
||||
func TestCreateRule_ForwardConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-fwdcfg")
|
||||
|
||||
out, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -102,6 +104,7 @@ func TestCreateRule_ForwardConfig(t *testing.T) {
|
||||
// DeleteLoadBalancer called store.DeleteListener directly, bypassing rule cascade,
|
||||
// leaving a rule's target group pinned as ResourceInUse permanently.
|
||||
func TestDeleteLoadBalancer_CascadesRuleDeletion(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "del-cascade")
|
||||
|
||||
// Rule on the listener forwards to the alt TG.
|
||||
@@ -138,6 +141,7 @@ func TestDeleteLoadBalancer_CascadesRuleDeletion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_PriorityInUse(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-pri")
|
||||
|
||||
mkInput := func() *elbv2.CreateRuleInput {
|
||||
@@ -160,6 +164,7 @@ func TestCreateRule_PriorityInUse(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_InvalidPriority(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-inv")
|
||||
|
||||
for _, p := range []int64{0, 50001} {
|
||||
@@ -175,6 +180,7 @@ func TestCreateRule_InvalidPriority(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_RedirectAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-redir")
|
||||
|
||||
out, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -198,6 +204,7 @@ func TestCreateRule_RedirectAction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_FixedResponseAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-fixed")
|
||||
|
||||
out, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -220,6 +227,7 @@ func TestCreateRule_FixedResponseAction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_RejectsBadRedirectStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-badredir")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -236,6 +244,7 @@ func TestCreateRule_RejectsBadRedirectStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_RejectsUnknownAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-rej")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -249,6 +258,7 @@ func TestCreateRule_RejectsUnknownAction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_HostHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-host")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -264,6 +274,7 @@ func TestCreateRule_HostHeader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_HTTPHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-hdr")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -282,6 +293,7 @@ func TestCreateRule_HTTPHeader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_SourceIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-src")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -309,6 +321,7 @@ func TestCreateRule_SourceIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRule_RejectsNewlineInjection(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cr-inj")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -326,6 +339,7 @@ func TestCreateRule_RejectsNewlineInjection(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestModifyRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "mod")
|
||||
|
||||
out, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -348,6 +362,7 @@ func TestModifyRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "del")
|
||||
|
||||
out, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -369,6 +384,7 @@ func TestDeleteRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteListener_CascadesRules(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "cas")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -388,6 +404,7 @@ func TestDeleteListener_CascadesRules(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteTargetGroup_BlockedByRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "tg-block")
|
||||
|
||||
_, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -404,6 +421,7 @@ func TestDeleteTargetGroup_BlockedByRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeRules_ByListener_SortedAndDefaultLast(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "desc")
|
||||
|
||||
for _, p := range []int64{30, 10, 20} {
|
||||
@@ -430,6 +448,7 @@ func TestDescribeRules_ByListener_SortedAndDefaultLast(t *testing.T) {
|
||||
// DescribeTags with an empty resource and got MissingParameter, aborting the
|
||||
// Ingress reconcile right after listener creation.
|
||||
func TestDefaultRule_HasArnAndIsTaggable(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "defrule")
|
||||
|
||||
rules, err := env.svc.DescribeRules(context.Background(), &elbv2.DescribeRulesInput{ListenerArn: aws.String(env.listenerArn)}, testAccountID)
|
||||
@@ -464,6 +483,7 @@ func TestDefaultRule_HasArnAndIsTaggable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetRulePriorities_Reorders(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "spri")
|
||||
|
||||
r1, err := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -500,6 +520,7 @@ func TestSetRulePriorities_Reorders(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetRulePriorities_DuplicateRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newRuleTestEnv(t, "spri-dup")
|
||||
|
||||
r1, _ := env.svc.CreateRule(context.Background(), &elbv2.CreateRuleInput{
|
||||
@@ -526,6 +547,7 @@ func TestSetRulePriorities_DuplicateRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildRuleArn(t *testing.T) {
|
||||
t.Parallel()
|
||||
listenerArn := "arn:aws:elasticloadbalancing:ap-southeast-2:123456789012:listener/app/my-alb/abc/lst123"
|
||||
got, err := buildRuleArn(listenerArn, "rule999")
|
||||
require.NoError(t, err)
|
||||
@@ -534,6 +556,7 @@ func TestBuildRuleArn(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHAProxyRender_PathPatternRule(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{
|
||||
LoadBalancerID: "lb1",
|
||||
LoadBalancerArn: "arn:lb1",
|
||||
@@ -568,6 +591,7 @@ func TestHAProxyRender_PathPatternRule(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHAProxyRender_HostHeaderWildcardSuffix(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb1", LoadBalancerArn: "arn:lb1", Type: LoadBalancerTypeApplication}
|
||||
listener := &ListenerRecord{
|
||||
ListenerArn: "arn:lst1", ListenerID: "lst1", LoadBalancerArn: "arn:lb1",
|
||||
@@ -590,6 +614,7 @@ func TestHAProxyRender_HostHeaderWildcardSuffix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHAProxyRender_RulesOrderedByPriority(t *testing.T) {
|
||||
t.Parallel()
|
||||
lb := &LoadBalancerRecord{LoadBalancerID: "lb1", LoadBalancerArn: "arn:lb1", Type: LoadBalancerTypeApplication}
|
||||
listener := &ListenerRecord{
|
||||
ListenerArn: "arn:lst1", ListenerID: "lst1", LoadBalancerArn: "arn:lb1",
|
||||
|
||||
@@ -26,6 +26,7 @@ func tagsFor(t *testing.T, svc *ELBv2ServiceImpl, arn string) map[string]string
|
||||
}
|
||||
|
||||
func TestAddTags_LoadBalancer(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
Name: aws.String("addtags-lb"),
|
||||
@@ -47,6 +48,7 @@ func TestAddTags_LoadBalancer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddTags_Listener(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-lst-lb")}, testAccountID)
|
||||
tgOut, _ := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{Name: aws.String("addtags-lst-tg")}, testAccountID)
|
||||
@@ -71,6 +73,7 @@ func TestAddTags_Listener(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddTags_CreateTimeTagsPreserved(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-tg-lb")}, testAccountID)
|
||||
tgOut, err := svc.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
@@ -91,6 +94,7 @@ func TestAddTags_CreateTimeTagsPreserved(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddTags_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
_, err := svc.AddTags(context.Background(), &elbv2.AddTagsInput{
|
||||
ResourceArns: []*string{aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/missing/lb-deadbeef")},
|
||||
@@ -101,6 +105,7 @@ func TestAddTags_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddTags_CrossAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-xacct")}, testAccountID)
|
||||
arn := *lbOut.LoadBalancers[0].LoadBalancerArn
|
||||
@@ -117,6 +122,7 @@ func TestAddTags_CrossAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddTags_MissingParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-mp")}, testAccountID)
|
||||
arn := lbOut.LoadBalancers[0].LoadBalancerArn
|
||||
@@ -131,6 +137,7 @@ func TestAddTags_MissingParams(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddTags_EmptyKeyRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("addtags-ek")}, testAccountID)
|
||||
arn := lbOut.LoadBalancers[0].LoadBalancerArn
|
||||
@@ -144,6 +151,7 @@ func TestAddTags_EmptyKeyRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveTags_LoadBalancer(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
Name: aws.String("rmtags-lb"),
|
||||
@@ -164,6 +172,7 @@ func TestRemoveTags_LoadBalancer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveTags_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
Name: aws.String("rmtags-idem"),
|
||||
@@ -181,6 +190,7 @@ func TestRemoveTags_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveTags_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
_, err := svc.RemoveTags(context.Background(), &elbv2.RemoveTagsInput{
|
||||
ResourceArns: []*string{aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/missing/tg-deadbeef")},
|
||||
@@ -191,6 +201,7 @@ func TestRemoveTags_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveTags_MissingParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
lbOut, _ := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("rmtags-mp")}, testAccountID)
|
||||
arn := lbOut.LoadBalancers[0].LoadBalancerArn
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,7 @@ func getTestSubnetID(t *testing.T, vpcSvc *handlers_ec2_vpc.VPCServiceImpl, vpcI
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_CreatesENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
// Find the subnet we created
|
||||
@@ -118,6 +119,7 @@ func TestCreateLoadBalancer_CreatesENIs(t *testing.T) {
|
||||
// Subnets list. Ignoring SubnetMappings skips ENI allocation, so the data-plane VM
|
||||
// never launches and the ALB is created but cannot serve traffic.
|
||||
func TestCreateLoadBalancer_SubnetMappings_CreatesENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -146,6 +148,7 @@ func TestCreateLoadBalancer_SubnetMappings_CreatesENIs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_MultipleSubnets(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
// Get VPC ID
|
||||
@@ -180,6 +183,7 @@ func TestCreateLoadBalancer_MultipleSubnets(t *testing.T) {
|
||||
// every subnet's ENI is threaded through to SystemInstanceInput, not just
|
||||
// the first one.
|
||||
func TestCreateLoadBalancer_MultiSubnet_AllENIsPassedToLauncher(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
vpcs, _ := vpcSvc.DescribeVpcs(context.Background(), &ec2.DescribeVpcsInput{}, testAccountID)
|
||||
@@ -232,6 +236,7 @@ func TestCreateLoadBalancer_MultiSubnet_AllENIsPassedToLauncher(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteLoadBalancer_CleansUpENIs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, _ := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -264,6 +269,7 @@ func TestDeleteLoadBalancer_CleansUpENIs(t *testing.T) {
|
||||
// delete fails persistently — the ownership edge (lb.ENIs) is the only thing
|
||||
// that would ever let a retry (or the orphan reaper) find and finish it.
|
||||
func TestDeleteLoadBalancer_PersistsSurvivingENIsOnPersistentDeleteFailure(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, js := testutil.StartTestJetStream(t)
|
||||
testutil.StubVpcdSGResponder(t, nc)
|
||||
|
||||
@@ -320,6 +326,7 @@ func TestDeleteLoadBalancer_PersistsSurvivingENIsOnPersistentDeleteFailure(t *te
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_InvalidSubnet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, _ := setupTestServiceWithVPC(t)
|
||||
|
||||
_, err := svc.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
@@ -331,6 +338,7 @@ func TestCreateLoadBalancer_InvalidSubnet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_RollbackOnPartialFailure(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, _ := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -349,6 +357,7 @@ func TestCreateLoadBalancer_RollbackOnPartialFailure(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_WithoutVPCService(t *testing.T) {
|
||||
t.Parallel()
|
||||
// When vpcService is nil (e.g. in pure unit tests), ENI creation is skipped
|
||||
svc := setupTestService(t)
|
||||
|
||||
@@ -364,6 +373,7 @@ func TestCreateLoadBalancer_WithoutVPCService(t *testing.T) {
|
||||
// --- Scheme networking integration tests ---
|
||||
|
||||
func TestCreateLoadBalancer_InternetFacing_AllocatesPublicIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -422,6 +432,7 @@ func TestCreateLoadBalancer_InternetFacing_AllocatesPublicIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_Internal_NoPublicIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -463,6 +474,7 @@ func TestCreateLoadBalancer_Internal_NoPublicIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_NLB_Internal_NoPublicIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -511,6 +523,7 @@ func TestCreateLoadBalancer_NLB_Internal_NoPublicIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteLoadBalancer_TerminatesVM_WithPublicIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -560,6 +573,7 @@ func TestDeleteLoadBalancer_TerminatesVM_WithPublicIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteLoadBalancer_ReapsFloatingIPNAT(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -618,6 +632,7 @@ func TestDeleteLoadBalancer_ReapsFloatingIPNAT(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeLoadBalancers_InternetFacing_IncludesPublicIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -669,6 +684,7 @@ func TestDescribeLoadBalancers_InternetFacing_IncludesPublicIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDescribeLoadBalancers_Internal_NoPublicIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -711,6 +727,7 @@ func TestDescribeLoadBalancers_Internal_NoPublicIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_LaunchFailure_SetsStateFailed(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -744,6 +761,7 @@ func TestCreateLoadBalancer_LaunchFailure_SetsStateFailed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateLoadBalancer_MissingCredentials_SetsStateFailed(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -778,6 +796,7 @@ func TestCreateLoadBalancer_MissingCredentials_SetsStateFailed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestENI_RequesterManagedFlag(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, _ := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -823,6 +842,7 @@ func TestENI_RequesterManagedFlag(t *testing.T) {
|
||||
// SecurityGroups are applied to the ALB ENI; without this the OVN port-group misses
|
||||
// the user's allow rules and the default-deny ACL drops all inbound traffic.
|
||||
func TestCreateLoadBalancer_AttachesSpecifiedSecurityGroupsToENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -866,6 +886,7 @@ func TestCreateLoadBalancer_AttachesSpecifiedSecurityGroupsToENI(t *testing.T) {
|
||||
// AWS-semantics fallback: an ALB created without SecurityGroups joins the
|
||||
// VPC's default SG (so the default-deny ACL has at least one allow source).
|
||||
func TestCreateLoadBalancer_NoSecurityGroupsFallsBackToDefault(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -902,6 +923,7 @@ func TestCreateLoadBalancer_NoSecurityGroupsFallsBackToDefault(t *testing.T) {
|
||||
// same SystemInstanceInput as the original launch; a mismatch causes writeFwCfgBlobs
|
||||
// to emit a different netcfg/cacert blob than the persisted QEMU command line.
|
||||
func TestRebuildSystemInstanceInput_HappyPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
@@ -964,6 +986,7 @@ func TestRebuildSystemInstanceInput_HappyPath(t *testing.T) {
|
||||
// the LB record is gone from KV but the VM record survives; recovery must not
|
||||
// silently boot the VM into an unowned state.
|
||||
func TestRebuildSystemInstanceInput_NoLBRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestService(t)
|
||||
|
||||
_, err := svc.RebuildSystemInstanceInput(RecoveryContext{
|
||||
@@ -978,6 +1001,7 @@ func TestRebuildSystemInstanceInput_NoLBRecord(t *testing.T) {
|
||||
// multi-subnet ALBs; recovery must produce the same ExtraENIInput shape so the
|
||||
// daemon wires the same taps and NICs the persisted QEMU args reference.
|
||||
func TestRebuildSystemInstanceInput_MultiENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
vpcs, _ := vpcSvc.DescribeVpcs(context.Background(), &ec2.DescribeVpcsInput{}, testAccountID)
|
||||
@@ -1024,6 +1048,7 @@ func TestRebuildSystemInstanceInput_MultiENI(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateClusterNLBSync_CarriesCrossAccountENI(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc, vpcSvc := setupTestServiceWithVPC(t)
|
||||
|
||||
subnets, err := vpcSvc.DescribeSubnets(context.Background(), &ec2.DescribeSubnetsInput{}, testAccountID)
|
||||
|
||||
@@ -89,6 +89,7 @@ func handleNATSMsg[In any, Out any](msg *nats.Msg, fn func(context.Context, *In,
|
||||
// --- Full round-trip E2E tests ---
|
||||
|
||||
func TestNATSE2E_CreateAndDescribeLoadBalancer(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
out, err := client.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
@@ -109,6 +110,7 @@ func TestNATSE2E_CreateAndDescribeLoadBalancer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_FullWorkflow(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
// 1. Create target group
|
||||
@@ -232,6 +234,7 @@ func TestNATSE2E_FullWorkflow(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_DeleteLoadBalancerCascadesListeners(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
// Create LB + TG + 2 listeners
|
||||
@@ -259,6 +262,7 @@ func TestNATSE2E_DeleteLoadBalancerCascadesListeners(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_ModifyListener(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
tgOut, _ := client.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{Name: aws.String("e2e-mod-tg")}, testAccountID)
|
||||
@@ -289,6 +293,7 @@ func TestNATSE2E_ModifyListener(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_TargetGroupInUseProtection(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
tgOut, _ := client.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{Name: aws.String("protected-tg")}, testAccountID)
|
||||
@@ -307,6 +312,7 @@ func TestNATSE2E_TargetGroupInUseProtection(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_TagsLifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
lbOut, err := client.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("e2e-tags-lb")}, testAccountID)
|
||||
@@ -348,6 +354,7 @@ func TestNATSE2E_TagsLifecycle(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_AttributeRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
tgOut, err := client.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{Name: aws.String("e2e-attr-tg")}, testAccountID)
|
||||
@@ -392,6 +399,7 @@ func TestNATSE2E_AttributeRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_RedirectListener(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
lbOut, err := client.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{Name: aws.String("e2e-redir-lb")}, testAccountID)
|
||||
@@ -425,6 +433,7 @@ func TestNATSE2E_RedirectListener(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_IPTargetType(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
tgOut, err := client.CreateTargetGroup(context.Background(), &elbv2.CreateTargetGroupInput{
|
||||
@@ -457,6 +466,7 @@ func TestNATSE2E_IPTargetType(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSE2E_SetSecurityGroupsAndIpAddressType(t *testing.T) {
|
||||
t.Parallel()
|
||||
client, _ := setupNATSELBv2Test(t)
|
||||
|
||||
lbOut, err := client.CreateLoadBalancer(context.Background(), &elbv2.CreateLoadBalancerInput{
|
||||
|
||||
@@ -72,6 +72,7 @@ func newTestListener(id, lbArn string) *ListenerRecord {
|
||||
// bucket is not merely named, it opens against the live server so the DNS
|
||||
// reconcile can actually be woken by a load-balancer change.
|
||||
func TestStore_WatchBucketOpensAWatchableHandle(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
|
||||
bucket := store.WatchBucket()
|
||||
@@ -84,6 +85,7 @@ func TestStore_WatchBucketOpensAWatchableHandle(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadBalancerStoreLifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("put and get", func(t *testing.T) {
|
||||
store := setupTestStore(t)
|
||||
require.NoError(t, store.PutLoadBalancer(t.Context(), newTestLB("getput1", "lb-getput1")))
|
||||
@@ -135,6 +137,7 @@ func TestLoadBalancerStoreLifecycle(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTargetGroupStoreLifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("put and get", func(t *testing.T) {
|
||||
store := setupTestStore(t)
|
||||
require.NoError(t, store.PutTargetGroup(t.Context(), newTestTG("getput1", "tg-getput1")))
|
||||
@@ -186,6 +189,7 @@ func TestTargetGroupStoreLifecycle(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListenerStoreLifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
lbArn := "arn:aws:elasticloadbalancing:us-east-1:" + testAccountID + ":loadbalancer/app/test/lb1"
|
||||
|
||||
t.Run("put and get", func(t *testing.T) {
|
||||
@@ -241,6 +245,7 @@ func TestListenerStoreLifecycle(t *testing.T) {
|
||||
// --- LB-specific lookups (no equivalent on TG/Listener) ---
|
||||
|
||||
func TestGetLoadBalancerByArn(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
lb := newTestLB("arn123", "arn-test")
|
||||
require.NoError(t, store.PutLoadBalancer(t.Context(), lb))
|
||||
@@ -256,6 +261,7 @@ func TestGetLoadBalancerByArn(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetLoadBalancerByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
lb := newTestLB("name123", "find-by-name")
|
||||
require.NoError(t, store.PutLoadBalancer(t.Context(), lb))
|
||||
@@ -267,6 +273,7 @@ func TestGetLoadBalancerByName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutLoadBalancer_Update(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
lb := newTestLB("upd123", "updatable")
|
||||
require.NoError(t, store.PutLoadBalancer(t.Context(), lb))
|
||||
@@ -285,6 +292,7 @@ func TestPutLoadBalancer_Update(t *testing.T) {
|
||||
// --- TG-specific lookups + targets ---
|
||||
|
||||
func TestGetTargetGroupByArn(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
tg := newTestTG("tgarn", "arn-tg")
|
||||
require.NoError(t, store.PutTargetGroup(t.Context(), tg))
|
||||
@@ -296,6 +304,7 @@ func TestGetTargetGroupByArn(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetTargetGroupByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
tg := newTestTG("tgname", "named-tg")
|
||||
require.NoError(t, store.PutTargetGroup(t.Context(), tg))
|
||||
@@ -311,6 +320,7 @@ func TestGetTargetGroupByName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTargetGroupWithTargets(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
tg := newTestTG("tgtargets", "targets-tg")
|
||||
tg.Targets = []Target{
|
||||
@@ -331,6 +341,7 @@ func TestTargetGroupWithTargets(t *testing.T) {
|
||||
// --- Listener-specific lookups ---
|
||||
|
||||
func TestListListenersByLB(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
lbArn1 := "arn:aws:elasticloadbalancing:us-east-1:" + testAccountID + ":loadbalancer/app/alb1/lb1"
|
||||
lbArn2 := "arn:aws:elasticloadbalancing:us-east-1:" + testAccountID + ":loadbalancer/app/alb2/lb2"
|
||||
@@ -356,6 +367,7 @@ func TestListListenersByLB(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetListenerByArn(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
lbArn := "arn:aws:elasticloadbalancing:us-east-1:" + testAccountID + ":loadbalancer/app/test/lb1"
|
||||
l := newTestListener("lstarn", lbArn)
|
||||
@@ -380,6 +392,7 @@ func TestGetListenerByArn(t *testing.T) {
|
||||
// --- Cross-resource isolation: shared IDs across record types must not collide ---
|
||||
|
||||
func TestResourceIsolation(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
|
||||
lb := newTestLB("shared1", "alb-shared")
|
||||
@@ -414,6 +427,7 @@ func TestResourceIsolation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTargetGroupsForLB(t *testing.T) {
|
||||
t.Parallel()
|
||||
store := setupTestStore(t)
|
||||
|
||||
// Non-existent LB returns nil, nil
|
||||
|
||||
@@ -29,6 +29,7 @@ func (s *stubResponder) close() {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_LaunchRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -77,6 +78,7 @@ func TestNATSSystemInstanceLauncher_LaunchRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_LaunchPropagatesRemoteError(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -99,6 +101,7 @@ func TestNATSSystemInstanceLauncher_LaunchPropagatesRemoteError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_LaunchFailsWhenNoResponder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -111,6 +114,7 @@ func TestNATSSystemInstanceLauncher_LaunchFailsWhenNoResponder(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_TerminateRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -138,6 +142,7 @@ func TestNATSSystemInstanceLauncher_TerminateRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNATSSystemInstanceLauncher_DefaultTimeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -152,6 +157,7 @@ func TestNewNATSSystemInstanceLauncher_DefaultTimeout(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_LaunchNilInput(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -162,6 +168,7 @@ func TestNATSSystemInstanceLauncher_LaunchNilInput(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_LaunchMissingInstanceType(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -172,6 +179,7 @@ func TestNATSSystemInstanceLauncher_LaunchMissingInstanceType(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_LaunchDecodeFailure(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -188,6 +196,7 @@ func TestNATSSystemInstanceLauncher_LaunchDecodeFailure(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_LaunchMissingOutput(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -204,6 +213,7 @@ func TestNATSSystemInstanceLauncher_LaunchMissingOutput(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_TerminateEmptyID(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -214,6 +224,7 @@ func TestNATSSystemInstanceLauncher_TerminateEmptyID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_TerminateNoResponder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -223,6 +234,7 @@ func TestNATSSystemInstanceLauncher_TerminateNoResponder(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_TerminateDecodeFailure(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
@@ -239,6 +251,7 @@ func TestNATSSystemInstanceLauncher_TerminateDecodeFailure(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNATSSystemInstanceLauncher_TerminatePropagatesRemoteError(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
defer nc.Close()
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDefaultHealthCheck(t *testing.T) {
|
||||
t.Parallel()
|
||||
hc := DefaultHealthCheck()
|
||||
|
||||
assert.Equal(t, ProtocolHTTP, hc.Protocol)
|
||||
|
||||
@@ -22,6 +22,7 @@ func newLifecycleService(t *testing.T) handlers_iam.IAMService {
|
||||
}
|
||||
|
||||
func TestSetAccountStatusMovesBetweenStates(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newLifecycleService(t)
|
||||
account, err := svc.CreateAccount("tenant@example.com")
|
||||
require.NoError(t, err)
|
||||
@@ -44,6 +45,7 @@ func TestSetAccountStatusMovesBetweenStates(t *testing.T) {
|
||||
// resumes service is worse than one that stays down, and "undo" here would
|
||||
// mean restoring deleted block storage.
|
||||
func TestTerminatingIsAOneWayDoor(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newLifecycleService(t)
|
||||
account, err := svc.CreateAccount("tenant@example.com")
|
||||
require.NoError(t, err)
|
||||
@@ -62,6 +64,7 @@ func TestTerminatingIsAOneWayDoor(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetAccountStatusRejectsAnUnknownStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newLifecycleService(t)
|
||||
account, err := svc.CreateAccount("tenant@example.com")
|
||||
require.NoError(t, err)
|
||||
@@ -74,6 +77,7 @@ func TestSetAccountStatusRejectsAnUnknownStatus(t *testing.T) {
|
||||
// Deleting the record while the account is still ACTIVE would leave every
|
||||
// resource it owns unattributable, so the status gate is the invariant.
|
||||
func TestDeleteAccountRequiresTerminating(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newLifecycleService(t)
|
||||
// Account ids are sequential and the first is the super admin, which is
|
||||
// protected — the tenant under test has to be the second.
|
||||
@@ -95,6 +99,7 @@ func TestDeleteAccountRequiresTerminating(t *testing.T) {
|
||||
// No credential grants deleting these two, so the refusal lives in the library
|
||||
// rather than only in the CLI that usually calls it.
|
||||
func TestProtectedAccountsCanNeverBeDeleted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newLifecycleService(t)
|
||||
|
||||
for accountID := range handlers_iam.UndeletableAccountIDs {
|
||||
@@ -113,6 +118,7 @@ func TestProtectedAccountsCanNeverBeDeleted(t *testing.T) {
|
||||
// A deleted account's name must be free again: a customer who leaves and comes
|
||||
// back must not be told their own address is taken.
|
||||
func TestReleaseDeletedFreesTheName(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -132,6 +138,7 @@ func TestReleaseDeletedFreesTheName(t *testing.T) {
|
||||
// Releasing a name that belongs to a live account would unindex it and let a
|
||||
// second account take the same address.
|
||||
func TestReleaseDeletedRefusesAnotherAccountsName(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -149,6 +156,7 @@ func TestReleaseDeletedRefusesAnotherAccountsName(t *testing.T) {
|
||||
// Teardown re-runs after a crash, so a name that is already released is a
|
||||
// success rather than a failure that blocks the account record's deletion.
|
||||
func TestReleaseDeletedIsIdempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ func newAccountNameIndex(t *testing.T) (*handlers_iam.AccountNameIndex, jetstrea
|
||||
}
|
||||
|
||||
func TestAccountNameIndexReserveCommitLookup(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -44,6 +45,7 @@ func TestAccountNameIndexReserveCommitLookup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccountNameIndexReserveConflicts(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -63,6 +65,7 @@ func TestAccountNameIndexReserveConflicts(t *testing.T) {
|
||||
|
||||
// Case and whitespace must not create a second account for the same person.
|
||||
func TestAccountNameIndexNormalizesName(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -78,6 +81,7 @@ func TestAccountNameIndexNormalizesName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccountNameIndexRelease(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -101,6 +105,7 @@ func TestAccountNameIndexRelease(t *testing.T) {
|
||||
// Release on a name that was never reserved is a no-op, not an error: it runs
|
||||
// on the failure path where the reservation may already be gone.
|
||||
func TestAccountNameIndexReleaseUnknownName(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, _ := newAccountNameIndex(t)
|
||||
require.NoError(t, index.Release(t.Context(), "nobody@example.com", "token-a"))
|
||||
}
|
||||
@@ -108,6 +113,7 @@ func TestAccountNameIndexReleaseUnknownName(t *testing.T) {
|
||||
// KV keys are readable by anyone with cluster access, so a customer's email
|
||||
// address must not appear in one.
|
||||
func TestAccountNameIndexKeysCarryNoEmailAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, js := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -126,6 +132,7 @@ func TestAccountNameIndexKeysCarryNoEmailAddress(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFindAccountByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newIAMService(t)
|
||||
|
||||
created, err := svc.CreateAccount("ben@example.com")
|
||||
@@ -144,6 +151,7 @@ func TestFindAccountByName(t *testing.T) {
|
||||
// A corrupt entry must surface as an error. Treating it as absent would let a
|
||||
// second account be created under a name that is already indexed.
|
||||
func TestAccountNameIndexRejectsCorruptEntry(t *testing.T) {
|
||||
t.Parallel()
|
||||
index, js := newAccountNameIndex(t)
|
||||
ctx := t.Context()
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ func resName(prefix string, i int) string {
|
||||
}
|
||||
|
||||
func TestGetAccountSummary_Counts(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Org A")
|
||||
@@ -87,6 +88,7 @@ func TestGetAccountSummary_Counts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAccountSummary_EmptyAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
acc, err := svc.CreateAccount("Empty Org")
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
acc1, err := svc.CreateAccount("Team Alpha")
|
||||
@@ -36,6 +37,7 @@ func TestCreateAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateAccount_SequentialIDs(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
var prevID string
|
||||
@@ -51,6 +53,7 @@ func TestCreateAccount_SequentialIDs(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
created, err := svc.CreateAccount("Lookup Test")
|
||||
@@ -64,6 +67,7 @@ func TestGetAccount(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAccount_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetAccount("999999999999")
|
||||
@@ -71,6 +75,7 @@ func TestGetAccount_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAccounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
svc.CreateAccount("Acct1")
|
||||
@@ -91,6 +96,7 @@ func TestListAccounts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAccounts_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accounts, err := svc.ListAccounts()
|
||||
@@ -103,6 +109,7 @@ func TestListAccounts_Empty(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAccountScopedUsers(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
acc1, err := svc.CreateAccount("Org A")
|
||||
@@ -160,6 +167,7 @@ func TestAccountScopedUsers(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAccountScopedPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
acc1, err := svc.CreateAccount("Policy Org A")
|
||||
@@ -199,6 +207,7 @@ func TestAccountScopedPolicies(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAccessKeyReturnsAccountID(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
acc, err := svc.CreateAccount("Key Org")
|
||||
@@ -230,6 +239,7 @@ func TestAccessKeyReturnsAccountID(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestCrossAccount_UserIsolation(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Account A")
|
||||
@@ -256,6 +266,7 @@ func TestCrossAccount_UserIsolation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCrossAccount_AccessKeyIsolation(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Key Org A")
|
||||
@@ -281,6 +292,7 @@ func TestCrossAccount_AccessKeyIsolation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCrossAccount_PolicyAttachmentFails(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Policy Org A")
|
||||
@@ -312,6 +324,7 @@ func TestCrossAccount_PolicyAttachmentFails(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCrossAccount_LookupAccessKeyReturnsCorrectAccount(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Lookup A")
|
||||
@@ -348,6 +361,7 @@ func TestCrossAccount_LookupAccessKeyReturnsCorrectAccount(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestSeedBootstrap_AccountScoped(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
encryptedSecret, err := svc.key.EncryptBase64("root-secret")
|
||||
@@ -387,6 +401,7 @@ func TestSeedBootstrap_AccountScoped(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateAccount_PublishesEvent(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, nc, _ := testutil.StartTestJetStream(t)
|
||||
|
||||
masterKey, err := GenerateMasterKey()
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestCanonicalResourceARNPreservesStoredPaths(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
trust := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]}`
|
||||
policy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:*","Resource":"*"}]}`
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestGenerateMasterKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
key, err := GenerateMasterKey()
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateMasterKey() error: %v", err)
|
||||
@@ -29,6 +30,7 @@ func TestGenerateMasterKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSaveAndReadMasterKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
key, err := GenerateMasterKey()
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateMasterKey() error: %v", err)
|
||||
@@ -60,6 +62,7 @@ func TestSaveAndReadMasterKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadMasterKeyWrongSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bad.key")
|
||||
|
||||
@@ -74,6 +77,7 @@ func TestReadMasterKeyWrongSize(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadMasterKeyNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := masterkey.ReadShared("/nonexistent/master.key")
|
||||
if err == nil {
|
||||
t.Fatal("masterkey.ReadShared() should fail for missing file")
|
||||
@@ -81,6 +85,7 @@ func TestReadMasterKeyNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSaveMasterKeyWrongSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bad.key")
|
||||
|
||||
@@ -91,6 +96,7 @@ func TestSaveMasterKeyWrongSize(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEncryptDecryptRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
key, err := GenerateMasterKey()
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateMasterKey() error: %v", err)
|
||||
@@ -118,6 +124,7 @@ func TestEncryptDecryptRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEncryptProducesDifferentCiphertexts(t *testing.T) {
|
||||
t.Parallel()
|
||||
key, _ := GenerateMasterKey()
|
||||
secret := "test-secret"
|
||||
|
||||
@@ -130,6 +137,7 @@ func TestEncryptProducesDifferentCiphertexts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecryptWrongKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
key1, _ := GenerateMasterKey()
|
||||
key2, _ := GenerateMasterKey()
|
||||
secret := "test-secret"
|
||||
@@ -146,6 +154,7 @@ func TestDecryptWrongKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecryptTamperedCiphertext(t *testing.T) {
|
||||
t.Parallel()
|
||||
key, _ := GenerateMasterKey()
|
||||
secret := "test-secret"
|
||||
|
||||
@@ -166,6 +175,7 @@ func TestDecryptTamperedCiphertext(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecryptInvalidBase64(t *testing.T) {
|
||||
t.Parallel()
|
||||
key, _ := GenerateMasterKey()
|
||||
_, err := DecryptSecret("not-valid-base64!!!", key)
|
||||
if err == nil {
|
||||
@@ -174,6 +184,7 @@ func TestDecryptInvalidBase64(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDecryptTooShort(t *testing.T) {
|
||||
t.Parallel()
|
||||
key, _ := GenerateMasterKey()
|
||||
// Valid base64 but too short for nonce + ciphertext
|
||||
_, err := DecryptSecret("AQID", key)
|
||||
@@ -183,6 +194,7 @@ func TestDecryptTooShort(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadBootstrapData(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bootstrap.json")
|
||||
|
||||
@@ -217,6 +229,7 @@ func TestLoadBootstrapData(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadBootstrapDataNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := LoadBootstrapData("/nonexistent/bootstrap.json")
|
||||
if err == nil {
|
||||
t.Fatal("LoadBootstrapData() should fail for missing file")
|
||||
@@ -224,6 +237,7 @@ func TestLoadBootstrapDataNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadBootstrapDataInvalidJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bootstrap.json")
|
||||
|
||||
@@ -238,6 +252,7 @@ func TestLoadBootstrapDataInvalidJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSaveBootstrapData_RoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bootstrap.json")
|
||||
|
||||
@@ -280,6 +295,7 @@ func TestSaveBootstrapData_RoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBootstrapData_ExtendedRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bootstrap.json")
|
||||
|
||||
@@ -335,6 +351,7 @@ func TestBootstrapData_ExtendedRoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBootstrapData_NoAdmin(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bootstrap.json")
|
||||
|
||||
@@ -357,6 +374,7 @@ func TestBootstrapData_NoAdmin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadBootstrapData_MissingVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "bootstrap.json")
|
||||
|
||||
@@ -372,6 +390,7 @@ func TestLoadBootstrapData_MissingVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSaveBootstrapData_InvalidPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
data := &BootstrapData{
|
||||
AccessKeyID: "AKIAIOSFODNN7EXAMPLE",
|
||||
EncryptedSecret: "dGVzdA==",
|
||||
|
||||
@@ -34,6 +34,7 @@ func createTestGroup(t *testing.T, svc *IAMServiceImpl, name string) *iam.Group
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateGroup(testAccountID, &iam.CreateGroupInput{
|
||||
@@ -50,6 +51,7 @@ func TestCreateGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateGroup_DefaultPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateGroup(testAccountID, &iam.CreateGroupInput{
|
||||
@@ -61,6 +63,7 @@ func TestCreateGroup_DefaultPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateGroup_Duplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "dup-group")
|
||||
|
||||
@@ -72,6 +75,7 @@ func TestCreateGroup_Duplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateGroup_InvalidName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
longName := strings.Repeat("a", 129)
|
||||
|
||||
@@ -83,6 +87,7 @@ func TestCreateGroup_InvalidName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateGroup_InvalidPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateGroup(testAccountID, &iam.CreateGroupInput{
|
||||
@@ -94,6 +99,7 @@ func TestCreateGroup_InvalidPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "get-group")
|
||||
createTestUser(t, svc, "member")
|
||||
@@ -114,6 +120,7 @@ func TestGetGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetGroup_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetGroup(testAccountID, &iam.GetGroupInput{
|
||||
@@ -124,6 +131,7 @@ func TestGetGroup_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetGroup_UsersNeverNil(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "empty-group")
|
||||
|
||||
@@ -136,6 +144,7 @@ func TestGetGroup_UsersNeverNil(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroups(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "group1")
|
||||
createTestGroup(t, svc, "group2")
|
||||
@@ -155,6 +164,7 @@ func TestListGroups(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroups_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.ListGroups(testAccountID, &iam.ListGroupsInput{})
|
||||
@@ -166,6 +176,7 @@ func TestListGroups_Empty(t *testing.T) {
|
||||
// TestListGroups_EmptyAfterOtherAccountsExist walks the filtering path rather
|
||||
// than the no-keys-at-all shortcut: the bucket has keys, none of them ours.
|
||||
func TestListGroups_EmptyAfterOtherAccountsExist(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateGroup("999988887777", &iam.CreateGroupInput{GroupName: aws.String("othergroup")})
|
||||
@@ -178,6 +189,7 @@ func TestListGroups_EmptyAfterOtherAccountsExist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroups_PathPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateGroup(testAccountID, &iam.CreateGroupInput{
|
||||
@@ -201,6 +213,7 @@ func TestListGroups_PathPrefix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "del-group")
|
||||
|
||||
@@ -217,6 +230,7 @@ func TestDeleteGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteGroup_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeleteGroup(testAccountID, &iam.DeleteGroupInput{
|
||||
@@ -227,6 +241,7 @@ func TestDeleteGroup_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteGroup_WithAttachedPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "attached-group")
|
||||
policy := createTestPolicy(t, svc, "GroupPolicy")
|
||||
@@ -245,6 +260,7 @@ func TestDeleteGroup_WithAttachedPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteGroup_WithMembers(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "populated-group")
|
||||
createTestUser(t, svc, "occupant")
|
||||
@@ -267,6 +283,7 @@ func TestDeleteGroup_WithMembers(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAddUserToGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "team")
|
||||
createTestUser(t, svc, "alice")
|
||||
@@ -286,6 +303,7 @@ func TestAddUserToGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddUserToGroup_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "team")
|
||||
createTestUser(t, svc, "bob")
|
||||
@@ -306,6 +324,7 @@ func TestAddUserToGroup_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddUserToGroup_LimitExceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "busy")
|
||||
|
||||
@@ -330,6 +349,7 @@ func TestAddUserToGroup_LimitExceeded(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddUserToGroup_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "team")
|
||||
|
||||
@@ -342,6 +362,7 @@ func TestAddUserToGroup_UserNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddUserToGroup_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "carol")
|
||||
|
||||
@@ -354,6 +375,7 @@ func TestAddUserToGroup_GroupNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveUserFromGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "team")
|
||||
createTestUser(t, svc, "dave")
|
||||
@@ -378,6 +400,7 @@ func TestRemoveUserFromGroup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveUserFromGroup_NotMember(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "team")
|
||||
createTestUser(t, svc, "erin")
|
||||
@@ -391,6 +414,7 @@ func TestRemoveUserFromGroup_NotMember(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveUserFromGroup_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "team")
|
||||
|
||||
@@ -406,6 +430,7 @@ func TestRemoveUserFromGroup_UserNotFound(t *testing.T) {
|
||||
// group that was deleted out from under the user is still cleanable — removal
|
||||
// operates purely on User.Groups and never fetches the group record.
|
||||
func TestRemoveUserFromGroup_DanglingPointer(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "doomed")
|
||||
createTestUser(t, svc, "frank")
|
||||
@@ -433,6 +458,7 @@ func TestRemoveUserFromGroup_DanglingPointer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroupsForUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "grace")
|
||||
for _, name := range []string{"alpha", "bravo"} {
|
||||
@@ -458,6 +484,7 @@ func TestListGroupsForUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroupsForUser_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "loner")
|
||||
|
||||
@@ -469,6 +496,7 @@ func TestListGroupsForUser_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroupsForUser_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListGroupsForUser(testAccountID, &iam.ListGroupsForUserInput{
|
||||
@@ -481,6 +509,7 @@ func TestListGroupsForUser_UserNotFound(t *testing.T) {
|
||||
// TestListGroupsForUser_SkipsMissingGroup proves a dangling membership pointer
|
||||
// is silently skipped rather than erroring the whole listing.
|
||||
func TestListGroupsForUser_SkipsMissingGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "live")
|
||||
createTestGroup(t, svc, "vanishing")
|
||||
@@ -509,6 +538,7 @@ func TestListGroupsForUser_SkipsMissingGroup(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAttachGroupPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "policy-group")
|
||||
policy := createTestPolicy(t, svc, "AttachPolicy")
|
||||
@@ -529,6 +559,7 @@ func TestAttachGroupPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachGroupPolicy_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "idem-group")
|
||||
policy := createTestPolicy(t, svc, "IdemPolicy")
|
||||
@@ -549,6 +580,7 @@ func TestAttachGroupPolicy_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachGroupPolicy_PolicyNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "needs-policy")
|
||||
|
||||
@@ -561,6 +593,7 @@ func TestAttachGroupPolicy_PolicyNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
policy := createTestPolicy(t, svc, "OrphanPolicy")
|
||||
|
||||
@@ -575,6 +608,7 @@ func TestAttachGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
// TestAttachGroupPolicy_AWSManagedOpaque proves an AWS-managed ARN with no
|
||||
// backing policy record round-trips opaquely instead of failing NoSuchEntity.
|
||||
func TestAttachGroupPolicy_AWSManagedOpaque(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "managed-group")
|
||||
const managedARN = "arn:aws:iam::aws:policy/service-role/AmazonEKS_CNI_Policy"
|
||||
@@ -595,6 +629,7 @@ func TestAttachGroupPolicy_AWSManagedOpaque(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachGroupPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "detach-group")
|
||||
policy := createTestPolicy(t, svc, "DetachPolicy")
|
||||
@@ -619,6 +654,7 @@ func TestDetachGroupPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachGroupPolicy_NotAttached(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "bare-group")
|
||||
policy := createTestPolicy(t, svc, "NotAttachedPolicy")
|
||||
@@ -632,6 +668,7 @@ func TestDetachGroupPolicy_NotAttached(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
policy := createTestPolicy(t, svc, "OrphanDetach")
|
||||
|
||||
@@ -644,6 +681,7 @@ func TestDetachGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAttachedGroupPolicies_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "empty-attach")
|
||||
|
||||
@@ -655,6 +693,7 @@ func TestListAttachedGroupPolicies_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAttachedGroupPolicies_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListAttachedGroupPolicies(testAccountID, &iam.ListAttachedGroupPoliciesInput{
|
||||
@@ -669,6 +708,7 @@ func TestListAttachedGroupPolicies_GroupNotFound(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestPutGroupPolicy_RoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "inline-group")
|
||||
|
||||
@@ -690,6 +730,7 @@ func TestPutGroupPolicy_RoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutGroupPolicy_IdempotentOverwrite(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "overwrite-group")
|
||||
|
||||
@@ -722,6 +763,7 @@ func TestPutGroupPolicy_IdempotentOverwrite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutGroupPolicy_InvalidName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "badname-group")
|
||||
|
||||
@@ -735,6 +777,7 @@ func TestPutGroupPolicy_InvalidName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutGroupPolicy_MalformedDocument(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "malformed-group")
|
||||
|
||||
@@ -748,6 +791,7 @@ func TestPutGroupPolicy_MalformedDocument(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutGroupPolicy_OversizedDocument(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "oversized-group")
|
||||
|
||||
@@ -762,6 +806,7 @@ func TestPutGroupPolicy_OversizedDocument(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.PutGroupPolicy(testAccountID, &iam.PutGroupPolicyInput{
|
||||
@@ -774,6 +819,7 @@ func TestPutGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetGroupPolicy_UnknownName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "get-unknown")
|
||||
|
||||
@@ -786,6 +832,7 @@ func TestGetGroupPolicy_UnknownName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetGroupPolicy(testAccountID, &iam.GetGroupPolicyInput{
|
||||
@@ -797,6 +844,7 @@ func TestGetGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroupPolicies_Sorted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "list-group")
|
||||
|
||||
@@ -822,6 +870,7 @@ func TestListGroupPolicies_Sorted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroupPolicies_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "empty-inline-group")
|
||||
|
||||
@@ -835,6 +884,7 @@ func TestListGroupPolicies_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListGroupPolicies_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListGroupPolicies(testAccountID, &iam.ListGroupPoliciesInput{
|
||||
@@ -845,6 +895,7 @@ func TestListGroupPolicies_GroupNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteGroupPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "del-inline-group")
|
||||
|
||||
@@ -876,6 +927,7 @@ func TestDeleteGroupPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteGroupPolicy_DoubleDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "double-del-group")
|
||||
|
||||
@@ -901,6 +953,7 @@ func TestDeleteGroupPolicy_DoubleDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeleteGroupPolicy(testAccountID, &iam.DeleteGroupPolicyInput{
|
||||
@@ -914,6 +967,7 @@ func TestDeleteGroupPolicy_GroupNotFound(t *testing.T) {
|
||||
// TestDeleteGroup_WithInlinePolicy proves a group carrying an inline policy
|
||||
// refuses deletion until the inline policy is removed, mirroring DeleteRole.
|
||||
func TestDeleteGroup_WithInlinePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "inline-conflict-group")
|
||||
|
||||
@@ -952,6 +1006,7 @@ func TestDeleteGroup_WithInlinePolicy(t *testing.T) {
|
||||
// permission set, and stop contributing once the user leaves the group. Without
|
||||
// this behaviour groups are cosmetic and grant nothing.
|
||||
func TestGetUserPolicies_GroupInheritance(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "grantor")
|
||||
createTestUser(t, svc, "ivy")
|
||||
@@ -1002,6 +1057,7 @@ func TestGetUserPolicies_GroupInheritance(t *testing.T) {
|
||||
// TestGetUserPolicies_DirectAndGroup proves direct user attachments and
|
||||
// group-inherited policies both surface in one combined effective set.
|
||||
func TestGetUserPolicies_DirectAndGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "combined-group")
|
||||
createTestUser(t, svc, "jack")
|
||||
@@ -1035,6 +1091,7 @@ func TestGetUserPolicies_DirectAndGroup(t *testing.T) {
|
||||
// skipped (not fail-closed): the user keeps their direct grants and the dangling
|
||||
// name remains cleanable via RemoveUserFromGroup.
|
||||
func TestGetUserPolicies_SkipsMissingGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "soon-gone")
|
||||
createTestUser(t, svc, "kim")
|
||||
@@ -1090,6 +1147,7 @@ func putRawGroupInlinePolicy(t *testing.T, svc *IAMServiceImpl, groupName, polic
|
||||
// permission set, and stop contributing once the inline policy is removed.
|
||||
// Without this, group inline policies are cosmetic and grant nothing.
|
||||
func TestGetUserPolicies_GroupInlineInheritance(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "inline-grantor")
|
||||
createTestUser(t, svc, "nora")
|
||||
@@ -1134,6 +1192,7 @@ func TestGetUserPolicies_GroupInlineInheritance(t *testing.T) {
|
||||
// and its inline document both surface in a member's combined effective set —
|
||||
// inline grants supplement managed grants rather than replacing them.
|
||||
func TestGetUserPolicies_GroupManagedAndInline(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "combined-inline-group")
|
||||
createTestUser(t, svc, "omar")
|
||||
@@ -1169,6 +1228,7 @@ func TestGetUserPolicies_GroupManagedAndInline(t *testing.T) {
|
||||
// document on a resolvable group fails the whole resolution closed rather than
|
||||
// silently dropping the grant source, mirroring GetRolePolicies.
|
||||
func TestGetUserPolicies_GroupInlineMalformedFailsClosed(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "corrupt-inline-group")
|
||||
createTestUser(t, svc, "pam")
|
||||
@@ -1190,6 +1250,7 @@ func TestGetUserPolicies_GroupInlineMalformedFailsClosed(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestDeleteUser_InGroup(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestGroup(t, svc, "blocker")
|
||||
createTestUser(t, svc, "leo")
|
||||
@@ -1224,6 +1285,7 @@ func TestDeleteUser_InGroup(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestGroups_AccountScoping(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Org A")
|
||||
|
||||
@@ -27,6 +27,7 @@ func createTestInstanceProfile(t *testing.T, svc *IAMServiceImpl, name string) *
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateInstanceProfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateInstanceProfile(testAccountID, &iam.CreateInstanceProfileInput{
|
||||
@@ -49,6 +50,7 @@ func TestCreateInstanceProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateInstanceProfile_DefaultPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateInstanceProfile(testAccountID, &iam.CreateInstanceProfileInput{
|
||||
@@ -60,6 +62,7 @@ func TestCreateInstanceProfile_DefaultPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateInstanceProfile_InvalidName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
longName := strings.Repeat("a", 129)
|
||||
|
||||
@@ -71,6 +74,7 @@ func TestCreateInstanceProfile_InvalidName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateInstanceProfile_InvalidPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateInstanceProfile(testAccountID, &iam.CreateInstanceProfileInput{
|
||||
@@ -82,6 +86,7 @@ func TestCreateInstanceProfile_InvalidPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateInstanceProfile_Duplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestInstanceProfile(t, svc, "dup-profile")
|
||||
|
||||
@@ -93,6 +98,7 @@ func TestCreateInstanceProfile_Duplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetInstanceProfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestInstanceProfile(t, svc, "get-profile")
|
||||
|
||||
@@ -105,6 +111,7 @@ func TestGetInstanceProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetInstanceProfile_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetInstanceProfile(testAccountID, &iam.GetInstanceProfileInput{
|
||||
@@ -115,6 +122,7 @@ func TestGetInstanceProfile_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetInstanceProfile_WithAttachedRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "embedded-role")
|
||||
createTestInstanceProfile(t, svc, "wrap-profile")
|
||||
@@ -134,6 +142,7 @@ func TestGetInstanceProfile_WithAttachedRole(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstanceProfiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
createTestInstanceProfile(t, svc, "profile1")
|
||||
@@ -154,6 +163,7 @@ func TestListInstanceProfiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstanceProfiles_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.ListInstanceProfiles(testAccountID, &iam.ListInstanceProfilesInput{})
|
||||
@@ -162,6 +172,7 @@ func TestListInstanceProfiles_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstanceProfiles_PathPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateInstanceProfile(testAccountID, &iam.CreateInstanceProfileInput{
|
||||
@@ -185,6 +196,7 @@ func TestListInstanceProfiles_PathPrefix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteInstanceProfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestInstanceProfile(t, svc, "del-profile")
|
||||
|
||||
@@ -201,6 +213,7 @@ func TestDeleteInstanceProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteInstanceProfile_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeleteInstanceProfile(testAccountID, &iam.DeleteInstanceProfileInput{
|
||||
@@ -211,6 +224,7 @@ func TestDeleteInstanceProfile_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteInstanceProfile_WithRoleAttached(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "attached-role")
|
||||
createTestInstanceProfile(t, svc, "loaded-profile")
|
||||
@@ -233,6 +247,7 @@ func TestDeleteInstanceProfile_WithRoleAttached(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAddRoleToInstanceProfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "binding-role")
|
||||
createTestInstanceProfile(t, svc, "binding-profile")
|
||||
@@ -252,6 +267,7 @@ func TestAddRoleToInstanceProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddRoleToInstanceProfile_RoleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestInstanceProfile(t, svc, "no-role-profile")
|
||||
|
||||
@@ -264,6 +280,7 @@ func TestAddRoleToInstanceProfile_RoleNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddRoleToInstanceProfile_ProfileNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "lonely-role")
|
||||
|
||||
@@ -276,6 +293,7 @@ func TestAddRoleToInstanceProfile_ProfileNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAddRoleToInstanceProfile_OneRoleLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "first-role")
|
||||
createTestRole(t, svc, "second-role")
|
||||
@@ -325,6 +343,7 @@ func runConcurrently(n int, fn func(i int) error) []error {
|
||||
// guard from the same revision, so the later write silently replaced the first
|
||||
// role. Exactly one attach may win; the loser must see LimitExceeded.
|
||||
func TestAddRoleToInstanceProfile_ConcurrentDistinctRoles(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
roles := []string{"race-role-a", "race-role-b"}
|
||||
for _, r := range roles {
|
||||
@@ -367,6 +386,7 @@ func TestAddRoleToInstanceProfile_ConcurrentDistinctRoles(t *testing.T) {
|
||||
// callers detach the same role at once and exactly one may win, the loser
|
||||
// re-reading an empty profile and reporting NoSuchEntity.
|
||||
func TestRemoveRoleFromInstanceProfile_ConcurrentDetach(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "detach-race-role")
|
||||
|
||||
@@ -409,6 +429,7 @@ func TestRemoveRoleFromInstanceProfile_ConcurrentDetach(t *testing.T) {
|
||||
// same lost update: a blind Put wrote back a stale RoleName and silently
|
||||
// undid a role attach that had already reported success.
|
||||
func TestTagInstanceProfile_ConcurrentWithAttach(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "tag-race-role")
|
||||
|
||||
@@ -446,6 +467,7 @@ func TestTagInstanceProfile_ConcurrentWithAttach(t *testing.T) {
|
||||
// TestTagInstanceProfile_ConcurrentDistinctKeys pins tag writes against each
|
||||
// other: under a blind Put all but one of the racing keys were lost.
|
||||
func TestTagInstanceProfile_ConcurrentDistinctKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestInstanceProfile(t, svc, "tag-keys-profile")
|
||||
|
||||
@@ -474,6 +496,7 @@ func TestTagInstanceProfile_ConcurrentDistinctKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveRoleFromInstanceProfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "remove-role")
|
||||
createTestInstanceProfile(t, svc, "remove-profile")
|
||||
@@ -498,6 +521,7 @@ func TestRemoveRoleFromInstanceProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveRoleFromInstanceProfile_NoRoleAttached(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestInstanceProfile(t, svc, "empty-profile")
|
||||
|
||||
@@ -510,6 +534,7 @@ func TestRemoveRoleFromInstanceProfile_NoRoleAttached(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveRoleFromInstanceProfile_WrongRoleName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "actual-role")
|
||||
createTestInstanceProfile(t, svc, "wrong-name-profile")
|
||||
@@ -529,6 +554,7 @@ func TestRemoveRoleFromInstanceProfile_WrongRoleName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstanceProfilesForRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "popular-role")
|
||||
|
||||
@@ -562,6 +588,7 @@ func TestListInstanceProfilesForRole(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstanceProfilesForRole_RoleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListInstanceProfilesForRole(testAccountID, &iam.ListInstanceProfilesForRoleInput{
|
||||
@@ -572,6 +599,7 @@ func TestListInstanceProfilesForRole_RoleNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListInstanceProfilesForRole_None(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "unused-role")
|
||||
|
||||
@@ -591,6 +619,7 @@ func TestListInstanceProfilesForRole_None(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestResolveInstanceProfile_ByName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestInstanceProfile(t, svc, "by-name-profile")
|
||||
|
||||
@@ -602,6 +631,7 @@ func TestResolveInstanceProfile_ByName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveInstanceProfile_ByARN(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestInstanceProfile(t, svc, "by-arn-profile")
|
||||
|
||||
@@ -612,6 +642,7 @@ func TestResolveInstanceProfile_ByARN(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveInstanceProfile_ByARNWithPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
out, err := svc.CreateInstanceProfile(testAccountID, &iam.CreateInstanceProfileInput{
|
||||
InstanceProfileName: aws.String("nested-profile"),
|
||||
@@ -626,6 +657,7 @@ func TestResolveInstanceProfile_ByARNWithPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveInstanceProfile_CrossAccountARNRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestInstanceProfile(t, svc, "shadow-profile")
|
||||
|
||||
@@ -636,6 +668,7 @@ func TestResolveInstanceProfile_CrossAccountARNRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveInstanceProfile_NameNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ResolveInstanceProfile(testAccountID, "ghost-profile")
|
||||
@@ -644,6 +677,7 @@ func TestResolveInstanceProfile_NameNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveInstanceProfile_ARNNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
arn := "arn:aws:iam::" + testAccountID + ":instance-profile/ghost"
|
||||
@@ -653,6 +687,7 @@ func TestResolveInstanceProfile_ARNNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveInstanceProfile_MalformedARN(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
cases := []string{
|
||||
@@ -670,6 +705,7 @@ func TestResolveInstanceProfile_MalformedARN(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResolveInstanceProfile_EmptyReference(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ResolveInstanceProfile(testAccountID, "")
|
||||
@@ -678,6 +714,7 @@ func TestResolveInstanceProfile_EmptyReference(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInstanceProfiles_AccountScoping(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Org A")
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
// a new writer path bypassing the helpers also fails CI.
|
||||
|
||||
func TestInvariant_AccessKeysBucket_RejectsNonAKIAPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
cases := []struct {
|
||||
@@ -48,6 +49,7 @@ func TestInvariant_AccessKeysBucket_RejectsNonAKIAPrefix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvariant_AccessKeysBucket_AcceptsAKIAPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
akid := LongLivedAccessKeyIDPrefix + "0123456789ABCDEF"
|
||||
|
||||
@@ -24,6 +24,7 @@ var rdsInternalActions = []string{
|
||||
}
|
||||
|
||||
func TestParseBuiltinManagedPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
raw map[string]string
|
||||
@@ -58,6 +59,7 @@ func TestParseBuiltinManagedPolicies(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Serial: mutates the package-level parse error that every constructor reads.
|
||||
func TestNewIAMServiceImpl_PropagatesBuiltinManagedPolicyParseError(t *testing.T) {
|
||||
parseErr := errors.New("malformed builtin policy")
|
||||
previousErr := builtinManagedPolicyParseErr
|
||||
@@ -73,6 +75,7 @@ func TestNewIAMServiceImpl_PropagatesBuiltinManagedPolicyParseError(t *testing.T
|
||||
// resolve to a grant, the way the stock EKS roles already do, rather than fail
|
||||
// closed on a document Spinifex never stores.
|
||||
func TestResolveAttachedPolicy_RDSManagedShimsResolve(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := &IAMServiceImpl{}
|
||||
for _, arn := range []string{rdsFullAccessARN, rdsReadOnlyAccessARN} {
|
||||
doc, include, err := s.resolveAttachedPolicy(t.Context(), "123456789012", arn)
|
||||
@@ -86,6 +89,7 @@ func TestResolveAttachedPolicy_RDSManagedShimsResolve(t *testing.T) {
|
||||
// a path to the master password. The gateway refuses the internal actions by
|
||||
// principal class regardless, but GetPolicyVersion must not claim otherwise.
|
||||
func TestRDSManagedShims_GrantNoInternalAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, arn := range []string{rdsFullAccessARN, rdsReadOnlyAccessARN} {
|
||||
doc, ok := builtinManagedPolicyDoc(arn)
|
||||
require.True(t, ok, "arn %q", arn)
|
||||
@@ -97,6 +101,7 @@ func TestRDSManagedShims_GrantNoInternalAction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRDSFullAccessShim_GrantsTheCustomerSurface(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, ok := builtinManagedPolicyDoc(rdsFullAccessARN)
|
||||
require.True(t, ok)
|
||||
|
||||
@@ -120,6 +125,7 @@ func TestRDSFullAccessShim_GrantsTheCustomerSurface(t *testing.T) {
|
||||
// Read-only means read-only: the shim is the whole grant for a role that carries
|
||||
// nothing else, so a mutation reaching Allow here would be the only check missed.
|
||||
func TestRDSReadOnlyAccessShim_GrantsReadsOnly(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, ok := builtinManagedPolicyDoc(rdsReadOnlyAccessARN)
|
||||
require.True(t, ok)
|
||||
|
||||
@@ -139,6 +145,7 @@ func TestRDSReadOnlyAccessShim_GrantsReadsOnly(t *testing.T) {
|
||||
// An RDS-looking managed ARN Spinifex does not model resolves to no grant rather
|
||||
// than an error, so a role carrying it is denied that grant and not broken.
|
||||
func TestResolveAttachedPolicy_UnmodeledRDSManagedARNGrantsNothing(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := &IAMServiceImpl{}
|
||||
for _, arn := range []string{
|
||||
"arn:aws:iam::aws:policy/AmazonRDSDataFullAccess",
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
const testIssuer = "https://10.0.0.1:9999/oidc/eks/ap-southeast-2/000000000001/h1"
|
||||
|
||||
func TestCreateOpenIDConnectProvider(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
acct := "000000000001"
|
||||
|
||||
@@ -39,6 +40,7 @@ func TestCreateOpenIDConnectProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateOpenIDConnectProvider_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
acct := "000000000001"
|
||||
in := &iam.CreateOpenIDConnectProviderInput{Url: aws.String(testIssuer)}
|
||||
@@ -53,6 +55,7 @@ func TestCreateOpenIDConnectProvider_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateOpenIDConnectProvider_InvalidURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
for _, bad := range []string{"", "http://insecure.example", "://nohost", "ftp://x"} {
|
||||
_, err := svc.CreateOpenIDConnectProvider("000000000001",
|
||||
@@ -64,6 +67,7 @@ func TestCreateOpenIDConnectProvider_InvalidURL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetOpenIDConnectProvider(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
acct := "000000000001"
|
||||
created, err := svc.CreateOpenIDConnectProvider(acct, &iam.CreateOpenIDConnectProviderInput{
|
||||
@@ -89,6 +93,7 @@ func TestGetOpenIDConnectProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetOpenIDConnectProvider_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
_, err := svc.GetOpenIDConnectProvider("000000000001", &iam.GetOpenIDConnectProviderInput{
|
||||
OpenIDConnectProviderArn: aws.String(OIDCProviderARN("000000000001", "10.0.0.1:9999/oidc/eks/x/y/z")),
|
||||
@@ -99,6 +104,7 @@ func TestGetOpenIDConnectProvider_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListOpenIDConnectProviders(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
acct := "000000000001"
|
||||
|
||||
@@ -137,6 +143,7 @@ func TestListOpenIDConnectProviders(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteOpenIDConnectProvider(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
acct := "000000000001"
|
||||
created, err := svc.CreateOpenIDConnectProvider(acct, &iam.CreateOpenIDConnectProviderInput{Url: aws.String(testIssuer)})
|
||||
@@ -160,6 +167,7 @@ func TestDeleteOpenIDConnectProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIssuerProviderARNRoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
arn := OIDCProviderARN("000000000001", strings.TrimPrefix(testIssuer, "https://"))
|
||||
got, err := issuerFromOIDCProviderARN(arn)
|
||||
if err != nil {
|
||||
|
||||
@@ -26,6 +26,7 @@ func newIAMService(t *testing.T) *handlers_iam.IAMServiceImpl {
|
||||
}
|
||||
|
||||
func TestNormalizeAccountName(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
@@ -43,6 +44,7 @@ func TestNormalizeAccountName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProvisionAccountCreatesAdminIdentity(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newIAMService(t)
|
||||
|
||||
got, err := handlers_iam.ProvisionAccount(svc, provisionAccountID, "ben@example.com")
|
||||
@@ -64,6 +66,7 @@ func TestProvisionAccountCreatesAdminIdentity(t *testing.T) {
|
||||
// A retry must finish the job rather than fail on the steps that already ran:
|
||||
// account creation has no rollback, so resuming is the only recovery path.
|
||||
func TestProvisionAccountResumesPartialState(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(t *testing.T, svc handlers_iam.IAMService)
|
||||
@@ -108,6 +111,7 @@ func TestProvisionAccountResumesPartialState(t *testing.T) {
|
||||
// A key from an abandoned attempt was never handed to anyone, so it must not
|
||||
// survive alongside the one that is.
|
||||
func TestProvisionAccountLeavesExactlyOneAccessKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newIAMService(t)
|
||||
|
||||
first, err := handlers_iam.ProvisionAccount(svc, provisionAccountID, "ben@example.com")
|
||||
@@ -126,6 +130,7 @@ func TestProvisionAccountLeavesExactlyOneAccessKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProvisionAccountRejectsEmptyInput(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := newIAMService(t)
|
||||
|
||||
_, err := handlers_iam.ProvisionAccount(svc, "", "ben@example.com")
|
||||
|
||||
@@ -35,6 +35,7 @@ func createTestRole(t *testing.T, svc *IAMServiceImpl, name string) *iam.Role {
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -62,6 +63,7 @@ func TestCreateRole(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_DefaultPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -74,6 +76,7 @@ func TestCreateRole_DefaultPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_DefaultMaxSessionDuration(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -85,6 +88,7 @@ func TestCreateRole_DefaultMaxSessionDuration(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_InvalidName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
longName := strings.Repeat("a", 65)
|
||||
|
||||
@@ -97,6 +101,7 @@ func TestCreateRole_InvalidName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_InvalidPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -109,6 +114,7 @@ func TestCreateRole_InvalidPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_PermissionsBoundarySet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -126,6 +132,7 @@ func TestCreateRole_PermissionsBoundarySet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_PermissionsBoundaryAbsent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
tests := []struct {
|
||||
@@ -151,6 +158,7 @@ func TestCreateRole_PermissionsBoundaryAbsent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_InvalidJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -162,6 +170,7 @@ func TestCreateRole_MalformedTrustPolicy_InvalidJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_WrongVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -173,6 +182,7 @@ func TestCreateRole_MalformedTrustPolicy_WrongVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_NoStatements(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -184,6 +194,7 @@ func TestCreateRole_MalformedTrustPolicy_NoStatements(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_MissingPrincipal(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -195,6 +206,7 @@ func TestCreateRole_MalformedTrustPolicy_MissingPrincipal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_BadEffect(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -206,6 +218,7 @@ func TestCreateRole_MalformedTrustPolicy_BadEffect(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_TooLarge(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
// Pad the Sid to push the doc past 2048 bytes.
|
||||
@@ -220,6 +233,7 @@ func TestCreateRole_MalformedTrustPolicy_TooLarge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_TrustPolicy_DenyEffectAccepted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Principal":"*","Action":"sts:AssumeRole"}]}`
|
||||
@@ -231,6 +245,7 @@ func TestCreateRole_TrustPolicy_DenyEffectAccepted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MaxSessionDuration_TooSmall(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -243,6 +258,7 @@ func TestCreateRole_MaxSessionDuration_TooSmall(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MaxSessionDuration_TooLarge(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -255,6 +271,7 @@ func TestCreateRole_MaxSessionDuration_TooLarge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_Duplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "dup-role")
|
||||
|
||||
@@ -267,6 +284,7 @@ func TestCreateRole_Duplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "get-role")
|
||||
|
||||
@@ -279,6 +297,7 @@ func TestGetRole(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRole_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetRole(testAccountID, &iam.GetRoleInput{
|
||||
@@ -289,6 +308,7 @@ func TestGetRole_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListRoles(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
createTestRole(t, svc, "role1")
|
||||
@@ -309,6 +329,7 @@ func TestListRoles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListRoles_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.ListRoles(testAccountID, &iam.ListRolesInput{})
|
||||
@@ -317,6 +338,7 @@ func TestListRoles_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListRoles_PathPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateRole(testAccountID, &iam.CreateRoleInput{
|
||||
@@ -342,6 +364,7 @@ func TestListRoles_PathPrefix(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "del-role")
|
||||
|
||||
@@ -358,6 +381,7 @@ func TestDeleteRole(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRole_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeleteRole(testAccountID, &iam.DeleteRoleInput{
|
||||
@@ -368,6 +392,7 @@ func TestDeleteRole_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRole_WithAttachedPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "attached-role")
|
||||
policy := createTestPolicy(t, svc, "RolePolicy")
|
||||
@@ -386,6 +411,7 @@ func TestDeleteRole_WithAttachedPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRole_InInstanceProfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "inprofile-role")
|
||||
|
||||
@@ -408,6 +434,7 @@ func TestDeleteRole_InInstanceProfile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "upd-role")
|
||||
|
||||
@@ -427,6 +454,7 @@ func TestUpdateRole(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateRole_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.UpdateRole(testAccountID, &iam.UpdateRoleInput{
|
||||
@@ -438,6 +466,7 @@ func TestUpdateRole_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateRole_InvalidMaxSessionDuration(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "session-role")
|
||||
|
||||
@@ -450,6 +479,7 @@ func TestUpdateRole_InvalidMaxSessionDuration(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAssumeRolePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "trust-role")
|
||||
|
||||
@@ -468,6 +498,7 @@ func TestUpdateAssumeRolePolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAssumeRolePolicy_Malformed(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "trust-role-malformed")
|
||||
|
||||
@@ -480,6 +511,7 @@ func TestUpdateAssumeRolePolicy_Malformed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAssumeRolePolicy_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.UpdateAssumeRolePolicy(testAccountID, &iam.UpdateAssumeRolePolicyInput{
|
||||
@@ -495,6 +527,7 @@ func TestUpdateAssumeRolePolicy_NotFound(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAttachRolePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "attachtarget")
|
||||
policy := createTestPolicy(t, svc, "AttachPolicy")
|
||||
@@ -515,6 +548,7 @@ func TestAttachRolePolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachRolePolicy_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "idempotent")
|
||||
policy := createTestPolicy(t, svc, "IdempotentPolicy")
|
||||
@@ -539,6 +573,7 @@ func TestAttachRolePolicy_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachRolePolicy_PolicyNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "needspolicy")
|
||||
|
||||
@@ -551,6 +586,7 @@ func TestAttachRolePolicy_PolicyNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachRolePolicy_RoleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
policy := createTestPolicy(t, svc, "OrphanPolicy")
|
||||
|
||||
@@ -563,6 +599,7 @@ func TestAttachRolePolicy_RoleNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachRolePolicy_AWSManagedPolicyNotSeeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "eks-node-role")
|
||||
// AWS-managed ARN with no backing policy in the store — stock EKS tooling
|
||||
@@ -597,6 +634,7 @@ func TestAttachRolePolicy_AWSManagedPolicyNotSeeded(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachRolePolicy_MalformedAWSManagedPolicyRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "malformed-managed-policy")
|
||||
|
||||
@@ -615,6 +653,7 @@ func TestAttachRolePolicy_MalformedAWSManagedPolicyRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachRolePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "detach-role")
|
||||
policy := createTestPolicy(t, svc, "DetachPolicy")
|
||||
@@ -639,6 +678,7 @@ func TestDetachRolePolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachRolePolicy_NotAttached(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "detach-empty")
|
||||
policy := createTestPolicy(t, svc, "NotAttachedPolicy")
|
||||
@@ -657,6 +697,7 @@ func TestDetachRolePolicy_NotAttached(t *testing.T) {
|
||||
// from the same revision and clobbered each other); the CAS path must persist
|
||||
// all three.
|
||||
func TestAttachRolePolicy_ConcurrentDistinctPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "eks-node-role")
|
||||
|
||||
@@ -696,6 +737,7 @@ func TestAttachRolePolicy_ConcurrentDistinctPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAttachedRolePolicies_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "empty-attach")
|
||||
|
||||
@@ -711,6 +753,7 @@ func TestListAttachedRolePolicies_Empty(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestGetRolePolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "eval-role")
|
||||
p1 := createTestPolicy(t, svc, "RolePolicy1")
|
||||
@@ -739,6 +782,7 @@ func TestGetRolePolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRolePolicies_NoPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "bare-role")
|
||||
|
||||
@@ -748,6 +792,7 @@ func TestGetRolePolicies_NoPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRolePolicies_NonexistentRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetRolePolicies(testAccountID, "ghost-role")
|
||||
@@ -759,6 +804,7 @@ func TestGetRolePolicies_NonexistentRole(t *testing.T) {
|
||||
// referencing an attached policy ARN that can no longer be resolved must return
|
||||
// an error so the gateway denies rather than evaluating a partial policy set.
|
||||
func TestGetRolePolicies_UnresolvablePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "dangling-role")
|
||||
|
||||
@@ -782,6 +828,7 @@ func TestGetRolePolicies_UnresolvablePolicy(t *testing.T) {
|
||||
// grant documents, so assumed-role authorization honours them instead of
|
||||
// denying every call.
|
||||
func TestGetRolePolicies_AWSManagedResolved(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "eks-node-role")
|
||||
|
||||
@@ -809,6 +856,7 @@ func TestGetRolePolicies_AWSManagedResolved(t *testing.T) {
|
||||
// does not model resolves to no grant (fail-closed deny) rather than erroring
|
||||
// the whole request.
|
||||
func TestGetRolePolicies_UnmodeledManagedSkipped(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "unknown-managed-role")
|
||||
_, err := svc.AttachRolePolicy(testAccountID, &iam.AttachRolePolicyInput{
|
||||
@@ -844,6 +892,7 @@ func inlineDenyDocument() string {
|
||||
}
|
||||
|
||||
func TestPutRolePolicy_RoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "inline-role")
|
||||
|
||||
@@ -865,6 +914,7 @@ func TestPutRolePolicy_RoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutRolePolicy_IdempotentOverwrite(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "overwrite-role")
|
||||
|
||||
@@ -897,6 +947,7 @@ func TestPutRolePolicy_IdempotentOverwrite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutRolePolicy_InvalidName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "badname-role")
|
||||
|
||||
@@ -910,6 +961,7 @@ func TestPutRolePolicy_InvalidName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutRolePolicy_MalformedDocument(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "malformed-role")
|
||||
|
||||
@@ -923,6 +975,7 @@ func TestPutRolePolicy_MalformedDocument(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutRolePolicy_OversizedDocument(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "oversized-role")
|
||||
|
||||
@@ -937,6 +990,7 @@ func TestPutRolePolicy_OversizedDocument(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutRolePolicy_RoleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.PutRolePolicy(testAccountID, &iam.PutRolePolicyInput{
|
||||
@@ -949,6 +1003,7 @@ func TestPutRolePolicy_RoleNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRolePolicy_UnknownName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "get-unknown")
|
||||
|
||||
@@ -961,6 +1016,7 @@ func TestGetRolePolicy_UnknownName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetRolePolicy_RoleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetRolePolicy(testAccountID, &iam.GetRolePolicyInput{
|
||||
@@ -972,6 +1028,7 @@ func TestGetRolePolicy_RoleNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListRolePolicies_Sorted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "list-role")
|
||||
|
||||
@@ -997,6 +1054,7 @@ func TestListRolePolicies_Sorted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListRolePolicies_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "empty-inline")
|
||||
|
||||
@@ -1010,6 +1068,7 @@ func TestListRolePolicies_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListRolePolicies_RoleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListRolePolicies(testAccountID, &iam.ListRolePoliciesInput{
|
||||
@@ -1020,6 +1079,7 @@ func TestListRolePolicies_RoleNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRolePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "del-inline")
|
||||
|
||||
@@ -1051,6 +1111,7 @@ func TestDeleteRolePolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRolePolicy_DoubleDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "double-del")
|
||||
|
||||
@@ -1076,6 +1137,7 @@ func TestDeleteRolePolicy_DoubleDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRolePolicy_RoleNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeleteRolePolicy(testAccountID, &iam.DeleteRolePolicyInput{
|
||||
@@ -1087,6 +1149,7 @@ func TestDeleteRolePolicy_RoleNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteRole_WithInlinePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "inline-conflict")
|
||||
|
||||
@@ -1119,6 +1182,7 @@ func TestDeleteRole_WithInlinePolicy(t *testing.T) {
|
||||
// TestGetRolePolicies_Inline proves the enforcement resolver walks inline
|
||||
// policies, not just managed attachments, so an inline statement is evaluated.
|
||||
func TestGetRolePolicies_Inline(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "inline-eval")
|
||||
|
||||
@@ -1138,6 +1202,7 @@ func TestGetRolePolicies_Inline(t *testing.T) {
|
||||
// TestGetRolePolicies_ManagedAndInline proves managed and inline documents both
|
||||
// surface from the resolver in one combined set.
|
||||
func TestGetRolePolicies_ManagedAndInline(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
role := createTestRole(t, svc, "combined-eval")
|
||||
policy := createTestPolicy(t, svc, "ManagedAllow")
|
||||
@@ -1167,6 +1232,7 @@ func TestGetRolePolicies_ManagedAndInline(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestRoles_AccountScoping(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Org A")
|
||||
@@ -1221,6 +1287,7 @@ func TestRoles_AccountScoping(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_ConditionRejected_StringEquals(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"sts:AssumeRole","Condition":{"StringEquals":{"sts:ExternalId":"abc"}}}]}`
|
||||
@@ -1233,6 +1300,7 @@ func TestCreateRole_MalformedTrustPolicy_ConditionRejected_StringEquals(t *testi
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_ConditionRejected_IpAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"sts:AssumeRole","Condition":{"IpAddress":{"aws:SourceIp":"10.0.0.0/8"}}}]}`
|
||||
@@ -1245,6 +1313,7 @@ func TestCreateRole_MalformedTrustPolicy_ConditionRejected_IpAddress(t *testing.
|
||||
}
|
||||
|
||||
func TestCreateRole_TrustPolicy_EmptyConditionAccepted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
// Condition: {} is trivially empty — no field actually present at
|
||||
@@ -1258,6 +1327,7 @@ func TestCreateRole_TrustPolicy_EmptyConditionAccepted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_TrustPolicy_NullConditionAccepted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"sts:AssumeRole","Condition":null}]}`
|
||||
@@ -1269,6 +1339,7 @@ func TestCreateRole_TrustPolicy_NullConditionAccepted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_NotPrincipalRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","NotPrincipal":{"AWS":"arn:aws:iam::123456789012:user/Bob"},"Action":"sts:AssumeRole"}]}`
|
||||
@@ -1281,6 +1352,7 @@ func TestCreateRole_MalformedTrustPolicy_NotPrincipalRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_NotActionRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":"*"},"NotAction":["sts:AssumeRole"]}]}`
|
||||
@@ -1293,6 +1365,7 @@ func TestCreateRole_MalformedTrustPolicy_NotActionRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_EmptyActionElement(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
// len(Action) > 0, but the single element is empty — the original
|
||||
@@ -1307,6 +1380,7 @@ func TestCreateRole_MalformedTrustPolicy_EmptyActionElement(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_EmptyPrincipalObject(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{},"Action":"sts:AssumeRole"}]}`
|
||||
@@ -1319,6 +1393,7 @@ func TestCreateRole_MalformedTrustPolicy_EmptyPrincipalObject(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_EmptyPrincipalArray(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":[],"Action":"sts:AssumeRole"}]}`
|
||||
@@ -1331,6 +1406,7 @@ func TestCreateRole_MalformedTrustPolicy_EmptyPrincipalArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateRole_MalformedTrustPolicy_MultiStatementOneForbidden(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
// First statement is clean; the second carries a Condition. The whole
|
||||
@@ -1348,6 +1424,7 @@ func TestCreateRole_MalformedTrustPolicy_MultiStatementOneForbidden(t *testing.T
|
||||
}
|
||||
|
||||
func TestUpdateAssumeRolePolicy_ConditionRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "update-cond")
|
||||
|
||||
@@ -1361,6 +1438,7 @@ func TestUpdateAssumeRolePolicy_ConditionRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAssumeRolePolicy_NotPrincipalRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "update-notprincipal")
|
||||
|
||||
@@ -1374,6 +1452,7 @@ func TestUpdateAssumeRolePolicy_NotPrincipalRejected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAssumeRolePolicy_NotActionRejected(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestRole(t, svc, "update-notaction")
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ func createTestUser(t *testing.T, svc *IAMServiceImpl, userName string) *iam.Use
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
|
||||
@@ -67,6 +68,7 @@ func TestCreateUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateUser_DefaultPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
|
||||
@@ -77,6 +79,7 @@ func TestCreateUser_DefaultPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateUser_Duplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
|
||||
@@ -92,6 +95,7 @@ func TestCreateUser_Duplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateUser_PermissionsBoundarySet(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
|
||||
@@ -108,6 +112,7 @@ func TestCreateUser_PermissionsBoundarySet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateUser_PermissionsBoundaryAbsent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
tests := []struct {
|
||||
@@ -132,6 +137,7 @@ func TestCreateUser_PermissionsBoundaryAbsent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "getuser")
|
||||
|
||||
@@ -143,6 +149,7 @@ func TestGetUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUser_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetUser(testAccountID, &iam.GetUserInput{
|
||||
@@ -153,6 +160,7 @@ func TestGetUser_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListUsers(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
createTestUser(t, svc, "user1")
|
||||
@@ -173,6 +181,7 @@ func TestListUsers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListUsers_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.ListUsers(testAccountID, &iam.ListUsersInput{})
|
||||
@@ -185,6 +194,7 @@ func TestListUsers_Empty(t *testing.T) {
|
||||
// match, so the filtering loop runs rather than the no-keys shortcut, and the
|
||||
// result must come back empty but non-nil.
|
||||
func TestListRequiredMembersAreEmptyNotNil(t *testing.T) {
|
||||
t.Parallel()
|
||||
const otherAccountID = "999988887777"
|
||||
|
||||
tests := []struct {
|
||||
@@ -271,6 +281,7 @@ func TestListRequiredMembersAreEmptyNotNil(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListUsers_PathFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
svc.CreateUser(testAccountID, &iam.CreateUserInput{
|
||||
@@ -291,6 +302,7 @@ func TestListUsers_PathFilter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "deleteuser")
|
||||
|
||||
@@ -307,6 +319,7 @@ func TestDeleteUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteUser_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeleteUser(testAccountID, &iam.DeleteUserInput{
|
||||
@@ -317,6 +330,7 @@ func TestDeleteUser_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteUser_WithAccessKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "userWithKeys")
|
||||
|
||||
@@ -333,6 +347,7 @@ func TestDeleteUser_WithAccessKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteUser_WithAttachedPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "policyuser")
|
||||
|
||||
@@ -361,6 +376,7 @@ func TestDeleteUser_WithAttachedPolicies(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateUser_InvalidName_TooLong(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
longName := strings.Repeat("a", 65)
|
||||
|
||||
@@ -372,6 +388,7 @@ func TestCreateUser_InvalidName_TooLong(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateUser_InvalidName_BadChars(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
|
||||
@@ -382,6 +399,7 @@ func TestCreateUser_InvalidName_BadChars(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateUser_InvalidPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateUser(testAccountID, &iam.CreateUserInput{
|
||||
@@ -393,6 +411,7 @@ func TestCreateUser_InvalidPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_InvalidName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
longName := strings.Repeat("a", 129)
|
||||
|
||||
@@ -405,6 +424,7 @@ func TestCreatePolicy_InvalidName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_InvalidPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -417,6 +437,7 @@ func TestCreatePolicy_InvalidPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_TooLarge(t *testing.T) {
|
||||
t.Parallel()
|
||||
largeDoc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"` + strings.Repeat("a", maxPolicyDocumentSize) + `"}]}`
|
||||
_, err := ValidatePolicyDocument(largeDoc)
|
||||
assert.Error(t, err)
|
||||
@@ -428,6 +449,7 @@ func TestValidatePolicyDocument_TooLarge(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestCreateAccessKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "keyuser")
|
||||
|
||||
@@ -445,6 +467,7 @@ func TestCreateAccessKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateAccessKey_SecretIsDecryptable(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "decryptuser")
|
||||
|
||||
@@ -466,6 +489,7 @@ func TestCreateAccessKey_SecretIsDecryptable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateAccessKey_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreateAccessKey(testAccountID, &iam.CreateAccessKeyInput{
|
||||
@@ -476,6 +500,7 @@ func TestCreateAccessKey_UserNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreateAccessKey_MaxLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "limituser")
|
||||
|
||||
@@ -498,6 +523,7 @@ func TestCreateAccessKey_MaxLimit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccessKeyQuota_Recovery(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "quotauser")
|
||||
|
||||
@@ -527,6 +553,7 @@ func TestAccessKeyQuota_Recovery(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccessKeyQuota_PerUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "user1")
|
||||
createTestUser(t, svc, "user2")
|
||||
@@ -543,6 +570,7 @@ func TestAccessKeyQuota_PerUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAccessKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "listkeysuser")
|
||||
|
||||
@@ -568,6 +596,7 @@ func TestListAccessKeys(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteAccessKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "delkeyuser")
|
||||
|
||||
@@ -598,6 +627,7 @@ func TestDeleteAccessKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteAccessKey_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "delnotfounduser")
|
||||
|
||||
@@ -610,6 +640,7 @@ func TestDeleteAccessKey_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAccessKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "updatekeyuser")
|
||||
|
||||
@@ -645,6 +676,7 @@ func TestUpdateAccessKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAccessKey_InvalidStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "invalidstatususer")
|
||||
|
||||
@@ -661,6 +693,7 @@ func TestUpdateAccessKey_InvalidStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAccessKey_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.UpdateAccessKey(testAccountID, &iam.UpdateAccessKeyInput{
|
||||
@@ -672,6 +705,7 @@ func TestUpdateAccessKey_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAccessKey_CaseSensitiveStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "caseuser")
|
||||
|
||||
@@ -702,6 +736,7 @@ func TestUpdateAccessKey_CaseSensitiveStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateAccessKey_CrossAccountBlocked(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
accA, err := svc.CreateAccount("Status Org A")
|
||||
@@ -730,6 +765,7 @@ func TestUpdateAccessKey_CrossAccountBlocked(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestLookupAccessKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "lookupuser")
|
||||
|
||||
@@ -746,6 +782,7 @@ func TestLookupAccessKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLookupAccessKey_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.LookupAccessKey("AKIANONEXISTENT12345")
|
||||
@@ -754,6 +791,7 @@ func TestLookupAccessKey_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLookupAccessKey_InactiveKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "inactiveuser")
|
||||
|
||||
@@ -778,6 +816,7 @@ func TestLookupAccessKey_InactiveKey(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestSeedBootstrap(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
encryptedSecret, err := svc.key.EncryptBase64("test-secret-key")
|
||||
@@ -820,6 +859,7 @@ func TestSeedBootstrap(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSeedBootstrap_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
encryptedSecret, _ := svc.key.EncryptBase64("test-secret")
|
||||
@@ -846,6 +886,7 @@ func TestSeedBootstrap_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSeedBootstrap_WithAdmin(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
systemSecret, err := svc.key.EncryptBase64("system-secret")
|
||||
@@ -914,6 +955,7 @@ func TestSeedBootstrap_WithAdmin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSeedBootstrap_AdminNil_BackwardCompat(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
encryptedSecret, err := svc.key.EncryptBase64("test-secret")
|
||||
@@ -944,6 +986,7 @@ func TestSeedBootstrap_AdminNil_BackwardCompat(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestIsEmpty_True(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
empty, err := svc.IsEmpty()
|
||||
@@ -952,6 +995,7 @@ func TestIsEmpty_True(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsEmpty_False(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "notempty")
|
||||
|
||||
@@ -965,6 +1009,7 @@ func TestIsEmpty_False(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestGenerateIAMID(t *testing.T) {
|
||||
t.Parallel()
|
||||
id, err := generateIAMID("AIDA")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "AIDA", id[:4])
|
||||
@@ -983,6 +1028,7 @@ func TestGenerateIAMID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateAccessKeyID(t *testing.T) {
|
||||
t.Parallel()
|
||||
id, err := generateAccessKeyID()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "AKIA", id[:4])
|
||||
@@ -994,6 +1040,7 @@ func TestGenerateAccessKeyID(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateSecretAccessKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
secret, err := admin.GenerateAWSSecretKey()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, secret, 40)
|
||||
@@ -1023,6 +1070,7 @@ func createTestPolicy(t *testing.T, svc *IAMServiceImpl, name string) *iam.Polic
|
||||
}
|
||||
|
||||
func TestCreatePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1045,6 +1093,7 @@ func TestCreatePolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_DefaultPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1057,6 +1106,7 @@ func TestCreatePolicy_DefaultPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_InvalidJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1068,6 +1118,7 @@ func TestCreatePolicy_InvalidJSON(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_InvalidVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1079,6 +1130,7 @@ func TestCreatePolicy_InvalidVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_NoStatements(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1090,6 +1142,7 @@ func TestCreatePolicy_NoStatements(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_InvalidEffect(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1101,6 +1154,7 @@ func TestCreatePolicy_InvalidEffect(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_MissingAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1112,6 +1166,7 @@ func TestCreatePolicy_MissingAction(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_MissingResource(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.CreatePolicy(testAccountID, &iam.CreatePolicyInput{
|
||||
@@ -1123,6 +1178,7 @@ func TestCreatePolicy_MissingResource(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_Duplicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
createTestPolicy(t, svc, "DupPolicy")
|
||||
@@ -1136,6 +1192,7 @@ func TestCreatePolicy_Duplicate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCreatePolicy_ArrayActions(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["ec2:DescribeInstances","ec2:RunInstances"],"Resource":"*"}]}`
|
||||
@@ -1148,6 +1205,7 @@ func TestCreatePolicy_ArrayActions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "GetMe")
|
||||
|
||||
@@ -1163,6 +1221,7 @@ func TestGetPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicy_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetPolicy(testAccountID, &iam.GetPolicyInput{
|
||||
@@ -1173,6 +1232,7 @@ func TestGetPolicy_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicy_MalformedARN(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetPolicy(testAccountID, &iam.GetPolicyInput{
|
||||
@@ -1183,6 +1243,7 @@ func TestGetPolicy_MalformedARN(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicy_ARNParsingEdgeCases(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`
|
||||
|
||||
@@ -1220,6 +1281,7 @@ func TestGetPolicy_ARNParsingEdgeCases(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicy_WithAttachments(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "AttachedPolicy")
|
||||
createTestUser(t, svc, "attachuser1")
|
||||
@@ -1242,6 +1304,7 @@ func TestGetPolicy_WithAttachments(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicyVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "VersionPolicy")
|
||||
|
||||
@@ -1262,6 +1325,7 @@ func TestGetPolicyVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicyVersion_InvalidVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "VersionPolicy2")
|
||||
|
||||
@@ -1274,6 +1338,7 @@ func TestGetPolicyVersion_InvalidVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetPolicyVersion_PolicyNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetPolicyVersion(testAccountID, &iam.GetPolicyVersionInput{
|
||||
@@ -1285,6 +1350,7 @@ func TestGetPolicyVersion_PolicyNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListPolicyVersions(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "ListVersionPolicy")
|
||||
|
||||
@@ -1301,6 +1367,7 @@ func TestListPolicyVersions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListPolicyVersions_PolicyNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListPolicyVersions(testAccountID, &iam.ListPolicyVersionsInput{
|
||||
@@ -1311,6 +1378,7 @@ func TestListPolicyVersions_PolicyNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
createTestPolicy(t, svc, "Policy1")
|
||||
@@ -1331,6 +1399,7 @@ func TestListPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListPolicies_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
out, err := svc.ListPolicies(testAccountID, &iam.ListPoliciesInput{})
|
||||
@@ -1339,6 +1408,7 @@ func TestListPolicies_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeletePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "DeleteMe")
|
||||
|
||||
@@ -1356,6 +1426,7 @@ func TestDeletePolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeletePolicy_NotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeletePolicy(testAccountID, &iam.DeletePolicyInput{
|
||||
@@ -1366,6 +1437,7 @@ func TestDeletePolicy_NotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeletePolicy_AttachedConflict(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "Attached")
|
||||
createTestUser(t, svc, "conflictuser")
|
||||
@@ -1400,6 +1472,7 @@ func TestDeletePolicy_AttachedConflict(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestAttachUserPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "AttachPolicy")
|
||||
createTestUser(t, svc, "attachme")
|
||||
@@ -1421,6 +1494,7 @@ func TestAttachUserPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachUserPolicy_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "IdempotentPolicy")
|
||||
createTestUser(t, svc, "idempotentuser")
|
||||
@@ -1446,6 +1520,7 @@ func TestAttachUserPolicy_Idempotent(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachUserPolicy_NonexistentPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "orphanuser")
|
||||
|
||||
@@ -1458,6 +1533,7 @@ func TestAttachUserPolicy_NonexistentPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachUserPolicy_NonexistentUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "OrphanPolicy")
|
||||
|
||||
@@ -1470,6 +1546,7 @@ func TestAttachUserPolicy_NonexistentUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachUserPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "DetachPolicy")
|
||||
createTestUser(t, svc, "detachme")
|
||||
@@ -1495,6 +1572,7 @@ func TestDetachUserPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachUserPolicy_NotAttached(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
created := createTestPolicy(t, svc, "NeverAttached")
|
||||
createTestUser(t, svc, "cleanuser")
|
||||
@@ -1508,6 +1586,7 @@ func TestDetachUserPolicy_NotAttached(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetachUserPolicy_NonexistentUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DetachUserPolicy(testAccountID, &iam.DetachUserPolicyInput{
|
||||
@@ -1519,6 +1598,7 @@ func TestDetachUserPolicy_NonexistentUser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAttachedUserPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
p1 := createTestPolicy(t, svc, "ListPolicy1")
|
||||
@@ -1549,6 +1629,7 @@ func TestListAttachedUserPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAttachedUserPolicies_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "nopolicies")
|
||||
|
||||
@@ -1560,6 +1641,7 @@ func TestListAttachedUserPolicies_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListAttachedUserPolicies_NonexistentUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListAttachedUserPolicies(testAccountID, &iam.ListAttachedUserPoliciesInput{
|
||||
@@ -1574,6 +1656,7 @@ func TestListAttachedUserPolicies_NonexistentUser(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestGetUserPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"ec2:DescribeInstances","Resource":"*"}]}`
|
||||
@@ -1609,6 +1692,7 @@ func TestGetUserPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUserPolicies_NoPolicies(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "emptyuser")
|
||||
|
||||
@@ -1618,6 +1702,7 @@ func TestGetUserPolicies_NoPolicies(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUserPolicies_NonexistentUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetUserPolicies(testAccountID, "ghostuser")
|
||||
@@ -1630,6 +1715,7 @@ func TestGetUserPolicies_NonexistentUser(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestValidatePolicyDocument_Valid(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "2012-10-17", doc.Version)
|
||||
@@ -1638,6 +1724,7 @@ func TestValidatePolicyDocument_Valid(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_MultipleStatements(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[
|
||||
@@ -1652,41 +1739,48 @@ func TestValidatePolicyDocument_MultipleStatements(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_BadJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{broken`)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_WrongVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2008-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "unsupported policy version")
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_EmptyStatements(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[]}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "at least one statement")
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_BadEffect(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Maybe","Action":"*","Resource":"*"}]}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Effect must be Allow or Deny")
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_MissingAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Resource":"*"}]}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Action is required")
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_MissingResource(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*"}]}`)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Resource is required")
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_SupportedCondition(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:*","Resource":"*",
|
||||
"Condition":{"IpAddress":{"aws:SourceIp":"10.0.0.0/8"}}}]}`)
|
||||
require.NoError(t, err)
|
||||
@@ -1695,6 +1789,7 @@ func TestValidatePolicyDocument_SupportedCondition(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_UnsupportedConditionOperator(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:*","Resource":"*",
|
||||
"Condition":{"StringEquals":{"aws:SourceIp":"10.0.0.1"}}}]}`)
|
||||
assert.Error(t, err)
|
||||
@@ -1702,6 +1797,7 @@ func TestValidatePolicyDocument_UnsupportedConditionOperator(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_UnsupportedConditionKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:*","Resource":"*",
|
||||
"Condition":{"StringEquals":{"aws:PrincipalOrgID":"o-1234"}}}]}`)
|
||||
assert.Error(t, err)
|
||||
@@ -1711,6 +1807,7 @@ func TestValidatePolicyDocument_UnsupportedConditionKey(t *testing.T) {
|
||||
// MFA does not exist in spinifex, so this key could never be true and accepting
|
||||
// it would mint a grant that silently never fires.
|
||||
func TestValidatePolicyDocument_RejectsMFACondition(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*",
|
||||
"Condition":{"Bool":{"aws:MultiFactorAuthPresent":"true"}}}]}`)
|
||||
assert.Error(t, err)
|
||||
@@ -1718,6 +1815,7 @@ func TestValidatePolicyDocument_RejectsMFACondition(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_RejectsPrincipal(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*",
|
||||
"Principal":{"AWS":"arn:aws:iam::123456789012:root"}}]}`)
|
||||
assert.Error(t, err)
|
||||
@@ -1725,6 +1823,7 @@ func TestValidatePolicyDocument_RejectsPrincipal(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_RejectsNotAction(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"*","Resource":"*",
|
||||
"NotAction":"sts:AssumeRole"}]}`)
|
||||
assert.Error(t, err)
|
||||
@@ -1735,6 +1834,7 @@ func TestValidatePolicyDocument_RejectsNotAction(t *testing.T) {
|
||||
// matcher cannot parse it, so the Allow never fires and the operator gets a bare
|
||||
// AccessDenied with nothing naming the cause.
|
||||
func TestValidatePolicyDocument_RejectsMalformedConditionValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
cond string
|
||||
@@ -1760,6 +1860,7 @@ func TestValidatePolicyDocument_RejectsMalformedConditionValues(t *testing.T) {
|
||||
// D4's lenient leaf parsing must survive at the API surface: a native JSON bool
|
||||
// is what Terraform emits, and rejecting it would fail ordinary valid policies.
|
||||
func TestValidatePolicyDocument_AcceptsNativeBoolAndCIDRForms(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, cond := range []string{
|
||||
`{"Bool":{"aws:SecureTransport":true}}`,
|
||||
`{"Bool":{"aws:SecureTransport":"TRUE"}}`,
|
||||
@@ -1775,6 +1876,7 @@ func TestValidatePolicyDocument_AcceptsNativeBoolAndCIDRForms(t *testing.T) {
|
||||
// Map iteration order makes a break-instead-of-continue bug non-deterministic,
|
||||
// so a single-operator document cannot pin the loop.
|
||||
func TestValidatePolicyDocument_RejectsUnsupportedAlongsideSupported(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"s3:*","Resource":"*",
|
||||
"Condition":{"IpAddress":{"aws:SourceIp":"10.0.0.0/8"},"StringEquals":{"aws:PrincipalOrgID":"o-1234"}}}]}`)
|
||||
require.Error(t, err)
|
||||
@@ -1782,6 +1884,7 @@ func TestValidatePolicyDocument_RejectsUnsupportedAlongsideSupported(t *testing.
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_RejectsNotResource(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"*","Resource":"*",
|
||||
"NotResource":"arn:aws:s3:::public/*"}]}`)
|
||||
assert.Error(t, err)
|
||||
@@ -1792,6 +1895,9 @@ func TestValidatePolicyDocument_RejectsNotResource(t *testing.T) {
|
||||
// Sensitive Data Not Logged Tests
|
||||
// ============================================================================
|
||||
|
||||
// Serial: swaps the process-wide default logger to capture output. In parallel
|
||||
// a racing restore would leave this asserting against an empty buffer, which
|
||||
// passes while proving nothing.
|
||||
func TestSensitiveDataNotLogged_CreateAccessKey(t *testing.T) {
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "loguser")
|
||||
@@ -1823,6 +1929,7 @@ func TestSensitiveDataNotLogged_CreateAccessKey(t *testing.T) {
|
||||
"encrypted secret must not appear in log output")
|
||||
}
|
||||
|
||||
// Serial: swaps the process-wide default logger, as above.
|
||||
func TestSensitiveDataNotLogged_MasterKey(t *testing.T) {
|
||||
// Capture log output during service init
|
||||
var buf strings.Builder
|
||||
@@ -1852,6 +1959,7 @@ func TestSensitiveDataNotLogged_MasterKey(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestInputValidation_UserNameLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
// 64 chars — should pass
|
||||
@@ -1866,6 +1974,7 @@ func TestInputValidation_UserNameLength(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInputValidation_PolicyNameLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
doc := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`
|
||||
|
||||
@@ -1887,6 +1996,7 @@ func TestInputValidation_PolicyNameLength(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInputValidation_PathLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
// 512 chars — should pass (path must start and end with /)
|
||||
@@ -1909,6 +2019,7 @@ func TestInputValidation_PathLength(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInputValidation_PolicyDocumentSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
// 6144 bytes — should pass
|
||||
filler6144 := strings.Repeat("a", 6144-len(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"`)-len(`"}]}`))
|
||||
doc6144 := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"` + filler6144 + `"}]}`
|
||||
@@ -1923,6 +2034,7 @@ func TestInputValidation_PolicyDocumentSize(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_OverlappingDenyAllow(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[
|
||||
@@ -1939,6 +2051,7 @@ func TestValidatePolicyDocument_OverlappingDenyAllow(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_WithoutSid(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[{"Effect":"Allow","Action":"s3:GetObject","Resource":"*"}]
|
||||
@@ -1948,6 +2061,7 @@ func TestValidatePolicyDocument_WithoutSid(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_ActionSingleString(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[{"Effect":"Allow","Action":"s3:*","Resource":"*"}]
|
||||
@@ -1957,6 +2071,7 @@ func TestValidatePolicyDocument_ActionSingleString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_ActionArray(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[{"Effect":"Allow","Action":["s3:Get*","s3:List*"],"Resource":"*"}]
|
||||
@@ -1966,6 +2081,7 @@ func TestValidatePolicyDocument_ActionArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_EmptyActionArray(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[{"Effect":"Allow","Action":[],"Resource":"*"}]
|
||||
@@ -1975,6 +2091,7 @@ func TestValidatePolicyDocument_EmptyActionArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_EmptyResourceArray(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[{"Effect":"Allow","Action":"s3:*","Resource":[]}]
|
||||
@@ -1984,6 +2101,7 @@ func TestValidatePolicyDocument_EmptyResourceArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyDocument_ResourceARNFormat(t *testing.T) {
|
||||
t.Parallel()
|
||||
doc, err := ValidatePolicyDocument(`{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[{"Effect":"Allow","Action":"s3:GetObject","Resource":"arn:aws:s3:::my-bucket/*"}]
|
||||
@@ -1997,6 +2115,7 @@ func TestValidatePolicyDocument_ResourceARNFormat(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestGeneratePolicyID(t *testing.T) {
|
||||
t.Parallel()
|
||||
id, err := generateIAMID("ANPA")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "ANPA", id[:4])
|
||||
@@ -2012,6 +2131,7 @@ func TestGeneratePolicyID(t *testing.T) {
|
||||
// ============================================================================
|
||||
|
||||
func TestIsIAMNameChar(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Valid characters
|
||||
for _, c := range "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+=,.@-_" {
|
||||
assert.True(t, isIAMNameChar(byte(c)), "expected valid: %c", c)
|
||||
@@ -2023,6 +2143,7 @@ func TestIsIAMNameChar(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateUserName(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
@@ -2051,6 +2172,7 @@ func TestValidateUserName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePolicyName(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
@@ -2078,6 +2200,7 @@ func TestValidatePolicyName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePath(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
@@ -2105,6 +2228,7 @@ func TestValidatePath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseCreatedAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Valid RFC3339
|
||||
ts := "2024-01-15T10:30:00Z"
|
||||
result := parseCreatedAt(ts)
|
||||
@@ -2130,6 +2254,7 @@ func TestParseCreatedAt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateIAMID_AllUpperHex(t *testing.T) {
|
||||
t.Parallel()
|
||||
for range 20 {
|
||||
id, err := generateIAMID("AIDA")
|
||||
assert.NoError(t, err)
|
||||
@@ -2142,6 +2267,7 @@ func TestGenerateIAMID_AllUpperHex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateAccessKeyID_AllUpperHex(t *testing.T) {
|
||||
t.Parallel()
|
||||
for range 20 {
|
||||
id, err := generateAccessKeyID()
|
||||
assert.NoError(t, err)
|
||||
@@ -2154,6 +2280,7 @@ func TestGenerateAccessKeyID_AllUpperHex(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachUserPolicy_AWSManagedOpaque(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "managed-user")
|
||||
const managedARN = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPullOnly"
|
||||
@@ -2175,6 +2302,7 @@ func TestAttachUserPolicy_AWSManagedOpaque(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttachUserPolicy_CustomerManagedMustExist(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "strict-user")
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
// TestEnsureSystemInstanceProfile_CreatesAll asserts the role (EC2 trust), inline
|
||||
// policy, and instance profile are all created and the profile ARN is returned.
|
||||
func TestEnsureSystemInstanceProfile_CreatesAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := iammock.New()
|
||||
arn, err := handlers_iam.EnsureSystemInstanceProfile(f, testRoleAcct, testRoleName, testPolicyName, testPolicyDoc)
|
||||
require.NoError(t, err)
|
||||
@@ -39,6 +40,7 @@ func TestEnsureSystemInstanceProfile_CreatesAll(t *testing.T) {
|
||||
// TestEnsureSystemInstanceProfile_Idempotent asserts a re-run against existing
|
||||
// role+profile creates nothing new but still re-asserts the inline policy.
|
||||
func TestEnsureSystemInstanceProfile_Idempotent(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := iammock.New()
|
||||
_, err := handlers_iam.EnsureSystemInstanceProfile(f, testRoleAcct, testRoleName, testPolicyName, testPolicyDoc)
|
||||
require.NoError(t, err)
|
||||
@@ -53,6 +55,7 @@ func TestEnsureSystemInstanceProfile_Idempotent(t *testing.T) {
|
||||
// TestConvergeSystemRolePolicy_RewritesExistingRole asserts an account that
|
||||
// already carries the role picks up a changed document.
|
||||
func TestConvergeSystemRolePolicy_RewritesExistingRole(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := iammock.New()
|
||||
_, err := handlers_iam.EnsureSystemInstanceProfile(f, testRoleAcct, testRoleName, testPolicyName, testPolicyDoc)
|
||||
require.NoError(t, err)
|
||||
@@ -66,6 +69,7 @@ func TestConvergeSystemRolePolicy_RewritesExistingRole(t *testing.T) {
|
||||
// keeps a fleet-wide converge pass from writing IAM entities into accounts that
|
||||
// never launched capacity: no role means nothing to converge.
|
||||
func TestConvergeSystemRolePolicy_AbsentRoleIsNotProvisioned(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := iammock.New()
|
||||
require.NoError(t, handlers_iam.ConvergeSystemRolePolicy(f, testRoleAcct, testRoleName, testPolicyName, testPolicyDoc))
|
||||
|
||||
@@ -78,6 +82,7 @@ func TestConvergeSystemRolePolicy_AbsentRoleIsNotProvisioned(t *testing.T) {
|
||||
// TestConvergeSystemRolePolicy_PropagatesPutFailure keeps a failed write a
|
||||
// failure: swallowing it reports a converged fleet that is not converged.
|
||||
func TestConvergeSystemRolePolicy_PropagatesPutFailure(t *testing.T) {
|
||||
t.Parallel()
|
||||
f := iammock.New()
|
||||
_, err := handlers_iam.EnsureSystemInstanceProfile(f, testRoleAcct, testRoleName, testPolicyName, testPolicyDoc)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -17,6 +17,7 @@ func sdkTag(key, value string) *iam.Tag {
|
||||
}
|
||||
|
||||
func TestValidateTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
tooMany := make([]*iam.Tag, maxTagsPerResource+1)
|
||||
for i := range tooMany {
|
||||
tooMany[i] = sdkTag("key"+strings.Repeat("a", i%100+1), "v")
|
||||
@@ -55,6 +56,7 @@ func TestValidateTags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMergeTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
existing []Tag
|
||||
@@ -91,12 +93,14 @@ func TestMergeTags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMergeTags_DoesNotMutateExisting(t *testing.T) {
|
||||
t.Parallel()
|
||||
existing := []Tag{{Key: "a", Value: "1"}}
|
||||
_ = mergeTags(existing, []*iam.Tag{sdkTag("a", "9")})
|
||||
assert.Equal(t, []Tag{{Key: "a", Value: "1"}}, existing)
|
||||
}
|
||||
|
||||
func TestRemoveTagKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
existing := []Tag{{Key: "a", Value: "1"}, {Key: "b", Value: "2"}, {Key: "c", Value: "3"}}
|
||||
|
||||
cases := []struct {
|
||||
@@ -262,6 +266,7 @@ func tagsAsMap(tags []*iam.Tag) map[string]string {
|
||||
}
|
||||
|
||||
func TestResourceTagging_RoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, ops := range allTagOps() {
|
||||
t.Run(ops.resource, func(t *testing.T) {
|
||||
svc := setupTestIAMService(t)
|
||||
@@ -292,6 +297,7 @@ func TestResourceTagging_RoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTagUser_GetUserSurfacesTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
user := createTestUser(t, svc, "tagme")
|
||||
|
||||
@@ -307,6 +313,7 @@ func TestTagUser_GetUserSurfacesTags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResourceTagging_MissingEntity(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, ops := range allTagOps() {
|
||||
t.Run(ops.resource, func(t *testing.T) {
|
||||
svc := setupTestIAMService(t)
|
||||
@@ -327,6 +334,7 @@ func TestResourceTagging_MissingEntity(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTagUser_MergedLimitExceeded(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "taglimit")
|
||||
|
||||
@@ -354,6 +362,7 @@ func TestTagUser_MergedLimitExceeded(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTagsToSDK(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Nil(t, tagsToSDK(nil))
|
||||
assert.Nil(t, tagsToSDK([]Tag{}))
|
||||
got := tagsToSDK([]Tag{{Key: "a", Value: "1"}})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestStringOrArr_UnmarshalSingleString(t *testing.T) {
|
||||
t.Parallel()
|
||||
var s StringOrArr
|
||||
err := json.Unmarshal([]byte(`"s3:GetObject"`), &s)
|
||||
require.NoError(t, err)
|
||||
@@ -16,6 +17,7 @@ func TestStringOrArr_UnmarshalSingleString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringOrArr_UnmarshalArray(t *testing.T) {
|
||||
t.Parallel()
|
||||
var s StringOrArr
|
||||
err := json.Unmarshal([]byte(`["s3:Get*","s3:List*"]`), &s)
|
||||
require.NoError(t, err)
|
||||
@@ -23,6 +25,7 @@ func TestStringOrArr_UnmarshalArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringOrArr_UnmarshalNull(t *testing.T) {
|
||||
t.Parallel()
|
||||
// A JSON null yields a nil slice (an inert field), not [""].
|
||||
var s StringOrArr
|
||||
err := json.Unmarshal([]byte(`null`), &s)
|
||||
@@ -31,6 +34,7 @@ func TestStringOrArr_UnmarshalNull(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringOrArr_UnmarshalEmptyArray(t *testing.T) {
|
||||
t.Parallel()
|
||||
var s StringOrArr
|
||||
err := json.Unmarshal([]byte(`[]`), &s)
|
||||
require.NoError(t, err)
|
||||
@@ -38,6 +42,7 @@ func TestStringOrArr_UnmarshalEmptyArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringOrArr_MarshalSingleElement(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := StringOrArr{"ec2:*"}
|
||||
data, err := json.Marshal(s)
|
||||
require.NoError(t, err)
|
||||
@@ -45,6 +50,7 @@ func TestStringOrArr_MarshalSingleElement(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringOrArr_MarshalMultipleElements(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := StringOrArr{"s3:Get*", "s3:Put*"}
|
||||
data, err := json.Marshal(s)
|
||||
require.NoError(t, err)
|
||||
@@ -52,6 +58,7 @@ func TestStringOrArr_MarshalMultipleElements(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringOrArr_RoundTrip_Single(t *testing.T) {
|
||||
t.Parallel()
|
||||
original := StringOrArr{"iam:CreateUser"}
|
||||
data, err := json.Marshal(original)
|
||||
require.NoError(t, err)
|
||||
@@ -63,6 +70,7 @@ func TestStringOrArr_RoundTrip_Single(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStringOrArr_RoundTrip_Array(t *testing.T) {
|
||||
t.Parallel()
|
||||
original := StringOrArr{"ec2:Describe*", "ec2:Run*", "ec2:Stop*"}
|
||||
data, err := json.Marshal(original)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
// ============================================================================
|
||||
|
||||
func TestPutUserPolicy_RoundTrip(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "inline-user")
|
||||
|
||||
@@ -38,6 +39,7 @@ func TestPutUserPolicy_RoundTrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutUserPolicy_IdempotentOverwrite(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "overwrite-user")
|
||||
|
||||
@@ -70,6 +72,7 @@ func TestPutUserPolicy_IdempotentOverwrite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutUserPolicy_InvalidName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "badname-user")
|
||||
|
||||
@@ -83,6 +86,7 @@ func TestPutUserPolicy_InvalidName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutUserPolicy_MalformedDocument(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "malformed-user")
|
||||
|
||||
@@ -96,6 +100,7 @@ func TestPutUserPolicy_MalformedDocument(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutUserPolicy_OversizedDocument(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "oversized-user")
|
||||
|
||||
@@ -110,6 +115,7 @@ func TestPutUserPolicy_OversizedDocument(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPutUserPolicy_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.PutUserPolicy(testAccountID, &iam.PutUserPolicyInput{
|
||||
@@ -122,6 +128,7 @@ func TestPutUserPolicy_UserNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUserPolicy_UnknownName(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "get-unknown")
|
||||
|
||||
@@ -134,6 +141,7 @@ func TestGetUserPolicy_UnknownName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUserPolicy_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.GetUserPolicy(testAccountID, &iam.GetUserPolicyInput{
|
||||
@@ -145,6 +153,7 @@ func TestGetUserPolicy_UserNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListUserPolicies_Sorted(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "list-user")
|
||||
|
||||
@@ -170,6 +179,7 @@ func TestListUserPolicies_Sorted(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListUserPolicies_Empty(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "empty-inline-user")
|
||||
|
||||
@@ -183,6 +193,7 @@ func TestListUserPolicies_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListUserPolicies_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.ListUserPolicies(testAccountID, &iam.ListUserPoliciesInput{
|
||||
@@ -193,6 +204,7 @@ func TestListUserPolicies_UserNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteUserPolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "del-inline-user")
|
||||
|
||||
@@ -224,6 +236,7 @@ func TestDeleteUserPolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteUserPolicy_DoubleDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "double-del-user")
|
||||
|
||||
@@ -249,6 +262,7 @@ func TestDeleteUserPolicy_DoubleDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteUserPolicy_UserNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
|
||||
_, err := svc.DeleteUserPolicy(testAccountID, &iam.DeleteUserPolicyInput{
|
||||
@@ -285,6 +299,7 @@ func putRawUserInlinePolicy(t *testing.T, svc *IAMServiceImpl, userName, policyN
|
||||
// stop contributing once the inline policy is removed. Without this, put-user-policy
|
||||
// appears to succeed while granting nothing — a silent no-op.
|
||||
func TestGetUserPolicies_UserInlineEnforced(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "quinn")
|
||||
|
||||
@@ -323,6 +338,7 @@ func TestGetUserPolicies_UserInlineEnforced(t *testing.T) {
|
||||
// set — inline grants supplement managed grants rather than replacing them, so a
|
||||
// deny-wins evaluator can honour an inline Deny alongside a managed Allow.
|
||||
func TestGetUserPolicies_UserManagedAndInline(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "rita")
|
||||
managed := createTestPolicy(t, svc, "ManagedGrant")
|
||||
@@ -351,6 +367,7 @@ func TestGetUserPolicies_UserManagedAndInline(t *testing.T) {
|
||||
// document on a user fails the whole resolution closed rather than silently
|
||||
// dropping the grant source, mirroring the group-inline handling.
|
||||
func TestGetUserPolicies_UserInlineMalformedFailsClosed(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "sam")
|
||||
|
||||
@@ -367,6 +384,7 @@ func TestGetUserPolicies_UserInlineMalformedFailsClosed(t *testing.T) {
|
||||
// TestDeleteUser_WithInlinePolicy proves a user carrying an inline policy refuses
|
||||
// deletion until the inline policy is removed, mirroring DeleteGroup.
|
||||
func TestDeleteUser_WithInlinePolicy(t *testing.T) {
|
||||
t.Parallel()
|
||||
svc := setupTestIAMService(t)
|
||||
createTestUser(t, svc, "inline-conflict-user")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user