Merge remote-tracking branch 'origin/master' into renovate/meltwater-drone-cache-1.x

This commit is contained in:
Dawid Wysokiński 2022-10-09 15:17:06 +02:00
commit 268ccb03fd
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
14 changed files with 314 additions and 67 deletions

1
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/ory/dockertest/v3 v3.9.1
github.com/stretchr/testify v1.8.0
github.com/uptrace/bun v1.1.8
github.com/uptrace/bun/dbfixture v1.1.8
github.com/uptrace/bun/dialect/pgdialect v1.1.8
github.com/uptrace/bun/driver/pgdriver v1.1.8
github.com/uptrace/bun/extra/bunotel v1.1.8

2
go.sum
View File

@ -111,6 +111,8 @@ github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYm
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/uptrace/bun v1.1.8 h1:slxuaP4LYWFbPRUmTtQhfJN+6eX/6ar2HDKYTcI50SA=
github.com/uptrace/bun v1.1.8/go.mod h1:iT89ESdV3uMupD9ixt6Khidht+BK0STabK/LeZE+B84=
github.com/uptrace/bun/dbfixture v1.1.8 h1:cDRqII+emIgmmhWSebdAZWu5vKmuIw+PZ/arf0oYJdo=
github.com/uptrace/bun/dbfixture v1.1.8/go.mod h1:GrRDt9lIfDpMyAGIJjWNsYeRHKjr8Gr2wDl2Rsf1sR4=
github.com/uptrace/bun/dialect/pgdialect v1.1.8 h1:wayJhjYDPGv8tgOBLolbBtSFQ0TihFoo8E1T129UdA8=
github.com/uptrace/bun/dialect/pgdialect v1.1.8/go.mod h1:nNbU8PHTjTUM+CRtGmqyBb9zcuRAB8I680/qoFSmBUk=
github.com/uptrace/bun/driver/pgdriver v1.1.8 h1:gyL22axRQfjJS2Umq0erzJnp0bLOdUE8/USKZHPQB8o=

View File

@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dbfixture"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/driver/pgdriver"
)
@ -126,6 +127,15 @@ func runMigrations(tb testing.TB, db *bun.DB) {
require.NoError(tb, err, "couldn't migrate (2)")
}
func loadFixtures(tb testing.TB, bunDB *bun.DB) *dbfixture.Fixture {
tb.Helper()
fixture := dbfixture.New(bunDB)
err := fixture.Load(context.Background(), os.DirFS("testdata"), "fixture.yml")
require.NoError(tb, err, "couldn't load fixtures")
return fixture
}
func generateSchema() string {
return strings.TrimFunc(strings.ReplaceAll(uuid.NewString(), "-", "_"), unicode.IsNumber)
}

View File

