Skip to content

Conversation

@mdelapenya
Copy link
Member

What does this PR do?

Updates the modulegen module template to use the testcontainers.Run() function with the moduleOpts pattern, and improves module development documentation with comprehensive best practices.

Changes:

  1. Module Template (_template/module.go.tmpl):

    • Updated to use testcontainers.Run() instead of directly passing opts...
    • Introduced moduleOpts slice pattern for consistent option handling
    • Follows the same pattern as all 49 migrated modules
  2. Test Updates (main_test.go):

    • Updated test assertions to match the new template structure
    • All 24 tests passing
  3. Documentation (docs/modules/index.md):

    • Enhanced module development best practices based on learnings from migrating all modules
    • Container struct design: enforce Container naming convention (not module-specific names)
    • Emphasized returning struct types, not interfaces for options
    • Detailed Run function implementation pattern (5-step guide)
    • Clear guidance on when to use built-in options vs CustomizeRequestOption vs custom Option types
    • Error handling patterns with practical examples
    • Option ordering best practices (defaults → user options → post-processing)
    • Container state inspection using strings.CutPrefix with early exit optimization

Why is it important?

  • Ensures new modules generated by modulegen follow the modern Run() API pattern
  • Provides comprehensive documentation for module developers based on real migration experience
  • Maintains consistency across all modules (existing and future)

Related issues

  • Part of the broader migration to the new testcontainers.Run() API
  • Completes the modulegen migration after all 49 modules have been successfully migrated

mdelapenya and others added 2 commits October 9, 2025 18:47
Enhance the module development documentation with comprehensive best
practices learned from migrating all 49 modules to use the Run function:

- Container struct design: enforce Container naming (not ModuleContainer),
  use private fields for state management
- Run function pattern: detailed 5-step implementation guide with code examples
- Option types: clarify when to use built-in options vs CustomizeRequestOption
  vs custom Option types, emphasize returning struct types not interfaces
- Error handling patterns in options with practical examples
- Option ordering: explain defaults → user options → post-processing
- Container state inspection: best practices using strings.CutPrefix with
  early exit optimization
- All examples follow patterns from successfully migrated modules

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@mdelapenya mdelapenya requested a review from a team as a code owner October 9, 2025 16:57
@mdelapenya mdelapenya added the chore Changes that do not impact the existing functionality label Oct 9, 2025
@netlify
Copy link

netlify bot commented Oct 9, 2025

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit 1b1f940
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-go/deploys/68e886990105490008e355b8
😎 Deploy Preview https://deploy-preview-3445--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Oct 9, 2025

Summary by CodeRabbit

  • New Features

    • Centralized “run” entrypoint for starting containers.
    • Helper to build connection strings from container state.
    • Richer access to container state (database, user, password).
  • Refactor

    • Unified container type across modules.
    • Option-driven configuration replaces prior single-purpose path.
    • Customizer now supports error propagation (breaking change).
  • Documentation

    • Expanded guides on options, processing order, and post-run inspection with examples.
  • Tests

    • Updated generator tests to validate the new option-based run flow.

Walkthrough

Replaces module-specific GenericContainerRequest flows with a unified public Container type and a new Run(ctx, img, opts ...testcontainers.ContainerCustomizer) (*Container, error); updates ContainerCustomizer to return error; generator templates and tests now build option slices, call Run, wrap the returned ctr, and perform post-run inspection.

Changes

Cohort / File(s) Summary
Documentation
docs/modules/index.md
Adds a public Container (embeds testcontainers.Container + internal state fields), documents Run(ctx, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error), updates ContainerCustomizer to return error, adds option-pattern guidance, post-run Inspect usage, and example ConnectionString method.
Generator template
modulegen/_template/module.go.tmpl
Changes generated module code to build []testcontainers.ContainerCustomizer (moduleOpts), append user opts, call testcontainers.Run(ctx, img, moduleOpts...), use the returned ctr to construct the module Container, and wrap errors as run <module>: %w.
Tests
modulegen/main_test.go
Updates test expectations to reflect initializing/appending moduleOpts, invoking testcontainers.Run, constructing the module container from returned ctr, and the new wrapped error message run <lower>: %w.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Module function
    participant RunAPI as testcontainers.Run
    participant Ctr as returned ctr
    participant Wrapper as Module Container
    participant Inspect as ctr.Inspect

    Caller->>RunAPI: Run(ctx, img, moduleOpts...)
    note right of RunAPI #E8F4FF: moduleOpts = defaults + user options
    RunAPI-->>Caller: ctr or error
    alt success
        Caller->>Wrapper: wrap ctr → &Container{Container: ctr, ...}
        Wrapper->>Inspect: ctr.Inspect(ctx)
        Inspect-->>Wrapper: env/ports/state
        Wrapper-->>Caller: populated Container (dbName,user,password)
    else error
        RunAPI-->>Caller: error
        Caller-->>Caller: return nil, fmt.Errorf("run %s: %w", lower, err)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • stevenh

Poem

"I hopped through code with nimble paws,
Options stacked in tidy laws,
Run gave a hop, the container sprung,
Secrets inspected, songs were sung.
A rabbit cheers — the build’s begun! 🐇"

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly summarizes the main change of updating the modulegen template to use the Run function when generating modules and follows a concise conventional commit style.
Description Check ✅ Passed The description clearly outlines the changes to the modulegen template, tests, and documentation and explains the purpose and importance of the migration to the new Run API pattern.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43ae0f6 and 1b1f940.

