style: gofmt + fix errcheck lint warning
All checks were successful
CI / Test (push) Successful in 3m2s
CI / Lint (push) Successful in 3m7s
CI / Release (push) Successful in 1m55s
CI / Notify Downstream (stt-module) (push) Successful in 1s
CI / Notify Downstream (voice-assistant) (push) Successful in 1s
CI / Notify (push) Successful in 2s
CI / Notify Downstream (chat-handler) (push) Successful in 1s
CI / Notify Downstream (pipeline-bridge) (push) Successful in 1s
CI / Notify Downstream (tts-module) (push) Successful in 1s

This commit is contained in:
2026-02-21 15:35:37 -05:00
parent 13ef1df109
commit f1dd96a42b
8 changed files with 875 additions and 875 deletions

View File

@@ -1,15 +1,15 @@
package handler
import (
"context"
"testing"
"context"
"testing"
"github.com/nats-io/nats.go"
"google.golang.org/protobuf/proto"
"github.com/nats-io/nats.go"
"google.golang.org/protobuf/proto"
"git.daviestechlabs.io/daviestechlabs/handler-base/config"
pb "git.daviestechlabs.io/daviestechlabs/handler-base/gen/messagespb"
"git.daviestechlabs.io/daviestechlabs/handler-base/natsutil"
"git.daviestechlabs.io/daviestechlabs/handler-base/config"
pb "git.daviestechlabs.io/daviestechlabs/handler-base/gen/messagespb"
"git.daviestechlabs.io/daviestechlabs/handler-base/natsutil"
)
// ────────────────────────────────────────────────────────────────────────────
@@ -17,75 +17,75 @@ pb "git.daviestechlabs.io/daviestechlabs/handler-base/gen/messagespb"
// ────────────────────────────────────────────────────────────────────────────
func TestNewHandler(t *testing.T) {
cfg := config.Load()
cfg.ServiceName = "test-handler"
cfg.NATSQueueGroup = "test-group"
cfg := config.Load()
cfg.ServiceName = "test-handler"
cfg.NATSQueueGroup = "test-group"
h := New("ai.test.subject", cfg)
if h.Subject != "ai.test.subject" {
t.Errorf("Subject = %q", h.Subject)
}
if h.QueueGroup != "test-group" {
t.Errorf("QueueGroup = %q", h.QueueGroup)
}
if h.Settings.ServiceName != "test-handler" {
t.Errorf("ServiceName = %q", h.Settings.ServiceName)
}
h := New("ai.test.subject", cfg)
if h.Subject != "ai.test.subject" {
t.Errorf("Subject = %q", h.Subject)
}
if h.QueueGroup != "test-group" {
t.Errorf("QueueGroup = %q", h.QueueGroup)
}
if h.Settings.ServiceName != "test-handler" {
t.Errorf("ServiceName = %q", h.Settings.ServiceName)
}
}
func TestNewHandlerNilSettings(t *testing.T) {
h := New("ai.test", nil)
if h.Settings == nil {
t.Fatal("Settings should be loaded automatically")
}
if h.Settings.ServiceName != "handler" {
t.Errorf("ServiceName = %q, want default", h.Settings.ServiceName)
}
h := New("ai.test", nil)
if h.Settings == nil {
t.Fatal("Settings should be loaded automatically")
}
if h.Settings.ServiceName != "handler" {
t.Errorf("ServiceName = %q, want default", h.Settings.ServiceName)
}
}
func TestCallbackRegistration(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
setupCalled := false
h.OnSetup(func(ctx context.Context) error {
setupCalled = true
return nil
})
setupCalled := false
h.OnSetup(func(ctx context.Context) error {
setupCalled = true
return nil
})
teardownCalled := false
h.OnTeardown(func(ctx context.Context) error {
teardownCalled = true
return nil
})
teardownCalled := false
h.OnTeardown(func(ctx context.Context) error {
teardownCalled = true
return nil
})
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, nil
})
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, nil
})
if h.onSetup == nil || h.onTeardown == nil || h.onTypedMessage == nil {
t.Error("callbacks should not be nil after registration")
}
if h.onSetup == nil || h.onTeardown == nil || h.onTypedMessage == nil {
t.Error("callbacks should not be nil after registration")
}
// Verify setup/teardown work when called directly.
_ = h.onSetup(context.Background())
_ = h.onTeardown(context.Background())
if !setupCalled || !teardownCalled {
t.Error("callbacks should have been invoked")
}
// Verify setup/teardown work when called directly.
_ = h.onSetup(context.Background())
_ = h.onTeardown(context.Background())
if !setupCalled || !teardownCalled {
t.Error("callbacks should have been invoked")
}
}
func TestTypedMessageRegistration(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return &pb.ChatResponse{Response: "ok"}, nil
})
})
if h.onTypedMessage == nil {
t.Error("onTypedMessage should not be nil after registration")
}
if h.onTypedMessage == nil {
t.Error("onTypedMessage should not be nil after registration")
}
}
// ────────────────────────────────────────────────────────────────────────────
@@ -93,101 +93,101 @@ t.Error("onTypedMessage should not be nil after registration")
// ────────────────────────────────────────────────────────────────────────────
func TestWrapHandler_ValidMessage(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
var receivedReq pb.ChatRequest
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
if err := natsutil.Decode(msg.Data, &receivedReq); err != nil {
return nil, err
}
var receivedReq pb.ChatRequest
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
if err := natsutil.Decode(msg.Data, &receivedReq); err != nil {
return nil, err
}
return &pb.ChatResponse{Response: "ok", UserId: receivedReq.GetUserId()}, nil
})
})
// Encode a message the same way services would.
encoded, err := proto.Marshal(&pb.ChatRequest{
RequestId: "test-001",
Message: "hello",
Premium: true,
})
if err != nil {
t.Fatal(err)
}
// Encode a message the same way services would.
encoded, err := proto.Marshal(&pb.ChatRequest{
RequestId: "test-001",
Message: "hello",
Premium: true,
})
if err != nil {
t.Fatal(err)
}
// Call wrapHandler directly without NATS.
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{
Subject: "ai.test.user.42.message",
Data: encoded,
})
// Call wrapHandler directly without NATS.
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{
Subject: "ai.test.user.42.message",
Data: encoded,
})
if receivedReq.GetRequestId() != "test-001" {
t.Errorf("request_id = %v", receivedReq.GetRequestId())
}
if receivedReq.GetPremium() != true {
t.Errorf("premium = %v", receivedReq.GetPremium())
}
if receivedReq.GetRequestId() != "test-001" {
t.Errorf("request_id = %v", receivedReq.GetRequestId())
}
if receivedReq.GetPremium() != true {
t.Errorf("premium = %v", receivedReq.GetPremium())
}
}
func TestWrapHandler_InvalidMessage(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
handlerCalled := false
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
handlerCalled = true
var req pb.ChatRequest
if err := natsutil.Decode(msg.Data, &req); err != nil {
return nil, err
}
return &pb.ChatResponse{}, nil
})
handlerCalled := false
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
handlerCalled = true
var req pb.ChatRequest
if err := natsutil.Decode(msg.Data, &req); err != nil {
return nil, err
}
return &pb.ChatResponse{}, nil
})
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{
Subject: "ai.test",
Data: []byte{0xFF, 0xFE, 0xFD}, // invalid protobuf
})
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{
Subject: "ai.test",
Data: []byte{0xFF, 0xFE, 0xFD}, // invalid protobuf
})
// The handler IS called (wrapHandler doesn't pre-decode), but it should
// return an error from Decode. Either way no panic.
_ = handlerCalled
// The handler IS called (wrapHandler doesn't pre-decode), but it should
// return an error from Decode. Either way no panic.
_ = handlerCalled
}
func TestWrapHandler_HandlerError(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, context.DeadlineExceeded
})
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, context.DeadlineExceeded
})
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "err-test"})
handler := h.wrapHandler(context.Background())
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "err-test"})
handler := h.wrapHandler(context.Background())
// Should not panic even when handler returns error.
handler(&nats.Msg{
Subject: "ai.test",
Data: encoded,
})
// Should not panic even when handler returns error.
handler(&nats.Msg{
Subject: "ai.test",
Data: encoded,
})
}
func TestWrapHandler_NilResponse(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, nil // fire-and-forget style
})
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, nil // fire-and-forget style
})
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "nil-resp"})
handler := h.wrapHandler(context.Background())
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "nil-resp"})
handler := h.wrapHandler(context.Background())
// Should not panic with nil response and no reply subject.
handler(&nats.Msg{
Subject: "ai.test",
Data: encoded,
})
// Should not panic with nil response and no reply subject.
handler(&nats.Msg{
Subject: "ai.test",
Data: encoded,
})
}
// ────────────────────────────────────────────────────────────────────────────
@@ -195,59 +195,59 @@ Data: encoded,
// ────────────────────────────────────────────────────────────────────────────
func TestWrapHandler_Typed(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
var received pb.ChatRequest
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
if err := natsutil.Decode(msg.Data, &received); err != nil {
return nil, err
}
var received pb.ChatRequest
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
if err := natsutil.Decode(msg.Data, &received); err != nil {
return nil, err
}
return &pb.ChatResponse{UserId: received.GetUserId(), Response: "ok"}, nil
})
})
encoded, _ := proto.Marshal(&pb.ChatRequest{
RequestId: "typed-001",
Message: "hello typed",
})
encoded, _ := proto.Marshal(&pb.ChatRequest{
RequestId: "typed-001",
Message: "hello typed",
})
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{Subject: "ai.test", Data: encoded})
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{Subject: "ai.test", Data: encoded})
if received.GetRequestId() != "typed-001" {
t.Errorf("RequestId = %q", received.GetRequestId())
}
if received.GetMessage() != "hello typed" {
t.Errorf("Message = %q", received.GetMessage())
}
if received.GetRequestId() != "typed-001" {
t.Errorf("RequestId = %q", received.GetRequestId())
}
if received.GetMessage() != "hello typed" {
t.Errorf("Message = %q", received.GetMessage())
}
}
func TestWrapHandler_TypedError(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, context.DeadlineExceeded
})
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, context.DeadlineExceeded
})
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "err"})
handler := h.wrapHandler(context.Background())
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "err"})
handler := h.wrapHandler(context.Background())
// Should not panic.
handler(&nats.Msg{Subject: "ai.test", Data: encoded})
// Should not panic.
handler(&nats.Msg{Subject: "ai.test", Data: encoded})
}
func TestWrapHandler_TypedNilResponse(t *testing.T) {
cfg := config.Load()
h := New("ai.test", cfg)
cfg := config.Load()
h := New("ai.test", cfg)
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, nil
})
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
return nil, nil
})
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "nil"})
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{Subject: "ai.test", Data: encoded})
encoded, _ := proto.Marshal(&pb.ChatRequest{RequestId: "nil"})
handler := h.wrapHandler(context.Background())
handler(&nats.Msg{Subject: "ai.test", Data: encoded})
}
// ────────────────────────────────────────────────────────────────────────────
@@ -255,25 +255,25 @@ handler(&nats.Msg{Subject: "ai.test", Data: encoded})
// ────────────────────────────────────────────────────────────────────────────
func BenchmarkWrapHandler(b *testing.B) {
cfg := config.Load()
h := New("ai.test", cfg)
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
var req pb.ChatRequest
_ = natsutil.Decode(msg.Data, &req)
return &pb.ChatResponse{Response: "ok"}, nil
})
cfg := config.Load()
h := New("ai.test", cfg)
h.OnTypedMessage(func(ctx context.Context, msg *nats.Msg) (proto.Message, error) {
var req pb.ChatRequest
_ = natsutil.Decode(msg.Data, &req)
return &pb.ChatResponse{Response: "ok"}, nil
})
encoded, _ := proto.Marshal(&pb.ChatRequest{
RequestId: "bench-001",
Message: "What is the capital of France?",
Premium: true,
TopK: 10,
})
handler := h.wrapHandler(context.Background())
msg := &nats.Msg{Subject: "ai.test", Data: encoded}
encoded, _ := proto.Marshal(&pb.ChatRequest{
RequestId: "bench-001",
Message: "What is the capital of France?",
Premium: true,
TopK: 10,
})
handler := h.wrapHandler(context.Background())
msg := &nats.Msg{Subject: "ai.test", Data: encoded}
b.ResetTimer()
for b.Loop() {
handler(msg)
}
b.ResetTimer()
for b.Loop() {
handler(msg)
}
}