@ -2,6 +2,8 @@ package bundb
import (
"context"
"database/sql"
"errors"
"fmt"
"gitea.dwysokinski.me/twhelp/dcbot/internal/bundb/internal/model"
@ -25,11 +27,43 @@ func (g *Group) Create(ctx context.Context, params domain.CreateGroupParams) (do
ChannelLostVillages: params.ChannelLostVillages(),
ServerKey: params.ServerKey(),
}
if _, err := g.db.NewInsert().
Model(&group).
Returning("*").
Exec(ctx); err != nil {
return domain.Group{}, fmt.Errorf("something went wrong while inserting group into the db: %w", err)
}
return group.ToDomain(), nil
}
func (g *Group) List(ctx context.Context, params domain.ListGroupsParams) ([]domain.Group, error) {
var groups []model.Group
if err := g.db.NewSelect().
Model(&groups).
Order("created_at ASC").
Apply(listGroupsParamsApplier{params}.apply).
Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("couldn't select groups from the db: %w", err)
}
result := make([]domain.Group, 0, len(groups))
for _, version := range groups {
result = append(result, version.ToDomain())
}
return result, nil
}
type listGroupsParamsApplier struct {
params domain.ListGroupsParams
}
func (l listGroupsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery {
if l.params.ServerIDs != nil {
q = q.Where("server_id IN (?)", bun.In(l.params.ServerIDs))
}
return q
}

View File

@ -6,10 +6,12 @@ import (
"time"
"gitea.dwysokinski.me/twhelp/dcbot/internal/bundb"
"gitea.dwysokinski.me/twhelp/dcbot/internal/bundb/internal/model"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/uptrace/bun/dbfixture"
)
func TestGroup_Create(t *testing.T) {
@ -41,3 +43,85 @@ func TestGroup_Create(t *testing.T) {
assert.WithinDuration(t, time.Now(), group.CreatedAt, 1*time.Second)
})
}
func TestGroup_List(t *testing.T) {
t.Parallel()
db := newDB(t)
fixture := loadFixtures(t, db)
repo := bundb.NewGroup(db)
groups := getAllGroupsFromFixture(t, fixture)
allGroups := make([]string, 0, len(groups))
for _, g := range groups {
allGroups = append(allGroups, g.ID.String())
}
tests := []struct {
name string
params domain.ListGroupsParams
expectedGroups []string
}{
{
name: "no params",
params: domain.ListGroupsParams{},
expectedGroups: allGroups,
},
{
name: "ServerIDs=[server-1]",
params: domain.ListGroupsParams{
ServerIDs: []string{"server-1"},
},
expectedGroups: []string{
"d56ad37f-2637-48ea-98f8-79627f3fcc96",
"429b790e-7186-4106-b531-4cc4931ce2ba",
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
res, err := repo.List(context.Background(), tt.params)
assert.NoError(t, err)
assert.Len(t, res, len(tt.expectedGroups))
for _, id := range tt.expectedGroups {
found := false
for _, grp := range res {
if grp.ID == id {
found = true
break
}
}
assert.True(t, found, "group (id=%s) not found", id)
}
})
}
}
func getAllGroupsFromFixture(tb testing.TB, fixture *dbfixture.Fixture) []model.Group {
tb.Helper()
//nolint:lll
ids := []string{"group-server-1-1", "group-server-1-2", "group-server-2-1", "group-server-2-2"}
groups := make([]model.Group, 0, len(ids))
for _, id := range ids {
groups = append(groups, getGroupFromFixture(tb, fixture, id))
}
return groups
}
func getGroupFromFixture(tb testing.TB, fixture *dbfixture.Fixture, id string) model.Group {
tb.Helper()
row, err := fixture.Row("Group." + id)
require.NoError(tb, err)
g, ok := row.(*model.Group)
require.True(tb, ok)
return *g
}

26
internal/bundb/testdata/fixture.yml vendored Normal file
View File

@ -0,0 +1,26 @@
- model: Group
rows:
- _id: group-server-1-1
id: d56ad37f-2637-48ea-98f8-79627f3fcc96
server_id: server-1
server_key: pl181
version_code: pl
created_at: 2022-03-15T15:00:10.000Z
- _id: group-server-1-2
id: 429b790e-7186-4106-b531-4cc4931ce2ba
server_id: server-1
server_key: pl181
version_code: pl
created_at: 2022-03-15T15:03:10.000Z
- _id: group-server-2-1
id: abeb6c8e-70b6-445c-989f-890cd2a1f87a
server_id: server-2
server_key: pl181
version_code: pl
created_at: 2022-03-18T15:03:10.000Z
- _id: group-server-2-2
id: 0be82203-4ca3-4b4c-a0c8-3a70099d88f7
server_id: server-2
server_key: pl181
version_code: pl
created_at: 2022-03-19T15:03:10.000Z

View File

