dcbot/internal/service/monitor_test.go
Dawid Wysokiński c487800200
All checks were successful
continuous-integration/drone/push Build is passing
feat: add a new command - monitor list (#28)
Reviewed-on: #28
2022-10-27 13:18:58 +00:00

734 lines
19 KiB
Go

package service_test
import (
"context"
"errors"
"testing"
"time"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
"gitea.dwysokinski.me/twhelp/dcbot/internal/service"
"gitea.dwysokinski.me/twhelp/dcbot/internal/service/internal/mock"
"gitea.dwysokinski.me/twhelp/dcbot/internal/twhelp"
"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestMonitor_Create(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
repo := &mock.FakeMonitorRepository{}
repo.ListReturns(nil, nil)
repo.CreateCalls(func(ctx context.Context, params domain.CreateMonitorParams) (domain.Monitor, error) {
return domain.Monitor{
ID: uuid.NewString(),
TribeID: params.TribeID(),
GroupID: params.GroupID(),
CreatedAt: time.Now(),
}, nil
})
groupSvc := &mock.FakeGroupReader{}
groupSvc.GetCalls(func(ctx context.Context, groupID, serverID string) (domain.Group, error) {
return domain.Group{
ID: groupID,
ServerID: serverID,
ChannelGains: "",
ChannelLosses: "",
ServerKey: "pl151",
VersionCode: "pl",
CreatedAt: time.Now(),
}, nil
})
client := &mock.FakeTWHelpClient{}
tribe := twhelp.Tribe{
ID: 150,
Tag: "*TAG*",
Name: uuid.NewString(),
ProfileURL: "https://pl151.plemiona.pl/game.php?screen=info_player&id=150",
DeletedAt: time.Time{},
}
client.GetTribeByTagReturns(tribe, nil)
groupID := uuid.NewString()
monitor, err := service.NewMonitor(repo, groupSvc, client, 10).
Create(context.Background(), groupID, uuid.NewString(), tribe.Tag)
assert.NoError(t, err)
assert.NotEmpty(t, monitor.ID)
assert.Equal(t, groupID, monitor.GroupID)
assert.Equal(t, tribe.ID, monitor.TribeID)
assert.NotEmpty(t, monitor.CreatedAt)
})
t.Run("ERR: group doesn't exist", func(t *testing.T) {
t.Parallel()
groupID := uuid.NewString()
groupSvc := &mock.FakeGroupReader{}
groupSvc.GetReturns(domain.Group{}, domain.GroupNotFoundError{ID: groupID})
monitor, err := service.NewMonitor(nil, groupSvc, nil, 10).
Create(context.Background(), groupID, uuid.NewString(), "tag")
assert.ErrorIs(t, err, domain.GroupDoesNotExistError{
ID: groupID,
})
assert.Zero(t, monitor)
})
t.Run("ERR: monitor limit has been reached", func(t *testing.T) {
t.Parallel()
const maxMonitorsPerGroup = 10
repo := &mock.FakeMonitorRepository{}
repo.ListReturns(make([]domain.Monitor, maxMonitorsPerGroup), nil)
groupSvc := &mock.FakeGroupReader{}
groupSvc.GetCalls(func(ctx context.Context, groupID, serverID string) (domain.Group, error) {
return domain.Group{
ID: groupID,
ServerID: serverID,
ChannelGains: "",
ChannelLosses: "",
ServerKey: "pl151",
VersionCode: "pl",
CreatedAt: time.Now(),
}, nil
})
monitor, err := service.NewMonitor(repo, groupSvc, nil, maxMonitorsPerGroup).
Create(context.Background(), uuid.NewString(), uuid.NewString(), "TAG")
assert.ErrorIs(t, err, domain.MonitorLimitReachedError{
Current: maxMonitorsPerGroup,
Limit: maxMonitorsPerGroup,
})
assert.Zero(t, monitor)
})
t.Run("ERR: tribe doesn't exist", func(t *testing.T) {
t.Parallel()
t.Run("API error", func(t *testing.T) {
t.Parallel()
repo := &mock.FakeMonitorRepository{}
repo.ListReturns(nil, nil)
repo.CreateCalls(func(ctx context.Context, params domain.CreateMonitorParams) (domain.Monitor, error) {
return domain.Monitor{
ID: uuid.NewString(),
TribeID: params.TribeID(),
GroupID: params.GroupID(),
CreatedAt: time.Now(),
}, nil
})
groupSvc := &mock.FakeGroupReader{}
groupSvc.GetCalls(func(ctx context.Context, groupID, serverID string) (domain.Group, error) {
return domain.Group{
ID: groupID,
ServerID: serverID,
ChannelGains: "",
ChannelLosses: "",
ServerKey: "pl151",
VersionCode: "pl",
CreatedAt: time.Now(),
}, nil
})
client := &mock.FakeTWHelpClient{}
client.GetTribeByTagReturns(twhelp.Tribe{}, twhelp.APIError{
Code: twhelp.ErrorCodeEntityNotFound,
Message: "tribe not found",
})
tag := "TAG"
monitor, err := service.NewMonitor(repo, groupSvc, client, 10).
Create(context.Background(), uuid.NewString(), uuid.NewString(), tag)
assert.ErrorIs(t, err, domain.TribeDoesNotExistError{
Tag: tag,
})
assert.Zero(t, monitor)
})
t.Run("tribe is deleted", func(t *testing.T) {
t.Parallel()
repo := &mock.FakeMonitorRepository{}
repo.ListReturns(nil, nil)
groupSvc := &mock.FakeGroupReader{}
groupSvc.GetCalls(func(ctx context.Context, groupID, serverID string) (domain.Group, error) {
return domain.Group{
ID: groupID,
ServerID: serverID,
ChannelGains: "",
ChannelLosses: "",
ServerKey: "pl151",
VersionCode: "pl",
CreatedAt: time.Now(),
}, nil
})
client := &mock.FakeTWHelpClient{}
tribe := twhelp.Tribe{
ID: 150,
Tag: "*TAG*",
Name: uuid.NewString(),
ProfileURL: "https://pl151.plemiona.pl/game.php?screen=info_player&id=150",
DeletedAt: time.Now(),
}
client.GetTribeByTagReturns(tribe, nil)
monitor, err := service.NewMonitor(repo, groupSvc, client, 10).
Create(context.Background(), uuid.NewString(), uuid.NewString(), tribe.Tag)
assert.ErrorIs(t, err, domain.TribeDoesNotExistError{
Tag: tribe.Tag,
})
assert.Zero(t, monitor)
})
})
}
func TestMonitor_List(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
groupID := uuid.NewString()
groupSvc := &mock.FakeGroupReader{}
groupSvc.GetCalls(func(ctx context.Context, groupID, serverID string) (domain.Group, error) {
return domain.Group{
ID: groupID,
ServerID: serverID,
ChannelGains: "",
ChannelLosses: "",
ServerKey: "pl151",
VersionCode: "pl",
CreatedAt: time.Now(),
}, nil
})
repo := &mock.FakeMonitorRepository{}
monitors := []domain.Monitor{
{
ID: uuid.NewString(),
TribeID: 124,
GroupID: groupID,
CreatedAt: time.Now(),
},
{
ID: uuid.NewString(),
TribeID: 125,
GroupID: groupID,
CreatedAt: time.Now(),
},
}
repo.ListReturns(monitors, nil)
client := &mock.FakeTWHelpClient{}
client.GetTribeByIDCalls(func(ctx context.Context, _ string, _ string, id int64) (twhelp.Tribe, error) {
return twhelp.Tribe{
ID: id,
Tag: uuid.NewString(),
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
DeletedAt: time.Time{},
}, nil
})
res, err := service.NewMonitor(repo, groupSvc, client, 10).
List(context.Background(), groupID, uuid.NewString())
assert.NoError(t, err)
assert.Len(t, res, len(monitors))
for _, m1 := range monitors {
var exist bool
for _, m2 := range res {
if cmp.Equal(m1, m2.Monitor) && m1.TribeID == m2.Tribe.ID {
exist = true
break
}
}
assert.True(t, exist)
}
})
}
func TestMonitor_Execute(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
client := &mock.FakeTWHelpClient{}
villages := map[string][]twhelp.VillageMeta{
"pl:pl181": {
{
ID: 1,
FullName: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
{
ID: 2,
FullName: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
{
ID: 3,
FullName: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
{
ID: 4,
FullName: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
},
"en:en130": {
{
ID: 100,
FullName: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
{
ID: 101,
FullName: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
},
"pl:pl180": {
{
ID: 200,
FullName: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
},
}
tribes := map[string][]twhelp.TribeMeta{
"pl:pl181": {
{
ID: 1,
Name: uuid.NewString(),
Tag: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
{
ID: 2,
Name: uuid.NewString(),
Tag: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
{
ID: 3,
Name: uuid.NewString(),
Tag: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
},
"en:en130": {
{
ID: 100,
Name: uuid.NewString(),
Tag: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
{
ID: 101,
Name: uuid.NewString(),
Tag: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
},
"pl:pl180": {
{
ID: 200,
Name: uuid.NewString(),
Tag: uuid.NewString(),
ProfileURL: uuid.NewString(),
},
},
}
players := map[string][]twhelp.PlayerMeta{
"pl:pl181": {
{
ID: 1,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{
Tribe: tribes["pl:pl181"][0],
Valid: true,
},
},
{
ID: 2,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{
Tribe: tribes["pl:pl181"][1],
Valid: true,
},
},
{
ID: 3,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{
Tribe: tribes["pl:pl181"][2],
Valid: true,
},
},
{
ID: 4,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{},
},
},
"en:en130": {
{
ID: 100,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{
Tribe: tribes["en:en130"][0],
Valid: true,
},
},
{
ID: 101,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{
Tribe: tribes["en:en130"][1],
Valid: true,
},
},
{
ID: 102,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{},
},
},
"pl:pl180": {
{
ID: 200,
Name: uuid.NewString(),
ProfileURL: uuid.NewString(),
Tribe: twhelp.NullTribeMeta{
Tribe: tribes["pl:pl180"][0],
Valid: true,
},
},
},
}
ennoblements := map[string][]twhelp.Ennoblement{
"pl:pl181": {
{
ID: 1,
Village: villages["pl:pl181"][0],
NewOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][0],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][1],
Valid: true,
},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
{
ID: 2, // self conquer, should be skipped
Village: villages["pl:pl181"][0],
NewOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][0],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][0],
Valid: true,
},
CreatedAt: time.Now().Add(-4 * time.Minute),
},
{
ID: 3, // internal conquer, should be skipped
Village: villages["pl:pl181"][1],
NewOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][0],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][2],
Valid: true,
},
CreatedAt: time.Now().Add(-3 * time.Minute),
},
{
ID: 4, // barbarian
Village: villages["pl:pl181"][2],
NewOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][0],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
{
ID: 5, // disabled notifications about gains, should be skipped
Village: villages["pl:pl181"][3],
NewOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][1],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
{
ID: 6, // disabled notifications about losses, should be skipped
Village: villages["pl:pl181"][3],
NewOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][3],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl181"][0],
Valid: true,
},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
},
"en:en130": {
{
ID: 100, // no monitor for these tribes, should be skipped
Village: villages["en:en130"][0],
NewOwner: twhelp.NullPlayerMeta{
Player: players["en:en130"][0],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
{
ID: 101, // no monitor for these tribes, should be skipped
Village: villages["en:en130"][0],
NewOwner: twhelp.NullPlayerMeta{
Player: players["en:en130"][2],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{
Player: players["en:en130"][0],
Valid: true,
},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
{
ID: 102,
Village: villages["en:en130"][1],
NewOwner: twhelp.NullPlayerMeta{
Player: players["en:en130"][1],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{
Player: players["en:en130"][2],
Valid: true,
},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
},
"pl:pl180": {
{
ID: 200, // api error, should be skipped
Village: villages["pl:pl180"][0],
NewOwner: twhelp.NullPlayerMeta{
Player: players["pl:pl180"][0],
Valid: true,
},
OldOwner: twhelp.NullPlayerMeta{},
CreatedAt: time.Now().Add(-5 * time.Minute),
},
},
}
client.ListEnnoblementsCalls(
func(
ctx context.Context,
version string,
server string,
_ twhelp.ListEnnoblementsQueryParams,
) ([]twhelp.Ennoblement, error) {
if version == "pl" && server == "pl180" {
return nil, errors.New("random error")
}
return ennoblements[version+":"+server], nil
},
)
groupSvc := &mock.FakeGroupReader{}
groups := []domain.Group{
{
ID: uuid.NewString(),
ServerID: uuid.NewString(),
ChannelGains: "", // should be skipped - ChannelGains and ChannelLosses are empty strings
ChannelLosses: "",
ServerKey: "pl181",
VersionCode: "pl",
CreatedAt: time.Now(),
},
{
ID: uuid.NewString(),
ServerID: uuid.NewString(),
ChannelGains: uuid.NewString(),
ChannelLosses: "",
ServerKey: "pl181",
VersionCode: "pl",
CreatedAt: time.Now(),
},
{
ID: uuid.NewString(),
ServerID: uuid.NewString(),
ChannelGains: "",
ChannelLosses: uuid.NewString(),
ServerKey: "pl181",
VersionCode: "pl",
CreatedAt: time.Now(),
},
{
ID: uuid.NewString(),
ServerID: uuid.NewString(),
ChannelGains: uuid.NewString(),
ChannelLosses: uuid.NewString(),
ServerKey: "en130",
VersionCode: "en",
CreatedAt: time.Now(),
},
{
ID: uuid.NewString(),
ServerID: uuid.NewString(),
ChannelGains: uuid.NewString(),
ChannelLosses: uuid.NewString(),
ServerKey: "pl180",
VersionCode: "pl",
CreatedAt: time.Now(),
},
}
groupSvc.ListReturns(groups, nil)
repo := &mock.FakeMonitorRepository{}
monitors := map[string][]domain.Monitor{
groups[1].ID: {
{
ID: uuid.NewString(),
TribeID: tribes["pl:pl181"][0].ID,
GroupID: groups[1].ID,
CreatedAt: time.Now(),
},
{
ID: uuid.NewString(),
TribeID: tribes["pl:pl181"][2].ID,
GroupID: groups[1].ID,
CreatedAt: time.Now(),
},
},
groups[2].ID: {
{
ID: uuid.NewString(),
TribeID: tribes["pl:pl181"][1].ID,
GroupID: groups[2].ID,
CreatedAt: time.Now(),
},
},
groups[3].ID: {
{
ID: uuid.NewString(),
TribeID: tribes["en:en130"][1].ID,
GroupID: groups[3].ID,
CreatedAt: time.Now(),
},
},
groups[4].ID: {
{
ID: uuid.NewString(),
TribeID: tribes["pl:pl180"][0].ID,
GroupID: groups[4].ID,
CreatedAt: time.Now(),
},
},
}
repo.ListCalls(func(ctx context.Context, groupID string) ([]domain.Monitor, error) {
return monitors[groupID], nil
})
notifications, err := service.NewMonitor(repo, groupSvc, client, 10).
Execute(context.Background())
assert.NoError(t, err)
expectedNotifications := []domain.EnnoblementNotification{
{
Type: domain.EnnoblementNotificationTypeGain,
ServerID: groups[1].ServerID,
ChannelID: groups[1].ChannelGains,
Ennoblement: ennoblementToDomainModel(ennoblements["pl:pl181"][0]),
},
{
Type: domain.EnnoblementNotificationTypeGain,
ServerID: groups[1].ServerID,
ChannelID: groups[1].ChannelGains,
Ennoblement: ennoblementToDomainModel(ennoblements["pl:pl181"][3]),
},
{
Type: domain.EnnoblementNotificationTypeLoss,
ServerID: groups[2].ServerID,
ChannelID: groups[2].ChannelLosses,
Ennoblement: ennoblementToDomainModel(ennoblements["pl:pl181"][0]),
},
{
Type: domain.EnnoblementNotificationTypeGain,
ServerID: groups[3].ServerID,
ChannelID: groups[3].ChannelGains,
Ennoblement: ennoblementToDomainModel(ennoblements["en:en130"][2]),
},
}
assert.Len(t, notifications, len(expectedNotifications))
for _, n := range expectedNotifications {
assert.Contains(t, notifications, n)
}
})
}
func ennoblementToDomainModel(e twhelp.Ennoblement) domain.Ennoblement {
return domain.Ennoblement{
ID: e.ID,
Village: domain.VillageMeta(e.Village),
NewOwner: domain.NullPlayerMeta{
Player: domain.PlayerMeta{
ID: e.NewOwner.Player.ID,
Name: e.NewOwner.Player.Name,
ProfileURL: e.NewOwner.Player.ProfileURL,
Tribe: domain.NullTribeMeta{
Tribe: domain.TribeMeta(e.NewOwner.Player.Tribe.Tribe),
Valid: e.NewOwner.Player.Tribe.Valid,
},
},
Valid: e.NewOwner.Valid,
},
OldOwner: domain.NullPlayerMeta{
Player: domain.PlayerMeta{
ID: e.OldOwner.Player.ID,
Name: e.OldOwner.Player.Name,
ProfileURL: e.OldOwner.Player.ProfileURL,
Tribe: domain.NullTribeMeta{
Tribe: domain.TribeMeta(e.OldOwner.Player.Tribe.Tribe),
Valid: e.OldOwner.Player.Tribe.Valid,
},
},
Valid: e.OldOwner.Valid,
},
CreatedAt: e.CreatedAt,
}
}