Skip to content

feat(etcd): use testcontainers-go for etcd tests #1721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

mdelapenya
Copy link
Contributor

@mdelapenya mdelapenya commented Apr 29, 2025

What does this PR do?

It uses Testcontainers Go's etcd module for testing the etcd Storage module.

It uses the latest release of the official etcd Docker image (there is no latest tag in the repo), which is replacing the bitnami/etcd:latest that was used before these changes.

The test store that is created spins up an etcd cluster of two nodes.

Why is it important?

Enable local testing of the module, also making the CI reproducible.

Summary by CodeRabbit

  • Refactor
    • Improved test isolation and reliability by updating tests to use dynamically created, temporary etcd clusters for each run.
    • Replaced shared global test resources with a helper function for creating fresh storage instances in tests.
  • Tests
    • Updated all tests and benchmarks to use the new test setup, ensuring each test runs against its own isolated environment.

@mdelapenya mdelapenya requested a review from a team as a code owner April 29, 2025 09:28
@mdelapenya mdelapenya requested review from gaby, sixcolors, ReneWerner87 and efectn and removed request for a team April 29, 2025 09:28
Copy link
Contributor

coderabbitai bot commented Apr 29, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • .github/workflows/benchmark.yml is excluded by !**/*.yml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes update the etcd storage test suite to improve test isolation and reproducibility. The test setup now uses a helper function to spin up a temporary etcd cluster for each test using testcontainers-go, replacing the previous global test store and static initialization. The import of the etcd client library in the main code is also updated to use an explicit alias. No core logic or exported API changes are made to the storage implementation itself; all functional changes are limited to the test setup and teardown.

Changes

File(s) Change Summary
etcd/etcd.go Changed the etcd client library import to use an explicit alias clientv3, updating references accordingly.
etcd/etcd_test.go Refactored tests to remove the global test store and TestMain. Introduced newTestStore helper using testcontainers-go for per-test etcd cluster setup and cleanup. Updated all tests and benchmarks to use this helper for improved isolation.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Function
    participant TestStore as newTestStore
    participant TestContainers as testcontainers-go
    participant EtcdCluster as Ephemeral Etcd Cluster
    participant Storage as Storage Instance

    Test->>TestStore: Call newTestStore(t)
    TestStore->>TestContainers: Start ephemeral etcd cluster
    TestContainers->>EtcdCluster: Spin up etcd container(s)
    TestStore->>EtcdCluster: Get client endpoint
    TestStore->>Storage: Create Storage with endpoint
    TestStore-->>Test: Return Storage instance
    Test->>Storage: Run test logic
    Test->>Storage: Defer store.Close()
    Test->>TestContainers: Container cleanup on test completion
Loading

Possibly related PRs

Suggested labels

✏️ Feature

Suggested reviewers

  • sixcolors
  • gaby
  • ReneWerner87
  • efectn

Poem

In the warren of code, a new test does bloom,
Each bunny spins up clusters, banishing test gloom.
No more sharing burrows, each test gets its own,
With testcontainers magic, isolation is grown.
Etcd hops in, ephemeral and spry—
Now every test runs with a sparkle in its eye!
🐇✨


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
etcd/etcd_test.go (2)

80-88: Speed up the TTL-expiry test to keep the suite snappy

Sleeping six seconds per run makes the test suite noticeably slower (especially in benchmarks where it may run multiple times). etcd honours 1-second leases; shaving the values keeps intent while reducing wall-clock time.

-err := testStore.Set("test", []byte("fiber_test_value"), 3*time.Second)
+err := testStore.Set("test", []byte("fiber_test_value"), 1*time.Second)

- time.Sleep(6 * time.Second)
+ time.Sleep(2 * time.Second)

44-56: Enable t.Parallel() to unlock concurrent execution (optional)

All tests now spin up isolated containers, so they are safe to run in parallel. Adding t.Parallel() at the top of each test accelerates the suite on multi-core CI runners. Keep an eye on resource limits, though, as each test will start its own etcd cluster.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d6a777 and ca1046c.

⛔ Files ignored due to path filters (4)
  • .github/workflows/benchmark.yml is excluded by !**/*.yml
  • .github/workflows/test-etcd.yml is excluded by !**/*.yml
  • etcd/go.mod is excluded by !**/*.mod
  • etcd/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (2)
  • etcd/etcd.go (1 hunks)
  • etcd/etcd_test.go (8 hunks)
