Skip to content

Commit

Permalink
Add tests for signal group feature
Browse files Browse the repository at this point in the history
  • Loading branch information
doobry-systemli committed Jun 2, 2024
1 parent b312edb commit f22c89c
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 2 deletions.
12 changes: 12 additions & 0 deletions internal/api/response/ticker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ func (s *TickersResponseTestSuite) TestTickersResponse() {
Avatar: "https://example.com/avatar.png",
},
},
SignalGroup: storage.TickerSignalGroup{
Active: true,
GroupID: "example",
GroupName: "Example",
GroupDescription: "Example",
GroupInviteLink: "https://signal.group/#example",
},
Location: storage.TickerLocation{
Lat: 0.0,
Lon: 0.0,
Expand Down Expand Up @@ -86,6 +93,11 @@ func (s *TickersResponseTestSuite) TestTickersResponse() {
s.Equal(ticker.Mastodon.Server, tickerResponse[0].Mastodon.Server)
s.Equal(ticker.Mastodon.User.DisplayName, tickerResponse[0].Mastodon.ScreenName)
s.Equal(ticker.Mastodon.User.Avatar, tickerResponse[0].Mastodon.ImageURL)
s.Equal(ticker.SignalGroup.Active, tickerResponse[0].SignalGroup.Active)
s.Equal(ticker.SignalGroup.Connected(), tickerResponse[0].SignalGroup.Connected)
s.Equal(ticker.SignalGroup.GroupID, tickerResponse[0].SignalGroup.GroupID)
s.Equal(ticker.SignalGroup.GroupName, tickerResponse[0].SignalGroup.GroupName)
s.Equal(ticker.SignalGroup.GroupDescription, tickerResponse[0].SignalGroup.GroupDescription)
s.Equal(ticker.Location.Lat, tickerResponse[0].Location.Lat)
s.Equal(ticker.Location.Lon, tickerResponse[0].Location.Lon)
}
Expand Down
147 changes: 147 additions & 0 deletions internal/api/tickers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ func (s *TickerTestSuite) Run(name string, subtest func()) {
s.ctx, _ = gin.CreateTestContext(s.w)
s.store = &storage.MockStorage{}
s.cfg = config.LoadConfig("")
s.cfg.SignalGroup = config.SignalGroup{
ApiUrl: "https://signal-cli.example.org/api/v1/rpc",
Account: "+1234567890",
}
s.cache = cache.NewCache(time.Minute)

subtest()
Expand Down Expand Up @@ -570,6 +574,149 @@ func (s *TickerTestSuite) TestDeleteTickerBluesky() {
})
}

func (s *TickerTestSuite) TestPutTickerSignalGroup() {
s.Run("when ticker not found", func() {
h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusNotFound, s.w.Code)
s.store.AssertExpectations(s.T())
})

s.Run("when body is invalid", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", nil)
s.ctx.Request.Header.Add("Content-Type", "application/json")
h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusBadRequest, s.w.Code)
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns error", func() {
s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(errors.New("storage error")).Once()

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusBadRequest, s.w.Code)
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns ticker", func() {
s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.Equal(gock.IsDone(), true)
s.store.AssertExpectations(s.T())
})

s.Run("when enabling signal group successfully", func() {
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]interface{}{
"groupId": "sample-group-id",
"timestamp": 1,
},
"id": 1,
})
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": []map[string]interface{}{
{
"id": "sample-group-id",
"name": "Example",
"description": "Example",
"groupInviteLink": "https://signal.group/#example",
},
},
"id": 1,
})

s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true,"GroupName":"Example","GroupDescription":"Example"}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.True(gock.IsDone())
s.store.AssertExpectations(s.T())
})

s.Run("when enabling signal group not successfully", func() {
gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
MatchHeader("Content-Type", "application/json").
Reply(500)

s.ctx.Set("ticker", storage.Ticker{})
body := `{"active":true,"GroupName":"Example","GroupDescription":"Example"}`
s.ctx.Request = httptest.NewRequest(http.MethodPut, "/v1/admin/tickers/1/signal_group", strings.NewReader(body))
s.ctx.Request.Header.Add("Content-Type", "application/json")

h := s.handler()
h.PutTickerSignalGroup(s.ctx)

s.Equal(http.StatusBadRequest, s.w.Code)
s.True(gock.IsDone())
s.store.AssertExpectations(s.T())
})
}

func (s *TickerTestSuite) TestDeleteTickerSignalGroup() {
s.Run("when ticker not found", func() {
h := s.handler()
h.DeleteTickerSignalGroup(s.ctx)

s.Equal(http.StatusNotFound, s.w.Code)
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns error", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.store.On("SaveTicker", mock.Anything).Return(errors.New("storage error")).Once()
h := s.handler()
h.DeleteTickerSignalGroup(s.ctx)

s.Equal(http.StatusBadRequest, s.w.Code)
s.store.AssertExpectations(s.T())
})

s.Run("when storage returns ticker", func() {
s.ctx.Set("ticker", storage.Ticker{})
s.store.On("SaveTicker", mock.Anything).Return(nil).Once()
h := s.handler()
h.DeleteTickerSignalGroup(s.ctx)

s.Equal(http.StatusOK, s.w.Code)
s.store.AssertExpectations(s.T())
})
}

