From 375c1b151fa8dd27eff0ad04a668ad9ad51588f4 Mon Sep 17 00:00:00 2001 From: NatanTechOfNY Date: Wed, 11 Nov 2026 08:14:52 -0500 Subject: [PATCH] fix(config): export rules and synonyms in index config export The BrowseRules/BrowseSynonyms aggregator receives a pointer to the response, but the helpers asserted the value type and silently dropped every page. Rules and synonyms were never exported, and scopes limited to them failed with 'No config to export'. Co-authored-by: Cursor --- pkg/cmd/shared/config/config.go | 12 ++-- pkg/cmd/shared/config/config_test.go | 83 ++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 pkg/cmd/shared/config/config_test.go diff --git a/pkg/cmd/shared/config/config.go b/pkg/cmd/shared/config/config.go index 5cd89230..94bb1929 100644 --- a/pkg/cmd/shared/config/config.go +++ b/pkg/cmd/shared/config/config.go @@ -15,8 +15,10 @@ func GetSynonyms(client *search.APIClient, srcIndex string) ([]search.SynonymHit srcIndex, *search.NewEmptySearchSynonymsParams(), search.WithAggregator(func(res any, _ error) { - response, _ := res.(search.SearchSynonymsResponse) - synonyms = append(synonyms, response.Hits...) + // The client passes a pointer to the aggregator, which is nil on request failure. + if response, ok := res.(*search.SearchSynonymsResponse); ok && response != nil { + synonyms = append(synonyms, response.Hits...) + } }), ) if err != nil { @@ -32,8 +34,10 @@ func GetRules(client *search.APIClient, srcIndex string) ([]search.Rule, error) srcIndex, *search.NewEmptySearchRulesParams(), search.WithAggregator(func(res any, _ error) { - response, _ := res.(search.SearchRulesResponse) - rules = append(rules, response.Hits...) + // The client passes a pointer to the aggregator, which is nil on request failure. + if response, ok := res.(*search.SearchRulesResponse); ok && response != nil { + rules = append(rules, response.Hits...) + } }), ) if err != nil { diff --git a/pkg/cmd/shared/config/config_test.go b/pkg/cmd/shared/config/config_test.go new file mode 100644 index 00000000..9f7590d1 --- /dev/null +++ b/pkg/cmd/shared/config/config_test.go @@ -0,0 +1,83 @@ +package config + +import ( + "testing" + + "github.com/algolia/algoliasearch-client-go/v4/algolia/search" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/algolia/cli/pkg/httpmock" + "github.com/algolia/cli/test" +) + +func TestGetIndexConfig(t *testing.T) { + tests := []struct { + name string + scope []string + wantSettings bool + wantRules []string + wantSynonyms []string + }{ + { + name: "all scopes", + scope: []string{"settings", "synonyms", "rules"}, + wantSettings: true, + wantRules: []string{"rule-1", "rule-2"}, + wantSynonyms: []string{"syn-1"}, + }, + { + name: "rules only", + scope: []string{"rules"}, + wantRules: []string{"rule-1", "rule-2"}, + }, + { + name: "synonyms only", + scope: []string{"synonyms"}, + wantSynonyms: []string{"syn-1"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := httpmock.Registry{} + r.Register( + httpmock.REST("GET", "1/indexes/foo/settings"), + httpmock.JSONResponse(search.SettingsResponse{}), + ) + r.Register( + httpmock.REST("POST", "1/indexes/foo/rules/search"), + httpmock.JSONResponse(search.SearchRulesResponse{ + Hits: []search.Rule{{ObjectID: "rule-1"}, {ObjectID: "rule-2"}}, + }), + ) + r.Register( + httpmock.REST("POST", "1/indexes/foo/synonyms/search"), + httpmock.JSONResponse(search.SearchSynonymsResponse{ + Hits: []search.SynonymHit{{ObjectID: "syn-1", Type: search.SYNONYM_TYPE_SYNONYM}}, + }), + ) + + f, _ := test.NewFactory(true, &r, nil, "") + client, err := f.SearchClient() + require.NoError(t, err) + + got, err := GetIndexConfig(client, "foo", tt.scope, f.IOStreams.ColorScheme()) + require.NoError(t, err) + + assert.Equal(t, tt.wantSettings, got.Settings != nil) + + var rules []string + for _, rule := range got.Rules { + rules = append(rules, rule.ObjectID) + } + assert.Equal(t, tt.wantRules, rules) + + var synonyms []string + for _, synonym := range got.Synonyms { + synonyms = append(synonyms, synonym.ObjectID) + } + assert.Equal(t, tt.wantSynonyms, synonyms) + }) + } +}