Files
schemas/cmd/service/amqp_test.go
T
argoyleandClaude Opus 5 57682f2dc3
schemas / check (push) Skipped
schemas / vulnerabilities (push) Skipped
schemas / check-release (push) Skipped
schemas / build (push) Skipped
pre-commit / pre-commit (push) Skipped
schemas / vulnerabilities (pull_request) Successful in 58s
schemas / check-release (pull_request) Successful in 59s
schemas / check (pull_request) Successful in 1m58s
pre-commit / pre-commit (pull_request) Successful in 2m54s
schemas / build (pull_request) Successful in 6m9s
schemas / deploy-prod (pull_request) Skipped
refactor: migrate eventsourced to codeberg and go-messaging-amqp
Move eventsourced modules from gitlab.com/unboundsoftware to codeberg.org/eventsourced.
The Codeberg amqp publisher is built on go-messaging-amqp, so the service
switches from sparetimecoders/goamqp as well:

- routing keys are mapped on the eventsourced publisher (newEventPublisher)
- cache consumers are typed handlers; Cache.Update drops the goamqp shape
- publishes detach from the request context (uncancelledPublisher)
- closeEvents is buffered and the receiver also watches rootCtx
- WithPrefetchLimit dropped (no-op, 20 is the default)
- amqp091-go v1.15.0 (GO-2026-6372)
- renovate groups codeberg.org/messaging with eventsourced

Only transient consumer queues are used, so no queue cutover is needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dpb7Y47dPz246oY9bCzh7k
2026-09-16 17:23:38 +02:00

86 lines
2.5 KiB
Go

package main
import (
"context"
"log/slog"
"testing"
"codeberg.org/eventsourced/eventsourced"
goamqp "codeberg.org/messaging/go-messaging-amqp"
spec "codeberg.org/messaging/messaging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitea.unbound.se/unboundsoftware/schemas/cache"
"gitea.unbound.se/unboundsoftware/schemas/domain"
)
// ownKeys is a literal list on purpose: deriving it from the code would keep a renamed key green.
var ownKeys = []string{
"SubGraph.Updated",
"Organization.Added",
"Organization.UserAdded",
"Organization.APIKeyAdded",
"Organization.APIKeyRemoved",
"Organization.Removed",
}
func Test_amqpSetups(t *testing.T) {
setups := amqpSetups(slog.Default(), make(chan error, 1), goamqp.NewPublisher(), cache.New(slog.Default()))
topology, err := goamqp.CollectTopology(serviceName, setups...)
require.NoError(t, err)
var consumed, publishesTo []string
for _, e := range topology.Endpoints {
switch e.Direction {
case spec.DirectionConsume:
assert.True(t, e.Ephemeral, "cache consumers must be transient, %s is durable", e.QueueName)
consumed = append(consumed, e.RoutingKey)
case spec.DirectionPublish:
publishesTo = append(publishesTo, e.ExchangeName)
}
}
assert.ElementsMatch(t, ownKeys, consumed)
assert.Equal(t, []string{"events.topic.exchange"}, publishesTo)
}
type recordingPublisher struct {
keys []string
ctxs []context.Context
}
func (r *recordingPublisher) Publish(ctx context.Context, routingKey string, _ any, _ ...goamqp.Header) error {
r.keys = append(r.keys, routingKey)
r.ctxs = append(r.ctxs, ctx)
return nil
}
func Test_newEventPublisher(t *testing.T) {
rec := &recordingPublisher{}
p, err := newEventPublisher(rec)
require.NoError(t, err)
for _, e := range []eventsourced.Event{
&domain.SubGraphUpdated{},
&domain.OrganizationAdded{},
&domain.UserAddedToOrganization{},
&domain.APIKeyAdded{},
&domain.APIKeyRemoved{},
&domain.OrganizationRemoved{},
} {
require.NoError(t, p.Publish(context.Background(), e))
}
assert.Equal(t, ownKeys, rec.keys, "published keys must match what the cache consumers bind")
}
func Test_uncancelledPublisher(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
rec := &recordingPublisher{}
p, err := newEventPublisher(uncancelledPublisher{rec})
require.NoError(t, err)
require.NoError(t, p.Publish(ctx, &domain.OrganizationAdded{}))
assert.NoError(t, rec.ctxs[0].Err(), "publish must not see the request's cancellation")
}