From a17f106b166b2a63a5a18046a45943fd81435a82 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 12 Oct 2026 20:16:47 +0200 Subject: [PATCH 1/2] chore(deps): bump cloud-commitments-go providers/aws and pkg for ApplyFamilyNUSizingRDSWithExpiry providers/aws 7ff8c1aee1bb -> 2c1e87b723ab, pkg 73d3366903bc -> 30bf383795db (forced by Go version resolution). No replace directives, no vendor dir. The bumped pkg drops target-coverage RI recs whose existing coverage is unknown (go a450912), so the helpers_test.go and purchase_safeguards_2121 fixtures that mean "known 0% covered" now set ExistingCoverageKnown. New tests pin the EC2 zonal AZ behaviour (go fb39c9d) through purchaseSingleRec. Refs #2132 Co-Authored-By: Claude Sonnet 5.5 --- cmd/ec2_zonal_az_test.go | 98 ++++++++++++++++++++++++++++ cmd/helpers_test.go | 7 ++ cmd/purchase_safeguards_2121_test.go | 1 + go.mod | 4 +- go.sum | 8 +-- 5 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 cmd/ec2_zonal_az_test.go diff --git a/cmd/ec2_zonal_az_test.go b/cmd/ec2_zonal_az_test.go new file mode 100644 index 000000000..f4d213d48 --- /dev/null +++ b/cmd/ec2_zonal_az_test.go @@ -0,0 +1,98 @@ +package main + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" + "path/filepath" + "sync" + "testing" + + "github.com/LeanerCloud/cloud-commitments-go/pkg/common" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// ec2FormRecorder is an offline RoundTripper that records the Action and +// AvailabilityZone of every EC2 query request and answers each with a +// non-retryable 400, so no purchase can ever succeed. +type ec2FormRecorder struct { + mu sync.Mutex + actions []string + zones []string +} + +func (r *ec2FormRecorder) RoundTrip(req *http.Request) (*http.Response, error) { + body, err := io.ReadAll(req.Body) + if err != nil { + return nil, err + } + values, err := url.ParseQuery(string(body)) + if err != nil { + return nil, err + } + r.mu.Lock() + r.actions = append(r.actions, values.Get("Action")) + r.zones = append(r.zones, values.Get("AvailabilityZone")) + r.mu.Unlock() + payload := `InvalidParameterValuestubbed` + return &http.Response{StatusCode: http.StatusBadRequest, Header: http.Header{"Content-Type": []string{"text/xml"}}, + Body: io.NopCloser(bytes.NewReader([]byte(payload))), Request: req}, nil +} + +func zonalEC2Rec(scope ec2types.Scope, az string) common.Recommendation { + rec := validEC2Rec() + rec.Account = "111111111111" + rec.Term = "1yr" + rec.PaymentOption = "partial-upfront" + rec.Details = &common.ComputeDetails{InstanceType: "m5.large", Platform: "Linux/UNIX", Tenancy: "default", + Scope: string(scope), AvailabilityZone: az} + return rec +} + +func purchaseEC2WithRecorder(t *testing.T, rec common.Recommendation) (common.PurchaseResult, *ec2FormRecorder) { + t.Helper() + t.Setenv("DISABLE_PURCHASE_DELAY", "true") + recorder := &ec2FormRecorder{} + awsCfg := aws.Config{ + HTTPClient: &http.Client{Transport: recorder}, + Credentials: aws.AnonymousCredentials{}, + Retryer: func() aws.Retryer { return aws.NopRetryer{} }, + } + cfg := Config{AuditLog: filepath.Join(t.TempDir(), "audit.jsonl")} + result, _ := purchaseSingleRec(context.Background(), awsCfg, rec, 1, false, cfg) + return result, recorder +} + +func TestPurchaseSingleRec_ZonalEC2KeepsItsAvailabilityZone(t *testing.T) { + result, recorder := purchaseEC2WithRecorder(t, zonalEC2Rec(ec2types.ScopeAvailabilityZone, "us-east-1b")) + + require.Error(t, result.Error) + assert.Contains(t, recorder.actions, "DescribeReservedInstancesOfferings") + assert.NotContains(t, recorder.actions, "PurchaseReservedInstancesOffering") + for i, action := range recorder.actions { + if action == "DescribeReservedInstancesOfferings" { + assert.Equal(t, "us-east-1b", recorder.zones[i], "the offering query must be pinned to the rec's AZ") + } + } +} + +func TestPurchaseSingleRec_RefusesInconsistentEC2ScopeBeforeAnyAWSCall(t *testing.T) { + for name, rec := range map[string]common.Recommendation{ + "zonal without AZ": zonalEC2Rec(ec2types.ScopeAvailabilityZone, ""), + "regional with AZ": zonalEC2Rec(ec2types.ScopeRegional, "us-east-1b"), + "zonal blank AZ": zonalEC2Rec(ec2types.ScopeAvailabilityZone, " "), + } { + t.Run(name, func(t *testing.T) { + result, recorder := purchaseEC2WithRecorder(t, rec) + + require.Error(t, result.Error) + assert.Contains(t, result.Error.Error(), "AvailabilityZone") + assert.Empty(t, recorder.actions, "no AWS call may precede the refusal") + }) + } +} diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index 6a55648d5..5d9cf83b3 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -912,6 +912,7 @@ func TestApplyTargetCoverage_RI(t *testing.T) { CommitmentCost: 1000, OnDemandCost: 2000, EstimatedSavings: 500, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: avg, RecommendedUtilization: recUtil, } @@ -1030,6 +1031,7 @@ func TestApplyTargetCoverage_RI_CostScaling(t *testing.T) { OnDemandCost: 2000, EstimatedSavings: 500, SavingsPercentage: 25, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: 8, } // target=80, existing=0, avg=8 → gap=80. @@ -1086,6 +1088,7 @@ func TestApplyTargetCoverage_RI_ExistingCoverage(t *testing.T) { CommitmentCost: 1000, OnDemandCost: 2000, EstimatedSavings: 500, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: avg, ExistingCoveragePct: existingCov, } @@ -1233,6 +1236,7 @@ func TestApplySizing(t *testing.T) { Service: common.ServiceEC2, Count: 10, CommitmentType: common.CommitmentReservedInstance, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: 8, } @@ -1271,6 +1275,7 @@ func TestApplyTargetCoverage_RI_Target100(t *testing.T) { ResourceType: "t3.medium", Count: count, CommitmentType: common.CommitmentReservedInstance, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: avg, } } @@ -1358,6 +1363,7 @@ func TestApplyTargetCoverage_DropTargetAlreadyMet(t *testing.T) { ResourceType: "t3.medium", Count: 5, CommitmentType: common.CommitmentReservedInstance, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: 8.0, ExistingCoveragePct: 90.0, } @@ -1384,6 +1390,7 @@ func TestApplyTargetCoverage_DropTargetSizedToZero(t *testing.T) { ResourceType: "t3.micro", Count: 1, CommitmentType: common.CommitmentReservedInstance, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: 0.4, ExistingCoveragePct: 0.0, } diff --git a/cmd/purchase_safeguards_2121_test.go b/cmd/purchase_safeguards_2121_test.go index d676b75ab..efe474628 100644 --- a/cmd/purchase_safeguards_2121_test.go +++ b/cmd/purchase_safeguards_2121_test.go @@ -26,6 +26,7 @@ func TestApplyTargetCoverage_NonPositiveCountDropped_2121(t *testing.T) { ResourceType: "db.r6g.large", CommitmentType: common.CommitmentReservedInstance, Count: count, + ExistingCoverageKnown: true, AverageInstancesUsedPerHour: 4, ExistingCoveragePct: 0, CommitmentCost: 1000, diff --git a/go.mod b/go.mod index 2ce6dbe77..377a8594a 100644 --- a/go.mod +++ b/go.mod @@ -82,8 +82,8 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2 v2.2.0 - github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261008151559-73d3366903bc - github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261006205158-7ff8c1aee1bb + github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261009181525-30bf383795db + github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261012180926-2c1e87b723ab github.com/LeanerCloud/cloud-commitments-go/providers/azure v0.0.0-20261007133317-58c25f04c49b github.com/LeanerCloud/cloud-commitments-go/providers/gcp v0.0.0-20261006205158-7ff8c1aee1bb github.com/aws/aws-sdk-go-v2/service/organizations v1.45.3 diff --git a/go.sum b/go.sum index 3f6bf34cf..ee996a239 100644 --- a/go.sum +++ b/go.sum @@ -78,10 +78,10 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261008151559-73d3366903bc h1:GTjjeD1cGMn+LfTSRB7CfO6kJBrfQyHu3kZAtHx1TJk= -github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261008151559-73d3366903bc/go.mod h1:ApWBliDXe099f3oDXBz41K/I9v4bHvn1dG/BGoRmHlw= -github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261006205158-7ff8c1aee1bb h1:gtfWXNBATJDSS+q3+5Jxt2+HYijOSVEnpGkXmeCkudY= -github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261006205158-7ff8c1aee1bb/go.mod h1:zgv5QhLOIk9Hqw1xcmr1lSFg4ZeChwTCMwxETFVcfj4= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261009181525-30bf383795db h1:d/eTZP31KewqE049NlQM3nZIUygRudHJ25Im2ibJk58= +github.com/LeanerCloud/cloud-commitments-go/pkg v0.0.0-20261009181525-30bf383795db/go.mod h1:8RTmzPazA8IeVZfn7wNaU7ILx0VK5eHZQozO21Mvtq8= +github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261012180926-2c1e87b723ab h1:E6cZ5WllhWGR8EgONN8MwWaGD3qLCCwYIXWgIysnfYU= +github.com/LeanerCloud/cloud-commitments-go/providers/aws v0.0.0-20261012180926-2c1e87b723ab/go.mod h1:C0ildZeRds0aRvDZp49MLY0kFWh/4FiM4lPWWWd3HFA= github.com/LeanerCloud/cloud-commitments-go/providers/azure v0.0.0-20261007133317-58c25f04c49b h1:yurugmfdZjf+dqjfqfDyfoBR3IAiFUpMKQ8URnZnJzQ= github.com/LeanerCloud/cloud-commitments-go/providers/azure v0.0.0-20261007133317-58c25f04c49b/go.mod h1:iIvPuWLWd+SXqLHAQNdodYMUAAcq7PnwfScXWgARt5A= github.com/LeanerCloud/cloud-commitments-go/providers/gcp v0.0.0-20261006205158-7ff8c1aee1bb h1:AJ4nKDtttjiTDemo9PDp2esoc35ymVaiCSr0zIewwpY= From b7a7f645d4fba4b129cf483bd75e6c106f997892 Mon Sep 17 00:00:00 2001 From: Cristian Magherusan-Stanciu Date: Mon, 12 Oct 2026 20:25:47 +0200 Subject: [PATCH 2/2] test(cmd): drop unused account literal that trips the git-secrets allowlist Refs #2132 Co-Authored-By: Claude Sonnet 5.5 --- cmd/ec2_zonal_az_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/ec2_zonal_az_test.go b/cmd/ec2_zonal_az_test.go index f4d213d48..7d6a46df5 100644 --- a/cmd/ec2_zonal_az_test.go +++ b/cmd/ec2_zonal_az_test.go @@ -46,7 +46,6 @@ func (r *ec2FormRecorder) RoundTrip(req *http.Request) (*http.Response, error) { func zonalEC2Rec(scope ec2types.Scope, az string) common.Recommendation { rec := validEC2Rec() - rec.Account = "111111111111" rec.Term = "1yr" rec.PaymentOption = "partial-upfront" rec.Details = &common.ComputeDetails{InstanceType: "m5.large", Platform: "Linux/UNIX", Tenancy: "default",