From 3a36db47c1924d52f8c6487cca84b7593a96139c Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 6 Feb 2025 22:56:34 +0100 Subject: [PATCH 01/11] feat(e2e): test for version Setup the end-to-end test machinery and add a simple test for the version command --- Taskfile.yml | 13 ++- devbox.json | 3 +- e2e/README.md | 66 ++++++++++++++ e2e/e2e_test.go | 120 ++++++++++++++++++++++++++ e2e/testscripts/version/version.txtar | 3 + go.mod | 1 + go.sum | 4 + 7 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 e2e/README.md create mode 100644 e2e/e2e_test.go create mode 100644 e2e/testscripts/version/version.txtar diff --git a/Taskfile.yml b/Taskfile.yml index ff07aa38..830edbce 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -18,14 +18,23 @@ tasks: desc: Run unit tests run: always cmd: go test ./... + e2e: + desc: Run end-to-end tests + summary: | + Run tests that mimic how user enters commands and flags. + These tests make real requests to the Algolia API. + To run them, create a `.env` file with the `ALGOLIA_APPLICATION_ID` + and `ALGOLIA_API_KEY` credentials. + cmd: go test ./e2e -tags=e2e + dotenv: [.env] lint: desc: Lint code cmd: golangci-lint run format: desc: Format code cmds: - - gofumpt -w pkg cmd test internal api - - golines -w pkg cmd test internal api + - gofumpt -w pkg cmd test internal api e2e + - golines -w pkg cmd test internal api e2e ci: desc: Test, lint, and format aliases: diff --git a/devbox.json b/devbox.json index 4717bfef..91e2ea4a 100644 --- a/devbox.json +++ b/devbox.json @@ -8,5 +8,6 @@ "golines@latest", "gh@latest", "curl@latest" - ] + ], + "env_from": ".env" } diff --git a/e2e/README.md b/e2e/README.md new file mode 100644 index 00000000..d7e79db0 --- /dev/null +++ b/e2e/README.md @@ -0,0 +1,66 @@ +# End-to-end tests + +These tests run CLI commands like a user would, +built on top of the [`go-internal/testscript`](https://pkg.go.dev/github.com/rogpeppe/go-internal/testscript) package. + +They make real API requests, +so they work best in an empty Algolia application. +To run these tests, +you need to set the `ALGOLIA_APPLICATION_ID` and `ALGOLIA_API_KEY` environment variables. +If you're using `devbox`, create a `testing.env` file with these variables. +If you start a development environment with `devbox shell`, +the environment variables will be available for you. + +## New tests + +The tests use a very simple format. +For more information, run `go doc testscript`. + +To add a new scenario, create a new directory under the `testscripts` directory, +and add your files with the extension `txtar`. +Each test directory can have multiple test files. +Multiple directories are tested in parallel. + +### Example + +A simple 'hello world' testscript may look like this: + +```txt +# Test if output is hello +exec echo 'hello' +! stderr . +stdout '^hello\n$' +``` + +Read the documentation of the `testscript` package for more information. + +To add the new directory to the test suite, +add a new function to the file `./e2e/e2e_test.go`. +The function name must begin with `Test`. + +```go +// TestHello is a basic example +func TestHello(t *testing.T) { + RunTestsInDir(t, "testscripts/hello") +} +``` + +## Notes + +Since this makes real real requests to the same Algolia application, +these tests aren't fully isolated from each other. + +To make tests interfere less, follow these guidelines: + +- Use a unique index name in each `txtar` file. + For example, use `test-index` in `indices.txtar` and `test-settings` in `settings.txtar` + +- Delete indices at the end of your test with `defer`. + For an example, see `indices.txtar`. + +- Don't test for number of indices, or empty lists. + Different tests might also create their own indices, + and thus will fail a test that expect a certain number of indices present. + You can ensure that the index with a given name exists or doesn't exist, + by searching for the index name's pattern in the standard output. + Again, see `indices.txtar`. diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go new file mode 100644 index 00000000..8247da65 --- /dev/null +++ b/e2e/e2e_test.go @@ -0,0 +1,120 @@ +//go:build e2e + +package e2e_test + +import ( + "fmt" + "os" + "strings" + "testing" + + "github.com/algolia/cli/pkg/cmd/root" + "github.com/cli/go-internal/testscript" +) + +// algolia runs the root command of the Algolia CLI +func algolia() int { + return int(root.Execute()) +} + +// TestMain sets the executable program so that we don't depend on the compiled binary +func TestMain(m *testing.M) { + os.Exit(testscript.RunMain(m, map[string]func() int{ + "algolia": algolia, + })) +} + +// testEnvironment stores the environment variables we need to setup for the tests +type testEnvironment struct { + AppID string + ApiKey string +} + +// getEnv reads the environment variables and prints errors for missing ones +func (e *testEnvironment) getEnv() error { + env := map[string]string{} + + required := []string{ + // The CLI testing Algolia app + "ALGOLIA_APPLICATION_ID", + // API key with sufficient permissions to run all tests + "ALGOLIA_API_KEY", + } + + var missing []string + + for _, envVar := range required { + val, ok := os.LookupEnv(envVar) + if val == "" || !ok { + missing = append(missing, envVar) + continue + } + + env[envVar] = val + } + + if len(missing) > 0 { + return fmt.Errorf("missing environment variables: %s", strings.Join(missing, ", ")) + } + + e.AppID = env["ALGOLIA_APPLICATION_ID"] + e.ApiKey = env["ALGOLIA_API_KEY"] + + return nil +} + +// For the `defer` function +var keyT struct{} + +// setupEnv sets up the environment variables for the test +func setupEnv(testEnv testEnvironment) func(ts *testscript.Env) error { + return func(ts *testscript.Env) error { + ts.Setenv("ALGOLIA_APPLICATION_ID", testEnv.AppID) + ts.Setenv("ALGOLIA_API_KEY", testEnv.ApiKey) + + ts.Values[keyT] = ts.T() + return nil + } +} + +// setupCmds sets up custom commands we want to make available in the test scripts +func setupCmds( + testEnv testEnvironment, +) map[string]func(ts *testscript.TestScript, neg bool, args []string) { + return map[string]func(ts *testscript.TestScript, neg bool, args []string){ + "defer": func(ts *testscript.TestScript, neg bool, args []string) { + if neg { + ts.Fatalf("unsupported ! defer") + } + tt, ok := ts.Value(keyT).(testscript.T) + if !ok { + ts.Fatalf("%v is not a testscript.T", ts.Value(keyT)) + } + ts.Defer(func() { + if err := ts.Exec(args[0], args[1:]...); err != nil { + tt.FailNow() + } + }) + }, + } +} + +// runTestsInDir runs all test scripts from a directory +func runTestsInDir(t *testing.T, dirName string) { + var testEnv testEnvironment + if err := testEnv.getEnv(); err != nil { + t.Fatal(err) + } + t.Parallel() + t.Log("Running e2e tests in", dirName) + testscript.Run(t, testscript.Params{ + Dir: dirName, + Setup: setupEnv(testEnv), + Cmds: setupCmds(testEnv), + }) +} + +// TestVersion tests the version option +func TestVersion(t *testing.T) { + runTestsInDir(t, "testscripts/version") +} diff --git a/e2e/testscripts/version/version.txtar b/e2e/testscripts/version/version.txtar new file mode 100644 index 00000000..4b6d3e27 --- /dev/null +++ b/e2e/testscripts/version/version.txtar @@ -0,0 +1,3 @@ +# Check that we're using the correct version +exec algolia --version +stdout '^algolia version main$' diff --git a/go.mod b/go.mod index d2ddafd3..1a3e4c1b 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/algolia/algoliasearch-client-go/v4 v4.13.0 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 github.com/briandowns/spinner v1.23.2 + github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24 github.com/cli/safeexec v1.0.1 github.com/dustin/go-humanize v1.0.1 github.com/getkin/kin-openapi v0.100.0 diff --git a/go.sum b/go.sum index ec369a71..0342113c 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w= github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= +github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24 h1:QDrhR4JA2n3ij9YQN0u5ZeuvRIIvsUGmf5yPlTS0w8E= +github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24/go.mod h1:rr9GNING0onuVw8MnracQHn7PcchnFlP882Y0II2KZk= github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -194,6 +196,8 @@ golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From c5252bb560566aa53f027398286df12bba2b6596 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 13 Feb 2025 23:21:09 +0100 Subject: [PATCH 02/11] feat(e2e): index commands --- e2e/e2e_test.go | 5 +++ e2e/testscripts/indices/indices.txtar | 61 ++++++++++++++++++++++++++ e2e/testscripts/indices/replicas.txtar | 17 +++++++ 3 files changed, 83 insertions(+) create mode 100644 e2e/testscripts/indices/indices.txtar create mode 100644 e2e/testscripts/indices/replicas.txtar diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 8247da65..77562265 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -118,3 +118,8 @@ func runTestsInDir(t *testing.T, dirName string) { func TestVersion(t *testing.T) { runTestsInDir(t, "testscripts/version") } + +// TestIndices test `algolia indices` commands +func TestIndices(t *testing.T) { + runTestsInDir(t, "testscripts/indices") +} diff --git a/e2e/testscripts/indices/indices.txtar b/e2e/testscripts/indices/indices.txtar new file mode 100644 index 00000000..1e9db274 --- /dev/null +++ b/e2e/testscripts/indices/indices.txtar @@ -0,0 +1,61 @@ +# Create a new index +exec algolia settings set test-index --searchableAttributes "foo" --wait +! stderr . + +# Cleanup +defer algolia indices delete test-index --confirm + +# Confirm that the index setting is set +exec algolia settings get test-index +stdout -count=1 '"searchableAttributes":\["foo"\]' + +# Test that index is listed +exec algolia indices list +stdout -count=1 ^test-index + +# Copy the index +exec algolia indices copy test-index test-copy --wait --confirm +! stderr . + +# Confirm that there are 2 indices now +exec algolia indices list +stdout -count=1 ^test-index +stdout -count=1 ^test-copy + +# Add replica indices to the copy +exec algolia settings set test-copy --replicas 'test-replica1,test-replica2' --wait +! stderr . + +# Confirm that there are 4 indices now +exec algolia indices list +stdout -count=1 ^test-index +stdout -count=1 ^test-copy +stdout -count=1 ^test-replica1 +stdout -count=1 ^test-replica2 + +# Delete one of the replica indices +exec algolia indices delete test-replica1 --confirm --wait +! stderr . + +# Confirm that there are 3 indices now +exec algolia indices list +stdout -count=1 ^test-index +stdout -count=1 ^test-copy +! stdout ^test-replica1 +stdout -count=1 ^test-replica2 + +# Confirm that the test-copy index still has 1 replica index +exec algolia settings get test-copy +stdout -count=1 test-replica2 +! stdout test-replica1 + +# Delete the copy index including its replicas +exec algolia indices delete test-copy --include-replicas --confirm --wait +! stderr . + +# Confirm that there is 1 index now +exec algolia indices list +stdout -count=1 ^test-index +! stdout ^test-copy +! stdout ^test-replica1 +! stdout ^test-replica2 diff --git a/e2e/testscripts/indices/replicas.txtar b/e2e/testscripts/indices/replicas.txtar new file mode 100644 index 00000000..391ea082 --- /dev/null +++ b/e2e/testscripts/indices/replicas.txtar @@ -0,0 +1,17 @@ +env INDEX_NAME=test-can-delete-index +env REPLICA_NAME=test-can-delete-replica + +# Create a new index with one replica index +exec algolia settings set ${INDEX_NAME} --replicas ${REPLICA_NAME} --wait +! stderr . + +# Check that you can delete both manually +exec algolia index delete ${INDEX_NAME} ${REPLICA_NAME} --confirm +! stderr . +! stdout . + +# Check that both indices have been deleted +exec algolia index list +! stderr . +! stdout ${INDEX_NAME} +! stdout ${REPLICA_NAME} From c8480e998b70bb8d48d6cbd0a35fb3d72c2066fa Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 13 Feb 2025 23:21:26 +0100 Subject: [PATCH 03/11] feat(e2e): settings tests --- e2e/e2e_test.go | 5 +++ e2e/testscripts/settings/settings.txtar | 42 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 e2e/testscripts/settings/settings.txtar diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 77562265..7135c44d 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -123,3 +123,8 @@ func TestVersion(t *testing.T) { func TestIndices(t *testing.T) { runTestsInDir(t, "testscripts/indices") } + +// TestSettings tests `algolia settings` commands +func TestSettings(t *testing.T) { + runTestsInDir(t, "testscripts/settings") +} diff --git a/e2e/testscripts/settings/settings.txtar b/e2e/testscripts/settings/settings.txtar new file mode 100644 index 00000000..685483b7 --- /dev/null +++ b/e2e/testscripts/settings/settings.txtar @@ -0,0 +1,42 @@ +# Test importing settings from a file +exec algolia settings import test-settings --file settings.json --wait +! stderr . +! stdout . + +# Defer deleting the test index +defer algolia indices delete test-settings --confirm --include-replicas + +# Check that settings are applied +exec algolia settings get test-settings +stdout -count=1 '"searchableAttributes":\["foo"\]' + +# Test applying some settings from flags +exec algolia settings set test-settings --attributesToRetrieve "foo" --searchableAttributes "bar" --unretrievableAttributes "baz" --attributesForFaceting "searchable(bar)" --replicas "test-settings-replica" --wait +! stderr . + +# Test that the correct settings are applied +exec algolia settings get test-settings +stdout -count=1 '"attributesToRetrieve":\["foo"\]' +stdout -count=1 '"searchableAttributes":\["bar"\]' +stdout -count=1 '"unretrievableAttributes":\["baz"\]' +stdout -count=1 '"attributesForFaceting":\["searchable\(bar\)"\]' +stdout -count=1 '"replicas":\["test-settings-replica"\]' + +# Change a setting +exec algolia settings set test-settings --searchableAttributes "not-changed" --wait +! stderr . + +# Check that change is not applied to replica +exec algolia settings get test-settings-replica +! stdout not-changed + +# Change another setting and forward change to replica +exec algolia settings set test-settings --searchableAttributes "changed" --forward-to-replicas --wait +! stderr . + +# Check that change is also applied to replica +exec algolia settings get test-settings-replica +stdout -count=1 changed + +-- settings.json -- +{"searchableAttributes": ["foo"]} From 1f8d2de9d3930abc84ab9d1a3ab69c5720ed9997 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 10 Feb 2025 10:24:16 +0100 Subject: [PATCH 04/11] feat(e2e): objects commands --- e2e/e2e_test.go | 5 ++++ e2e/testscripts/objects/objects.txtar | 42 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 e2e/testscripts/objects/objects.txtar diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 7135c44d..46368b31 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -128,3 +128,8 @@ func TestIndices(t *testing.T) { func TestSettings(t *testing.T) { runTestsInDir(t, "testscripts/settings") } + +// TestObjects tests `algolia objects` commands +func TestObjects(t *testing.T) { + runTestsInDir(t, "testscripts/objects") +} diff --git a/e2e/testscripts/objects/objects.txtar b/e2e/testscripts/objects/objects.txtar new file mode 100644 index 00000000..6e3e75f3 --- /dev/null +++ b/e2e/testscripts/objects/objects.txtar @@ -0,0 +1,42 @@ +env INDEX_NAME=test-objects + +# Import a record without objectID from a file +! exec algolia objects import ${INDEX_NAME} --file record.jsonl --wait +! stdout . +stderr '^missing objectID on line 0$' + +# Defer cleanup +defer algolia index delete ${INDEX_NAME} --confirm + +# Import a record with autogenerated objectID +exec algolia objects import ${INDEX_NAME} --file record.jsonl --wait --auto-generate-object-id-if-not-exist +! stderr . + +# Check that record exists (use aliases) +exec algolia records list ${INDEX_NAME} +! stderr . +stdout -count=1 '"name":"foo"' +stdout -count=1 'objectID' + +# Add another record from stdin with objectID +stdin objectID.jsonl +exec algolia records import ${INDEX_NAME} --wait --file - +! stderr . + +# Update a record +exec algolia objects update ${INDEX_NAME} --file update.jsonl --wait --create-if-not-exists --continue-on-error +! stderr . + +# Check that record has that new attribute +exec algolia objects browse ${INDEX_NAME} +! stderr . +stdout -count=1 '"level":1' + +-- record.jsonl -- +{"name": "foo"} + +-- objectID.jsonl -- +{"objectID": "test-record-1", "name": "test"} + +-- update.jsonl -- +{"objectID": "test-record-1", "level": 1} From e53eab0064b057367317abb8c5b41bb0538050ed Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Mon, 10 Feb 2025 19:41:45 +0100 Subject: [PATCH 05/11] feat(e2e): synonyms commands --- e2e/e2e_test.go | 5 +++ e2e/testscripts/synonyms/synonyms.txtar | 41 +++++++++++++++++++++++++ pkg/cmd/synonyms/import/import.go | 6 ++++ 3 files changed, 52 insertions(+) create mode 100644 e2e/testscripts/synonyms/synonyms.txtar diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 46368b31..8d4ff78b 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -133,3 +133,8 @@ func TestSettings(t *testing.T) { func TestObjects(t *testing.T) { runTestsInDir(t, "testscripts/objects") } + +// TestSynonyms tests `algolia synonyms` commands +func TestSynonyms(t *testing.T) { + runTestsInDir(t, "testscripts/synonyms") +} diff --git a/e2e/testscripts/synonyms/synonyms.txtar b/e2e/testscripts/synonyms/synonyms.txtar new file mode 100644 index 00000000..2b3df45f --- /dev/null +++ b/e2e/testscripts/synonyms/synonyms.txtar @@ -0,0 +1,41 @@ +env INDEX_NAME=test-synonyms + +# List synonyms (empty index should return error) +! exec algolia synonyms browse ${INDEX_NAME} +! stdout . +stderr -count=1 'index test-synonyms doesn''t exist' + +# Don't know why the stdout is empty here. Running manually, I do get a message +# Import synonyms from a file +exec algolia synonyms import ${INDEX_NAME} --file synonyms.jsonl --wait +! stderr . +! stdout . + +# Defer cleanup +defer algolia index delete ${INDEX_NAME} --confirm +! stderr . + +# Don't know why the stdout is empty here. Running manually, I do get a message +# Import a synonym from the command line +stdin stdin.json +exec algolia synonyms import ${INDEX_NAME} --file - --wait +! stderr . +! stdout . + +# Don't know why the stdout is empty here. Running manually, I do get a message +# Save a synonym using flags +exec algolia synonyms save ${INDEX_NAME} --id 'test-synonym-4' --type altCorrection1 --word foo --corrections bar --wait +! stderr . +! stdout . + +# List synonyms +exec algolia synonyms browse ${INDEX_NAME} +! stderr . +stdout -count=4 'objectID' + +-- synonyms.jsonl -- +{"objectID": "test-synonym-1", "type": "synonym", "synonyms": ["foo", "bar"]} +{"objectID": "test-synonym-2", "type": "synonym", "synonyms": ["bar", "baz"]} + +-- stdin.json -- +{"objectID": "test-synonym-3", "type": "onewaysynonym", "input": "add", "synonyms": ["save"]} diff --git a/pkg/cmd/synonyms/import/import.go b/pkg/cmd/synonyms/import/import.go index fa7f8ee8..246074d5 100644 --- a/pkg/cmd/synonyms/import/import.go +++ b/pkg/cmd/synonyms/import/import.go @@ -126,11 +126,13 @@ func runImportCmd(opts *ImportOptions) error { // Unmarshal as map[string]interface{} to get the type of the synonym if err := json.Unmarshal(lineB, &synonym); err != nil { + opts.IO.StopProgressIndicator() return fmt.Errorf("failed to parse JSON synonym on line %d: %s", count, err) } err = validateSynonym(synonym) if err != nil { + opts.IO.StopProgressIndicator() return fmt.Errorf("%s on line %d", err, count) } @@ -144,6 +146,7 @@ func runImportCmd(opts *ImportOptions) error { WithForwardToReplicas(opts.ForwardToReplicas), ) if err != nil { + opts.IO.StopProgressIndicator() return err } if opts.Wait { @@ -164,6 +167,7 @@ func runImportCmd(opts *ImportOptions) error { WithForwardToReplicas(opts.ForwardToReplicas), ) if err != nil { + opts.IO.StopProgressIndicator() return err } if opts.Wait { @@ -177,6 +181,7 @@ func runImportCmd(opts *ImportOptions) error { WithForwardToReplicas(opts.ForwardToReplicas), ) if err != nil { + opts.IO.StopProgressIndicator() return err } if opts.Wait { @@ -188,6 +193,7 @@ func runImportCmd(opts *ImportOptions) error { for _, taskID := range taskIDs { _, err := client.WaitForTask(opts.Index, taskID) if err != nil { + opts.IO.StopProgressIndicator() return err } } From 7b4f79888814f3a31e3560dc70f2c7c3d0ca6b4c Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 11 Feb 2025 19:43:49 +0100 Subject: [PATCH 06/11] feat(e2e): rules commands --- e2e/e2e_test.go | 5 ++++ e2e/testscripts/rules/rules.txtar | 41 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 e2e/testscripts/rules/rules.txtar diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 8d4ff78b..3efcc609 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -138,3 +138,8 @@ func TestObjects(t *testing.T) { func TestSynonyms(t *testing.T) { runTestsInDir(t, "testscripts/synonyms") } + +// TestRules tests `algolia rules` commands +func TestRules(t *testing.T) { + runTestsInDir(t, "testscripts/rules") +} diff --git a/e2e/testscripts/rules/rules.txtar b/e2e/testscripts/rules/rules.txtar new file mode 100644 index 00000000..033446da --- /dev/null +++ b/e2e/testscripts/rules/rules.txtar @@ -0,0 +1,41 @@ +env INDEX_NAME=test-rules + +# List rules (empty index should return error) +! exec algolia rules browse ${INDEX_NAME} +! stdout . +stderr -count=1 'index test-rules doesn''t exist' + +# Importing a rule without objectID should fail +stdin without-objectID.json +! exec algolia rules import ${INDEX_NAME} --file - +! stdout . +stderr objectID + +# Importing a rule without consequence should also fail +stdin without-consequence.json +! exec algolia rules import ${INDEX_NAME} --file - +! stdout . +stderr consequence + +# Import rule +exec algolia rules import ${INDEX_NAME} --file rules.json --wait +! stderr . +! stdout . + +# Delete the rule +exec algolia rules delete ${INDEX_NAME} --rule-ids "test-rule-1" --wait --confirm +! stderr . +! stdout . + +# Defer cleanup +defer algolia index delete ${INDEX_NAME} --confirm +! stderr . + +-- without-objectID.json -- +{} + +-- without-consequence.json -- +{"objectID": "foo"} + +-- rules.json -- +{"conditions":[{"anchoring":"contains","pattern":"foo"}],"consequence":{"promote":[{"objectID":"foo","position":0}]},"objectID":"test-rule-1"} From fabb541ea49d0a73a3407a55058516f549d93c54 Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Fri, 14 Feb 2025 08:05:45 +0100 Subject: [PATCH 07/11] fixuxp synoynms --- e2e/testscripts/synonyms/synonyms.txtar | 3 --- 1 file changed, 3 deletions(-) diff --git a/e2e/testscripts/synonyms/synonyms.txtar b/e2e/testscripts/synonyms/synonyms.txtar index 2b3df45f..bb22588f 100644 --- a/e2e/testscripts/synonyms/synonyms.txtar +++ b/e2e/testscripts/synonyms/synonyms.txtar @@ -5,7 +5,6 @@ env INDEX_NAME=test-synonyms ! stdout . stderr -count=1 'index test-synonyms doesn''t exist' -# Don't know why the stdout is empty here. Running manually, I do get a message # Import synonyms from a file exec algolia synonyms import ${INDEX_NAME} --file synonyms.jsonl --wait ! stderr . @@ -15,14 +14,12 @@ exec algolia synonyms import ${INDEX_NAME} --file synonyms.jsonl --wait defer algolia index delete ${INDEX_NAME} --confirm ! stderr . -# Don't know why the stdout is empty here. Running manually, I do get a message # Import a synonym from the command line stdin stdin.json exec algolia synonyms import ${INDEX_NAME} --file - --wait ! stderr . ! stdout . -# Don't know why the stdout is empty here. Running manually, I do get a message # Save a synonym using flags exec algolia synonyms save ${INDEX_NAME} --id 'test-synonym-4' --type altCorrection1 --word foo --corrections bar --wait ! stderr . From 4c9ebc5b75333f83138d882b869fc7ea339c1b4b Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Tue, 11 Feb 2025 19:59:32 +0100 Subject: [PATCH 08/11] feat(e2e): search commands --- e2e/e2e_test.go | 5 +++++ e2e/testscripts/search/search.txtar | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 e2e/testscripts/search/search.txtar diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index 3efcc609..50324a1e 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -143,3 +143,8 @@ func TestSynonyms(t *testing.T) { func TestRules(t *testing.T) { runTestsInDir(t, "testscripts/rules") } + +// TestSearch tests `algolia search` +func TestSearch(t *testing.T) { + runTestsInDir(t, "testscripts/search") +} diff --git a/e2e/testscripts/search/search.txtar b/e2e/testscripts/search/search.txtar new file mode 100644 index 00000000..3de0b0f1 --- /dev/null +++ b/e2e/testscripts/search/search.txtar @@ -0,0 +1,18 @@ +env INDEX_NAME=test-search + +# Add a record to an index +exec algolia records import ${INDEX_NAME} --file record.json --wait +! stderr . +! stdout . + +# Defer cleanup +defer algolia index delete ${INDEX_NAME} --confirm +! stderr . + +# Search for something +exec algolia search ${INDEX_NAME} --query "test" +! stderr . +stdout -count=1 '"nbHits":1' + +-- record.json -- +{"objectID": "test-record-1", "name": "Test record"} From 0fd27647c42f3d5b32ae85cf0b1c3765a75441bd Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Fri, 14 Mar 2025 12:52:28 +0100 Subject: [PATCH 09/11] chore: go mod tidy --- go.mod | 1 + go.sum | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 1a3e4c1b..40423fc7 100644 --- a/go.mod +++ b/go.mod @@ -77,5 +77,6 @@ require ( golang.org/x/net v0.37.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect + golang.org/x/tools v0.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index 0342113c..8cdd9979 100644 --- a/go.sum +++ b/go.sum @@ -196,8 +196,8 @@ golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 8bef1bdd59e10fc4337d39faaf946201f6c031da Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Fri, 14 Mar 2025 13:38:28 +0100 Subject: [PATCH 10/11] chore: update e2e README --- e2e/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/e2e/README.md b/e2e/README.md index d7e79db0..4a143693 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -7,13 +7,13 @@ They make real API requests, so they work best in an empty Algolia application. To run these tests, you need to set the `ALGOLIA_APPLICATION_ID` and `ALGOLIA_API_KEY` environment variables. -If you're using `devbox`, create a `testing.env` file with these variables. +If you're using `devbox`, create a `.env` file in the project root directory with these variables. If you start a development environment with `devbox shell`, -the environment variables will be available for you. +the environment variables will be available to you. ## New tests -The tests use a very simple format. +The tests use a simple format. For more information, run `go doc testscript`. To add a new scenario, create a new directory under the `testscripts` directory, @@ -59,8 +59,8 @@ To make tests interfere less, follow these guidelines: For an example, see `indices.txtar`. - Don't test for number of indices, or empty lists. - Different tests might also create their own indices, - and thus will fail a test that expect a certain number of indices present. - You can ensure that the index with a given name exists or doesn't exist, + As other tests might create their own indices and objects, + checks that expect a certain number of items might fail. + You can ensure that the index with a given name exists or doesn't exist by searching for the index name's pattern in the standard output. Again, see `indices.txtar`. From 4edcab91d3e7e0153625158e98359c9c6e359efd Mon Sep 17 00:00:00 2001 From: Kai Welke Date: Thu, 27 Mar 2025 10:15:06 +0100 Subject: [PATCH 11/11] fix(e2e): replace hard-coded index name --- e2e/testscripts/indices/indices.txtar | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/e2e/testscripts/indices/indices.txtar b/e2e/testscripts/indices/indices.txtar index 1e9db274..9418b478 100644 --- a/e2e/testscripts/indices/indices.txtar +++ b/e2e/testscripts/indices/indices.txtar @@ -1,35 +1,38 @@ +env INDEX_NAME=test-index +env COPY_NAME=test-copy + # Create a new index -exec algolia settings set test-index --searchableAttributes "foo" --wait +exec algolia settings set ${INDEX_NAME} --searchableAttributes "foo" --wait ! stderr . # Cleanup -defer algolia indices delete test-index --confirm +defer algolia indices delete ${INDEX_NAME} --confirm # Confirm that the index setting is set -exec algolia settings get test-index +exec algolia settings get ${INDEX_NAME} stdout -count=1 '"searchableAttributes":\["foo"\]' # Test that index is listed exec algolia indices list -stdout -count=1 ^test-index +stdout -count=1 ^${INDEX_NAME} # Copy the index -exec algolia indices copy test-index test-copy --wait --confirm +exec algolia indices copy ${INDEX_NAME} ${COPY_NAME} --wait --confirm ! stderr . # Confirm that there are 2 indices now exec algolia indices list -stdout -count=1 ^test-index -stdout -count=1 ^test-copy +stdout -count=1 ^${INDEX_NAME} +stdout -count=1 ^${COPY_NAME} # Add replica indices to the copy -exec algolia settings set test-copy --replicas 'test-replica1,test-replica2' --wait +exec algolia settings set ${COPY_NAME} --replicas 'test-replica1,test-replica2' --wait ! stderr . # Confirm that there are 4 indices now exec algolia indices list -stdout -count=1 ^test-index -stdout -count=1 ^test-copy +stdout -count=1 ^${INDEX_NAME} +stdout -count=1 ^${COPY_NAME} stdout -count=1 ^test-replica1 stdout -count=1 ^test-replica2 @@ -39,23 +42,23 @@ exec algolia indices delete test-replica1 --confirm --wait # Confirm that there are 3 indices now exec algolia indices list -stdout -count=1 ^test-index -stdout -count=1 ^test-copy +stdout -count=1 ^${INDEX_NAME} +stdout -count=1 ^${COPY_NAME} ! stdout ^test-replica1 stdout -count=1 ^test-replica2 # Confirm that the test-copy index still has 1 replica index -exec algolia settings get test-copy +exec algolia settings get ${COPY_NAME} stdout -count=1 test-replica2 ! stdout test-replica1 # Delete the copy index including its replicas -exec algolia indices delete test-copy --include-replicas --confirm --wait +exec algolia indices delete ${COPY_NAME} --include-replicas --confirm --wait ! stderr . # Confirm that there is 1 index now exec algolia indices list -stdout -count=1 ^test-index -! stdout ^test-copy +stdout -count=1 ^${INDEX_NAME} +! stdout ^${COPY_NAME} ! stdout ^test-replica1 ! stdout ^test-replica2