package onboarding import ( "context" "errors" "testing" "github.com/google/uuid" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20210111" ) type fakeTencentSMSSender struct { request *sms.SendSmsRequest result *sms.SendSmsResponse err error calls int } func (f *fakeTencentSMSSender) SendSmsWithContext(_ context.Context, request *sms.SendSmsRequest) (*sms.SendSmsResponse, error) { f.calls++ f.request = request return f.result, f.err } func TestTencentSMSProviderUsesApprovedTemplateAndChecksPerPhoneStatus(t *testing.T) { provider, err := NewTencentSMSProvider("secret-id", "secret-key", "1400006666", "安芯伴", "123456") if err != nil { t.Fatal(err) } p := provider.(*TencentSMSProvider) fake := &fakeTencentSMSSender{result: &sms.SendSmsResponse{Response: &sms.SendSmsResponseParams{SendStatusSet: []*sms.SendStatus{{Code: common.StringPtr("Ok")}}}}} p.client = fake id := uuid.NewString() if err = p.Send(context.Background(), "+8613800000000", "123456", id); err != nil { t.Fatal(err) } r := fake.request if fake.calls != 1 || *r.SmsSdkAppId != "1400006666" || *r.SignName != "安芯伴" || *r.TemplateId != "123456" || len(r.PhoneNumberSet) != 1 || *r.PhoneNumberSet[0] != "+8613800000000" || len(r.TemplateParamSet) != 1 || *r.TemplateParamSet[0] != "123456" || *r.SessionContext != id { t.Fatal("Tencent SMS request does not match approved OTP template") } fake.result.Response.SendStatusSet[0].Code = common.StringPtr("FailedOperation") if err = p.Send(context.Background(), "+8613800000000", "123456", uuid.NewString()); err == nil { t.Fatal("provider accepted a failed per-phone send status") } fake.err = errors.New("transport timeout") if err = p.Send(context.Background(), "+8613800000000", "123456", uuid.NewString()); err == nil { t.Fatal("provider accepted an uncertain send") } before := fake.calls if err = p.Send(context.Background(), "+15551234567", "123456", uuid.NewString()); err == nil || fake.calls != before { t.Fatal("domestic template sent to an international phone") } } func TestTencentSMSProviderRejectsMissingConfiguration(t *testing.T) { for _, config := range [][5]string{ {"", "key", "1400006666", "安芯伴", "123456"}, {"id", "", "1400006666", "安芯伴", "123456"}, {"id", "key", "not-numeric", "安芯伴", "123456"}, {"id", "key", "1400006666", "", "123456"}, {"id", "key", "1400006666", "安芯伴", ""}, } { if _, err := NewTencentSMSProvider(config[0], config[1], config[2], config[3], config[4]); err == nil { t.Fatal("invalid Tencent SMS configuration accepted") } } } func TestTencentSMSRoutesEachPurposeToItsOwnTemplate(t *testing.T) { templates := map[string]string{"login": "101", "register": "102", "change_password": "103", "change_phone": "104"} provider, err := NewTencentSMSProviderWithTemplates("id", "key", "1400006666", "安芯伴", templates) if err != nil { t.Fatal(err) } p := provider.(*TencentSMSProvider) fake := &fakeTencentSMSSender{result: &sms.SendSmsResponse{Response: &sms.SendSmsResponseParams{SendStatusSet: []*sms.SendStatus{{Code: common.StringPtr("Ok")}}}}} p.client = fake for purpose, want := range map[string]string{SMSPurposeLogin: "101", SMSPurposeRegister: "102", SMSPurposeChangePassword: "103", SMSPurposeChangePhone: "104"} { if err := p.SendForPurpose(context.Background(), "+8613800000000", "123456", uuid.NewString(), purpose); err != nil || *fake.request.TemplateId != want { t.Fatalf("purpose %s did not select its own template: %v", purpose, err) } } if err := p.SendForPurpose(context.Background(), "+8613800000000", "123456", uuid.NewString(), "UNKNOWN"); err == nil || fake.calls != 4 { t.Fatal("unknown purpose sent a message") } delete(templates, "change_phone") if _, err := NewTencentSMSProviderWithTemplates("id", "key", "1400006666", "安芯伴", templates); err == nil { t.Fatal("missing purpose silently fell back to another template") } } func TestTencentSMSRegistrationIncludesConfiguredExpiryParameter(t *testing.T) { templates := map[string]string{"login": "101", "register": "102", "change_password": "103", "change_phone": "104"} provider, err := NewTencentSMSProviderWithTemplates("id", "key", "1400006666", "安芯伴", templates, map[string][]string{"register": {"code", "expires_minutes"}}) if err != nil { t.Fatal(err) } p := provider.(*TencentSMSProvider) fake := &fakeTencentSMSSender{result: &sms.SendSmsResponse{Response: &sms.SendSmsResponseParams{SendStatusSet: []*sms.SendStatus{{Code: common.StringPtr("Ok")}}}}} p.client = fake if err := p.SendForPurpose(context.Background(), "+8613800000000", "123456", uuid.NewString(), SMSPurposeRegister); err != nil { t.Fatal(err) } params := fake.request.TemplateParamSet if len(params) != 2 || *params[0] != "123456" || *params[1] != "5" { t.Fatal("registration template must receive code followed by its real expiry in minutes") } if err := p.Send(context.Background(), "+8613800000000", "123456", uuid.NewString()); err != nil || len(fake.request.TemplateParamSet) != 1 { t.Fatal("registration parameters leaked into login template") } for _, params := range [][]string{{}, {"expires_minutes"}, {"code", "unknown"}, {"code", "code"}} { if _, err := NewTencentSMSProviderWithTemplates("id", "key", "1400006666", "安芯伴", templates, map[string][]string{"register": params}); err == nil { t.Fatal("invalid parameters accepted") } } }