diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac6cba..528f698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed + +- The caveat summary in markdown output and PR comments names the kinds + of assumption the estimate actually has ("an assumed instance count", + "a price from another region", …). It always listed the same three + examples, even when none of them applied. + ## [0.3.17] - 2026-09-29 ### Added diff --git a/internal/render/caveat_kinds_test.go b/internal/render/caveat_kinds_test.go new file mode 100644 index 0000000..9b8a6ba --- /dev/null +++ b/internal/render/caveat_kinds_test.go @@ -0,0 +1,33 @@ +package render + +import ( + "testing" + + "github.com/c3xdev/c3x/internal/domain" +) + +// The summary names the caveats this estimate has, in order of first +// appearance, not a fixed list of examples. +func TestCaveatKindsNamesWhatIsPresent(t *testing.T) { + c := func(code string) domain.LabeledCaveat { + return domain.LabeledCaveat{Caveat: domain.Caveat{Code: code}} + } + cs := []domain.LabeledCaveat{ + c(domain.CaveatAssumedCount), c(domain.CaveatAssumedCount), + c(domain.CaveatRegionFallback), c(domain.CaveatAssumedCount), + c("some_future_code"), + } + got := caveatKinds(cs) + want := "an assumed instance count, a price from another region, some_future_code" + if got != want { + t.Errorf("caveatKinds = %q, want %q", got, want) + } + for _, code := range []string{ + domain.CaveatRegionFallback, domain.CaveatNoPrice, domain.CaveatUsageMissing, + domain.CaveatUnresolved, domain.CaveatAssumedCount, domain.CaveatStub, domain.CaveatStalePrice, + } { + if _, ok := caveatPhrases[code]; !ok { + t.Errorf("no plain-words phrase for caveat code %q", code) + } + } +} diff --git a/internal/render/markdown.go b/internal/render/markdown.go index 76e0723..12a02db 100644 --- a/internal/render/markdown.go +++ b/internal/render/markdown.go @@ -238,6 +238,37 @@ func escapeMD(s string) string { // layout, PR comments included: one visible line saying the total rests // on assumptions, and the list behind a
so a long one doesn't // flood the thread. Empty when there is nothing to qualify. +// caveatPhrases names each caveat code in plain words for the summary line. +var caveatPhrases = map[string]string{ + domain.CaveatRegionFallback: "a price from another region", + domain.CaveatNoPrice: "a price that could not be found", + domain.CaveatUsageMissing: "usage not provided", + domain.CaveatUnresolved: "an attribute that could not be evaluated", + domain.CaveatAssumedCount: "an assumed instance count", + domain.CaveatStub: "an offline placeholder price", + domain.CaveatStalePrice: "a cached price past its freshness window", +} + +// caveatKinds lists the kinds of caveat present, in order of first +// appearance, so the summary names what this estimate actually assumed +// rather than a fixed set of examples. +func caveatKinds(cs []domain.LabeledCaveat) string { + seen := map[string]bool{} + var kinds []string + for _, c := range cs { + if seen[c.Code] { + continue + } + seen[c.Code] = true + if p, ok := caveatPhrases[c.Code]; ok { + kinds = append(kinds, p) + } else { + kinds = append(kinds, c.Code) + } + } + return strings.Join(kinds, ", ") +} + func caveatsMarkdown(cs []domain.LabeledCaveat) string { if len(cs) == 0 { return "" @@ -247,7 +278,7 @@ func caveatsMarkdown(cs []domain.LabeledCaveat) string { if len(cs) == 1 { noun = "caveat" } - fmt.Fprintf(&b, "\n> ⚠️ **%d %s:** parts of this estimate rest on assumptions (a price from another region, usage not provided, an attribute that could not be evaluated), so it may misstate the real cost.\n\n", len(cs), noun) + fmt.Fprintf(&b, "\n> ⚠️ **%d %s:** parts of this estimate rest on assumptions (%s), so it may misstate the real cost.\n\n", len(cs), noun, caveatKinds(cs)) b.WriteString("
Caveats\n\n| Resource | Line | Caveat |\n|---|---|---|\n") for _, c := range cs { line := c.Line diff --git a/testdata/golden/eks-cluster.md b/testdata/golden/eks-cluster.md index 860a5f9..8f8e096 100644 --- a/testdata/golden/eks-cluster.md +++ b/testdata/golden/eks-cluster.md @@ -67,7 +67,7 @@ **Project total: $0/mo** -> ⚠️ **20 caveats:** parts of this estimate rest on assumptions (a price from another region, usage not provided, an attribute that could not be evaluated), so it may misstate the real cost. +> ⚠️ **20 caveats:** parts of this estimate rest on assumptions (an offline placeholder price), so it may misstate the real cost.
Caveats diff --git a/testdata/golden/monorepo-with-modules.md b/testdata/golden/monorepo-with-modules.md index 4beadf5..44ebee0 100644 --- a/testdata/golden/monorepo-with-modules.md +++ b/testdata/golden/monorepo-with-modules.md @@ -15,7 +15,7 @@ **Project total: $0/mo** -> ⚠️ **3 caveats:** parts of this estimate rest on assumptions (a price from another region, usage not provided, an attribute that could not be evaluated), so it may misstate the real cost. +> ⚠️ **3 caveats:** parts of this estimate rest on assumptions (an offline placeholder price), so it may misstate the real cost.
Caveats diff --git a/testdata/golden/multi-cloud.md b/testdata/golden/multi-cloud.md index 27f8925..b52e72b 100644 --- a/testdata/golden/multi-cloud.md +++ b/testdata/golden/multi-cloud.md @@ -30,7 +30,7 @@ **Project total: $0/mo** -> ⚠️ **8 caveats:** parts of this estimate rest on assumptions (a price from another region, usage not provided, an attribute that could not be evaluated), so it may misstate the real cost. +> ⚠️ **8 caveats:** parts of this estimate rest on assumptions (an offline placeholder price), so it may misstate the real cost.
Caveats diff --git a/testdata/golden/vpc-stack.md b/testdata/golden/vpc-stack.md index aa89698..a96153a 100644 --- a/testdata/golden/vpc-stack.md +++ b/testdata/golden/vpc-stack.md @@ -73,7 +73,7 @@ **Project total: $10.95/mo** -> ⚠️ **13 caveats:** parts of this estimate rest on assumptions (a price from another region, usage not provided, an attribute that could not be evaluated), so it may misstate the real cost. +> ⚠️ **13 caveats:** parts of this estimate rest on assumptions (an offline placeholder price), so it may misstate the real cost.
Caveats