🧰 Additional context used
🧠 Learnings (1)
etcd/etcd_test.go (2)
Learnt from: luk3skyw4lker
PR: gofiber/storage#1342
File: clickhouse/clickhouse_test.go:138-160
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The `Test_Reset` function in `clickhouse/clickhouse_test.go` already includes a verification step to ensure the storage is empty after a reset operation by checking that a previously set key returns an empty byte slice.
Learnt from: luk3skyw4lker
PR: gofiber/storage#1342
File: clickhouse/clickhouse_test.go:138-160
Timestamp: 2024-07-01T15:48:53.094Z
Learning: The `Test_Reset` function in `clickhouse/clickhouse_test.go` already includes a verification step to ensure the storage is empty after a reset operation by checking that a previously set key returns an empty byte slice.
🧬 Code Graph Analysis (1)
etcd/etcd_test.go (1)
etcd/etcd.go (2)
  • Storage (10-12)
  • New (14-33)
🔇 Additional comments (2)
etcd/etcd.go (1)

7-7: Explicit alias is a welcome readability boost

Using a package alias (clientv3) makes downstream references clearer and avoids potential name clashes. No further action required.

etcd/etcd_test.go (1)

32-34: ⚠️ Potential issue

Call require.NoError before scheduling cleanup to avoid a nil-pointer panic

If etcd.Run fails, c is nil. Passing that nil reference into testcontainers.CleanupContainer may dereference a nil pointer inside the helper. Swap the two calls:

-c, err := etcd.Run(ctx, img, etcd.WithNodes("etcd-1", "etcd-2"), etcd.WithClusterToken("test-cluster"))
-testcontainers.CleanupContainer(t, c)
-require.NoError(t, err)
+c, err := etcd.Run(ctx, img, etcd.WithNodes("etcd-1", "etcd-2"), etcd.WithClusterToken("test-cluster"))
+require.NoError(t, err)
+testcontainers.CleanupContainer(t, c)
⛔ Skipped due to learnings
Learnt from: mdelapenya
PR: gofiber/storage#1665
File: cassandra/cassandra_test.go:35-38
Timestamp: 2025-04-20T23:52:03.362Z
Learning: In testcontainers-go, calling `testcontainers.CleanupContainer(t, c)` before checking the error from container creation is safe due to the implementation details of the library. The CleanupContainer function handles nil or partially initialized containers gracefully.

Comment on lines +29 to 30
ctx := context.Background()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add a timeout-bound context to prevent hanging tests

context.Background() has an unlimited lifetime. If the container pull or startup hangs (for example, due to a flaky network or Docker daemon issue), the test will block indefinitely. Consider adding a reasonable timeout:

-ctx := context.Background()
+ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+defer cancel()

This keeps the test suite responsive in CI environments.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ctx := context.Background()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
🤖 Prompt for AI Agents (early access)
In etcd/etcd_test.go around lines 29 to 30, replace the use of context.Background() with a context that has a timeout to prevent the test from hanging indefinitely. Create a context with a timeout using context.WithTimeout, specifying a reasonable duration (e.g., a few seconds), and ensure to defer the cancellation function to release resources. This change will keep the test suite responsive by automatically cancelling the context if the operation takes too long.

@gaby gaby requested a review from Copilot April 29, 2025 11:45
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR updates the etcd tests to use Testcontainers Go, enabling the creation of ephemeral 2-node etcd clusters for improved test isolation and reliability while also updating related workflows.

  • Refactored test initialization by replacing a global test resource with a helper function that creates new etcd clusters for each test run.
  • Updated the docker image references and Go versions in CI workflows to support the new testing approach.

Reviewed Changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.

File Description
etcd/etcd_test.go Introduced newTestStore to spin up temporary etcd clusters using Testcontainers Go.
etcd/etcd.go Updated import alias for the etcd client package.
.github/workflows/test-etcd.yml Updated image references and Go versions for etcd testing.
.github/workflows/benchmark.yml Removed deprecated docker-based etcd setup and added environment variable settings.
Files not reviewed (1)
  • etcd/go.mod: Language not supported

Copy link
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 LGTM, just a small comment about image version

@mdelapenya
Copy link
Contributor Author

mdelapenya commented Apr 30, 2025

I see the benchmarks do fail with a massive degradation 🤔 Do you think it's because of the etcd cluster? I verified the containers are started before the the benchmarks code, so they should not interfere, right?

@gaby
Copy link
Member

gaby commented May 2, 2025

@mdelapenya @ReneWerner87 The bitnami image was faster for some reason, and it also supports semver.

https://hub.docker.com/r/bitnami/etcd

@ReneWerner87
Copy link
Member

Ok then let's take this

@mdelapenya
Copy link
Contributor Author

I'll take a look, as I'm not sure if the testcontainers module supports the image. I'm on PTO until Monday, will check it then 🙏

@gaby
Copy link
Member

gaby commented May 5, 2025

@mdelapenya My guess is that a single etcd is more performant than a cluster in this case.

The cluster is adding network latency, replication, etc

@mdelapenya
Copy link
Contributor Author

We have just merged a PR fixing 1-node clusters in etcd: testcontainers/testcontainers-go#3149

I think we can create a tc-go release soon to address that. I'm putting this PR as draft until then

@mdelapenya mdelapenya marked this pull request as draft May 5, 2025 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants