mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:57:31 +00:00
fix: set quotas (#6597)
* feat: set quotas
* fix: start new period on younger anchor
* cleanup e2e config
* fix set notifications
* lint
* test: fix quota projection tests
* fix add quota tests
* make quota fields nullable
* enable amount 0
* fix initial setup
* create a prerelease
* avoid success comments
* fix quota projection primary key
* Revert "fix quota projection primary key"
This reverts commit e72f4d7fa1
.
* simplify write model
* fix aggregate id
* avoid push without changes
* test set quota lifecycle
* test set quota mutations
* fix quota unit test
* fix: quotas
* test quota.set event projection
* use SetQuota in integration tests
* fix: release quotas 3
* reset releaserc
* fix comment
* test notification order doesn't matter
* test notification order doesn't matter
* test with unmarshalled events
* test with unmarshalled events
This commit is contained in:
@@ -110,7 +110,7 @@ type InstanceSetup struct {
|
||||
RefreshTokenExpiration time.Duration
|
||||
}
|
||||
Quotas *struct {
|
||||
Items []*AddQuota
|
||||
Items []*SetQuota
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
|
||||
if err != nil {
|
||||
return "", "", nil, nil, err
|
||||
}
|
||||
validations = append(validations, c.AddQuotaCommand(quota.NewAggregate(quotaId, instanceID), q))
|
||||
validations = append(validations, c.SetQuotaCommand(quota.NewAggregate(quotaId, instanceID), nil, true, q))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/repository/quota"
|
||||
)
|
||||
|
||||
@@ -32,25 +31,25 @@ func (q QuotaUnit) Enum() quota.Unit {
|
||||
}
|
||||
}
|
||||
|
||||
// AddQuota returns and error if the quota already exists.
|
||||
// AddQuota is deprecated. Use SetQuota instead.
|
||||
func (c *Commands) AddQuota(
|
||||
ctx context.Context,
|
||||
q *AddQuota,
|
||||
q *SetQuota,
|
||||
) (*domain.ObjectDetails, error) {
|
||||
instanceId := authz.GetInstance(ctx).InstanceID()
|
||||
|
||||
wm, err := c.getQuotaWriteModel(ctx, instanceId, instanceId, q.Unit.Enum())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if wm.active {
|
||||
if wm.AggregateID != "" {
|
||||
return nil, errors.ThrowAlreadyExists(nil, "COMMAND-WDfFf", "Errors.Quota.AlreadyExists")
|
||||
}
|
||||
aggregateId, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.AddQuotaCommand(quota.NewAggregate(aggregateId, instanceId), q))
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.SetQuotaCommand(quota.NewAggregate(aggregateId, instanceId), wm, true, q))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,23 +64,52 @@ func (c *Commands) AddQuota(
|
||||
return writeModelToObjectDetails(&wm.WriteModel), nil
|
||||
}
|
||||
|
||||
// SetQuota creates a new quota or updates an existing one.
|
||||
func (c *Commands) SetQuota(
|
||||
ctx context.Context,
|
||||
q *SetQuota,
|
||||
) (*domain.ObjectDetails, error) {
|
||||
instanceId := authz.GetInstance(ctx).InstanceID()
|
||||
wm, err := c.getQuotaWriteModel(ctx, instanceId, instanceId, q.Unit.Enum())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aggregateId := wm.AggregateID
|
||||
createNewQuota := aggregateId == ""
|
||||
if aggregateId == "" {
|
||||
aggregateId, err = c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.SetQuotaCommand(quota.NewAggregate(aggregateId, instanceId), wm, createNewQuota, q))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cmds) > 0 {
|
||||
events, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = AppendAndReduce(wm, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return writeModelToObjectDetails(&wm.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveQuota(ctx context.Context, unit QuotaUnit) (*domain.ObjectDetails, error) {
|
||||
instanceId := authz.GetInstance(ctx).InstanceID()
|
||||
|
||||
wm, err := c.getQuotaWriteModel(ctx, instanceId, instanceId, unit.Enum())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !wm.active {
|
||||
if wm.AggregateID == "" {
|
||||
return nil, errors.ThrowNotFound(nil, "COMMAND-WDfFf", "Errors.Quota.NotFound")
|
||||
}
|
||||
|
||||
aggregate := quota.NewAggregate(wm.AggregateID, instanceId)
|
||||
|
||||
events := []eventstore.Command{
|
||||
quota.NewRemovedEvent(ctx, &aggregate.Aggregate, unit.Enum()),
|
||||
}
|
||||
events := []eventstore.Command{quota.NewRemovedEvent(ctx, &aggregate.Aggregate, unit.Enum())}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -104,6 +132,16 @@ type QuotaNotification struct {
|
||||
CallURL string
|
||||
}
|
||||
|
||||
// SetQuota configures a quota and activates it if it isn't active already
|
||||
type SetQuota struct {
|
||||
Unit QuotaUnit `json:"unit"`
|
||||
From time.Time `json:"from"`
|
||||
ResetInterval time.Duration `json:"ResetInterval,omitempty"`
|
||||
Amount uint64 `json:"Amount,omitempty"`
|
||||
Limit bool `json:"Limit,omitempty"`
|
||||
Notifications QuotaNotifications `json:"Notifications,omitempty"`
|
||||
}
|
||||
|
||||
type QuotaNotifications []*QuotaNotification
|
||||
|
||||
func (q *QuotaNotification) validate() error {
|
||||
@@ -111,94 +149,51 @@ func (q *QuotaNotification) validate() error {
|
||||
if err != nil {
|
||||
return errors.ThrowInvalidArgument(err, "QUOTA-bZ0Fj", "Errors.Quota.Invalid.CallURL")
|
||||
}
|
||||
|
||||
if !u.IsAbs() || u.Host == "" {
|
||||
return errors.ThrowInvalidArgument(nil, "QUOTA-HAYmN", "Errors.Quota.Invalid.CallURL")
|
||||
}
|
||||
|
||||
if q.Percent < 1 {
|
||||
return errors.ThrowInvalidArgument(nil, "QUOTA-pBfjq", "Errors.Quota.Invalid.Percent")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *QuotaNotifications) toAddedEventNotifications(idGenerator id.Generator) ([]*quota.AddedEventNotification, error) {
|
||||
if q == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
notifications := make([]*quota.AddedEventNotification, len(*q))
|
||||
for idx, notification := range *q {
|
||||
|
||||
id, err := idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
notifications[idx] = "a.AddedEventNotification{
|
||||
ID: id,
|
||||
Percent: notification.Percent,
|
||||
Repeat: notification.Repeat,
|
||||
CallURL: notification.CallURL,
|
||||
}
|
||||
}
|
||||
|
||||
return notifications, nil
|
||||
}
|
||||
|
||||
type AddQuota struct {
|
||||
Unit QuotaUnit
|
||||
From time.Time
|
||||
ResetInterval time.Duration
|
||||
Amount uint64
|
||||
Limit bool
|
||||
Notifications QuotaNotifications
|
||||
}
|
||||
|
||||
func (q *AddQuota) validate() error {
|
||||
func (q *SetQuota) validate() error {
|
||||
for _, notification := range q.Notifications {
|
||||
if err := notification.validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if q.Unit.Enum() == quota.Unimplemented {
|
||||
return errors.ThrowInvalidArgument(nil, "QUOTA-OTeSh", "Errors.Quota.Invalid.Unimplemented")
|
||||
}
|
||||
|
||||
if q.Amount < 1 {
|
||||
if q.Amount < 0 {
|
||||
return errors.ThrowInvalidArgument(nil, "QUOTA-hOKSJ", "Errors.Quota.Invalid.Amount")
|
||||
}
|
||||
|
||||
if q.ResetInterval < time.Minute {
|
||||
return errors.ThrowInvalidArgument(nil, "QUOTA-R5otd", "Errors.Quota.Invalid.ResetInterval")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddQuotaCommand(a *quota.Aggregate, q *AddQuota) preparation.Validation {
|
||||
func (c *Commands) SetQuotaCommand(a *quota.Aggregate, wm *quotaWriteModel, createNew bool, q *SetQuota) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
|
||||
if err := q.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) (cmd []eventstore.Command, err error) {
|
||||
|
||||
notifications, err := q.Notifications.toAddedEventNotifications(c.idGenerator)
|
||||
if err != nil {
|
||||
changes, err := wm.NewChanges(c.idGenerator, createNew, q.Amount, q.From, q.ResetInterval, q.Limit, q.Notifications...)
|
||||
if len(changes) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []eventstore.Command{quota.NewAddedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
return []eventstore.Command{quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
q.Unit.Enum(),
|
||||
q.From,
|
||||
q.ResetInterval,
|
||||
q.Amount,
|
||||
q.Limit,
|
||||
notifications,
|
||||
changes...,
|
||||
)}, err
|
||||
},
|
||||
nil
|
||||
|
@@ -1,14 +1,26 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
zitadel_errors "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/repository/quota"
|
||||
)
|
||||
|
||||
type quotaWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
unit quota.Unit
|
||||
active bool
|
||||
rollingAggregateID string
|
||||
unit quota.Unit
|
||||
from time.Time
|
||||
resetInterval time.Duration
|
||||
amount uint64
|
||||
limit bool
|
||||
notifications []*quota.SetEventNotification
|
||||
}
|
||||
|
||||
// newQuotaWriteModel aggregateId is filled by reducing unit matching events
|
||||
@@ -30,6 +42,7 @@ func (wm *quotaWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
AggregateTypes(quota.AggregateType).
|
||||
EventTypes(
|
||||
quota.AddedEventType,
|
||||
quota.SetEventType,
|
||||
quota.RemovedEventType,
|
||||
).EventData(map[string]interface{}{"unit": wm.unit})
|
||||
|
||||
@@ -38,15 +51,137 @@ func (wm *quotaWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
|
||||
func (wm *quotaWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
wm.ChangeDate = event.CreationDate()
|
||||
switch e := event.(type) {
|
||||
case *quota.AddedEvent:
|
||||
wm.AggregateID = e.Aggregate().ID
|
||||
wm.ChangeDate = e.CreationDate()
|
||||
wm.active = true
|
||||
case *quota.SetEvent:
|
||||
wm.rollingAggregateID = e.Aggregate().ID
|
||||
if e.Amount != nil {
|
||||
wm.amount = *e.Amount
|
||||
}
|
||||
if e.From != nil {
|
||||
wm.from = *e.From
|
||||
}
|
||||
if e.Limit != nil {
|
||||
wm.limit = *e.Limit
|
||||
}
|
||||
if e.ResetInterval != nil {
|
||||
wm.resetInterval = *e.ResetInterval
|
||||
}
|
||||
if e.Notifications != nil {
|
||||
wm.notifications = *e.Notifications
|
||||
}
|
||||
case *quota.RemovedEvent:
|
||||
wm.AggregateID = e.Aggregate().ID
|
||||
wm.active = false
|
||||
wm.rollingAggregateID = ""
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
if err := wm.WriteModel.Reduce(); err != nil {
|
||||
return err
|
||||
}
|
||||
// wm.WriteModel.Reduce() sets the aggregateID to the first event's aggregateID, but we need the last one
|
||||
wm.AggregateID = wm.rollingAggregateID
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewChanges returns all changes that need to be applied to the aggregate.
|
||||
// If createNew is true, all quota properties are set.
|
||||
func (wm *quotaWriteModel) NewChanges(
|
||||
idGenerator id.Generator,
|
||||
createNew bool,
|
||||
amount uint64,
|
||||
from time.Time,
|
||||
resetInterval time.Duration,
|
||||
limit bool,
|
||||
notifications ...*QuotaNotification,
|
||||
) (changes []quota.QuotaChange, err error) {
|
||||
setEventNotifications, err := QuotaNotifications(notifications).newSetEventNotifications(idGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// we sort the input notifications already, so we can return early if they have duplicates
|
||||
err = sortSetEventNotifications(setEventNotifications)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if createNew {
|
||||
return []quota.QuotaChange{
|
||||
quota.ChangeAmount(amount),
|
||||
quota.ChangeFrom(from),
|
||||
quota.ChangeResetInterval(resetInterval),
|
||||
quota.ChangeLimit(limit),
|
||||
quota.ChangeNotifications(setEventNotifications),
|
||||
}, nil
|
||||
}
|
||||
changes = make([]quota.QuotaChange, 0, 5)
|
||||
if wm.amount != amount {
|
||||
changes = append(changes, quota.ChangeAmount(amount))
|
||||
}
|
||||
if wm.from != from {
|
||||
changes = append(changes, quota.ChangeFrom(from))
|
||||
}
|
||||
if wm.resetInterval != resetInterval {
|
||||
changes = append(changes, quota.ChangeResetInterval(resetInterval))
|
||||
}
|
||||
if wm.limit != limit {
|
||||
changes = append(changes, quota.ChangeLimit(limit))
|
||||
}
|
||||
// If the number of notifications differs, we renew the notifications and we can return early
|
||||
if len(setEventNotifications) != len(wm.notifications) {
|
||||
changes = append(changes, quota.ChangeNotifications(setEventNotifications))
|
||||
return changes, nil
|
||||
}
|
||||
// Now we sort the existing notifications too, so comparing the input properties with the existing ones is easier.
|
||||
// We ignore the sorting error for the existing notifications, because this is system state, not user input.
|
||||
// If sorting fails this time, the notifications are listed in the event payload and the projection cleans them up anyway.
|
||||
_ = sortSetEventNotifications(wm.notifications)
|
||||
for i, notification := range setEventNotifications {
|
||||
if notification.CallURL != wm.notifications[i].CallURL ||
|
||||
notification.Percent != wm.notifications[i].Percent ||
|
||||
notification.Repeat != wm.notifications[i].Repeat {
|
||||
changes = append(changes, quota.ChangeNotifications(setEventNotifications))
|
||||
return changes, nil
|
||||
}
|
||||
}
|
||||
return changes, err
|
||||
}
|
||||
|
||||
// newSetEventNotifications returns quota.SetEventNotification elements with generated IDs.
|
||||
func (q QuotaNotifications) newSetEventNotifications(idGenerator id.Generator) (setNotifications []*quota.SetEventNotification, err error) {
|
||||
if q == nil {
|
||||
return make([]*quota.SetEventNotification, 0), nil
|
||||
}
|
||||
notifications := make([]*quota.SetEventNotification, len(q))
|
||||
for idx, notification := range q {
|
||||
notifications[idx] = "a.SetEventNotification{
|
||||
Percent: notification.Percent,
|
||||
Repeat: notification.Repeat,
|
||||
CallURL: notification.CallURL,
|
||||
}
|
||||
notifications[idx].ID, err = idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return notifications, nil
|
||||
}
|
||||
|
||||
// sortSetEventNotifications reports an error if there are duplicate notifications or if a pointer is nil
|
||||
func sortSetEventNotifications(notifications []*quota.SetEventNotification) (err error) {
|
||||
slices.SortFunc(notifications, func(i, j *quota.SetEventNotification) int {
|
||||
if i == nil || j == nil {
|
||||
err = zitadel_errors.ThrowInternal(errors.New("sorting slices of *quota.SetEventNotification with nil pointers is not supported"), "QUOTA-8YXPk", "Errors.Internal")
|
||||
return 0
|
||||
}
|
||||
if i.Percent == j.Percent && i.CallURL == j.CallURL && i.Repeat == j.Repeat {
|
||||
// TODO: translate
|
||||
err = zitadel_errors.ThrowInternal(fmt.Errorf("%+v", i), "QUOTA-Pty2n", "Errors.Quota.Notifications.Duplicate")
|
||||
return 0
|
||||
}
|
||||
if i.Percent < j.Percent ||
|
||||
i.Percent == j.Percent && i.CallURL < j.CallURL ||
|
||||
i.Percent == j.Percent && i.CallURL == j.CallURL && i.Repeat == false && j.Repeat == true {
|
||||
return -1
|
||||
}
|
||||
return +1
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
373
internal/command/quota_model_test.go
Normal file
373
internal/command/quota_model_test.go
Normal file
@@ -0,0 +1,373 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
id_mock "github.com/zitadel/zitadel/internal/id/mock"
|
||||
"github.com/zitadel/zitadel/internal/repository/quota"
|
||||
)
|
||||
|
||||
func TestQuotaWriteModel_NewChanges(t *testing.T) {
|
||||
type fields struct {
|
||||
from time.Time
|
||||
resetInterval time.Duration
|
||||
amount uint64
|
||||
limit bool
|
||||
notifications []*quota.SetEventNotification
|
||||
}
|
||||
type args struct {
|
||||
idGenerator id.Generator
|
||||
createNew bool
|
||||
amount uint64
|
||||
from time.Time
|
||||
resetInterval time.Duration
|
||||
limit bool
|
||||
notifications []*QuotaNotification
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantEvent quota.SetEvent
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{{
|
||||
name: "change reset interval",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: make([]*quota.SetEventNotification, 0),
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Minute,
|
||||
limit: true,
|
||||
notifications: make([]*QuotaNotification, 0),
|
||||
},
|
||||
wantEvent: quota.SetEvent{
|
||||
ResetInterval: durationPtr(time.Minute),
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change reset interval and amount",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: make([]*quota.SetEventNotification, 0),
|
||||
},
|
||||
args: args{
|
||||
amount: 10,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Minute,
|
||||
limit: true,
|
||||
notifications: make([]*QuotaNotification, 0),
|
||||
},
|
||||
wantEvent: quota.SetEvent{
|
||||
ResetInterval: durationPtr(time.Minute),
|
||||
Amount: uint64Ptr(10),
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change nothing",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*quota.SetEventNotification{},
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*QuotaNotification{},
|
||||
},
|
||||
wantEvent: quota.SetEvent{},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change limit to zero value",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: make([]*quota.SetEventNotification, 0),
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: false,
|
||||
notifications: make([]*QuotaNotification, 0),
|
||||
},
|
||||
wantEvent: quota.SetEvent{Limit: boolPtr(false)},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change amount to zero value",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: make([]*quota.SetEventNotification, 0),
|
||||
},
|
||||
args: args{
|
||||
amount: 0,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: make([]*QuotaNotification, 0),
|
||||
},
|
||||
wantEvent: quota.SetEvent{Amount: uint64Ptr(0)},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change from to zero value",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: make([]*quota.SetEventNotification, 0),
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Time{},
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: make([]*QuotaNotification, 0),
|
||||
},
|
||||
wantEvent: quota.SetEvent{From: &time.Time{}},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "add notification",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*QuotaNotification{{
|
||||
Percent: 20,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "notification1"),
|
||||
},
|
||||
wantEvent: quota.SetEvent{Notifications: &[]*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 20,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}}},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change nothing with notification",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*QuotaNotification{{
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
idGenerator: id_mock.NewIDGenerator(t),
|
||||
},
|
||||
wantEvent: quota.SetEvent{},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change nothing but notification order",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}, {
|
||||
ID: "notification2",
|
||||
Percent: 10,
|
||||
Repeat: false,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*QuotaNotification{{
|
||||
Percent: 10,
|
||||
Repeat: false,
|
||||
CallURL: "https://call.url",
|
||||
}, {
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "newnotification1", "newnotification2"),
|
||||
},
|
||||
wantEvent: quota.SetEvent{},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "change notification to zero value",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*QuotaNotification{},
|
||||
},
|
||||
wantEvent: quota.SetEvent{Notifications: &[]*quota.SetEventNotification{}},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "create new without notification",
|
||||
fields: fields{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
},
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*QuotaNotification{},
|
||||
},
|
||||
wantEvent: quota.SetEvent{Notifications: &[]*quota.SetEventNotification{}},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "create new with all values values",
|
||||
args: args{
|
||||
amount: 5,
|
||||
from: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
resetInterval: time.Hour,
|
||||
limit: true,
|
||||
notifications: []*QuotaNotification{{
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "notification1"),
|
||||
createNew: true,
|
||||
},
|
||||
wantEvent: quota.SetEvent{
|
||||
From: timePtr(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),
|
||||
ResetInterval: durationPtr(time.Hour),
|
||||
Amount: uint64Ptr(5),
|
||||
Limit: boolPtr(true),
|
||||
Notifications: &[]*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 10,
|
||||
Repeat: true,
|
||||
CallURL: "https://call.url",
|
||||
}},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
}, {
|
||||
name: "create new with zero values",
|
||||
args: args{createNew: true},
|
||||
wantEvent: quota.SetEvent{
|
||||
From: &time.Time{},
|
||||
ResetInterval: durationPtr(0),
|
||||
Amount: uint64Ptr(0),
|
||||
Limit: boolPtr(false),
|
||||
Notifications: &[]*quota.SetEventNotification{},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
wm := "aWriteModel{
|
||||
from: tt.fields.from,
|
||||
resetInterval: tt.fields.resetInterval,
|
||||
amount: tt.fields.amount,
|
||||
limit: tt.fields.limit,
|
||||
notifications: tt.fields.notifications,
|
||||
}
|
||||
gotChanges, err := wm.NewChanges(tt.args.idGenerator, tt.args.createNew, tt.args.amount, tt.args.from, tt.args.resetInterval, tt.args.limit, tt.args.notifications...)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("NewChanges(%v, %v, %v, %v, %v, %v)", tt.args.createNew, tt.args.amount, tt.args.from, tt.args.resetInterval, tt.args.limit, tt.args.notifications)) {
|
||||
return
|
||||
}
|
||||
marshalled, err := json.Marshal(quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "instance1").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
quota.Unimplemented,
|
||||
gotChanges...,
|
||||
))
|
||||
assert.NoError(t, err)
|
||||
unmarshalled := new(quota.SetEvent)
|
||||
assert.NoError(t, json.Unmarshal(marshalled, unmarshalled))
|
||||
assert.Equalf(t, tt.wantEvent, *unmarshalled, "NewChanges(%v, %v, %v, %v, %v, %v)", tt.args.createNew, tt.args.amount, tt.args.from, tt.args.resetInterval, tt.args.limit, tt.args.notifications)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func uint64Ptr(i uint64) *uint64 { return &i }
|
||||
func boolPtr(b bool) *bool { return &b }
|
||||
func durationPtr(d time.Duration) *time.Duration { return &d }
|
||||
func timePtr(t time.Time) *time.Time { return &t }
|
@@ -25,7 +25,7 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
addQuota *AddQuota
|
||||
setQuota *SetQuota
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@@ -44,14 +44,18 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
quota.NewAddedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
time.Now(),
|
||||
30*24*time.Hour,
|
||||
1000,
|
||||
false,
|
||||
nil,
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(false),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -59,13 +63,12 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
addQuota: &AddQuota{
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Time{},
|
||||
ResetInterval: 0,
|
||||
Amount: 0,
|
||||
Limit: false,
|
||||
Notifications: nil,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
Amount: 1000,
|
||||
Limit: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
@@ -83,7 +86,7 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
addQuota: &AddQuota{
|
||||
setQuota: &SetQuota{
|
||||
Unit: "unimplemented",
|
||||
From: time.Time{},
|
||||
ResetInterval: 0,
|
||||
@@ -108,25 +111,28 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewAddedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
30*24*time.Hour,
|
||||
1000,
|
||||
true,
|
||||
nil,
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
addQuota: &AddQuota{
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
@@ -142,21 +148,25 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "removed, ok",
|
||||
name: "recreate quota, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewAddedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
time.Now(),
|
||||
30*24*time.Hour,
|
||||
1000,
|
||||
true,
|
||||
nil,
|
||||
quota.ChangeFrom(time.Now()),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithInstanceID(
|
||||
@@ -171,25 +181,28 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewAddedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota2", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
30*24*time.Hour,
|
||||
1000,
|
||||
true,
|
||||
nil,
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota2"),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
addQuota: &AddQuota{
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
@@ -214,32 +227,35 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewAddedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
30*24*time.Hour,
|
||||
1000,
|
||||
true,
|
||||
[]*quota.AddedEventNotification{
|
||||
{
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(
|
||||
[]*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 20,
|
||||
Repeat: false,
|
||||
CallURL: "https://url.com",
|
||||
},
|
||||
},
|
||||
}},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", quota.NewAddQuotaUnitUniqueConstraint(quota.RequestsAllAuthenticated)),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1", "notification1"),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
addQuota: &AddQuota{
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
@@ -267,7 +283,288 @@ func TestQuota_AddQuota(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := r.AddQuota(tt.args.ctx, tt.args.addQuota)
|
||||
got, err := r.AddQuota(tt.args.ctx, tt.args.setQuota)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if tt.res.err != nil && !tt.res.err(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuota_SetQuota(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
setQuota *SetQuota
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "already existing",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
Amount: 1000,
|
||||
Limit: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create quota, validation fail",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
setQuota: &SetQuota{
|
||||
Unit: "unimplemented",
|
||||
From: time.Time{},
|
||||
ResetInterval: 0,
|
||||
Amount: 0,
|
||||
Limit: false,
|
||||
Notifications: nil,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-OTeSh", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create quota, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1"),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
Amount: 1000,
|
||||
Limit: true,
|
||||
Notifications: nil,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "recreate quota, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
quota.ChangeFrom(time.Now()),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewRemovedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota2", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(make([]*quota.SetEventNotification, 0)),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota2"),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
Amount: 1000,
|
||||
Limit: true,
|
||||
Notifications: nil,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create quota with notifications, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
quota.ChangeFrom(time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC)),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
quota.ChangeNotifications(
|
||||
[]*quota.SetEventNotification{{
|
||||
ID: "notification1",
|
||||
Percent: 20,
|
||||
Repeat: false,
|
||||
CallURL: "https://url.com",
|
||||
}},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "quota1", "notification1"),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
|
||||
setQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Date(2023, 9, 1, 0, 0, 0, 0, time.UTC),
|
||||
ResetInterval: 30 * 24 * time.Hour,
|
||||
Amount: 1000,
|
||||
Limit: true,
|
||||
Notifications: QuotaNotifications{
|
||||
{
|
||||
Percent: 20,
|
||||
Repeat: false,
|
||||
CallURL: "https://url.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "INSTANCE",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
}
|
||||
got, err := r.SetQuota(tt.args.ctx, tt.args.setQuota)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -325,14 +622,17 @@ func TestQuota_RemoveQuota(t *testing.T) {
|
||||
expectFilter(
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewAddedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
time.Now(),
|
||||
30*24*time.Hour,
|
||||
1000,
|
||||
true,
|
||||
nil,
|
||||
quota.ChangeFrom(time.Now()),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(true),
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithInstanceID(
|
||||
@@ -363,14 +663,17 @@ func TestQuota_RemoveQuota(t *testing.T) {
|
||||
expectFilter(
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"INSTANCE",
|
||||
quota.NewAddedEvent(context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.NewSetEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
context.Background(),
|
||||
"a.NewAggregate("quota1", "INSTANCE").Aggregate,
|
||||
quota.SetEventType,
|
||||
),
|
||||
QuotaRequestsAllAuthenticated.Enum(),
|
||||
time.Now(),
|
||||
30*24*time.Hour,
|
||||
1000,
|
||||
false,
|
||||
nil,
|
||||
quota.ChangeFrom(time.Now()),
|
||||
quota.ChangeResetInterval(30*24*time.Hour),
|
||||
quota.ChangeAmount(1000),
|
||||
quota.ChangeLimit(false),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -517,9 +820,9 @@ func TestQuota_QuotaNotification_validate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuota_AddQuota_validate(t *testing.T) {
|
||||
func TestQuota_SetQuota_validate(t *testing.T) {
|
||||
type args struct {
|
||||
addQuota *AddQuota
|
||||
addQuota *SetQuota
|
||||
}
|
||||
type res struct {
|
||||
err func(error) bool
|
||||
@@ -532,7 +835,7 @@ func TestQuota_AddQuota_validate(t *testing.T) {
|
||||
{
|
||||
name: "notification url parse failed",
|
||||
args: args{
|
||||
addQuota: &AddQuota{
|
||||
addQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Now(),
|
||||
ResetInterval: time.Minute * 10,
|
||||
@@ -556,7 +859,7 @@ func TestQuota_AddQuota_validate(t *testing.T) {
|
||||
{
|
||||
name: "unit unimplemented",
|
||||
args: args{
|
||||
addQuota: &AddQuota{
|
||||
addQuota: &SetQuota{
|
||||
Unit: "unimplemented",
|
||||
From: time.Now(),
|
||||
ResetInterval: time.Minute * 10,
|
||||
@@ -571,28 +874,10 @@ func TestQuota_AddQuota_validate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "amount 0",
|
||||
args: args{
|
||||
addQuota: &AddQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Now(),
|
||||
ResetInterval: time.Minute * 10,
|
||||
Amount: 0,
|
||||
Limit: true,
|
||||
Notifications: nil,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "QUOTA-hOKSJ", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reset interval under 1 min",
|
||||
args: args{
|
||||
addQuota: &AddQuota{
|
||||
addQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Now(),
|
||||
ResetInterval: time.Second * 10,
|
||||
@@ -610,7 +895,7 @@ func TestQuota_AddQuota_validate(t *testing.T) {
|
||||
{
|
||||
name: "validate, ok",
|
||||
args: args{
|
||||
addQuota: &AddQuota{
|
||||
addQuota: &SetQuota{
|
||||
Unit: QuotaRequestsAllAuthenticated,
|
||||
From: time.Now(),
|
||||
ResetInterval: time.Minute * 10,
|
||||
|
Reference in New Issue
Block a user