package command import ( "context" "errors" "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "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/notification/channels/smtp" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/zerrors" ) func TestCommandSide_AddSMTPConfig(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore idGenerator id.Generator alg crypto.EncryptionAlgorithm } type args struct { instanceID string smtp *AddSMTPConfig } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "resourceowner empty, invalid argument", fields: fields{ eventstore: expectEventstore(), }, args: args{ smtp: &AddSMTPConfig{}, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-PQN0wsqSyi", "Errors.ResourceOwnerMissing")) }, }, }, { name: "smtp config, custom domain not existing", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, true, ), ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ smtp: &AddSMTPConfig{ ResourceOwner: "INSTANCE", From: "from@domain.ch", Host: "host:587", User: "user", Password: "password", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "INST-xtWIiR2ZbR", "Errors.SMTPConfig.SenderAdressNotCustomDomain")) }, }, }, { name: "add smtp config, from empty", fields: fields{ eventstore: expectEventstore(), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), }, args: args{ smtp: &AddSMTPConfig{ ResourceOwner: "INSTANCE", From: " ", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-SAAFpV8VKV", "Errors.Invalid.Argument")) }, }, }, { name: "add smtp config, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "domain.ch", false, ), ), eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, false, ), ), ), expectPush( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "configid", "test", true, "from@domain.ch", "name", "", "host:587", "user", &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("password"), }, ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ smtp: &AddSMTPConfig{ ResourceOwner: "INSTANCE", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: "host:587", User: "user", Password: "password", }, }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "add smtp config with reply to address, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "domain.ch", false, ), ), eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, false, ), ), ), expectPush( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "configid", "test", true, "from@domain.ch", "name", "replyto@domain.ch", "host:587", "user", &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("password"), }, ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ smtp: &AddSMTPConfig{ ResourceOwner: "INSTANCE", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", ReplyToAddress: "replyto@domain.ch", Host: "host:587", User: "user", Password: "password", }, }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "smtp config, port is missing", fields: fields{ eventstore: expectEventstore(), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ smtp: &AddSMTPConfig{ ResourceOwner: "INSTANCE", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: "host", User: "user", Password: "password", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-EvAtufIinh", "Errors.Invalid.Argument")) }, }, }, { name: "smtp config, host is empty", fields: fields{ eventstore: expectEventstore(), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ smtp: &AddSMTPConfig{ ResourceOwner: "INSTANCE", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: " ", User: "user", Password: "password", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-EvAtufIinh", "Errors.Invalid.Argument")) }, }, }, { name: "add smtp config, ipv6 works", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "domain.ch", false, ), ), eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, false, ), ), ), expectPush( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "configid", "test", true, "from@domain.ch", "name", "", "[2001:db8::1]:2525", "user", &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("password"), }, ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), }, args: args{ smtp: &AddSMTPConfig{ ResourceOwner: "INSTANCE", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: "[2001:db8::1]:2525", User: "user", Password: "password", }, }, 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(t), idGenerator: tt.fields.idGenerator, smtpEncryption: tt.fields.alg, } err := r.AddSMTPConfig(context.Background(), tt.args.smtp) 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 { assertObjectDetails(t, tt.res.want, tt.args.smtp.Details) assert.NotEmpty(t, tt.args.smtp.ID) } }) } } func TestCommandSide_ChangeSMTPConfig(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore } type args struct { smtp *ChangeSMTPConfig } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "resourceowner empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ smtp: &ChangeSMTPConfig{}, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-jwA8gxldy3", "Errors.ResourceOwnerMissing")) }, }, }, { name: "id empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-2JPlSRzuHy", "Errors.IDMissing")) }, }, }, { name: "empty config, invalid argument error", fields: fields{ eventstore: expectEventstore(), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "configID", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-gyPUXOTA4N", "Errors.Invalid.Argument")) }, }, }, { name: "smtp not existing, not found error", fields: fields{ eventstore: expectEventstore( expectFilter(), ), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: "host:587", User: "user", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-j5IDFtt3T1", "Errors.SMTPConfig.NotFound")) }, }, }, { name: "smtp domain not matched", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "domain.ch", false, ), ), eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, true, ), ), eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from@domain.ch", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), ), ), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Tls: true, From: "from@wrongdomain.ch", FromName: "name", Host: "host:587", User: "user", }, }, res: res{ err: zerrors.IsErrorInvalidArgument, }, }, { name: "no changes, precondition error", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "domain.ch", false, ), ), eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, true, ), ), eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from@domain.ch", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), ), ), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: "host:587", User: "user", }, }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "smtp config change, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "domain.ch", false, ), ), eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, true, ), ), eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "", true, "from@domain.ch", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), ), expectPush( newSMTPConfigChangedEvent( context.Background(), "ID", "test", false, "from2@domain.ch", "name2", "replyto@domain.ch", "host2:587", "user2", ), ), ), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Tls: false, From: "from2@domain.ch", FromName: "name2", ReplyToAddress: "replyto@domain.ch", Host: "host2:587", User: "user2", }, }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "smtp config, port is missing", fields: fields{ eventstore: expectEventstore(), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: "host", User: "user", Password: "password", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-kZNVkuL32L", "Errors.Invalid.Argument")) }, }, }, { name: "smtp config, host is empty", fields: fields{ eventstore: expectEventstore(), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Tls: true, From: "from@domain.ch", FromName: "name", Host: " ", User: "user", Password: "password", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-kZNVkuL32L", "Errors.Invalid.Argument")) }, }, }, { name: "smtp config change, ipv6 works", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewDomainAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "domain.ch", false, ), ), eventFromEventPusher( instance.NewDomainPolicyAddedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, true, true, true, ), ), eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "", true, "from@domain.ch", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), ), expectPush( newSMTPConfigChangedEvent( context.Background(), "ID", "test", false, "from2@domain.ch", "name2", "replyto@domain.ch", "[2001:db8::1]:2525", "user2", ), ), ), }, args: args{ smtp: &ChangeSMTPConfig{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Tls: false, From: "from2@domain.ch", FromName: "name2", ReplyToAddress: "replyto@domain.ch", Host: "[2001:db8::1]:2525", User: "user2", }, }, 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(t), } err := r.ChangeSMTPConfig(context.Background(), tt.args.smtp) 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 { assertObjectDetails(t, tt.res.want, tt.args.smtp.Details) } }) } } func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore alg crypto.EncryptionAlgorithm } type args struct { instanceID string id string password string } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "smtp config, error resourceOwner empty", fields: fields{ eventstore: expectEventstore(), }, args: args{}, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-gHAyvUXCAF", "Errors.ResourceOwnerMissing")) }, }, }, { name: "smtp config, error id empty", fields: fields{ eventstore: expectEventstore(), }, args: args{ instanceID: "INSTANCE", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-BCkAf7LcJA", "Errors.IDMissing")) }, }, }, { name: "smtp config, error not found", fields: fields{ eventstore: expectEventstore( expectFilter(), ), }, args: args{ instanceID: "INSTANCE", password: "", id: "ID", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-rDHzqjGuKQ", "Errors.SMTPConfig.NotFound")) }, }, }, { name: "change smtp config password, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), eventFromEventPusher( instance.NewSMTPConfigActivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), expectPush( instance.NewSMTPConfigPasswordChangedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("password"), }, ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ password: "password", id: "ID", instanceID: "INSTANCE", }, 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(t), smtpEncryption: tt.fields.alg, } got, err := r.ChangeSMTPConfigPassword(context.Background(), tt.args.instanceID, tt.args.id, tt.args.password) 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 { assertObjectDetails(t, tt.res.want, got) } }) } } func TestCommandSide_AddSMTPConfigHTTP(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore idGenerator id.Generator } type args struct { http *AddSMTPConfigHTTP } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "add smtp config, resourceowner empty", fields: fields{ eventstore: expectEventstore(), }, args: args{ http: &AddSMTPConfigHTTP{}, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-FTNDXc8ACS", "Errors.ResourceOwnerMissing")) }, }, }, { name: "add smtp config, ok", fields: fields{ eventstore: expectEventstore( expectFilter(), expectPush( instance.NewSMTPConfigHTTPAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "configid", "test", "endpoint", ), ), ), idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "configid"), }, args: args{ http: &AddSMTPConfigHTTP{ ResourceOwner: "INSTANCE", Description: "test", Endpoint: "endpoint", }, }, 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(t), idGenerator: tt.fields.idGenerator, } err := r.AddSMTPConfigHTTP(context.Background(), tt.args.http) 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 { assertObjectDetails(t, tt.res.want, tt.args.http.Details) assert.NotEmpty(t, tt.args.http.ID) } }) } } func TestCommandSide_ChangeSMTPConfigHTTP(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore } type args struct { http *ChangeSMTPConfigHTTP } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{{ name: "resourceowner empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ http: &ChangeSMTPConfigHTTP{}, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-k7QCGOWyJA", "Errors.ResourceOwnerMissing")) }, }, }, { name: "id empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ http: &ChangeSMTPConfigHTTP{ ResourceOwner: "INSTANCE", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-2MHkV8ObWo", "Errors.IDMissing")) }, }, }, { name: "smtp not existing, not found error", fields: fields{ eventstore: expectEventstore( expectFilter(), ), }, args: args{ http: &ChangeSMTPConfigHTTP{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Endpoint: "endpoint", }, }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-xIrdledqv4", "Errors.SMTPConfig.NotFound")) }, }, }, { name: "no changes, precondition error", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigHTTPAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", "endpoint", ), ), ), ), }, args: args{ http: &ChangeSMTPConfigHTTP{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Endpoint: "endpoint", }, }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "smtp config change, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigHTTPAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "", "endpoint", ), ), ), expectPush( newSMTPConfigHTTPChangedEvent( context.Background(), "ID", "test", "endpoint2", ), ), ), }, args: args{ http: &ChangeSMTPConfigHTTP{ ResourceOwner: "INSTANCE", ID: "ID", Description: "test", Endpoint: "endpoint2", }, }, 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(t), } err := r.ChangeSMTPConfigHTTP(context.Background(), tt.args.http) 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 { assertObjectDetails(t, tt.res.want, tt.args.http.Details) } }) } } func TestCommandSide_ActivateSMTPConfig(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore alg crypto.EncryptionAlgorithm } type args struct { ctx context.Context instanceID string id string } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{{ name: "resourceowner empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ instanceID: "", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-h5htMCebv3", "Errors.ResourceOwnerMissing")) }, }, }, { name: "id empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ instanceID: "INSTANCE", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-1hPl6oVMJa", "Errors.IDMissing")) }, }, }, { name: "smtp not existing, not found error", fields: fields{ eventstore: expectEventstore( expectFilter(), ), }, args: args{ ctx: context.Background(), instanceID: "INSTANCE", id: "id", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-E9K20hxOS9", "Errors.SMTPConfig.NotFound")) }, }, }, { name: "activate smtp config, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), ), expectPush( instance.NewSMTPConfigActivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), id: "ID", instanceID: "INSTANCE", }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "activate smtp config, already active", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), eventFromEventPusher( instance.NewSMTPConfigActivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), ), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), id: "ID", instanceID: "INSTANCE", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "COMMAND-vUHBSmBzaw", "Errors.SMTPConfig.AlreadyActive")) }, }, }, { name: "activate smtp config http, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigHTTPAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", "endpoint", ), ), ), expectPush( instance.NewSMTPConfigActivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), id: "ID", instanceID: "INSTANCE", }, 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(t), smtpEncryption: tt.fields.alg, } got, err := r.ActivateSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.id) 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 { assertObjectDetails(t, tt.res.want, got) } }) } } func TestCommandSide_DeactivateSMTPConfig(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore alg crypto.EncryptionAlgorithm } type args struct { instanceID string id string } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{{ name: "resourceOwner empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{}, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-pvNHou89Tw", "Errors.ResourceOwnerMissing")) }, }, }, { name: "id empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ instanceID: "INSTANCE", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-jLTIMrtApO", "Errors.IDMissing")) }, }, }, { name: "smtp not existing, not found error", fields: fields{ eventstore: expectEventstore( expectFilter(), ), }, args: args{ instanceID: "INSTANCE", id: "id", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-k39PJ", "Errors.SMTPConfig.NotFound")) }, }, }, { name: "deactivate smtp config, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), eventFromEventPusher( instance.NewSMTPConfigActivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), expectPush( instance.NewSMTPConfigDeactivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), }, args: args{ id: "ID", instanceID: "INSTANCE", }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "deactivate smtp config, already deactivated", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), eventFromEventPusher( instance.NewSMTPConfigActivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), eventFromEventPusher( instance.NewSMTPConfigDeactivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), ), }, args: args{ id: "ID", instanceID: "INSTANCE", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "COMMAND-km8g3", "Errors.SMTPConfig.AlreadyDeactivated")) }, }, }, { name: "deactivate smtp config http, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigHTTPAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", "endpoint", ), ), eventFromEventPusher( instance.NewSMTPConfigActivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), expectPush( instance.NewSMTPConfigDeactivatedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), }, args: args{ id: "ID", instanceID: "INSTANCE", }, 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(t), smtpEncryption: tt.fields.alg, } got, err := r.DeactivateSMTPConfig(context.Background(), tt.args.instanceID, tt.args.id) 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 { assertObjectDetails(t, tt.res.want, got) } }) } } func TestCommandSide_RemoveSMTPConfig(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore alg crypto.EncryptionAlgorithm } type args struct { instanceID string id string } type res struct { want *domain.ObjectDetails err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "resourceowner empty, invalid argument error", fields: fields{ eventstore: expectEventstore(), }, args: args{ id: "ID", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-t2WsPRgGaK", "Errors.ResourceOwnerMissing")) }, }, }, { name: "id empty, invalid argument error", fields: fields{ eventstore: expectEventstore(), }, args: args{ instanceID: "INSTANCE", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-0ZV5whuUfu", "Errors.IDMissing")) }, }, }, { name: "smtp config, error not found", fields: fields{ eventstore: expectEventstore( expectFilter(), ), }, args: args{ instanceID: "INSTANCE", id: "ID", }, res: res{ err: func(err error) bool { return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-09CXlTDL6w", "Errors.SMTPConfig.NotFound")) }, }, }, { name: "remove smtp config, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "host:587", "user", &crypto.CryptoValue{}, ), ), ), expectPush( instance.NewSMTPConfigRemovedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), }, args: args{ id: "ID", instanceID: "INSTANCE", }, res: res{ want: &domain.ObjectDetails{ ResourceOwner: "INSTANCE", }, }, }, { name: "remove smtp config http, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigHTTPAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", "endpoint", ), ), ), expectPush( instance.NewSMTPConfigRemovedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", ), ), ), }, args: args{ id: "ID", instanceID: "INSTANCE", }, 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(t), smtpEncryption: tt.fields.alg, } got, err := r.RemoveSMTPConfig(context.Background(), tt.args.instanceID, tt.args.id) 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 { assertObjectDetails(t, tt.res.want, got) } }) } } func TestCommandSide_TestSMTPConfig(t *testing.T) { type fields struct { eventstore func(t *testing.T) *eventstore.Eventstore alg crypto.EncryptionAlgorithm } type args struct { ctx context.Context instanceID string id string email string config smtp.Config } type res struct { err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "id empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), }, res: res{ err: zerrors.IsErrorInvalidArgument, }, }, { name: "email empty, precondition error", fields: fields{ eventstore: expectEventstore(), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), instanceID: "INSTANCE", id: "id", }, res: res{ err: zerrors.IsErrorInvalidArgument, }, }, { name: "if password is empty, smtp id must not", fields: fields{ eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), instanceID: "INSTANCE", id: "", email: "email", config: smtp.Config{ From: "test@example,com", FromName: "Test", SMTP: smtp.SMTP{ User: "user", Password: "", Host: "example.com:2525", }, }, }, res: res{ err: zerrors.IsErrorInvalidArgument, }, }, { name: "password empty and smtp config not found, error", fields: fields{ eventstore: expectEventstore( expectFilter(), ), }, args: args{ ctx: context.Background(), instanceID: "INSTANCE", id: "id", email: "email", config: smtp.Config{ From: "test@example,com", FromName: "Test", SMTP: smtp.SMTP{ User: "user", Password: "", Host: "example.com:2525", }, }, }, res: res{ err: zerrors.IsNotFound, }, }, { name: "valid new smtp config, wrong auth, ok", fields: fields{ eventstore: expectEventstore(), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), instanceID: "INSTANCE", email: "email", config: smtp.Config{ From: "test@example.com", FromName: "Test", SMTP: smtp.SMTP{ User: "user", Password: "password", Host: "mail.smtp2go.com:2525", }, }, }, res: res{ err: zerrors.IsInternal, }, }, { name: "valid smtp config using stored password, wrong auth, ok", fields: fields{ eventstore: expectEventstore( expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "mail.smtp2go.com:2525", "user", &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("password"), }, ), ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ ctx: context.Background(), instanceID: "INSTANCE", email: "email", id: "ID", config: smtp.Config{ From: "test@example.com", FromName: "Test", SMTP: smtp.SMTP{ User: "user", Password: "", Host: "mail.smtp2go.com:2525", }, }, }, res: res{ err: zerrors.IsInternal, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore(t), smtpEncryption: tt.fields.alg, } err := r.TestSMTPConfig(tt.args.ctx, tt.args.instanceID, tt.args.id, tt.args.email, &tt.args.config) 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) } }) } } func TestCommandSide_TestSMTPConfigById(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore alg crypto.EncryptionAlgorithm } type args struct { ctx context.Context instanceID string id string email string } type res struct { err func(error) bool } tests := []struct { name string fields fields args args res res }{ { name: "id empty, precondition error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), }, res: res{ err: zerrors.IsErrorInvalidArgument, }, }, { name: "email empty, precondition error", fields: fields{ eventstore: eventstoreExpect( t, ), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), instanceID: "INSTANCE", id: "id", }, res: res{ err: zerrors.IsErrorInvalidArgument, }, }, { name: "smtp config not found error", fields: fields{ eventstore: eventstoreExpect( t, expectFilter(), ), }, args: args{ ctx: context.Background(), instanceID: "INSTANCE", id: "id", email: "email", }, res: res{ err: zerrors.IsNotFound, }, }, { name: "valid smtp config, wrong auth, ok", fields: fields{ eventstore: eventstoreExpect( t, expectFilter( eventFromEventPusher( instance.NewSMTPConfigAddedEvent( context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, "ID", "test", true, "from", "name", "", "mail.smtp2go.com:2525", "user", &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("password"), }, ), ), ), ), alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)), }, args: args{ ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), id: "ID", instanceID: "INSTANCE", email: "test@example.com", }, res: res{ err: zerrors.IsInternal, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, smtpEncryption: tt.fields.alg, } err := r.TestSMTPConfigById(tt.args.ctx, tt.args.instanceID, tt.args.id, tt.args.email) 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) } }) } } func newSMTPConfigChangedEvent(ctx context.Context, id, description string, tls bool, fromAddress, fromName, replyTo, host, user string) *instance.SMTPConfigChangedEvent { changes := []instance.SMTPConfigChanges{ instance.ChangeSMTPConfigDescription(description), instance.ChangeSMTPConfigTLS(tls), instance.ChangeSMTPConfigFromAddress(fromAddress), instance.ChangeSMTPConfigFromName(fromName), instance.ChangeSMTPConfigReplyToAddress(replyTo), instance.ChangeSMTPConfigSMTPHost(host), instance.ChangeSMTPConfigSMTPUser(user), } event, _ := instance.NewSMTPConfigChangeEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, id, changes, ) return event } func newSMTPConfigHTTPChangedEvent(ctx context.Context, id, description, endpoint string) *instance.SMTPConfigHTTPChangedEvent { changes := []instance.SMTPConfigHTTPChanges{ instance.ChangeSMTPConfigHTTPDescription(description), instance.ChangeSMTPConfigHTTPEndpoint(endpoint), } event, _ := instance.NewSMTPConfigHTTPChangeEvent(ctx, &instance.NewAggregate("INSTANCE").Aggregate, id, changes, ) return event }