diff --git a/.github/workflows/learn-github-actions.yml b/.github/workflows/learn-github-actions.yml deleted file mode 100644 index 6e653cf..0000000 --- a/.github/workflows/learn-github-actions.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: learn-github-actions -run-name: ${{ github.actor }} is learning GitHub Actions -on: [push] -jobs: - check-bats-version: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '20' - - run: npm install -g bats - - run: bats -v diff --git a/src/cmd/serve.go b/src/cmd/serve.go index fa5963e..e0edcd7 100644 --- a/src/cmd/serve.go +++ b/src/cmd/serve.go @@ -104,7 +104,7 @@ func serve(cmd *cobra.Command, args []string) { router.HandleFunc("/poo/latest", pooRecorder.HandleNotifyLatestPoo).Methods("GET") router.HandleFunc("/poo/record", pooRecorder.HandleRecordPoo).Methods("POST") - router.HandleFunc("/homeassistant/publish", homeassistant.HandleHaMessage).Methods("POST") + router.HandleFunc("/homeassistant/publish", homeassistant.DefaultHandler.HandleHaMessage).Methods("POST") router.HandleFunc("/location/record", locationRecorder.HandleRecordLocation).Methods("POST") diff --git a/src/components/homeassistant/homeassistant.go b/src/components/homeassistant/homeassistant.go index 4043f2d..a031dc7 100644 --- a/src/components/homeassistant/homeassistant.go +++ b/src/components/homeassistant/homeassistant.go @@ -23,7 +23,19 @@ type actionTask struct { DueHour int `json:"due_hour"` } -func HandleHaMessage(w http.ResponseWriter, r *http.Request) { +type HaMessageHandler struct { + PooRecorderHandler func(message haMessage) + LocationRecorderHandler func(message haMessage) + TicktickHandler func(message haMessage) +} + +var DefaultHandler = HaMessageHandler{ + PooRecorderHandler: handlePooRecorderMsg, + LocationRecorderHandler: handleLocationRecorderMsg, + TicktickHandler: handleTicktickMsg, +} + +func (handler HaMessageHandler) HandleHaMessage(w http.ResponseWriter, r *http.Request) { var message haMessage decoder := json.NewDecoder(r.Body) decoder.DisallowUnknownFields() @@ -36,13 +48,12 @@ func HandleHaMessage(w http.ResponseWriter, r *http.Request) { switch message.Target { case "poo_recorder": - handlePooRecorderMsg(message) + handler.PooRecorderHandler(message) case "location_recorder": - handleLocationRecorderMsg(message) + handler.LocationRecorderHandler(message) case "ticktick": - handleTicktickMsg(message) + handler.TicktickHandler(message) } - } func handlePooRecorderMsg(message haMessage) { @@ -50,7 +61,6 @@ func handlePooRecorderMsg(message haMessage) { case "get_latest": handleGetLatestPoo() } - } func handleLocationRecorderMsg(message haMessage) { diff --git a/src/components/homeassistant/homeassistant_test.go b/src/components/homeassistant/homeassistant_test.go new file mode 100644 index 0000000..459c56c --- /dev/null +++ b/src/components/homeassistant/homeassistant_test.go @@ -0,0 +1,65 @@ +package homeassistant + +import ( + "fmt" + "math/rand/v2" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/stretchr/testify/mock" +) + +type MockedHaHandler struct { + mock.Mock +} + +func (m *MockedHaHandler) handlePooRecorderMsg(message haMessage) { + m.Called(message) +} + +func (m *MockedHaHandler) handleLocationRecorderMsg(message haMessage) { + m.Called(message) +} + +func (m *MockedHaHandler) handleTicktickMsg(message haMessage) { + m.Called(message) +} + +func TestHandleHaMessagePooRecorder(t *testing.T) { + requestBody := `{"target": "poo_recorder", "action": "get_latest", "content": ""}` + req := httptest.NewRequest(http.MethodPost, "/homeassistant/publish", strings.NewReader(requestBody)) + w := httptest.NewRecorder() + + testObj := new(MockedHaHandler) + mockedHandler := HaMessageHandler{ + PooRecorderHandler: testObj.handlePooRecorderMsg, + LocationRecorderHandler: testObj.handleLocationRecorderMsg, + TicktickHandler: testObj.handleTicktickMsg, + } + testObj.On("handlePooRecorderMsg", haMessage{Target: "poo_recorder", Action: "get_latest"}).Return() + mockedHandler.HandleHaMessage(w, req) + testObj.AssertCalled(t, "handlePooRecorderMsg", haMessage{Target: "poo_recorder", Action: "get_latest"}) +} + +func TestHandleHaMessageLocationRecorder(t *testing.T) { + expectedLatitude := rand.Float64() * 359 + expectedLongitude := rand.Float64() * 359 + expectedAltitude := rand.Float64() * 10000 + requestBody := fmt.Sprintf(`{"target": "location_recorder", "action": "record", + "content": "{'Person': 'Tom', 'latitude': %f, 'longitude': %f, 'altitude': %f}"}`, expectedLatitude, expectedLongitude, expectedAltitude) + req := httptest.NewRequest(http.MethodPost, "/homeassistant/publish", strings.NewReader(requestBody)) + w := httptest.NewRecorder() + testobj := new(MockedHaHandler) + mockedHandler := HaMessageHandler{ + PooRecorderHandler: testobj.handlePooRecorderMsg, + LocationRecorderHandler: testobj.handleLocationRecorderMsg, + TicktickHandler: testobj.handleTicktickMsg, + } + testobj.On("handleLocationRecorderMsg", haMessage{Target: "location_recorder", Action: "record", + Content: fmt.Sprintf("{'Person': 'Tom', 'latitude': %f, 'longitude': %f, 'altitude': %f}", expectedLatitude, expectedLongitude, expectedAltitude)}).Return() + mockedHandler.HandleHaMessage(w, req) + testobj.AssertCalled(t, "handleLocationRecorderMsg", haMessage{Target: "location_recorder", Action: "record", + Content: fmt.Sprintf("{'Person': 'Tom', 'latitude': %f, 'longitude': %f, 'altitude': %f}", expectedLatitude, expectedLongitude, expectedAltitude)}) +} diff --git a/src/go.mod b/src/go.mod index 96c3d17..7ec9e03 100644 --- a/src/go.mod +++ b/src/go.mod @@ -8,11 +8,13 @@ require ( github.com/jomei/notionapi v1.13.2 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 + github.com/stretchr/testify v1.9.0 golang.org/x/term v0.24.0 modernc.org/sqlite v1.33.1 ) require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/google/uuid v1.6.0 // indirect @@ -25,6 +27,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -33,6 +36,7 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect diff --git a/src/go.sum b/src/go.sum index 31842a7..a1676a1 100644 --- a/src/go.sum +++ b/src/go.sum @@ -72,6 +72,7 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=