📒 Files selected for processing (1)
  • docs/modules/index.md (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (1.24.x, modulegen) / test: modulegen/1.24.x
  • GitHub Check: test (1.25.x, modulegen) / test: modulegen/1.25.x
  • GitHub Check: Analyze (go)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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 (1)
docs/modules/index.md (1)

328-344: Clarify the "wrong" example.

The section shows the correct way to call built-in options and warns against using .Customize(), but the explanation could be clearer about why this is wrong.

Consider adding a brief explanation:

 // ❌ Wrong: Don't use .Customize() method
 func WithConfigFile(cfg string) testcontainers.CustomizeRequestOption {
     return func(req *testcontainers.GenericContainerRequest) error {
-        return testcontainers.WithFiles(cfgFile).Customize(req)  // Wrong!
+        return testcontainers.WithFiles(cfgFile).Customize(req)  // Wrong: adds unnecessary indirection
     }
 }

The direct call pattern testcontainers.WithFiles(cfgFile)(req) is cleaner because CustomizeRequestOption is already a function that takes *GenericContainerRequest, so calling .Customize() is redundant.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0b6154 and aae4baf.

📒 Files selected for processing (3)
  • docs/modules/index.md (2 hunks)
  • modulegen/_template/module.go.tmpl (1 hunks)
  • modulegen/main_test.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test (1.25.x, modulegen) / test: modulegen/1.25.x
  • GitHub Check: test (1.25.x, modulegen) / test: modulegen/1.25.x
  • GitHub Check: test (1.24.x, modulegen) / test: modulegen/1.24.x
  • GitHub Check: test (1.24.x, modulegen) / test: modulegen/1.24.x
  • GitHub Check: test (1.25.x, modulegen) / test: modulegen/1.25.x
  • GitHub Check: Analyze (go)
🔇 Additional comments (7)
modulegen/_template/module.go.tmpl (1)

22-32: LGTM!

The testcontainers.Run() invocation and error handling follow the documented pattern correctly:

  • Properly handles the case where ctr might be nil
  • Wraps the container even on error (allows inspection of partial state)
  • Uses consistent error message format: "run {{ $lower }}: %w"
modulegen/main_test.go (1)

429-436: Tests verify incorrect template behavior.

These assertions correctly verify the current template output, but they also verify the incorrect option ordering identified in modulegen/_template/module.go.tmpl. Specifically:

  • Line 429 expects an empty moduleOpts slice
  • Line 430 expects the misleading "Add default options" comment
  • Line 431 expects user options to be appended immediately

When the template is fixed to follow the documented best practice (defaults → user options → post-processing), these assertions will need to be updated to:

require.Contains(t, data[16], "moduleOpts := []testcontainers.ContainerCustomizer{")
require.Contains(t, data[17], "// Add default options here")
// ... verify default options in slice ...
require.Equal(t, "\t// Step 2: Append user-provided options", data[X])
require.Equal(t, "\tmoduleOpts = append(moduleOpts, opts...)", data[X+1])

This comment is contingent on fixing the template issue flagged in modulegen/_template/module.go.tmpl.

docs/modules/index.md (5)

115-135: Excellent guidance on Container struct design.

This section clearly establishes the naming convention and composition pattern for module containers. Key strengths:

  • Enforces consistent Container naming across modules
  • Shows proper embedding of testcontainers.Container
  • Explains when to use private vs public fields
  • Includes helpful warning about legacy module names

136-186: Clear and comprehensive Run function pattern.

The 5-step pattern documented here is exactly what modules should follow:

  1. Process custom options
  2. Build moduleOpts with defaults
  3. Append user options
  4. Call testcontainers.Run
  5. Return with proper error wrapping

The example code demonstrates best practices including conditional option handling, proper error wrapping (fmt.Errorf("run redis: %w", err)), and correct nil container handling.

Note: The template in modulegen/_template/module.go.tmpl currently doesn't follow this pattern (specifically steps 2-3 are out of order). See my comment on that file.


206-282: Excellent guidance on option types and patterns.

This section provides clear, actionable guidance on when to use each option pattern:

  • Built-in options for simple configuration
  • CustomizeRequestOption for complex multi-operation logic
  • Custom Option type for transferring internal state

The emphasis on returning struct types rather than interfaces is particularly valuable and prevents common API design mistakes.


347-365: Critical guidance on option ordering.

This section clearly establishes the correct option ordering pattern:

  1. Module defaults
  2. User-provided options
  3. Post-processing options

The example code correctly demonstrates this pattern with helpful comments.

Important: The template in modulegen/_template/module.go.tmpl currently does NOT follow this ordering (it appends user options before defaults). This should be fixed to match the documented pattern. See my comment on that file.


367-406: Exemplary guidance on container state inspection.

This section demonstrates best practices for reading container state after creation:

  • Uses strings.CutPrefix (idiomatic Go standard library approach)
  • Shows early exit optimization with individual found flags
  • Provides complete working example with error handling
  • Summarizes best practices clearly

The pattern of checking all found flags together before breaking is particularly elegant:

if foundDB && foundUser && foundPass {
    break
}

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fbb6b8 and 0502c4e.

📒 Files selected for processing (1)
  • docs/modules/index.md (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test (1.24.x, modulegen) / test: modulegen/1.24.x
  • GitHub Check: Analyze (go)

Copy link

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0502c4e and 43ae0f6.

📒 Files selected for processing (1)
  • docs/modules/index.md (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (1.25.x, modulegen) / test: modulegen/1.25.x
  • GitHub Check: test (1.24.x, modulegen) / test: modulegen/1.24.x
  • GitHub Check: Analyze (go)

@mdelapenya mdelapenya self-assigned this Oct 10, 2025
@mdelapenya mdelapenya merged commit 03c0c8b into testcontainers:main Oct 10, 2025
21 checks passed
@mdelapenya mdelapenya deleted the chore-modulegen-use-run branch October 10, 2025 04:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Changes that do not impact the existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant