feat: Add Twilio Verification Service (#8678)

# Which Problems Are Solved
Twilio supports a robust, multi-channel verification service that
notably supports multi-region SMS sender numbers required for our use
case. Currently, Zitadel does much of the work of the Twilio Verify (eg.
localization, code generation, messaging) but doesn't support the pool
of sender numbers that Twilio Verify does.

# How the Problems Are Solved
To support this API, we need to be able to store the Twilio Service ID
and send that in a verification request where appropriate: phone number
verification and SMS 2FA code paths.

This PR does the following: 
- Adds the ability to use Twilio Verify of standard messaging through
Twilio
- Adds support for international numbers and more reliable verification
messages sent from multiple numbers
- Adds a new Twilio configuration option to support Twilio Verify in the
admin console
- Sends verification SMS messages through Twilio Verify
- Implements Twilio Verification Checks for codes generated through the
same

# Additional Changes

# Additional Context
- base was implemented by @zhirschtritt in
https://github.com/zitadel/zitadel/pull/8268 ❤️
- closes https://github.com/zitadel/zitadel/issues/8581

---------

Co-authored-by: Zachary Hirschtritt <zachary.hirschtritt@klaviyo.com>
Co-authored-by: Joey Biscoglia <joey.biscoglia@klaviyo.com>
This commit is contained in:
Livio Spring
2024-09-26 09:14:33 +02:00
committed by GitHub
parent 4eaa3163b6
commit 14e2aba1bc
89 changed files with 3888 additions and 782 deletions

View File

@@ -13,14 +13,29 @@ import (
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
"github.com/zitadel/zitadel/internal/notification/senders"
"github.com/zitadel/zitadel/internal/notification/senders/mock"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/user"
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestCommandSide_ChangeHumanPhone(t *testing.T) {
defaultGenerators := &SecretGenerators{
OTPSMS: &crypto.GeneratorConfig{
Length: 8,
Expiry: time.Hour,
IncludeLowerLetters: true,
IncludeUpperLetters: true,
IncludeDigits: true,
IncludeSymbols: true,
},
}
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
userEncryption crypto.EncryptionAlgorithm
defaultSecretGenerators *SecretGenerators
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
}
type args struct {
ctx context.Context
@@ -41,9 +56,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
{
name: "invalid phone, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -61,8 +74,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
{
name: "user not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -83,8 +95,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
{
name: "phone not changed, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -126,8 +137,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
{
name: "verified phone changed, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -186,8 +196,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
{
name: "phone changed to verified, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -242,8 +251,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
{
name: "phone changed to verified, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -298,8 +306,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
{
name: "phone changed with code, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -316,6 +323,36 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewSMSConfigTwilioAddedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
"",
"sid",
"senderNumber",
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
"",
),
),
eventFromEventPusher(
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
),
),
),
expectPush(
user.NewHumanPhoneChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
@@ -330,9 +367,100 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
Crypted: []byte("a"),
},
time.Hour*1,
"",
),
),
),
userEncryption: crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)),
defaultSecretGenerators: defaultGenerators,
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("a", time.Hour),
},
args: args{
ctx: context.Background(),
email: &domain.Phone{
ObjectRoot: models.ObjectRoot{
AggregateID: "user1",
},
PhoneNumber: "+41711234567",
},
resourceOwner: "org1",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Phone{
ObjectRoot: models.ObjectRoot{
AggregateID: "user1",
ResourceOwner: "org1",
},
PhoneNumber: "+41711234567",
},
},
},
{
name: "phone changed with code (external), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewSMSConfigTwilioAddedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
"",
"sid",
"senderNumber",
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
"verifyServiceSID",
),
),
eventFromEventPusher(
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
),
),
),
expectPush(
user.NewHumanPhoneChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"+41711234567",
),
user.NewHumanPhoneCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil,
0,
"id",
),
),
),
userEncryption: crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)),
defaultSecretGenerators: defaultGenerators,
},
args: args{
ctx: context.Background(),
@@ -359,7 +487,10 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
userEncryption: tt.fields.userEncryption,
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
}
got, err := r.ChangeHumanPhone(tt.args.ctx, tt.args.email, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
@@ -377,7 +508,8 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
func TestCommandSide_VerifyHumanPhone(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
phoneCodeVerifier func(ctx context.Context, id string) (senders.CodeGenerator, error)
}
type args struct {
ctx context.Context
@@ -399,9 +531,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -415,9 +545,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
{
name: "code missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -431,8 +559,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
{
name: "user not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -449,8 +576,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
{
name: "code not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -482,8 +608,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
{
name: "invalid code, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -515,6 +640,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
Crypted: []byte("a"),
},
time.Hour*1,
"",
),
),
),
@@ -539,8 +665,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
{
name: "valid code, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -572,6 +697,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
Crypted: []byte("a"),
},
time.Hour*1,
"",
),
),
),
@@ -595,11 +721,80 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
},
},
},
{
name: "valid code (external), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
eventFromEventPusher(
user.NewHumanPhoneChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"+411234567",
),
),
eventFromEventPusherWithCreationDateNow(
user.NewHumanPhoneCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil,
0,
"id",
),
),
eventFromEventPusherWithCreationDateNow(
user.NewHumanPhoneCodeSentEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
&senders.CodeGeneratorInfo{
ID: "id",
VerificationID: "verificationID",
},
),
),
),
expectPush(
user.NewHumanPhoneVerifiedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
phoneCodeVerifier: func(ctx context.Context, id string) (senders.CodeGenerator, error) {
sender := mock.NewMockCodeGenerator(gomock.NewController(t))
sender.EXPECT().VerifyCode("verificationID", "a")
return sender, nil
},
},
args: args{
ctx: context.Background(),
userID: "user1",
code: "a",
resourceOwner: "org1",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
phoneCodeVerifier: tt.fields.phoneCodeVerifier,
}
got, err := r.VerifyHumanPhone(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
@@ -616,9 +811,21 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
}
func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
defaultGenerators := &SecretGenerators{
OTPSMS: &crypto.GeneratorConfig{
Length: 8,
Expiry: time.Hour,
IncludeLowerLetters: true,
IncludeUpperLetters: true,
IncludeDigits: true,
IncludeSymbols: true,
},
}
type fields struct {
eventstore *eventstore.Eventstore
userEncryption crypto.EncryptionAlgorithm
eventstore func(*testing.T) *eventstore.Eventstore
userEncryption crypto.EncryptionAlgorithm
defaultSecretGenerators *SecretGenerators
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
}
type args struct {
ctx context.Context
@@ -638,9 +845,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -653,8 +858,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
{
name: "user not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -670,8 +874,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
{
name: "phone already verified, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -713,8 +916,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
{
name: "new code, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -739,16 +941,33 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
),
expectFilter(
eventFromEventPusher(
instance.NewSecretGeneratorAddedEvent(context.Background(),
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
domain.SecretGeneratorTypeVerifyPhoneCode,
8,
time.Hour,
true,
true,
true,
true,
)),
"id",
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewSMSConfigTwilioAddedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
"",
"sid",
"senderNumber",
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
"",
),
),
eventFromEventPusher(
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
),
),
),
expectPush(
user.NewHumanPhoneCodeAddedEvent(context.Background(),
@@ -760,10 +979,92 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
Crypted: []byte("12345678"),
},
time.Hour*1,
"",
),
),
),
userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"),
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
defaultSecretGenerators: defaultGenerators,
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("12345678", time.Hour),
},
args: args{
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "new code (external), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
eventFromEventPusher(
user.NewHumanPhoneChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"+411234567",
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
),
),
),
expectFilter(
eventFromEventPusher(
instance.NewSMSConfigTwilioAddedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
"",
"sid",
"senderNumber",
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
"verifyServiceSID",
),
),
eventFromEventPusher(
instance.NewSMSConfigActivatedEvent(
context.Background(),
&instance.NewAggregate("instanceID").Aggregate,
"id",
),
),
),
expectPush(
user.NewHumanPhoneCodeAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
nil,
0,
"id",
),
),
),
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
defaultSecretGenerators: defaultGenerators,
},
args: args{
ctx: context.Background(),
@@ -780,8 +1081,10 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
userEncryption: tt.fields.userEncryption,
eventstore: tt.fields.eventstore(t),
userEncryption: tt.fields.userEncryption,
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
}
got, err := r.CreateHumanPhoneVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
if tt.res.err == nil {
@@ -799,12 +1102,13 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
}
type args struct {
ctx context.Context
userID string
resourceOwner string
generatorInfo *senders.CodeGeneratorInfo
}
type res struct {
err func(error) bool
@@ -818,9 +1122,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -833,8 +1135,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
{
name: "user not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -850,8 +1151,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
{
name: "code sent, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -877,6 +1177,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
expectPush(
user.NewHumanPhoneCodeSentEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
&senders.CodeGeneratorInfo{},
),
),
),
@@ -885,6 +1186,55 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
generatorInfo: &senders.CodeGeneratorInfo{},
},
res: res{},
},
{
name: "code sent (external), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
eventFromEventPusher(
user.NewHumanPhoneChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"+411234567",
),
),
),
expectPush(
user.NewHumanPhoneCodeSentEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
&senders.CodeGeneratorInfo{
ID: "generatorID",
VerificationID: "verificationID",
},
),
),
),
},
args: args{
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
generatorInfo: &senders.CodeGeneratorInfo{
ID: "generatorID",
VerificationID: "verificationID",
},
},
res: res{},
},
@@ -892,9 +1242,9 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
}
err := r.HumanPhoneVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
err := r.HumanPhoneVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.generatorInfo)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -907,7 +1257,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
func TestCommandSide_RemoveHumanPhone(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(*testing.T) *eventstore.Eventstore
}
type args struct {
ctx context.Context
@@ -927,9 +1277,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
@@ -942,8 +1290,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
{
name: "user not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
),
},
@@ -959,8 +1306,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
{
name: "phone not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -991,8 +1337,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
{
name: "remove phone, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
@@ -1037,7 +1382,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
}
got, err := r.RemoveHumanPhone(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
if tt.res.err == nil {