diff --git a/Dockerfile.test b/Dockerfile.test index 2b319e850..0e50a0446 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -7,9 +7,18 @@ # keep the digest in sync with the builder stage there. FROM golang:1.25.4-alpine3.21@sha256:3289aac2aac769e031d644313d094dbda745f28af81cd7a94137e73eefd58b33 +# Run the suite as a non-root user (trivy DS-0002). tests/e2e is a stdlib-only +# module so no module downloads are needed; GOCACHE lives in the user's home, +# which it owns and can write to during both the pre-compile below and the +# runtime CMD. +RUN adduser -D -u 10001 e2e +ENV GOCACHE=/home/e2e/.cache/go-build + WORKDIR /e2e -COPY tests/e2e/ ./ +COPY --chown=e2e:e2e tests/e2e/ ./ + +USER e2e # Pre-compile the test binary so `docker compose up` failures are runtime # failures, not compile errors discovered after the stack is already up. diff --git a/internal/config/store_postgres_recommendations.go b/internal/config/store_postgres_recommendations.go index f8d05f2fc..ce3c3a722 100644 --- a/internal/config/store_postgres_recommendations.go +++ b/internal/config/store_postgres_recommendations.go @@ -325,24 +325,33 @@ func recEffectiveSavingsPct(rec *RecommendationRecord) (float64, bool) { if rec.Term == 0 { return 0, false } - hasOnDemand := rec.OnDemandCost != nil && *rec.OnDemandCost > 0 - if !hasOnDemand && rec.MonthlyCost == nil { + onDemand, ok := recOnDemandBaseline(rec) + if !ok || onDemand == 0 { return 0, false } - if rec.Provider == providerAWS && !hasOnDemand { + return (rec.Savings / onDemand) * 100, true +} + +// recOnDemandBaseline returns the monthly on-demand baseline used as the +// denominator in recEffectiveSavingsPct, and whether one could be determined. +// It prefers the provider-reported OnDemandCost; otherwise it reconstructs the +// baseline as monthly_cost + net savings + amortized upfront. It returns +// ok=false when no baseline can be derived: no explicit on-demand and no +// monthly_cost, or an AWS row without an explicit on-demand baseline (the +// reconstruction diverges from Cost Explorer's true baseline -- the frontend's +// #323 guard). Callers must ensure rec.Term != 0 before calling. +func recOnDemandBaseline(rec *RecommendationRecord) (float64, bool) { + if rec.OnDemandCost != nil && *rec.OnDemandCost > 0 { + return *rec.OnDemandCost, true + } + if rec.MonthlyCost == nil { return 0, false } - var onDemand float64 - if hasOnDemand { - onDemand = *rec.OnDemandCost - } else { - amortized := rec.UpfrontCost / float64(rec.Term*12) - onDemand = *rec.MonthlyCost + rec.Savings + amortized - } - if onDemand == 0 { + if rec.Provider == providerAWS { return 0, false } - return (rec.Savings / onDemand) * 100, true + amortized := rec.UpfrontCost / float64(rec.Term*12) + return *rec.MonthlyCost + rec.Savings + amortized, true } // ListStoredRecommendations reads recommendations matching the filter.