From d8e37fbdb8815d4d40f9385b4acf599783c748bb Mon Sep 17 00:00:00 2001 From: joshsiv-mulga Date: Tue, 21 Jul 2026 00:24:47 +1000 Subject: [PATCH] test(integration): cover RunInstances capacity distribution across nodes Proves the default (non-placement-group) path packs a multi-instance launch across nodes proportionally to spare capacity rather than exhausting one node first. --- .../run_instances_capacity_test.go | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 tests/integration/run_instances_capacity_test.go diff --git a/tests/integration/run_instances_capacity_test.go b/tests/integration/run_instances_capacity_test.go new file mode 100644 index 000000000..c1daeb8c6 --- /dev/null +++ b/tests/integration/run_instances_capacity_test.go @@ -0,0 +1,50 @@ +//go:build integration + +package integration + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/stretchr/testify/require" +) + +// TestRunInstances_CapacityDistributionAcrossNodes proves the default +// (non-placement-group) path spreads a multi-instance launch across +// ExpectedNodes proportionally to each node's spare capacity +// (placement.go's spreadAllocate: 1-per-node first round, then remaining +// instances packed onto whichever node has the most spare capacity). Node A +// reports 3 available, node B reports 1: requesting MaxCount=3 must land 2 on +// A and 1 on B, proving genuine per-node dispatch counts rather than a single +// node absorbing the whole request. +func TestRunInstances_CapacityDistributionAcrossNodes(t *testing.T) { + gw := StartGateway(t, withExpectedNodes(2)) + + const ( + instanceType = "t3.micro" + nodeA = "capacity-node-a" + nodeB = "capacity-node-b" + ) + + stubNodeCapacity(t, gw, nodeA, instanceType, 3) + stubNodeCapacity(t, gw, nodeB, instanceType, 1) + chA := captureLaunchTemplateNodeInput(t, gw, instanceType, nodeA) + chB := captureLaunchTemplateNodeInput(t, gw, instanceType, nodeB) + + out, err := gw.EC2Client(t).RunInstances(&ec2.RunInstancesInput{ + ImageId: aws.String("ami-0123456789abcdef0"), + InstanceType: aws.String(instanceType), + MinCount: aws.Int64(1), + MaxCount: aws.Int64(3), + }) + require.NoError(t, err, "run-instances across two nodes of unequal capacity") + require.Len(t, out.Instances, 2, "aggregateResults merges one reservation frame per node dispatched, regardless of each node's Assigned count") + + gotA := awaitLaunchTemplateNodeInput(t, chA) + gotB := awaitLaunchTemplateNodeInput(t, chB) + require.Equal(t, int64(2), aws.Int64Value(gotA.MinCount), "the higher-capacity node must absorb the extra instance beyond the 1-per-node round") + require.Equal(t, int64(2), aws.Int64Value(gotA.MaxCount)) + require.Equal(t, int64(1), aws.Int64Value(gotB.MinCount), "the lower-capacity node must only get its 1-per-node share") + require.Equal(t, int64(1), aws.Int64Value(gotB.MaxCount)) +}