@ -1,7 +1,5 @@
package domain
import "fmt"
type ErrorCode uint8
const (
@ -31,37 +29,3 @@ func (e RequiredError) UserError() string {
func (e RequiredError) Code() ErrorCode {
return ErrorCodeValidationError
}
type ServerDoesNotExistError struct {
VersionCode string
Key string
}
func (e ServerDoesNotExistError) Error() string {
return fmt.Sprintf("server (VersionCode=%s,Key=%s) doesn't exist", e.VersionCode, e.Key)
}
func (e ServerDoesNotExistError) UserError() string {
return e.Error()
}
func (e ServerDoesNotExistError) Code() ErrorCode {
return ErrorCodeValidationError
}
type ServerIsClosedError struct {
VersionCode string
Key string
}
func (e ServerIsClosedError) Error() string {
return fmt.Sprintf("server (VersionCode=%s,Key=%s) is closed", e.VersionCode, e.Key)
}
func (e ServerIsClosedError) UserError() string {
return e.Error()
}
func (e ServerIsClosedError) Code() ErrorCode {
return ErrorCodeValidationError
}

View File

@ -1,7 +1,6 @@
package domain_test
import (
"fmt"
"testing"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
@ -19,29 +18,3 @@ func TestRequiredError(t *testing.T) {
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}
func TestServerDoesNotExistError(t *testing.T) {
t.Parallel()
err := domain.ServerDoesNotExistError{
VersionCode: "pl",
Key: "pl151",
}
var _ domain.UserError = err
assert.Equal(t, fmt.Sprintf("server (VersionCode=%s,Key=%s) doesn't exist", err.VersionCode, err.Key), err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}
func TestServerIsClosedError(t *testing.T) {
t.Parallel()
err := domain.ServerIsClosedError{
VersionCode: "pl",
Key: "pl151",
}
var _ domain.UserError = err
assert.Equal(t, fmt.Sprintf("server (VersionCode=%s,Key=%s) is closed", err.VersionCode, err.Key), err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}

View File

@ -1,6 +1,9 @@
package domain
import "time"
import (
"fmt"
"time"
)
type Group struct {
ID string
@ -67,3 +70,24 @@ func (c CreateGroupParams) ChannelGainedVillages() string {
func (c CreateGroupParams) ChannelLostVillages() string {
return c.channelLostVillages
}
type ListGroupsParams struct {
ServerIDs []string
}
type GroupLimitReachedError struct {
Current int // current number of groups
Limit int // maximum number of groups
}
func (e GroupLimitReachedError) Error() string {
return fmt.Sprintf("group limit has been reached (%d/%d)", e.Current, e.Limit)
}
func (e GroupLimitReachedError) UserError() string {
return e.Error()
}
func (e GroupLimitReachedError) Code() ErrorCode {
return ErrorCodeValidationError
}

View File

@ -1,6 +1,7 @@
package domain_test
import (
"fmt"
"testing"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
@ -70,8 +71,22 @@ func TestNewCreateGroupParams(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, tt.serverID, res.ServerID())
assert.Equal(t, tt.serverKey, res.ServerKey())
assert.Equal(t, tt.versionCode, res.VersionCode())
assert.Equal(t, tt.channelGainedVillages, res.ChannelGainedVillages())
assert.Equal(t, tt.channelLostVillages, res.ChannelLostVillages())
})
}
}
func TestGroupLimitReachedError(t *testing.T) {
t.Parallel()
err := domain.GroupLimitReachedError{
Current: 10,
Limit: 10,
}
var _ domain.UserError = err
assert.Equal(t, fmt.Sprintf("group limit has been reached (%d/%d)", err.Current, err.Limit), err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}

37
internal/domain/tw.go Normal file
View File

@ -0,0 +1,37 @@
package domain
import "fmt"
type ServerDoesNotExistError struct {
VersionCode string
Key string
}
func (e ServerDoesNotExistError) Error() string {
return fmt.Sprintf("server (VersionCode=%s,Key=%s) doesn't exist", e.VersionCode, e.Key)
}
func (e ServerDoesNotExistError) UserError() string {
return e.Error()
}
func (e ServerDoesNotExistError) Code() ErrorCode {
return ErrorCodeValidationError
}
type ServerIsClosedError struct {
VersionCode string
Key string
}
func (e ServerIsClosedError) Error() string {
return fmt.Sprintf("server (VersionCode=%s,Key=%s) is closed", e.VersionCode, e.Key)
}
func (e ServerIsClosedError) UserError() string {
return e.Error()
}
func (e ServerIsClosedError) Code() ErrorCode {
return ErrorCodeValidationError
}

View File

@ -0,0 +1,35 @@
package domain_test
import (
"fmt"
"testing"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
"github.com/stretchr/testify/assert"
)
func TestServerDoesNotExistError(t *testing.T) {
t.Parallel()
err := domain.ServerDoesNotExistError{
VersionCode: "pl",
Key: "pl151",
}
var _ domain.UserError = err
assert.Equal(t, fmt.Sprintf("server (VersionCode=%s,Key=%s) doesn't exist", err.VersionCode, err.Key), err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}
func TestServerIsClosedError(t *testing.T) {
t.Parallel()
err := domain.ServerIsClosedError{
VersionCode: "pl",
Key: "pl151",
}
var _ domain.UserError = err
assert.Equal(t, fmt.Sprintf("server (VersionCode=%s,Key=%s) is closed", err.VersionCode, err.Key), err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}

View File

@ -9,9 +9,14 @@ import (
"gitea.dwysokinski.me/twhelp/dcbot/internal/twhelp"
)
const (
maxGroupsPerServer = 10
)
//counterfeiter:generate -o internal/mock/group_repository.gen.go . GroupRepository
type GroupRepository interface {
Create(ctx context.Context, params domain.CreateGroupParams) (domain.Group, error)
List(ctx context.Context, params domain.ListGroupsParams) ([]domain.Group, error)
}
type Group struct {
@ -24,6 +29,20 @@ func NewGroup(repo GroupRepository, client TWHelpClient) *Group {
}
func (g *Group) Create(ctx context.Context, params domain.CreateGroupParams) (domain.Group, error) {
groups, err := g.repo.List(ctx, domain.ListGroupsParams{
ServerIDs: []string{params.ServerID()},
})
if err != nil {
return domain.Group{}, fmt.Errorf("GroupRepository.List: %w", err)
}
if len(groups) >= maxGroupsPerServer {
return domain.Group{}, domain.GroupLimitReachedError{
Current: len(groups),
Limit: maxGroupsPerServer,
}
}
server, err := g.client.GetServer(ctx, params.VersionCode(), params.ServerKey())
if err != nil {
var apiErr twhelp.APIError

View File

@ -42,6 +42,7 @@ func TestGroup_Create(t *testing.T) {
CreatedAt: time.Now(),
}, nil
})
repo.ListReturns(nil, nil)
client := &mock.FakeTWHelpClient{}
client.GetServerCalls(func(_ context.Context, _ string, server string) (twhelp.Server, error) {
@ -63,9 +64,28 @@ func TestGroup_Create(t *testing.T) {
assert.NotEmpty(t, g.CreatedAt)
})
t.Run("ERR: server doesnt exist", func(t *testing.T) {
t.Run("ERR: group limit has been reached", func(t *testing.T) {
t.Parallel()
const maxGroupsPerServer = 10
repo := &mock.FakeGroupRepository{}
repo.ListReturns(make([]domain.Group, maxGroupsPerServer), nil)
g, err := service.NewGroup(repo, nil).Create(context.Background(), params)
assert.ErrorIs(t, err, domain.GroupLimitReachedError{
Current: maxGroupsPerServer,
Limit: maxGroupsPerServer,
})
assert.Zero(t, g)
})
t.Run("ERR: server doesn't exist", func(t *testing.T) {
t.Parallel()
repo := &mock.FakeGroupRepository{}
repo.ListReturns(nil, nil)
client := &mock.FakeTWHelpClient{}
client.GetServerCalls(func(_ context.Context, _ string, _ string) (twhelp.Server, error) {
return twhelp.Server{}, twhelp.APIError{
@ -74,7 +94,7 @@ func TestGroup_Create(t *testing.T) {
}
})
g, err := service.NewGroup(nil, client).Create(context.Background(), params)
g, err := service.NewGroup(repo, client).Create(context.Background(), params)
assert.ErrorIs(t, err, domain.ServerDoesNotExistError{
VersionCode: params.VersionCode(),
Key: params.ServerKey(),
@ -85,6 +105,9 @@ func TestGroup_Create(t *testing.T) {
t.Run("ERR: server is closed", func(t *testing.T) {
t.Parallel()
repo := &mock.FakeGroupRepository{}
repo.ListReturns(nil, nil)
client := &mock.FakeTWHelpClient{}
client.GetServerCalls(func(ctx context.Context, _ string, server string) (twhelp.Server, error) {
return twhelp.Server{
@ -94,7 +117,7 @@ func TestGroup_Create(t *testing.T) {
}, nil
})
g, err := service.NewGroup(nil, client).Create(context.Background(), params)
g, err := service.NewGroup(repo, client).Create(context.Background(), params)
assert.ErrorIs(t, err, domain.ServerIsClosedError{
VersionCode: params.VersionCode(),
Key: params.ServerKey(),