From 90fdc93a1545e3aac22b0422c4171b1ee240e587 Mon Sep 17 00:00:00 2001 From: Joshua Rich Date: Wed, 31 Jul 2024 22:16:31 +1000 Subject: [PATCH] test(agent): :white_check_mark: logger is required --- internal/agent/agent.go | 3 +- ...aces_mocks_test.go => agent_mocks_test.go} | 237 ++++++++++++++++++ internal/agent/register_test.go | 6 +- 3 files changed, 242 insertions(+), 4 deletions(-) rename internal/agent/{interfaces_mocks_test.go => agent_mocks_test.go} (59%) diff --git a/internal/agent/agent.go b/internal/agent/agent.go index 9e66b2e2a..73d090b9f 100644 --- a/internal/agent/agent.go +++ b/internal/agent/agent.go @@ -4,6 +4,8 @@ // https://opensource.org/licenses/MIT // revive:disable:unused-receiver +// +//go:generate moq -out agent_mocks_test.go . UI Registry SensorTracker package agent import ( @@ -140,7 +142,6 @@ func ForceRegister(value bool) Option { // then spawns a sensor tracker and the workers to gather sensor data and // publish it to Home Assistant. // - //revive:disable:function-length func (agent *Agent) Run(ctx context.Context, trk SensorTracker, reg Registry) error { var wg sync.WaitGroup diff --git a/internal/agent/interfaces_mocks_test.go b/internal/agent/agent_mocks_test.go similarity index 59% rename from internal/agent/interfaces_mocks_test.go rename to internal/agent/agent_mocks_test.go index 5c2821ede..bc0cfe074 100644 --- a/internal/agent/interfaces_mocks_test.go +++ b/internal/agent/agent_mocks_test.go @@ -4,10 +4,247 @@ package agent import ( + "context" + "github.com/joshuar/go-hass-agent/internal/agent/ui" "github.com/joshuar/go-hass-agent/internal/hass/sensor" + "github.com/joshuar/go-hass-agent/internal/preferences" "sync" ) +// Ensure, that UIMock does implement UI. +// If this is not the case, regenerate this file with moq. +var _ UI = &UIMock{} + +// UIMock is a mock implementation of UI. +// +// func TestSomethingThatUsesUI(t *testing.T) { +// +// // make and configure a mocked UI +// mockedUI := &UIMock{ +// DisplayNotificationFunc: func(n ui.Notification) { +// panic("mock out the DisplayNotification method") +// }, +// DisplayRegistrationWindowFunc: func(ctx context.Context, prefs *preferences.Preferences, doneCh chan struct{}) { +// panic("mock out the DisplayRegistrationWindow method") +// }, +// DisplayTrayIconFunc: func(ctx context.Context, agent ui.Agent, trk ui.SensorTracker) { +// panic("mock out the DisplayTrayIcon method") +// }, +// RunFunc: func(ctx context.Context, agent ui.Agent, doneCh chan struct{}) { +// panic("mock out the Run method") +// }, +// } +// +// // use mockedUI in code that requires UI +// // and then make assertions. +// +// } +type UIMock struct { + // DisplayNotificationFunc mocks the DisplayNotification method. + DisplayNotificationFunc func(n ui.Notification) + + // DisplayRegistrationWindowFunc mocks the DisplayRegistrationWindow method. + DisplayRegistrationWindowFunc func(ctx context.Context, prefs *preferences.Preferences, doneCh chan struct{}) + + // DisplayTrayIconFunc mocks the DisplayTrayIcon method. + DisplayTrayIconFunc func(ctx context.Context, agent ui.Agent, trk ui.SensorTracker) + + // RunFunc mocks the Run method. + RunFunc func(ctx context.Context, agent ui.Agent, doneCh chan struct{}) + + // calls tracks calls to the methods. + calls struct { + // DisplayNotification holds details about calls to the DisplayNotification method. + DisplayNotification []struct { + // N is the n argument value. + N ui.Notification + } + // DisplayRegistrationWindow holds details about calls to the DisplayRegistrationWindow method. + DisplayRegistrationWindow []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Prefs is the prefs argument value. + Prefs *preferences.Preferences + // DoneCh is the doneCh argument value. + DoneCh chan struct{} + } + // DisplayTrayIcon holds details about calls to the DisplayTrayIcon method. + DisplayTrayIcon []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Agent is the agent argument value. + Agent ui.Agent + // Trk is the trk argument value. + Trk ui.SensorTracker + } + // Run holds details about calls to the Run method. + Run []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // Agent is the agent argument value. + Agent ui.Agent + // DoneCh is the doneCh argument value. + DoneCh chan struct{} + } + } + lockDisplayNotification sync.RWMutex + lockDisplayRegistrationWindow sync.RWMutex + lockDisplayTrayIcon sync.RWMutex + lockRun sync.RWMutex +} + +// DisplayNotification calls DisplayNotificationFunc. +func (mock *UIMock) DisplayNotification(n ui.Notification) { + if mock.DisplayNotificationFunc == nil { + panic("UIMock.DisplayNotificationFunc: method is nil but UI.DisplayNotification was just called") + } + callInfo := struct { + N ui.Notification + }{ + N: n, + } + mock.lockDisplayNotification.Lock() + mock.calls.DisplayNotification = append(mock.calls.DisplayNotification, callInfo) + mock.lockDisplayNotification.Unlock() + mock.DisplayNotificationFunc(n) +} + +// DisplayNotificationCalls gets all the calls that were made to DisplayNotification. +// Check the length with: +// +// len(mockedUI.DisplayNotificationCalls()) +func (mock *UIMock) DisplayNotificationCalls() []struct { + N ui.Notification +} { + var calls []struct { + N ui.Notification + } + mock.lockDisplayNotification.RLock() + calls = mock.calls.DisplayNotification + mock.lockDisplayNotification.RUnlock() + return calls +} + +// DisplayRegistrationWindow calls DisplayRegistrationWindowFunc. +func (mock *UIMock) DisplayRegistrationWindow(ctx context.Context, prefs *preferences.Preferences, doneCh chan struct{}) { + if mock.DisplayRegistrationWindowFunc == nil { + panic("UIMock.DisplayRegistrationWindowFunc: method is nil but UI.DisplayRegistrationWindow was just called") + } + callInfo := struct { + Ctx context.Context + Prefs *preferences.Preferences + DoneCh chan struct{} + }{ + Ctx: ctx, + Prefs: prefs, + DoneCh: doneCh, + } + mock.lockDisplayRegistrationWindow.Lock() + mock.calls.DisplayRegistrationWindow = append(mock.calls.DisplayRegistrationWindow, callInfo) + mock.lockDisplayRegistrationWindow.Unlock() + mock.DisplayRegistrationWindowFunc(ctx, prefs, doneCh) +} + +// DisplayRegistrationWindowCalls gets all the calls that were made to DisplayRegistrationWindow. +// Check the length with: +// +// len(mockedUI.DisplayRegistrationWindowCalls()) +func (mock *UIMock) DisplayRegistrationWindowCalls() []struct { + Ctx context.Context + Prefs *preferences.Preferences + DoneCh chan struct{} +} { + var calls []struct { + Ctx context.Context + Prefs *preferences.Preferences + DoneCh chan struct{} + } + mock.lockDisplayRegistrationWindow.RLock() + calls = mock.calls.DisplayRegistrationWindow + mock.lockDisplayRegistrationWindow.RUnlock() + return calls +} + +// DisplayTrayIcon calls DisplayTrayIconFunc. +func (mock *UIMock) DisplayTrayIcon(ctx context.Context, agent ui.Agent, trk ui.SensorTracker) { + if mock.DisplayTrayIconFunc == nil { + panic("UIMock.DisplayTrayIconFunc: method is nil but UI.DisplayTrayIcon was just called") + } + callInfo := struct { + Ctx context.Context + Agent ui.Agent + Trk ui.SensorTracker + }{ + Ctx: ctx, + Agent: agent, + Trk: trk, + } + mock.lockDisplayTrayIcon.Lock() + mock.calls.DisplayTrayIcon = append(mock.calls.DisplayTrayIcon, callInfo) + mock.lockDisplayTrayIcon.Unlock() + mock.DisplayTrayIconFunc(ctx, agent, trk) +} + +// DisplayTrayIconCalls gets all the calls that were made to DisplayTrayIcon. +// Check the length with: +// +// len(mockedUI.DisplayTrayIconCalls()) +func (mock *UIMock) DisplayTrayIconCalls() []struct { + Ctx context.Context + Agent ui.Agent + Trk ui.SensorTracker +} { + var calls []struct { + Ctx context.Context + Agent ui.Agent + Trk ui.SensorTracker + } + mock.lockDisplayTrayIcon.RLock() + calls = mock.calls.DisplayTrayIcon + mock.lockDisplayTrayIcon.RUnlock() + return calls +} + +// Run calls RunFunc. +func (mock *UIMock) Run(ctx context.Context, agent ui.Agent, doneCh chan struct{}) { + if mock.RunFunc == nil { + panic("UIMock.RunFunc: method is nil but UI.Run was just called") + } + callInfo := struct { + Ctx context.Context + Agent ui.Agent + DoneCh chan struct{} + }{ + Ctx: ctx, + Agent: agent, + DoneCh: doneCh, + } + mock.lockRun.Lock() + mock.calls.Run = append(mock.calls.Run, callInfo) + mock.lockRun.Unlock() + mock.RunFunc(ctx, agent, doneCh) +} + +// RunCalls gets all the calls that were made to Run. +// Check the length with: +// +// len(mockedUI.RunCalls()) +func (mock *UIMock) RunCalls() []struct { + Ctx context.Context + Agent ui.Agent + DoneCh chan struct{} +} { + var calls []struct { + Ctx context.Context + Agent ui.Agent + DoneCh chan struct{} + } + mock.lockRun.RLock() + calls = mock.calls.Run + mock.lockRun.RUnlock() + return calls +} + // Ensure, that RegistryMock does implement Registry. // If this is not the case, regenerate this file with moq. var _ Registry = &RegistryMock{} diff --git a/internal/agent/register_test.go b/internal/agent/register_test.go index 1da7eeae1..1e6c96f75 100644 --- a/internal/agent/register_test.go +++ b/internal/agent/register_test.go @@ -144,17 +144,17 @@ func TestAgent_checkRegistration(t *testing.T) { { name: "headless", args: args{path: t.TempDir()}, - fields: fields{prefs: headless, headless: true, id: "go-hass-agent-test"}, + fields: fields{prefs: headless, headless: true, id: "go-hass-agent-test", logger: slog.Default()}, }, { name: "headless error", args: args{path: t.TempDir()}, - fields: fields{prefs: headlessErr, headless: true, id: "go-hass-agent-test"}, + fields: fields{prefs: headlessErr, headless: true, id: "go-hass-agent-test", logger: slog.Default()}, wantErr: true, }, { name: "force register", - fields: fields{prefs: alreadyRegistered, headless: true, forceRegister: true, id: "go-hass-agent-test"}, + fields: fields{prefs: alreadyRegistered, headless: true, forceRegister: true, id: "go-hass-agent-test", logger: slog.Default()}, args: args{path: t.TempDir(), trk: &SensorTrackerMock{ResetFunc: func() {}}}, }, }