package flow import ( "context" customer "git.sendico.io/tech/sxchnge/product/customers/model" "git.sendico.io/tech/sxchnge/product/notifications" model "git.sendico.io/tech/sxchnge/product/verification/model" verification "git.sendico.io/tech/sxchnge/product/verification/store" merrors "git.sendico.io/tech/sxchnge/shared/errors" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/v2/bson" "strings" "testing" "time" ) type tokenStub struct { verification.DB request *verification.Request result verification.CreateResult err error } func (s *tokenStub) CreateWithResult(_ context.Context, r *verification.Request) (verification.CreateResult, error) { s.request = r return s.result, s.err } type senderStub struct { messages []notifications.Message err error } func (s *senderStub) Send(_ context.Context, m notifications.Message) error { s.messages = append(s.messages, m) return s.err } func TestIssueRetriesSamePayloadAndBindsServerSecret(t *testing.T) { expiry := time.Now().UTC().Add(time.Minute) tokens := &tokenStub{result: verification.CreateResult{VerificationToken: "123456", ExpiresAt: expiry}} sender := &senderStub{err: merrors.ServiceUnavailable("queue")} service := &Service{Tokens: tokens, Sender: sender, Secret: []byte(strings.Repeat("s", 32))} a := &customer.Account{} a.ID = bson.NewObjectID() a.Login = "user@example.com" a.Locale = "ru-RU" _, err := service.Issue(context.Background(), a, model.PurposeLogin, "pending-scope", "client-key", expiry) require.Error(t, err) key := *tokens.request.IdempotencyKey require.NotEqual(t, "client-key", key) require.Equal(t, "pending-scope", tokens.request.ContextRef) require.LessOrEqual(t, tokens.request.Ttl, time.Minute) sender.err = nil _, err = service.Issue(context.Background(), a, model.PurposeLogin, "pending-scope", "client-key", expiry) require.NoError(t, err) require.Equal(t, sender.messages[0], sender.messages[1]) require.Equal(t, "ru", sender.messages[0].Locale) service.Secret = []byte(strings.Repeat("b", 32)) require.NotEqual(t, key, service.key(a.ID.Hex(), string(model.PurposeLogin), "pending-scope", "client-key")) tokens.result.ExpiresAt = time.Now().Add(-time.Minute) _, err = service.Issue(context.Background(), a, model.PurposeLogin, "pending-scope", "expired") require.ErrorIs(t, err, verification.ErrTokenExpired) require.Len(t, sender.messages, 2) } func TestValidateActivationDestination(t *testing.T) { for _, destination := range []string{"https://app.example/verify-email", "http://localhost:5126/verify-email"} { s := &Service{Secret: []byte(strings.Repeat("s", 32)), ActivationURL: destination} require.NoError(t, s.Validate()) } for _, destination := range []string{"http://app.example/verify-email", "//app.example", "https://user:pass@app.example/verify-email", "https://app.example/#/verify"} { s := &Service{Secret: []byte(strings.Repeat("s", 32)), ActivationURL: destination} require.Error(t, s.Validate()) } }