Nik Afiq fb62076fbc
All checks were successful
CI / test (push) Successful in 5s
CI / build-ha-gateway (push) Successful in 44s
CI / build-discord-bot (push) Successful in 46s
Add gRPC tests for entity and light services
- Implement tests for the Entity gRPC service, covering GetState and ListStates methods.
- Create tests for the Light gRPC service, including TurnOn, TurnOff, Toggle, and ListLights methods.
- Introduce mock service implementations to simulate behavior and validate interactions.
- Add logging interceptor tests to ensure proper logging levels based on handler errors.
- Develop application layer tests for entity and light functionalities, ensuring correct state management and error propagation.
2026-04-09 23:12:04 +09:00

79 lines
1.9 KiB
Go

package grpc
import (
"context"
"log/slog"
"sync"
"testing"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type recordingHandler struct {
mu sync.Mutex
levels []slog.Level
}
func (h *recordingHandler) Enabled(context.Context, slog.Level) bool {
return true
}
func (h *recordingHandler) Handle(ctx context.Context, record slog.Record) error {
h.mu.Lock()
defer h.mu.Unlock()
h.levels = append(h.levels, record.Level)
return nil
}
func (h *recordingHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return h
}
func (h *recordingHandler) WithGroup(name string) slog.Handler {
return h
}
func TestLoggingUnaryInterceptor(t *testing.T) {
tests := []struct {
name string
handlerErr error
wantLevel slog.Level
}{
{name: "nil error logs info", wantLevel: slog.LevelInfo},
{name: "not found logs warn", handlerErr: status.Error(codes.NotFound, "missing"), wantLevel: slog.LevelWarn},
{name: "internal logs error", handlerErr: status.Error(codes.Internal, "boom"), wantLevel: slog.LevelError},
{name: "unimplemented logs warn", handlerErr: status.Error(codes.Unimplemented, "stub"), wantLevel: slog.LevelWarn},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
h := &recordingHandler{}
interceptor := LoggingUnaryInterceptor(slog.New(h))
_, err := interceptor(
context.Background(),
"struct{}{}",
&grpc.UnaryServerInfo{FullMethod: "/ha.v1.LightService/TurnOn"},
func(ctx context.Context, req any) (any, error) {
return "ok", tt.handlerErr
},
)
if status.Code(err) != status.Code(tt.handlerErr) {
t.Fatalf("handler error = %v, want %v", err, tt.handlerErr)
}
h.mu.Lock()
defer h.mu.Unlock()
if len(h.levels) == 0 {
t.Fatal("no log records captured")
}
got := h.levels[len(h.levels)-1]
if got != tt.wantLevel {
t.Fatalf("log level = %v, want %v", got, tt.wantLevel)
}
})
}
}