120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
//go:build integration
|
|
|
|
package mq
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.ipao.vip/rogee/go-sip/internal/tenant"
|
|
)
|
|
|
|
func TestLocalRabbitMQConfirmAckAndDeadLetter(t *testing.T) {
|
|
url := os.Getenv("RABBITMQ_URL")
|
|
if url == "" {
|
|
t.Skip("RABBITMQ_URL is not configured")
|
|
}
|
|
broker, err := OpenWithPrefetch(url, "", 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer broker.Close()
|
|
|
|
tenantKey := fmt.Sprintf("integration-%d", time.Now().UnixNano())
|
|
queue, err := broker.DeclareTenantQueue(tenantKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
routingKey, err := tenant.CommandRoutingKey(tenantKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
deadLetterQueue, err := tenant.DeadLetterQueue(tenantKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := []byte(`{"command_id":"integration-command"}`)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer cancel()
|
|
acked := make(chan struct{}, 1)
|
|
consumeDone := make(chan error, 1)
|
|
go func() {
|
|
consumeDone <- broker.Consume(ctx, queue, func(_ context.Context, gotRoutingKey string, gotBody []byte) error {
|
|
if gotRoutingKey != routingKey || string(gotBody) != string(body) {
|
|
return fmt.Errorf("delivery mismatch: key=%q body=%q", gotRoutingKey, gotBody)
|
|
}
|
|
acked <- struct{}{}
|
|
return nil
|
|
})
|
|
}()
|
|
if err := broker.Publish(ctx, DefaultExchange, routingKey, body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
select {
|
|
case <-acked:
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
cancel()
|
|
select {
|
|
case <-consumeDone:
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("consumer did not stop")
|
|
}
|
|
|
|
permanentCtx, permanentCancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer permanentCancel()
|
|
permanentSeen := make(chan struct{}, 1)
|
|
permanentDone := make(chan error, 1)
|
|
go func() {
|
|
permanentDone <- broker.Consume(permanentCtx, queue, func(_ context.Context, _ string, _ []byte) error {
|
|
permanentSeen <- struct{}{}
|
|
return Permanent(errors.New("synthetic permanent command error"))
|
|
})
|
|
}()
|
|
if err := broker.Publish(permanentCtx, DefaultExchange, routingKey, body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
select {
|
|
case <-permanentSeen:
|
|
case <-permanentCtx.Done():
|
|
t.Fatal(permanentCtx.Err())
|
|
}
|
|
permanentCancel()
|
|
select {
|
|
case <-permanentDone:
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("permanent consumer did not stop")
|
|
}
|
|
|
|
dlqCtx, dlqCancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer dlqCancel()
|
|
deadLettered := make(chan struct{}, 1)
|
|
dlqDone := make(chan error, 1)
|
|
go func() {
|
|
dlqDone <- broker.Consume(dlqCtx, deadLetterQueue, func(_ context.Context, _ string, gotBody []byte) error {
|
|
if string(gotBody) != string(body) {
|
|
return fmt.Errorf("dead-letter body mismatch: %q", gotBody)
|
|
}
|
|
deadLettered <- struct{}{}
|
|
return nil
|
|
})
|
|
}()
|
|
select {
|
|
case <-deadLettered:
|
|
dlqCancel()
|
|
case <-dlqCtx.Done():
|
|
t.Fatal(dlqCtx.Err())
|
|
}
|
|
select {
|
|
case <-dlqDone:
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("dead-letter consumer did not stop")
|
|
}
|
|
}
|