func (s *TickerTestSuite) TestDeleteTicker() {
s.Run("when ticker not found", func() {
h := s.handler()
Expand Down
11 changes: 10 additions & 1 deletion internal/bridge/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ func (s *BridgeTestSuite) SetupTest() {
Handle: "handle",
AppKey: "app_key",
},
SignalGroup: storage.TickerSignalGroup{
Active: true,
GroupID: "group_id",
GroupName: "group_name",
GroupDescription: "group_description",
},
}
messageWithoutBridges = storage.Message{
Text: "Hello World",
Expand Down Expand Up @@ -90,6 +96,9 @@ func (s *BridgeTestSuite) SetupTest() {
Uri: "at://did:plc:sample-uri",
Cid: "cid",
},
SignalGroup: storage.SignalGroupMeta{
Timestamp: 123,
},
}
}

Expand Down Expand Up @@ -141,7 +150,7 @@ func (s *BridgeTestSuite) TestDelete() {

func (s *BridgeTestSuite) TestRegisterBridges() {
bridges := RegisterBridges(config.Config{}, nil)
s.Equal(3, len(bridges))
s.Equal(4, len(bridges))
}

func TestBrigde(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion internal/bridge/signal_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (sb *SignalGroupBridge) Send(ticker storage.Ticker, message *storage.Messag
}

func (sb *SignalGroupBridge) Delete(ticker storage.Ticker, message *storage.Message) error {
if !sb.config.SignalGroup.Enabled() || !ticker.SignalGroup.Connected() || !ticker.SignalGroup.Active || message.SignalGroup.Timestamp != 0 {
if !sb.config.SignalGroup.Enabled() || !ticker.SignalGroup.Connected() || !ticker.SignalGroup.Active || message.SignalGroup.Timestamp == 0 {
return nil
}

Expand Down
157 changes: 157 additions & 0 deletions internal/bridge/signal_group_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package bridge

import (
"github.com/h2non/gock"
"github.com/systemli/ticker/internal/config"
"github.com/systemli/ticker/internal/storage"
)

func (s *BridgeTestSuite) TestSignalGroupSend() {
s.Run("when signalGroup is inactive", func() {
bridge := s.signalGroupBridge(config.Config{}, &storage.MockStorage{})

err := bridge.Send(tickerWithoutBridges, &messageWithoutBridges)
s.NoError(err)
})

s.Run("when signalGroup is active but signal-cli api fails", func() {
bridge := s.signalGroupBridge(config.Config{
SignalGroup: config.SignalGroup{
ApiUrl: "https://signal-cli.example.org/api/v1/rpc",
Account: "0123456789",
},
}, &storage.MockStorage{})

gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
Reply(500)

err := bridge.Send(tickerWithBridges, &storage.Message{})
s.Error(err)
s.True(gock.IsDone())
})

s.Run("when response timestamp == 0", func() {
bridge := s.signalGroupBridge(config.Config{
SignalGroup: config.SignalGroup{
ApiUrl: "https://signal-cli.example.org/api/v1/rpc",
Account: "0123456789",
},
}, &storage.MockStorage{})

gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]int{
"timestamp": 0,
},
"id": 1,
})

err := bridge.Send(tickerWithBridges, &storage.Message{})
s.Error(err)
s.True(gock.IsDone())
})

s.Run("happy path", func() {
bridge := s.signalGroupBridge(config.Config{
SignalGroup: config.SignalGroup{
ApiUrl: "https://signal-cli.example.org/api/v1/rpc",
Account: "0123456789",
},
}, &storage.MockStorage{})

gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]int{
"timestamp": 1,
},
"id": 1,
})

err := bridge.Send(tickerWithBridges, &storage.Message{})
s.NoError(err)
s.True(gock.IsDone())
})
}

func (s *BridgeTestSuite) TestSignalDelete() {
s.Run("when signal not connected", func() {
bridge := s.signalGroupBridge(config.Config{}, &storage.MockStorage{})

err := bridge.Delete(tickerWithoutBridges, &messageWithoutBridges)
s.NoError(err)
})

s.Run("when message has no signal meta", func() {
bridge := s.signalGroupBridge(config.Config{
SignalGroup: config.SignalGroup{
ApiUrl: "https://signal-cli.example.org/api/v1/rpc",
Account: "0123456789",
},
}, &storage.MockStorage{})

err := bridge.Delete(tickerWithBridges, &messageWithoutBridges)
s.NoError(err)
})

s.Run("when signal is inactive", func() {
bridge := s.signalGroupBridge(config.Config{}, &storage.MockStorage{})

err := bridge.Delete(tickerWithBridges, &messageWithoutBridges)
s.NoError(err)
})

s.Run("when delete fails", func() {
bridge := s.signalGroupBridge(config.Config{
SignalGroup: config.SignalGroup{
ApiUrl: "https://signal-cli.example.org/api/v1/rpc",
Account: "0123456789",
},
}, &storage.MockStorage{})

gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
Reply(500)

err := bridge.Delete(tickerWithBridges, &messageWithBridges)
s.Error(err)
s.True(gock.IsDone())
})

s.Run("happy path", func() {
bridge := s.signalGroupBridge(config.Config{
SignalGroup: config.SignalGroup{
ApiUrl: "https://signal-cli.example.org/api/v1/rpc",
Account: "0123456789",
},
}, &storage.MockStorage{})

gock.New("https://signal-cli.example.org").
Post("/api/v1/rpc").
Reply(200).
JSON(map[string]interface{}{
"jsonrpc": "2.0",
"result": map[string]int{
"timestamp": 1,
},
"id": 1,
})

err := bridge.Delete(tickerWithBridges, &messageWithBridges)
s.NoError(err)
s.True(gock.IsDone())
})
}

func (s *BridgeTestSuite) signalGroupBridge(config config.Config, storage storage.Storage) *SignalGroupBridge {
return &SignalGroupBridge{
config: config,
storage: storage,
}
}
Loading

0 comments on commit f22c89c

Please sign in to comment.