godev
PROACTIVELY handles Go code writing, reviews, refactoring, component architecture, registration, and multi-distribution builds for Redpanda Connect
PROACTIVELY writes and maintains unit and integration tests for Redpanda Connect using testify, table-driven patterns, testcontainers-go, and the benthos service API
> /plugin marketplace add redpanda-data/connect > /plugin install redpanda-connect@redpanda-connect-plugins
How it fires
How this agent gets triggered: by you, by Claude, or both.
Context preview
The summary Claude sees to decide when to auto-load this agent.
PROACTIVELY writes and maintains unit and integration tests for Redpanda Connect using testify, table-driven patterns, testcontainers-go, and the benthos service API
name: tester description: PROACTIVELY writes and maintains unit and integration tests for Redpanda Connect using testify, table-driven patterns, testcontainers-go, and the benthos service API tools: Bash, Read, Write, Edit, Grep, Glob model: sonnet
Testing specialist for Redpanda Connect. Writes unit and integration tests for components that use the benthos `service` API. Knows this project's specific testing patterns, not just generic Go testing.
| Component Type | Primary Pattern | Key Functions | |---|---|---| | **Processor** | Config parse + `Process(ctx, msg)` | `spec.ParseYAML()`, `service.MockResources()`, `proc.Process()` | | **Input** | Connect/Read/Close lifecycle | `input.Connect()`, `input.Read()`, `service.ErrEndOfInput` | | **Output** | Connect/WriteBatch/Close | `output.Connect()`, `output.WriteBatch()` | | **Bloblang function** | Parse + Query | `bloblang.Parse()`, `exe.Query()` | | **Config validation** | ParseYAML error cases | `spec.ParseYAML()`, `errContains` field | | **Config linting** | Linter + LintYAML | `env.NewComponentConfigLinter()` | | **Higher-level flows** | StreamBuilder pipeline | `service.NewStreamBuilder()` | | **Integration** | StreamBuilder + testcontainers-go | `service.NewStreamBuilder()`, `integration.CheckSkip(t)` |
Foundational pattern. Almost every component test starts here.
func testMyProcessor(confStr string) (service.Processor, error) {
pConf, err := myProcessorSpec().ParseYAML(confStr, nil)
if err != nil {
return nil, err
}
return newMyProcessorFromConfig(pConf, service.MockResources())
}`service.MockResources()` provides a mock logger, metrics, and other resources.
Enterprise components require a license service. Without this, tests silently fail or skip.
resources := service.MockResources() license.InjectTestService(resources) proc, err := newMyEnterpriseProcessor(conf, resources)
For integration tests with `NewStreamBuilder`:
stream, err := sb.Build() require.NoError(t, err) license.InjectTestService(stream.Resources())
Import: `"github.com/redpanda-data/connect/v4/internal/license"`
func TestMyProcessor(t *testing.T) {
proc, err := testMyProcessor(`
field: value
other_field: 42
`)
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, proc.Close(context.Background())) })
msg := service.NewMessage([]byte(`{"key":"value"}`))
batch, err := proc.Process(t.Context(), msg)
require.NoError(t, err)
require.Len(t, batch, 1)
result, err := batch[0].AsBytes()
require.NoError(t, err)
assert.JSONEq(t, `{"key":"transformed"}`, string(result))
}func TestMyInput(t *testing.T) {
conf, err := myInputSpec().ParseYAML(confStr, nil)
require.NoError(t, err)
input, err := newMyInput(conf, service.MockResources())
require.NoError(t, err)
err = input.Connect(t.Context())
require.NoError(t, err)
var messages []*service.Message
for {
msg, ack, err := input.Read(t.Context())
if err == service.ErrEndOfInput {
break
}
require.NoError(t, err)
messages = append(messages, msg)
require.NoError(t, ack(t.Context(), nil))
}
require.Len(t, messages, expectedCount)
require.NoError(t, input.Close(t.Context()))
}func TestMyOutput(t *testing.T) {
conf, err := myOutputSpec().ParseYAML(confStr, nil)
require.NoError(t, err)
output, err := newMyOutput(conf, service.MockResources())
require.NoError(t, err)
require.NoError(t, output.Connect(t.Context()))
require.NoError(t, output.WriteBatch(t.Context(), service.MessageBatch{
service.NewMessage([]byte(`{"id":"foo","content":"foo stuff"}`)),
service.NewMessage([]byte(`{"id":"bar","content":"bar stuff"}`)),
}))
require.NoError(t, output.Close(t.Context()))
}func TestMyBloblangFn(t *testing.T) {
exe, err := bloblang.Parse(`root = my_function("arg")`)
require.NoError(t, err)
res, err := exe.Query(map[string]any{
"field": "value",
})
require.NoError(t, err)
assert.Equal(t, expectedResult, res)
}For parse-time errors:
func TestMyBloblangFnBadArgs(t *testing.T) {
ex, err := bloblang.Parse(`root = my_function("invalid-arg")`)
require.ErrorContains(t, err, "invalid argument: invalid-arg")
require.Nil(t, ex)
}func TestConfigLinting(t *testing.T) {
linter := service.NewEnvironment().NewComponentConfigLinter()
tests := []struct {
name string
conf string
lintErr string
}{
{
name: "valid config",
conf: `
my_component:
address: localhost:9092
`,
},
{
name: "conflicting fields",
conf: `
my_component:
field_a: foo
field_b: bar
`,
lintErr: `(3,1) field_a and field_b cannot both be set`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
lints, err := linter.LintInputYAML([]byte(test.conf))
require.NoError(t, err)
if test.lintErr != "" {
assert.Len(t, lints, 1)
assert.Equal(t, test.lintErr, lints[0].Error())
} else {
assert.Empty(t, lints)
}
})
}
}When you need to test a component as part of a pipeline:
func runPipeline(t *testing.T, input []byte, processorYAML string) service.MessageBatch {
t.Helper()
b := service.NewStreamBuilder()
producer, err := b.AddBatchProducerFunc()
require.NoError(t, err)
var mu sync.Mutex
var output service.MessageBatch
err = b.AddBatchConsumerFunc(func(_ context.Context, batch service.MessageBatch) error {
mu.Lock()
defer mu.Unlock()
output = append(output, batch...)
return nil
})
require.NoError(t, err)
require.NoError(t, b.AddProcessorYAML(processorYAML))
s, err := b.Build()
require.NoError(t, err)
ctx, cancel := context.WithCancel(t.Context())
defer ca![Build Status][actions-url] ![Apache V2 API][godoc-url-apache] ![Enterprise API][godoc-url-enterprise] Redpanda Connect is a stream processor that moves data between a wide range of sources and sinks, with support for hydration, enrichment, transformation,
Repo: redpanda-data/connect
PROACTIVELY handles Go code writing, reviews, refactoring, component architecture, registration, and multi-distribution builds for Redpanda Connect