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

173 lines
4.2 KiB
Go

package app
import (
"context"
"errors"
"reflect"
"testing"
"time"
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/ports/driven"
)
func TestEntityAppGetState(t *testing.T) {
now := time.Date(2026, 4, 9, 10, 0, 0, 0, time.UTC)
t.Run("happy path maps attributes to strings", func(t *testing.T) {
app := NewEntityApp(&mockHAClient{
getStateFunc: func(ctx context.Context, entityID string) (*driven.HAState, error) {
if entityID != "light.kitchen" {
t.Fatalf("GetState() entityID = %q, want %q", entityID, "light.kitchen")
}
return &driven.HAState{
EntityID: "light.kitchen",
State: "on",
Attributes: map[string]any{
"friendly_name": "Kitchen",
"brightness": 42,
"available": true,
},
LastChanged: now,
LastUpdated: now,
}, nil
},
})
got, err := app.GetState(context.Background(), "light.kitchen")
if err != nil {
t.Fatalf("GetState() error = %v", err)
}
want := &domain.EntityState{
EntityID: "light.kitchen",
State: "on",
Attributes: map[string]string{
"friendly_name": "Kitchen",
"brightness": "42",
"available": "true",
},
LastChanged: now,
LastUpdated: now,
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("GetState() = %#v, want %#v", got, want)
}
})
t.Run("propagates ha.GetState error", func(t *testing.T) {
wantErr := errors.New("missing")
app := NewEntityApp(&mockHAClient{
getStateFunc: func(ctx context.Context, entityID string) (*driven.HAState, error) {
return nil, wantErr
},
})
_, err := app.GetState(context.Background(), "light.kitchen")
if !errors.Is(err, wantErr) {
t.Fatalf("GetState() error = %v, want %v", err, wantErr)
}
})
}
func TestEntityAppListStates(t *testing.T) {
now := time.Date(2026, 4, 9, 10, 0, 0, 0, time.UTC)
allStates := []*driven.HAState{
{
EntityID: "light.kitchen",
State: "on",
Attributes: map[string]any{"friendly_name": "Kitchen"},
LastChanged: now,
LastUpdated: now,
},
{
EntityID: "switch.fan",
State: "off",
Attributes: map[string]any{"friendly_name": "Fan"},
LastChanged: now,
LastUpdated: now,
},
{
EntityID: "sensor.temp",
State: "21",
Attributes: map[string]any{"unit": "C"},
LastChanged: now,
LastUpdated: now,
},
}
tests := []struct {
name string
ids []domain.EntityID
domainFilter string
want []*domain.EntityState
}{
{
name: "no filters",
want: []*domain.EntityState{
haStateToDomain(allStates[0]),
haStateToDomain(allStates[1]),
haStateToDomain(allStates[2]),
},
},
{
name: "id filter only",
ids: []domain.EntityID{"switch.fan"},
want: []*domain.EntityState{
haStateToDomain(allStates[1]),
},
},
{
name: "domain filter only",
domainFilter: "light",
want: []*domain.EntityState{
haStateToDomain(allStates[0]),
},
},
{
name: "both filters",
ids: []domain.EntityID{"light.kitchen", "sensor.temp"},
domainFilter: "light",
want: []*domain.EntityState{
haStateToDomain(allStates[0]),
},
},
{
name: "id not found returns empty",
ids: []domain.EntityID{"light.missing"},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := NewEntityApp(&mockHAClient{
listStatesFunc: func(ctx context.Context) ([]*driven.HAState, error) {
return allStates, nil
},
})
got, err := app.ListStates(context.Background(), tt.ids, tt.domainFilter)
if err != nil {
t.Fatalf("ListStates() error = %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("ListStates() = %#v, want %#v", got, tt.want)
}
})
}
t.Run("propagates ha.ListStates error", func(t *testing.T) {
wantErr := errors.New("list failed")
app := NewEntityApp(&mockHAClient{
listStatesFunc: func(ctx context.Context) ([]*driven.HAState, error) {
return nil, wantErr
},
})
_, err := app.ListStates(context.Background(), nil, "")
if !errors.Is(err, wantErr) {
t.Fatalf("ListStates() error = %v, want %v", err, wantErr)
}
})
}