mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:47:32 +00:00
Project commands (#26)
* feat: eventstore repository * fix: remove gorm * version * feat: pkg * feat: add some files for project * feat: eventstore without eventstore-lib * rename files * gnueg * fix: key json * fix: add object * fix: change imports * fix: internal models * fix: some imports * fix: global model * fix: add some functions on repo * feat(eventstore): sdk * fix(eventstore): search query * fix(eventstore): rename app to eventstore * delete empty test * remove unused func * merge master * fix(eventstore): tests * fix(models): delete unused struct * fix: some funcitons * feat(eventstore): implemented push events * fix: move project eventstore to project package * fix: change project eventstore funcs * feat(eventstore): overwrite context data * fix: change project eventstore * fix: add project repo to mgmt server * feat(types): SQL-config * fix: commented code * feat(eventstore): options to overwrite editor * feat: auth interceptor and cockroach migrations * fix: migrations * fix: fix filter * fix: not found on getbyid * fix: add sequence * fix: add some tests * fix(eventstore): nullable sequence * fix: add some tests * merge * fix: add some tests * fix(migrations): correct statements for sequence * fix: add some tests * fix: add some tests * fix: changes from mr * Update internal/eventstore/models/field.go Co-Authored-By: livio-a <livio.a@gmail.com> * fix(eventstore): code quality * fix: add types to aggregate/Event-types * fix(eventstore): rename modifier* to editor* * fix(eventstore): delete editor_org * fix(migrations): remove editor_org field, rename modifier_* to editor_* * fix: generate files * fix(eventstore): tests * fix(eventstore): rename modifier to editor * fix(migrations): add cluster migration, fix(migrations): fix typo of host in clean clsuter * fix(eventstore): move health * fix(eventstore): AggregateTypeFilter aggregateType as param * code quality * feat: start implementing project members * feat: remove member funcs * feat: remove member model * feat: remove member events * feat: remove member repo model * fix: better error func testing * Update docs/local.md Co-Authored-By: Silvan <silvan.reusser@gmail.com> * Update docs/local.md Co-Authored-By: Silvan <silvan.reusser@gmail.com> * fix: mr requests * fix: md file Co-authored-by: adlerhurst <silvan.reusser@gmail.com> Co-authored-by: livio-a <livio.a@gmail.com>
This commit is contained in:
35
internal/project/model/project.go
Normal file
35
internal/project/model/project.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
in_model "github.com/caos/zitadel/internal/model"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
State ProjectState
|
||||
Name string
|
||||
}
|
||||
|
||||
type ProjectState in_model.Enum
|
||||
|
||||
var states = []string{"Active", "Inactive"}
|
||||
|
||||
func NewProject(id string) *Project {
|
||||
return &Project{ObjectRoot: es_models.ObjectRoot{ID: id}, State: Active}
|
||||
}
|
||||
|
||||
func (p *Project) IsActive() bool {
|
||||
if p.State == Active {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Project) IsValid() bool {
|
||||
if p.Name == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
23
internal/project/model/state.go
Normal file
23
internal/project/model/state.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package model
|
||||
|
||||
type state int32
|
||||
|
||||
func (s state) String() string {
|
||||
return states[s]
|
||||
}
|
||||
|
||||
const (
|
||||
Active state = iota
|
||||
Inactive
|
||||
)
|
||||
|
||||
func ProjectStateToInt(s ProjectState) int32 {
|
||||
if s == nil {
|
||||
return 0
|
||||
}
|
||||
return int32(s.(state))
|
||||
}
|
||||
|
||||
func ProjectStateFromInt(index int32) ProjectState {
|
||||
return state(index)
|
||||
}
|
37
internal/project/model/types.go
Normal file
37
internal/project/model/types.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package model
|
||||
|
||||
import "github.com/caos/zitadel/internal/eventstore/models"
|
||||
|
||||
const (
|
||||
ProjectAggregate models.AggregateType = "project"
|
||||
|
||||
ProjectAdded models.EventType = "project.added"
|
||||
ProjectChanged models.EventType = "project.changed"
|
||||
ProjectDeactivated models.EventType = "project.deactivated"
|
||||
ProjectReactivated models.EventType = "project.reactivated"
|
||||
|
||||
ProjectMemberAdded models.EventType = "project.member.added"
|
||||
ProjectMemberChanged models.EventType = "project.member.changed"
|
||||
ProjectMemberRemoved models.EventType = "project.member.removed"
|
||||
|
||||
ProjectRoleAdded models.EventType = "project.role.added"
|
||||
ProjectRoleRemoved models.EventType = "project.role.removed"
|
||||
|
||||
ProjectGrantAdded models.EventType = "project.grant.added"
|
||||
ProjectGrantChanged models.EventType = "project.grant.changed"
|
||||
ProjectGrantDeactivated models.EventType = "project.grant.deactivated"
|
||||
ProjectGrantReactivated models.EventType = "project.grant.reactivated"
|
||||
|
||||
GrantMemberAdded models.EventType = "project.grant.member.added"
|
||||
GrantMemberChanged models.EventType = "project.grant.member.changed"
|
||||
GrantMemberRemoved models.EventType = "project.grant.member.removed"
|
||||
|
||||
ApplicationAdded models.EventType = "project.application.added"
|
||||
ApplicationChanged models.EventType = "project.application.changed"
|
||||
ApplicationDeactivated models.EventType = "project.application.deactivated"
|
||||
ApplicationReactivated models.EventType = "project.application.reactivated"
|
||||
|
||||
OIDCConfigAdded models.EventType = "project.application.config.oidc.added"
|
||||
OIDCConfigChanged models.EventType = "project.application.config.oidc.changed"
|
||||
OIDCConfigSecretChanged models.EventType = "project.application.config.oidc.secret.changed"
|
||||
)
|
55
internal/project/repository/eventsourcing/events.go
Normal file
55
internal/project/repository/eventsourcing/events.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/caos/logging"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
func ProjectFromEvents(project *Project, events ...*es_models.Event) (*Project, error) {
|
||||
if project == nil {
|
||||
project = &Project{}
|
||||
}
|
||||
|
||||
return project, project.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (p *Project) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
if err := p.AppendEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) AppendEvent(event *es_models.Event) error {
|
||||
p.ObjectRoot.AppendEvent(event)
|
||||
|
||||
switch event.Type {
|
||||
case model.ProjectAdded, model.ProjectChanged:
|
||||
if err := json.Unmarshal(event.Data, p); err != nil {
|
||||
logging.Log("EVEN-idl93").WithError(err).Error("could not unmarshal event data")
|
||||
return err
|
||||
}
|
||||
p.State = model.ProjectStateToInt(model.Active)
|
||||
return nil
|
||||
case model.ProjectDeactivated:
|
||||
return p.appendDeactivatedEvent()
|
||||
case model.ProjectReactivated:
|
||||
return p.appendReactivatedEvent()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) appendDeactivatedEvent() error {
|
||||
p.State = model.ProjectStateToInt(model.Inactive)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) appendReactivatedEvent() error {
|
||||
p.State = model.ProjectStateToInt(model.Active)
|
||||
return nil
|
||||
}
|
169
internal/project/repository/eventsourcing/events_test.go
Normal file
169
internal/project/repository/eventsourcing/events_test.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProjectFromEvents(t *testing.T) {
|
||||
type args struct {
|
||||
event []*es_models.Event
|
||||
project *Project
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Project
|
||||
}{
|
||||
{
|
||||
name: "project from events, ok",
|
||||
args: args{
|
||||
event: []*es_models.Event{
|
||||
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded},
|
||||
},
|
||||
project: &Project{Name: "ProjectName"},
|
||||
},
|
||||
result: &Project{ObjectRoot: es_models.ObjectRoot{ID: "ID"}, State: int32(model.Active), Name: "ProjectName"},
|
||||
},
|
||||
{
|
||||
name: "project from events, nil project",
|
||||
args: args{
|
||||
event: []*es_models.Event{
|
||||
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded},
|
||||
},
|
||||
project: nil,
|
||||
},
|
||||
result: &Project{ObjectRoot: es_models.ObjectRoot{ID: "ID"}, State: int32(model.Active)},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.project != nil {
|
||||
data, _ := json.Marshal(tt.args.project)
|
||||
tt.args.event[0].Data = data
|
||||
}
|
||||
result, _ := ProjectFromEvents(tt.args.project, tt.args.event...)
|
||||
if result.Name != tt.result.Name {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.result.Name, result.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendEvent(t *testing.T) {
|
||||
type args struct {
|
||||
event *es_models.Event
|
||||
project *Project
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Project
|
||||
}{
|
||||
{
|
||||
name: "append added event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded},
|
||||
project: &Project{Name: "ProjectName"},
|
||||
},
|
||||
result: &Project{ObjectRoot: es_models.ObjectRoot{ID: "ID"}, State: int32(model.Active), Name: "ProjectName"},
|
||||
},
|
||||
{
|
||||
name: "append change event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectChanged},
|
||||
project: &Project{Name: "ProjectName"},
|
||||
},
|
||||
result: &Project{ObjectRoot: es_models.ObjectRoot{ID: "ID"}, State: int32(model.Active), Name: "ProjectName"},
|
||||
},
|
||||
{
|
||||
name: "append deactivate event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectDeactivated},
|
||||
},
|
||||
result: &Project{ObjectRoot: es_models.ObjectRoot{ID: "ID"}, State: int32(model.Inactive)},
|
||||
},
|
||||
{
|
||||
name: "append reactivate event",
|
||||
args: args{
|
||||
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectReactivated},
|
||||
},
|
||||
result: &Project{ObjectRoot: es_models.ObjectRoot{ID: "ID"}, State: int32(model.Active)},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.project != nil {
|
||||
data, _ := json.Marshal(tt.args.project)
|
||||
tt.args.event.Data = data
|
||||
}
|
||||
result := &Project{}
|
||||
result.AppendEvent(tt.args.event)
|
||||
if result.State != tt.result.State {
|
||||
t.Errorf("got wrong result state: expected: %v, actual: %v ", tt.result.State, result.State)
|
||||
}
|
||||
if result.Name != tt.result.Name {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.result.Name, result.Name)
|
||||
}
|
||||
if result.ObjectRoot.ID != tt.result.ObjectRoot.ID {
|
||||
t.Errorf("got wrong result id: expected: %v, actual: %v ", tt.result.ObjectRoot.ID, result.ObjectRoot.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendDeactivatedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
project *Project
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Project
|
||||
}{
|
||||
{
|
||||
name: "append reactivate event",
|
||||
args: args{
|
||||
project: &Project{},
|
||||
},
|
||||
result: &Project{State: int32(model.Inactive)},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.args.project.appendDeactivatedEvent()
|
||||
if tt.args.project.State != tt.result.State {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.project)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendReactivatedEvent(t *testing.T) {
|
||||
type args struct {
|
||||
project *Project
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
result *Project
|
||||
}{
|
||||
{
|
||||
name: "append reactivate event",
|
||||
args: args{
|
||||
project: &Project{},
|
||||
},
|
||||
result: &Project{State: int32(model.Active)},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.args.project.appendReactivatedEvent()
|
||||
if tt.args.project.State != tt.result.State {
|
||||
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.project)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
110
internal/project/repository/eventsourcing/eventstore.go
Normal file
110
internal/project/repository/eventsourcing/eventstore.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
es_int "github.com/caos/zitadel/internal/eventstore"
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
type ProjectEventstore struct {
|
||||
es_int.Eventstore
|
||||
}
|
||||
|
||||
type ProjectConfig struct {
|
||||
es_int.Eventstore
|
||||
}
|
||||
|
||||
func StartProject(conf ProjectConfig) (*ProjectEventstore, error) {
|
||||
return &ProjectEventstore{Eventstore: conf.Eventstore}, nil
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) ProjectByID(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
|
||||
filter, err := ProjectByIDQuery(project.ID, project.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events, err := es.Eventstore.FilterEvents(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(events) == 0 {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-8due3", "Could not find project events")
|
||||
}
|
||||
foundProject, err := ProjectFromEvents(nil, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ProjectToModel(foundProject), nil
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) CreateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
|
||||
if !project.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Name is required")
|
||||
}
|
||||
project.State = proj_model.Active
|
||||
repoProject := ProjectFromModel(project)
|
||||
projectAggregate, err := ProjectCreateAggregate(ctx, es.Eventstore.AggregateCreator(), repoProject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoProject.AppendEvents(projectAggregate.Events...)
|
||||
return ProjectToModel(repoProject), nil
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) UpdateProject(ctx context.Context, existing *proj_model.Project, new *proj_model.Project) (*proj_model.Project, error) {
|
||||
if !new.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Name is required")
|
||||
}
|
||||
repoExisting := ProjectFromModel(existing)
|
||||
repoNew := ProjectFromModel(new)
|
||||
projectAggregate, err := ProjectUpdateAggregate(ctx, es.AggregateCreator(), repoExisting, repoNew)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoExisting.AppendEvents(projectAggregate.Events...)
|
||||
return ProjectToModel(repoExisting), nil
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) DeactivateProject(ctx context.Context, existing *proj_model.Project) (*proj_model.Project, error) {
|
||||
if !existing.IsActive() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die45", "project must be active")
|
||||
}
|
||||
repoExisting := ProjectFromModel(existing)
|
||||
projectAggregate, err := ProjectDeactivateAggregate(ctx, es.AggregateCreator(), repoExisting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoExisting.AppendEvents(projectAggregate.Events...)
|
||||
return ProjectToModel(repoExisting), nil
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) ReactivateProject(ctx context.Context, existing *proj_model.Project) (*proj_model.Project, error) {
|
||||
if existing.IsActive() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die45", "project must be inactive")
|
||||
}
|
||||
repoExisting := ProjectFromModel(existing)
|
||||
projectAggregate, err := ProjectReactivateAggregate(ctx, es.AggregateCreator(), repoExisting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoExisting.AppendEvents(projectAggregate.Events...)
|
||||
return ProjectToModel(repoExisting), nil
|
||||
}
|
33
internal/project/repository/eventsourcing/eventstore_mock.go
Normal file
33
internal/project/repository/eventsourcing/eventstore_mock.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/caos/zitadel/internal/eventstore/mock"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
func GetMockProjectByIDOK(ctrl *gomock.Controller) *ProjectEventstore {
|
||||
data, _ := json.Marshal(Project{Name: "Name"})
|
||||
events := []*es_models.Event{
|
||||
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded, Data: data},
|
||||
}
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
return &ProjectEventstore{Eventstore: mockEs}
|
||||
}
|
||||
|
||||
func GetMockProjectByIDNoEvents(ctrl *gomock.Controller) *ProjectEventstore {
|
||||
events := []*es_models.Event{}
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
return &ProjectEventstore{Eventstore: mockEs}
|
||||
}
|
||||
|
||||
func GetMockManipulateProject(ctrl *gomock.Controller) *ProjectEventstore {
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
|
||||
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
||||
return &ProjectEventstore{Eventstore: mockEs}
|
||||
}
|
311
internal/project/repository/eventsourcing/eventstore_test.go
Normal file
311
internal/project/repository/eventsourcing/eventstore_test.go
Normal file
@@ -0,0 +1,311 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"github.com/golang/mock/gomock"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProjectByID(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *ProjectEventstore
|
||||
project *model.Project
|
||||
}
|
||||
type res struct {
|
||||
project *model.Project
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project from events, ok",
|
||||
args: args{
|
||||
es: GetMockProjectByIDOK(ctrl),
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}},
|
||||
},
|
||||
res: res{
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project from events, no events",
|
||||
args: args{
|
||||
es: GetMockProjectByIDNoEvents(ctrl),
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}},
|
||||
},
|
||||
res: res{
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project from events, no id",
|
||||
args: args{
|
||||
es: GetMockProjectByIDNoEvents(ctrl),
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "", Sequence: 1}},
|
||||
},
|
||||
res: res{
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.args.es.ProjectByID(nil, tt.args.project)
|
||||
|
||||
if !tt.res.wantErr && result.ID != tt.res.project.ID {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.project.ID, result.ID)
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateProject(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *ProjectEventstore
|
||||
ctx context.Context
|
||||
project *model.Project
|
||||
}
|
||||
type res struct {
|
||||
project *model.Project
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project from events, ok",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name"},
|
||||
},
|
||||
res: res{
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create project no name",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}},
|
||||
},
|
||||
res: res{
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.args.es.CreateProject(tt.args.ctx, tt.args.project)
|
||||
|
||||
if !tt.res.wantErr && result.ID == "" {
|
||||
t.Errorf("result has no id")
|
||||
}
|
||||
if !tt.res.wantErr && result.Name != tt.res.project.Name {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.project.Name, result.Name)
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateProject(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *ProjectEventstore
|
||||
ctx context.Context
|
||||
existing *model.Project
|
||||
new *model.Project
|
||||
}
|
||||
type res struct {
|
||||
project *model.Project
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project from events, ok",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name"},
|
||||
new: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew"},
|
||||
},
|
||||
res: res{
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "create project no name",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name"},
|
||||
new: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: ""},
|
||||
},
|
||||
res: res{
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.args.es.UpdateProject(tt.args.ctx, tt.args.existing, tt.args.new)
|
||||
|
||||
if !tt.res.wantErr && result.ID == "" {
|
||||
t.Errorf("result has no id")
|
||||
}
|
||||
if !tt.res.wantErr && result.Name != tt.res.project.Name {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.project.Name, result.Name)
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeactivateProject(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *ProjectEventstore
|
||||
ctx context.Context
|
||||
existing *model.Project
|
||||
new *model.Project
|
||||
}
|
||||
type res struct {
|
||||
project *model.Project
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "deactivate project, ok",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Active},
|
||||
},
|
||||
res: res{
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew", State: model.Inactive},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deactivate project with inactive state",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Inactive},
|
||||
},
|
||||
res: res{
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.args.es.DeactivateProject(tt.args.ctx, tt.args.existing)
|
||||
|
||||
if !tt.res.wantErr && result.ID == "" {
|
||||
t.Errorf("result has no id")
|
||||
}
|
||||
if !tt.res.wantErr && result.State != tt.res.project.State {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.project.State, result.State)
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReactivateProject(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *ProjectEventstore
|
||||
ctx context.Context
|
||||
existing *model.Project
|
||||
new *model.Project
|
||||
}
|
||||
type res struct {
|
||||
project *model.Project
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "deactivate project, ok",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Inactive},
|
||||
},
|
||||
res: res{
|
||||
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew", State: model.Active},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deactivate project with inactive state",
|
||||
args: args{
|
||||
es: GetMockManipulateProject(ctrl),
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Active},
|
||||
},
|
||||
res: res{
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := tt.args.es.ReactivateProject(tt.args.ctx, tt.args.existing)
|
||||
|
||||
if !tt.res.wantErr && result.ID == "" {
|
||||
t.Errorf("result has no id")
|
||||
}
|
||||
if !tt.res.wantErr && result.State != tt.res.project.State {
|
||||
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.project.State, result.State)
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
131
internal/project/repository/eventsourcing/project.go
Normal file
131
internal/project/repository/eventsourcing/project.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"github.com/sony/sonyflake"
|
||||
)
|
||||
|
||||
var idGenerator = sonyflake.NewSonyflake(sonyflake.Settings{})
|
||||
|
||||
const (
|
||||
projectVersion = "v1"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
es_models.ObjectRoot
|
||||
Name string `json:"name,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (p *Project) Changes(changed *Project) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
if changed.Name != "" && p.Name != changed.Name {
|
||||
changes["name"] = changed.Name
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func ProjectFromModel(project *model.Project) *Project {
|
||||
return &Project{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
ID: project.ObjectRoot.ID,
|
||||
Sequence: project.Sequence,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
},
|
||||
Name: project.Name,
|
||||
State: model.ProjectStateToInt(project.State),
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectToModel(project *Project) *model.Project {
|
||||
return &model.Project{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
ID: project.ID,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
Sequence: project.Sequence,
|
||||
},
|
||||
Name: project.Name,
|
||||
State: model.ProjectStateFromInt(project.State),
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectByIDQuery(id string, latestSequence uint64) (*es_models.SearchQuery, error) {
|
||||
if id == "" {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dke74", "id should be filled")
|
||||
}
|
||||
return ProjectQuery(latestSequence).
|
||||
AggregateIDFilter(id), nil
|
||||
}
|
||||
|
||||
func ProjectQuery(latestSequence uint64) *es_models.SearchQuery {
|
||||
return es_models.NewSearchQuery().
|
||||
AggregateTypeFilter(model.ProjectAggregate).
|
||||
LatestSequenceFilter(latestSequence)
|
||||
}
|
||||
|
||||
func ProjectAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, id string, sequence uint64) (*es_models.Aggregate, error) {
|
||||
return aggCreator.NewAggregate(ctx, id, model.ProjectAggregate, projectVersion, sequence)
|
||||
}
|
||||
|
||||
func ProjectCreateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, project *Project) (*es_models.Aggregate, error) {
|
||||
if project == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie6", "project should not be nil")
|
||||
}
|
||||
var err error
|
||||
id, err := idGenerator.NextID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
project.ID = strconv.FormatUint(id, 10)
|
||||
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, project.ID, project.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return agg.AppendEvent(model.ProjectAdded, project)
|
||||
}
|
||||
|
||||
func ProjectUpdateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *Project, new *Project) (*es_models.Aggregate, error) {
|
||||
if existing == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dk93d", "existing project should not be nil")
|
||||
}
|
||||
if new == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dhr74", "new project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, existing.ID, existing.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes := existing.Changes(new)
|
||||
return agg.AppendEvent(model.ProjectChanged, changes)
|
||||
}
|
||||
|
||||
func ProjectDeactivateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *Project) (*es_models.Aggregate, error) {
|
||||
if existing == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-ueh45", "existing project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, existing.ID, existing.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.ProjectDeactivated, nil)
|
||||
}
|
||||
|
||||
func ProjectReactivateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *Project) (*es_models.Aggregate, error) {
|
||||
if existing == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-37dur", "existing project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, existing.ID, existing.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.ProjectReactivated, nil)
|
||||
}
|
453
internal/project/repository/eventsourcing/project_test.go
Normal file
453
internal/project/repository/eventsourcing/project_test.go
Normal file
@@ -0,0 +1,453 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChanges(t *testing.T) {
|
||||
type args struct {
|
||||
existing *Project
|
||||
new *Project
|
||||
}
|
||||
type res struct {
|
||||
changesLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project name changes",
|
||||
args: args{
|
||||
existing: &Project{Name: "Name"},
|
||||
new: &Project{Name: "NameChanged"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
args: args{
|
||||
existing: &Project{Name: "Name"},
|
||||
new: &Project{Name: "Name"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
changes := tt.args.existing.Changes(tt.args.new)
|
||||
if len(changes) != tt.res.changesLen {
|
||||
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectByIDQuery(t *testing.T) {
|
||||
type args struct {
|
||||
id string
|
||||
sequence uint64
|
||||
}
|
||||
type res struct {
|
||||
filterLen int
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project by id query ok",
|
||||
args: args{
|
||||
id: "ID",
|
||||
sequence: 1,
|
||||
},
|
||||
res: res{
|
||||
filterLen: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project by id query, no id",
|
||||
args: args{
|
||||
sequence: 1,
|
||||
},
|
||||
res: res{
|
||||
filterLen: 3,
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
query, err := ProjectByIDQuery(tt.args.id, tt.args.sequence)
|
||||
if !tt.res.wantErr && query == nil {
|
||||
t.Errorf("query should not be nil")
|
||||
}
|
||||
if !tt.res.wantErr && len(query.Filters) != tt.res.filterLen {
|
||||
t.Errorf("got wrong filter len: expected: %v, actual: %v ", tt.res.filterLen, len(query.Filters))
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectQuery(t *testing.T) {
|
||||
type args struct {
|
||||
sequence uint64
|
||||
}
|
||||
type res struct {
|
||||
filterLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project query ok",
|
||||
args: args{
|
||||
sequence: 1,
|
||||
},
|
||||
res: res{
|
||||
filterLen: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
query := ProjectQuery(tt.args.sequence)
|
||||
if query == nil {
|
||||
t.Errorf("query should not be nil")
|
||||
}
|
||||
if len(query.Filters) != tt.res.filterLen {
|
||||
t.Errorf("got wrong filter len: expected: %v, actual: %v ", tt.res.filterLen, len(query.Filters))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
aggCreator *models.AggregateCreator
|
||||
id string
|
||||
sequence uint64
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
aggType models.AggregateType
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project update aggregate ok",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
id: "ID",
|
||||
sequence: 1,
|
||||
},
|
||||
res: res{
|
||||
eventLen: 0,
|
||||
aggType: model.ProjectAggregate,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, _ := ProjectAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.id, tt.args.sequence)
|
||||
|
||||
if agg == nil {
|
||||
t.Errorf("agg should not be nil")
|
||||
}
|
||||
if len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectCreateAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
new *Project
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventType models.EventType
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project update aggregate ok",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
new: &Project{ObjectRoot: models.ObjectRoot{ID: "ID"}, Name: "ProjectName", State: int32(model.Active)},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectAdded,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new project nil",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
new: nil,
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectAdded,
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectCreateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
|
||||
}
|
||||
if !tt.res.wantErr && agg.Events[0].Data == nil {
|
||||
t.Errorf("should have data in event")
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectUpdateAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *Project
|
||||
new *Project
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventType models.EventType
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project update aggregate ok",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &Project{ObjectRoot: models.ObjectRoot{ID: "ID"}, Name: "ProjectName", State: int32(model.Active)},
|
||||
new: &Project{ObjectRoot: models.ObjectRoot{ID: "ID"}, Name: "ProjectName_Changed", State: int32(model.Active)},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectChanged,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing project nil",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: nil,
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectChanged,
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new project nil",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &Project{ObjectRoot: models.ObjectRoot{ID: "ID"}, Name: "ProjectName", State: int32(model.Active)},
|
||||
new: nil,
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectChanged,
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectUpdateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing, tt.args.new)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
|
||||
}
|
||||
if !tt.res.wantErr && agg.Events[0].Data == nil {
|
||||
t.Errorf("should have data in event")
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectDeactivateAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *Project
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventType models.EventType
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project deactivate aggregate ok",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &Project{ObjectRoot: models.ObjectRoot{ID: "ID"}, Name: "ProjectName", State: int32(model.Active)},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectDeactivated,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing project nil",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: nil,
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectDeactivated,
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectDeactivateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectReactivateAggregate(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
existing *Project
|
||||
aggCreator *models.AggregateCreator
|
||||
}
|
||||
type res struct {
|
||||
eventLen int
|
||||
eventType models.EventType
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project reactivate aggregate ok",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: &Project{ObjectRoot: models.ObjectRoot{ID: "ID"}, Name: "ProjectName", State: int32(model.Inactive)},
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectReactivated,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing project nil",
|
||||
args: args{
|
||||
ctx: auth.NewMockContext("orgID", "userID"),
|
||||
existing: nil,
|
||||
aggCreator: models.NewAggregateCreator("Test"),
|
||||
},
|
||||
res: res{
|
||||
eventLen: 1,
|
||||
eventType: model.ProjectReactivated,
|
||||
wantErr: true,
|
||||
errFunc: caos_errs.IsPreconditionFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectReactivateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
}
|
||||
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
|
||||
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
|
||||
}
|
||||
if tt.res.wantErr && !tt.res.errFunc(err) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user