From 92ce1d4e860ca6265e91ea62ca421df59791550a Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Wed, 22 Apr 2026 15:32:50 +0200 Subject: [PATCH 1/6] feat: add to and from flags to FOASCLI sunset diff command --- tools/cli/internal/cli/sunset/diff.go | 84 ++++++++++++-- tools/cli/internal/cli/sunset/diff_test.go | 128 ++++++++++++++++++++- tools/cli/test/e2e/cli/sunset_test.go | 51 +++++++- 3 files changed, 245 insertions(+), 18 deletions(-) diff --git a/tools/cli/internal/cli/sunset/diff.go b/tools/cli/internal/cli/sunset/diff.go index c11eadf157..417d1e6ed0 100644 --- a/tools/cli/internal/cli/sunset/diff.go +++ b/tools/cli/internal/cli/sunset/diff.go @@ -19,6 +19,7 @@ import ( "fmt" "sort" "strings" + "time" "github.com/mongodb/openapi/tools/cli/internal/cli/flag" "github.com/mongodb/openapi/tools/cli/internal/cli/usage" @@ -35,6 +36,10 @@ type DiffOpts struct { specPath string outputPath string format string + from string + to string + toDate *time.Time + fromDate *time.Time } type Diff struct { @@ -68,7 +73,10 @@ func (o *DiffOpts) Run() error { specSunsets := sunset.NewListFromSpec(specInfo) // Find differences - var diffs = findDiffs(baseSunsets, specSunsets, o.basePath, o.specPath) + diffs, err := o.findDiffs(baseSunsets, specSunsets) + if err != nil { + return err + } // Write to output bytes, err := o.newSunsetDiffBytes(diffs) @@ -84,7 +92,7 @@ func (o *DiffOpts) Run() error { return nil } -func findDiffs(baseSunsets, specSunsets []*sunset.Sunset, baseSpecPath, specPath string) []*Diff { +func (o *DiffOpts) findDiffs(baseSunsets, specSunsets []*sunset.Sunset) ([]*Diff, error) { // Create maps for easy lookup baseMap := make(map[string]*sunset.Sunset) for _, s := range baseSunsets { @@ -106,15 +114,14 @@ func findDiffs(baseSunsets, specSunsets []*sunset.Sunset, baseSpecPath, specPath if specSunset, exists := specMap[key]; exists { // Endpoint exists in both specs if baseSunset.SunsetDate != specSunset.SunsetDate { - // Different sunset dates diffs = append(diffs, &Diff{ Operation: baseSunset.Operation, Path: baseSunset.Path, Version: baseSunset.Version, BaseSunsetDate: baseSunset.SunsetDate, SpecSunsetDate: specSunset.SunsetDate, - BaseSpec: baseSpecPath, - Spec: specPath, + BaseSpec: o.basePath, + Spec: o.specPath, Team: baseSunset.Team, }) } @@ -126,8 +133,8 @@ func findDiffs(baseSunsets, specSunsets []*sunset.Sunset, baseSpecPath, specPath Version: baseSunset.Version, BaseSunsetDate: baseSunset.SunsetDate, SpecSunsetDate: "", - BaseSpec: baseSpecPath, - Spec: specPath, + BaseSpec: o.basePath, + Spec: o.specPath, Team: baseSunset.Team, }) } @@ -142,8 +149,8 @@ func findDiffs(baseSunsets, specSunsets []*sunset.Sunset, baseSpecPath, specPath Version: specSunset.Version, BaseSunsetDate: "", SpecSunsetDate: specSunset.SunsetDate, - BaseSpec: baseSpecPath, - Spec: specPath, + BaseSpec: o.basePath, + Spec: o.specPath, Team: specSunset.Team, }) } @@ -156,7 +163,39 @@ func findDiffs(baseSunsets, specSunsets []*sunset.Sunset, baseSpecPath, specPath return iKey < jKey }) - return diffs + // Filter diffs by date range if specified + filteredDiffs, err := o.diffsInRange(diffs) + if err != nil { + return nil, err + } + + return filteredDiffs, nil +} + +func (o *DiffOpts) diffsInRange(diffs []*Diff) ([]*Diff, error) { + var out []*Diff + + if o.from == "" && o.to == "" { + return diffs, nil + } + + for _, d := range diffs { + baseSunsetDate, err := time.Parse("2006-01-02", d.BaseSunsetDate) + if err != nil { + baseSunsetDate = time.Time{} // zero value for time if parsing fails + } + + specSunsetDate, err := time.Parse("2006-01-02", d.SpecSunsetDate) + if err != nil { + specSunsetDate = time.Time{} // zero value for time if parsing fails + } + + if isDateInRange(&baseSunsetDate, o.fromDate, o.toDate) || isDateInRange(&specSunsetDate, o.fromDate, o.toDate) { + out = append(out, d) + } + } + + return out, nil } func makeKey(path, operation, version string) string { @@ -186,6 +225,26 @@ func (o *DiffOpts) newSunsetDiffBytes(diffs []*Diff) ([]byte, error) { return yamlData, nil } +func (o *DiffOpts) validate() error { + if o.from != "" { + value, err := time.Parse("2006-01-02", o.from) + if err != nil { + return err + } + o.fromDate = &value + } + + if o.to != "" { + value, err := time.Parse("2006-01-02", o.to) + if err != nil { + return err + } + o.toDate = &value + } + + return nil +} + // DiffBuilder builds the diff command with the following signature: // sunset diff --base base-spec.json --spec spec.json. func DiffBuilder() *cobra.Command { @@ -197,6 +256,9 @@ func DiffBuilder() *cobra.Command { Use: "diff --base spec1.json --spec spec2.json", Short: "List API endpoints with different sunset dates between two OpenAPI specs.", Args: cobra.NoArgs, + PreRunE: func(_ *cobra.Command, _ []string) error { + return opts.validate() + }, RunE: func(_ *cobra.Command, _ []string) error { return opts.Run() }, @@ -206,6 +268,8 @@ func DiffBuilder() *cobra.Command { cmd.Flags().StringVarP(&opts.specPath, flag.Spec, flag.SpecShort, "", usage.Spec) cmd.Flags().StringVarP(&opts.outputPath, flag.Output, flag.OutputShort, "", usage.Output) cmd.Flags().StringVarP(&opts.format, flag.Format, flag.FormatShort, "json", usage.Format) + cmd.Flags().StringVar(&opts.from, flag.From, "", usage.From) + cmd.Flags().StringVar(&opts.to, flag.To, "", usage.To) _ = cmd.MarkFlagRequired(flag.Base) _ = cmd.MarkFlagRequired(flag.Spec) diff --git a/tools/cli/internal/cli/sunset/diff_test.go b/tools/cli/internal/cli/sunset/diff_test.go index c9b3b6fc50..e5777bd75c 100644 --- a/tools/cli/internal/cli/sunset/diff_test.go +++ b/tools/cli/internal/cli/sunset/diff_test.go @@ -15,10 +15,10 @@ package sunset import ( - "testing" - "github.com/mongodb/openapi/tools/cli/internal/openapi/sunset" "github.com/stretchr/testify/assert" + "testing" + "time" ) func TestFindDiffsEmpty(t *testing.T) { @@ -32,7 +32,13 @@ func TestFindDiffsEmpty(t *testing.T) { }, } - diff := findDiffs(baseSpecSunsets, baseSpecSunsets, "base.json", "spec.json") + opts := &DiffOpts{ + basePath: "base.json", + specPath: "spec.json", + } + + diff, err := opts.findDiffs(baseSpecSunsets, baseSpecSunsets) + assert.Nil(t, err) assert.Empty(t, diff) } @@ -112,7 +118,14 @@ func TestFindDiffsNotEmpty(t *testing.T) { }, } - diff := findDiffs(baseSpecSunsets, specSunsets, "base.json", "spec.json") + opts := &DiffOpts{ + basePath: "base.json", + specPath: "spec.json", + } + + diff, err := opts.findDiffs(baseSpecSunsets, specSunsets) + + assert.Nil(t, err) assert.Len(t, diff, 5) assert.Equal(t, "GET", diff[0].Operation) @@ -161,6 +174,113 @@ func TestFindDiffsNotEmpty(t *testing.T) { assert.Equal(t, "APIx", diff[4].Team) } +func TestFindDiffsFiltersByDate(t *testing.T) { + baseSpecSunsets := []*sunset.Sunset{ + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2023-01-01", + SunsetDate: "2025-06-01", + Team: "APIx", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/test", + Version: "2023-01-01", + SunsetDate: "2025-07-01", + Team: "Test", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/groups", + Version: "2023-01-01", + SunsetDate: "2025-06-01", + Team: "Groups", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/groups", + Version: "2023-02-01", + SunsetDate: "2025-07-01", + Team: "Groups", + }, + } + specSunsets := []*sunset.Sunset{ + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2023-01-01", + SunsetDate: "2025-06-02", + Team: "APIx", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/users", + Version: "2023-01-01", + SunsetDate: "2025-06-01", + Team: "Users", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/groups", + Version: "2023-01-01", + SunsetDate: "2025-07-03", + Team: "Groups", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/groups", + Version: "2023-02-01", + SunsetDate: "2025-07-03", + Team: "Groups", + }, + } + + fromDate := time.Date(2025, time.June, 1, 0, 0, 0, 0, time.UTC) + toDate := time.Date(2025, time.June, 15, 0, 0, 0, 0, time.UTC) + + opts := &DiffOpts{ + basePath: "base.json", + specPath: "spec.json", + from: "2025-06-01", + to: "2025-06-15", + fromDate: &fromDate, + toDate: &toDate, + } + + diff, err := opts.findDiffs(baseSpecSunsets, specSunsets) + + assert.Nil(t, err) + assert.Len(t, diff, 3) + + assert.Equal(t, "GET", diff[0].Operation) + assert.Equal(t, "/api/atlas/v2/groups", diff[0].Path) + assert.Equal(t, "2023-01-01", diff[0].Version) + assert.Equal(t, "2025-06-01", diff[0].BaseSunsetDate) + assert.Equal(t, "2025-07-03", diff[0].SpecSunsetDate) + assert.Equal(t, "base.json", diff[0].BaseSpec) + assert.Equal(t, "spec.json", diff[0].Spec) + assert.Equal(t, "Groups", diff[0].Team) + + assert.Equal(t, "GET", diff[1].Operation) + assert.Equal(t, "/api/atlas/v2/users", diff[1].Path) + assert.Equal(t, "2023-01-01", diff[1].Version) + assert.Empty(t, diff[1].BaseSunsetDate) + assert.Equal(t, "2025-06-01", diff[1].SpecSunsetDate) + assert.Equal(t, "base.json", diff[1].BaseSpec) + assert.Equal(t, "spec.json", diff[1].Spec) + assert.Equal(t, "Users", diff[1].Team) + + assert.Equal(t, "GET", diff[2].Operation) + assert.Equal(t, "/api/atlas/v2/versions", diff[2].Path) + assert.Equal(t, "2023-01-01", diff[2].Version) + assert.Equal(t, "2025-06-01", diff[2].BaseSunsetDate) + assert.Equal(t, "2025-06-02", diff[2].SpecSunsetDate) + assert.Equal(t, "base.json", diff[2].BaseSpec) + assert.Equal(t, "spec.json", diff[2].Spec) + assert.Equal(t, "APIx", diff[2].Team) +} + func TestMakeKey(t *testing.T) { key := makeKey("/api/atlas/v2/groups", "GET", "2023-01-01") assert.Equal(t, "GET-/api/atlas/v2/groups-2023-01-01", key) diff --git a/tools/cli/test/e2e/cli/sunset_test.go b/tools/cli/test/e2e/cli/sunset_test.go index d02d2dcdb9..267c943f16 100644 --- a/tools/cli/test/e2e/cli/sunset_test.go +++ b/tools/cli/test/e2e/cli/sunset_test.go @@ -13,9 +13,9 @@ import ( "github.com/stretchr/testify/require" ) -func TestDiff_NoChanges(t *testing.T) { +func TestSunsetDiff_NoChanges(t *testing.T) { baseSpecPath := "../../data/base_spec.json" - outputPath := "diff.json" + outputPath := getOutputFolder(t, "sunset") + "/diff.json" cliPath := NewBin(t) cmd := exec.CommandContext(context.Background(), cliPath, @@ -43,10 +43,10 @@ func TestDiff_NoChanges(t *testing.T) { assert.Empty(t, results) } -func TestDiff_WithChanges(t *testing.T) { +func TestSunsetDiff_WithChanges(t *testing.T) { baseSpecPath := "../../data/base_spec.json" specPath := "../../data/base_spec_with_mismatching_sunset_dates.json" - outputPath := "diff.json" + outputPath := getOutputFolder(t, "sunset") + "/diff.json" cliPath := NewBin(t) cmd := exec.CommandContext(context.Background(), cliPath, @@ -104,3 +104,46 @@ func TestDiff_WithChanges(t *testing.T) { assert.Equal(t, baseSpecPath, results[2].BaseSpec) assert.Equal(t, specPath, results[2].Spec) } + +func TestSunsetDiff_WithFilteredChanges(t *testing.T) { + baseSpecPath := "../../data/base_spec.json" + specPath := "../../data/base_spec_with_mismatching_sunset_dates.json" + outputPath := getOutputFolder(t, "sunset") + "/diff.json" + + cliPath := NewBin(t) + cmd := exec.CommandContext(context.Background(), cliPath, + "sunset", + "diff", + "-b", + baseSpecPath, + "-s", + specPath, + "-o", + outputPath, + "--from", + "2026-02-20", + "--to", + "2026-03-03", + ) + + var o, e bytes.Buffer + cmd.Stdout = &o + cmd.Stderr = &e + require.NoError(t, cmd.Run(), e.String()) + + b, err := os.ReadFile(outputPath) + require.NoError(t, err) + assert.NotEmpty(t, b) + var results []*sunset.Diff + require.NoError(t, json.Unmarshal(b, &results)) + + assert.Len(t, results, 1) + + assert.Equal(t, "GET", results[0].Operation) + assert.Equal(t, "/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/search/deployment", results[0].Path) + assert.Equal(t, "2023-01-01", results[0].Version) + assert.Equal(t, "2026-03-01", results[0].BaseSunsetDate) + assert.Empty(t, results[0].SpecSunsetDate) + assert.Equal(t, baseSpecPath, results[0].BaseSpec) + assert.Equal(t, specPath, results[0].Spec) +} From aa3671572d5180bd51d39e6de222ad4b2e679c59 Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Wed, 22 Apr 2026 15:39:20 +0200 Subject: [PATCH 2/6] fix: lint --- tools/cli/internal/cli/sunset/diff_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/cli/internal/cli/sunset/diff_test.go b/tools/cli/internal/cli/sunset/diff_test.go index e5777bd75c..2a35244f3d 100644 --- a/tools/cli/internal/cli/sunset/diff_test.go +++ b/tools/cli/internal/cli/sunset/diff_test.go @@ -15,10 +15,11 @@ package sunset import ( - "github.com/mongodb/openapi/tools/cli/internal/openapi/sunset" - "github.com/stretchr/testify/assert" "testing" "time" + + "github.com/mongodb/openapi/tools/cli/internal/openapi/sunset" + "github.com/stretchr/testify/assert" ) func TestFindDiffsEmpty(t *testing.T) { @@ -38,7 +39,7 @@ func TestFindDiffsEmpty(t *testing.T) { } diff, err := opts.findDiffs(baseSpecSunsets, baseSpecSunsets) - assert.Nil(t, err) + assert.NoError(t, err) assert.Empty(t, diff) } @@ -125,7 +126,7 @@ func TestFindDiffsNotEmpty(t *testing.T) { diff, err := opts.findDiffs(baseSpecSunsets, specSunsets) - assert.Nil(t, err) + assert.NoError(t, err) assert.Len(t, diff, 5) assert.Equal(t, "GET", diff[0].Operation) @@ -250,7 +251,7 @@ func TestFindDiffsFiltersByDate(t *testing.T) { diff, err := opts.findDiffs(baseSpecSunsets, specSunsets) - assert.Nil(t, err) + assert.NoError(t, err) assert.Len(t, diff, 3) assert.Equal(t, "GET", diff[0].Operation) From e085695eee775f454394e9501a8188bd5913ce05 Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Wed, 22 Apr 2026 15:42:01 +0200 Subject: [PATCH 3/6] fix: lint --- tools/cli/internal/cli/sunset/diff_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/cli/internal/cli/sunset/diff_test.go b/tools/cli/internal/cli/sunset/diff_test.go index 2a35244f3d..c0cb762d1c 100644 --- a/tools/cli/internal/cli/sunset/diff_test.go +++ b/tools/cli/internal/cli/sunset/diff_test.go @@ -20,6 +20,7 @@ import ( "github.com/mongodb/openapi/tools/cli/internal/openapi/sunset" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestFindDiffsEmpty(t *testing.T) { @@ -39,7 +40,7 @@ func TestFindDiffsEmpty(t *testing.T) { } diff, err := opts.findDiffs(baseSpecSunsets, baseSpecSunsets) - assert.NoError(t, err) + require.NoError(t, err) assert.Empty(t, diff) } @@ -126,7 +127,7 @@ func TestFindDiffsNotEmpty(t *testing.T) { diff, err := opts.findDiffs(baseSpecSunsets, specSunsets) - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, diff, 5) assert.Equal(t, "GET", diff[0].Operation) @@ -251,7 +252,7 @@ func TestFindDiffsFiltersByDate(t *testing.T) { diff, err := opts.findDiffs(baseSpecSunsets, specSunsets) - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, diff, 3) assert.Equal(t, "GET", diff[0].Operation) From 2e4011f2c7482e40b6c9203aa14b7b91556dc0ca Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Thu, 23 Apr 2026 12:05:46 +0200 Subject: [PATCH 4/6] fix: use path.join --- tools/cli/test/e2e/cli/sunset_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/cli/test/e2e/cli/sunset_test.go b/tools/cli/test/e2e/cli/sunset_test.go index 267c943f16..5d5cce6b53 100644 --- a/tools/cli/test/e2e/cli/sunset_test.go +++ b/tools/cli/test/e2e/cli/sunset_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "os" "os/exec" + "path" "testing" "github.com/mongodb/openapi/tools/cli/internal/cli/sunset" @@ -15,7 +16,7 @@ import ( func TestSunsetDiff_NoChanges(t *testing.T) { baseSpecPath := "../../data/base_spec.json" - outputPath := getOutputFolder(t, "sunset") + "/diff.json" + outputPath := path.Join(getOutputFolder(t, "sunset"), "diff.json") cliPath := NewBin(t) cmd := exec.CommandContext(context.Background(), cliPath, @@ -46,7 +47,7 @@ func TestSunsetDiff_NoChanges(t *testing.T) { func TestSunsetDiff_WithChanges(t *testing.T) { baseSpecPath := "../../data/base_spec.json" specPath := "../../data/base_spec_with_mismatching_sunset_dates.json" - outputPath := getOutputFolder(t, "sunset") + "/diff.json" + outputPath := path.Join(getOutputFolder(t, "sunset"), "diff.json") cliPath := NewBin(t) cmd := exec.CommandContext(context.Background(), cliPath, @@ -108,7 +109,7 @@ func TestSunsetDiff_WithChanges(t *testing.T) { func TestSunsetDiff_WithFilteredChanges(t *testing.T) { baseSpecPath := "../../data/base_spec.json" specPath := "../../data/base_spec_with_mismatching_sunset_dates.json" - outputPath := getOutputFolder(t, "sunset") + "/diff.json" + outputPath := path.Join(getOutputFolder(t, "sunset"), "diff.json") cliPath := NewBin(t) cmd := exec.CommandContext(context.Background(), cliPath, From 185b3e5f0cfd12210b4078ba6c06d3ba5d93049e Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Thu, 23 Apr 2026 15:11:17 +0200 Subject: [PATCH 5/6] fix: allow only one flag of to/from and add test --- tools/cli/internal/cli/sunset/diff_test.go | 213 +++++++++++++++++++++ tools/cli/internal/cli/sunset/list.go | 2 +- 2 files changed, 214 insertions(+), 1 deletion(-) diff --git a/tools/cli/internal/cli/sunset/diff_test.go b/tools/cli/internal/cli/sunset/diff_test.go index c0cb762d1c..7c288a40a1 100644 --- a/tools/cli/internal/cli/sunset/diff_test.go +++ b/tools/cli/internal/cli/sunset/diff_test.go @@ -287,3 +287,216 @@ func TestMakeKey(t *testing.T) { key := makeKey("/api/atlas/v2/groups", "GET", "2023-01-01") assert.Equal(t, "GET-/api/atlas/v2/groups-2023-01-01", key) } + +func TestDiffsInRangeToAndFrom(t *testing.T) { + fromDate := time.Date(2025, time.June, 3, 0, 0, 0, 0, time.UTC) + toDate := time.Date(2025, time.June, 21, 0, 0, 0, 0, time.UTC) + + opts := &DiffOpts{ + from: "2025-06-03", + to: "2025-06-21", + fromDate: &fromDate, + toDate: &toDate, + } + + diffs := []*Diff{ + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2023-01-01", + BaseSunsetDate: "2025-05-02", + SpecSunsetDate: "2025-05-04", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2024-01-01", + BaseSunsetDate: "2025-06-02", + SpecSunsetDate: "2025-06-04", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2025-01-01", + BaseSunsetDate: "2025-06-10", + SpecSunsetDate: "2025-06-12", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2026-01-01", + BaseSunsetDate: "2025-06-20", + SpecSunsetDate: "2025-06-22", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2027-01-01", + BaseSunsetDate: "2025-07-02", + SpecSunsetDate: "2025-07-04", + }, + } + + result, err := opts.diffsInRange(diffs) + + require.NoError(t, err) + assert.Len(t, result, 3) + + assert.Equal(t, "GET", result[0].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[0].Path) + assert.Equal(t, "2024-01-01", result[0].Version) + assert.Equal(t, "2025-06-02", result[0].BaseSunsetDate) + assert.Equal(t, "2025-06-04", result[0].SpecSunsetDate) + + assert.Equal(t, "GET", result[1].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[1].Path) + assert.Equal(t, "2025-01-01", result[1].Version) + assert.Equal(t, "2025-06-10", result[1].BaseSunsetDate) + assert.Equal(t, "2025-06-12", result[1].SpecSunsetDate) + + assert.Equal(t, "GET", result[2].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[2].Path) + assert.Equal(t, "2026-01-01", result[2].Version) + assert.Equal(t, "2025-06-20", result[2].BaseSunsetDate) + assert.Equal(t, "2025-06-22", result[2].SpecSunsetDate) +} + +func TestDiffsInRangeOnlyTo(t *testing.T) { + toDate := time.Date(2025, time.June, 11, 0, 0, 0, 0, time.UTC) + + opts := &DiffOpts{ + to: "2025-06-11", + toDate: &toDate, + } + + diffs := []*Diff{ + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2023-01-01", + BaseSunsetDate: "", + SpecSunsetDate: "2025-05-04", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2024-01-01", + BaseSunsetDate: "2025-06-02", + SpecSunsetDate: "2025-06-04", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2025-01-01", + BaseSunsetDate: "2025-06-10", + SpecSunsetDate: "2025-06-12", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2026-01-01", + BaseSunsetDate: "2025-06-20", + SpecSunsetDate: "2025-06-22", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2027-01-01", + BaseSunsetDate: "", + SpecSunsetDate: "2025-07-04", + }, + } + + result, err := opts.diffsInRange(diffs) + + require.NoError(t, err) + assert.Len(t, result, 3) + + assert.Equal(t, "GET", result[0].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[0].Path) + assert.Equal(t, "2023-01-01", result[0].Version) + assert.Empty(t, result[0].BaseSunsetDate) + assert.Equal(t, "2025-05-04", result[0].SpecSunsetDate) + + assert.Equal(t, "GET", result[1].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[1].Path) + assert.Equal(t, "2024-01-01", result[1].Version) + assert.Equal(t, "2025-06-02", result[1].BaseSunsetDate) + assert.Equal(t, "2025-06-04", result[1].SpecSunsetDate) + + assert.Equal(t, "GET", result[2].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[2].Path) + assert.Equal(t, "2025-01-01", result[2].Version) + assert.Equal(t, "2025-06-10", result[2].BaseSunsetDate) + assert.Equal(t, "2025-06-12", result[2].SpecSunsetDate) +} + +func TestDiffsInRangeOnlyFrom(t *testing.T) { + fromDate := time.Date(2025, time.June, 11, 0, 0, 0, 0, time.UTC) + + opts := &DiffOpts{ + from: "2025-06-11", + fromDate: &fromDate, + } + + diffs := []*Diff{ + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2023-01-01", + BaseSunsetDate: "2025-05-02", + SpecSunsetDate: "", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2024-01-01", + BaseSunsetDate: "2025-06-02", + SpecSunsetDate: "2025-06-04", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2025-01-01", + BaseSunsetDate: "2025-06-10", + SpecSunsetDate: "2025-06-12", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2026-01-01", + BaseSunsetDate: "2025-06-20", + SpecSunsetDate: "2025-06-22", + }, + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2027-01-01", + BaseSunsetDate: "2025-07-02", + SpecSunsetDate: "", + }, + } + + result, err := opts.diffsInRange(diffs) + + require.NoError(t, err) + assert.Len(t, result, 3) + + assert.Equal(t, "GET", result[0].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[0].Path) + assert.Equal(t, "2025-01-01", result[0].Version) + assert.Equal(t, "2025-06-10", result[0].BaseSunsetDate) + assert.Equal(t, "2025-06-12", result[0].SpecSunsetDate) + + assert.Equal(t, "GET", result[1].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[1].Path) + assert.Equal(t, "2026-01-01", result[1].Version) + assert.Equal(t, "2025-06-20", result[1].BaseSunsetDate) + assert.Equal(t, "2025-06-22", result[1].SpecSunsetDate) + + assert.Equal(t, "GET", result[2].Operation) + assert.Equal(t, "/api/atlas/v2/versions", result[2].Path) + assert.Equal(t, "2027-01-01", result[2].Version) + assert.Equal(t, "2025-07-02", result[2].BaseSunsetDate) + assert.Empty(t, result[2].SpecSunsetDate) +} diff --git a/tools/cli/internal/cli/sunset/list.go b/tools/cli/internal/cli/sunset/list.go index fb59fb186d..aaf2a6aa1d 100644 --- a/tools/cli/internal/cli/sunset/list.go +++ b/tools/cli/internal/cli/sunset/list.go @@ -94,7 +94,7 @@ func (o *ListOpts) newSunsetInRange(sunsets []*sunset.Sunset) ([]*sunset.Sunset, } func isDateInRange(date, from, to *time.Time) bool { - if date == nil { + if date == nil || date.IsZero() { return false } From b63147e6e709a65c3d428aa32963c8746344c847 Mon Sep 17 00:00:00 2001 From: Lovisa Berggren Date: Fri, 24 Apr 2026 18:01:57 +0200 Subject: [PATCH 6/6] fix: address comments --- tools/cli/internal/cli/sunset/diff.go | 25 ++++++++--- tools/cli/internal/cli/sunset/diff_test.go | 49 ++++++++++++++++++++++ tools/cli/internal/cli/sunset/list.go | 2 +- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/tools/cli/internal/cli/sunset/diff.go b/tools/cli/internal/cli/sunset/diff.go index 417d1e6ed0..f9eb49f73a 100644 --- a/tools/cli/internal/cli/sunset/diff.go +++ b/tools/cli/internal/cli/sunset/diff.go @@ -180,17 +180,17 @@ func (o *DiffOpts) diffsInRange(diffs []*Diff) ([]*Diff, error) { } for _, d := range diffs { - baseSunsetDate, err := time.Parse("2006-01-02", d.BaseSunsetDate) + baseSunsetDate, err := parseSunsetDate(d.BaseSunsetDate) if err != nil { - baseSunsetDate = time.Time{} // zero value for time if parsing fails + return nil, err } - specSunsetDate, err := time.Parse("2006-01-02", d.SpecSunsetDate) + specSunsetDate, err := parseSunsetDate(d.SpecSunsetDate) if err != nil { - specSunsetDate = time.Time{} // zero value for time if parsing fails + return nil, err } - if isDateInRange(&baseSunsetDate, o.fromDate, o.toDate) || isDateInRange(&specSunsetDate, o.fromDate, o.toDate) { + if isDateInRange(baseSunsetDate, o.fromDate, o.toDate) || isDateInRange(specSunsetDate, o.fromDate, o.toDate) { out = append(out, d) } } @@ -198,6 +198,17 @@ func (o *DiffOpts) diffsInRange(diffs []*Diff) ([]*Diff, error) { return out, nil } +func parseSunsetDate(dateStr string) (*time.Time, error) { + if dateStr == "" { + return nil, nil + } + parsedDate, err := time.Parse("2006-01-02", dateStr) + if err != nil { + return nil, err + } + return &parsedDate, err +} + func makeKey(path, operation, version string) string { return operation + "-" + path + "-" + version } @@ -242,6 +253,10 @@ func (o *DiffOpts) validate() error { o.toDate = &value } + if o.from != "" && o.to != "" && o.fromDate.After(*o.toDate) { + return fmt.Errorf("%s date cannot be after %s date", flag.From, flag.To) + } + return nil } diff --git a/tools/cli/internal/cli/sunset/diff_test.go b/tools/cli/internal/cli/sunset/diff_test.go index 7c288a40a1..95b968683a 100644 --- a/tools/cli/internal/cli/sunset/diff_test.go +++ b/tools/cli/internal/cli/sunset/diff_test.go @@ -361,6 +361,33 @@ func TestDiffsInRangeToAndFrom(t *testing.T) { assert.Equal(t, "2025-06-22", result[2].SpecSunsetDate) } +func TestDiffsInRangeToAndFromInvalidSunsetDate(t *testing.T) { + fromDate := time.Date(2025, time.June, 3, 0, 0, 0, 0, time.UTC) + toDate := time.Date(2025, time.June, 21, 0, 0, 0, 0, time.UTC) + + opts := &DiffOpts{ + from: "2025-06-03", + to: "2025-06-21", + fromDate: &fromDate, + toDate: &toDate, + } + + diffs := []*Diff{ + { + Operation: "GET", + Path: "/api/atlas/v2/versions", + Version: "2023-01-01", + BaseSunsetDate: "2025-05", // Invalid date format + SpecSunsetDate: "2025-05-04", + }, + } + + result, err := opts.diffsInRange(diffs) + + require.Error(t, err) + require.Empty(t, result) +} + func TestDiffsInRangeOnlyTo(t *testing.T) { toDate := time.Date(2025, time.June, 11, 0, 0, 0, 0, time.UTC) @@ -500,3 +527,25 @@ func TestDiffsInRangeOnlyFrom(t *testing.T) { assert.Equal(t, "2025-07-02", result[2].BaseSunsetDate) assert.Empty(t, result[2].SpecSunsetDate) } + +func TestValidate(t *testing.T) { + opts := &DiffOpts{ + from: "2025-06-01", + to: "2025-06-15", + } + + err := opts.validate() + require.NoError(t, err) + assert.Equal(t, time.Date(2025, time.June, 1, 0, 0, 0, 0, time.UTC), *opts.fromDate) + assert.Equal(t, time.Date(2025, time.June, 15, 0, 0, 0, 0, time.UTC), *opts.toDate) +} + +func TestValidateToIsAfterFrom(t *testing.T) { + opts := &DiffOpts{ + from: "2025-06-15", + to: "2025-06-01", + } + + err := opts.validate() + require.Error(t, err) +} diff --git a/tools/cli/internal/cli/sunset/list.go b/tools/cli/internal/cli/sunset/list.go index aaf2a6aa1d..fb59fb186d 100644 --- a/tools/cli/internal/cli/sunset/list.go +++ b/tools/cli/internal/cli/sunset/list.go @@ -94,7 +94,7 @@ func (o *ListOpts) newSunsetInRange(sunsets []*sunset.Sunset) ([]*sunset.Sunset, } func isDateInRange(date, from, to *time.Time) bool { - if date == nil || date.IsZero() { + if date == nil { return false }