package com.mydreamjob.auth.sms; import com.aliyun.dysmsapi20170525.Client; import com.aliyun.dysmsapi20170525.models.*; import com.aliyun.teautil.models.RuntimeOptions; import com.mydreamjob.common.exception.BusinessException; import com.mydreamjob.common.exception.ErrorCode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; class AliyunSmsCodeSenderTest { private Client client; private AliyunSmsCodeSender sender; private static final String PHONE="19900009890", CODE="652901"; @BeforeEach void setup() { client=mock(Client.class); var config=new AliyunSmsProperties(); config.setAccessKeyId("test-key-not-real"); config.setAccessKeySecret("test-secret-not-real"); config.setSignName("测试签名"); config.setTemplateCode("SMS_TEST_ONLY"); sender=new AliyunSmsCodeSender(client,config); } private SendSmsResponse response(int http,String code) { return new SendSmsResponse().setStatusCode(http).setBody(new SendSmsResponseBody().setCode(code)); } @Test void acceptedRequestUsesApprovedSignAndTemplateAndNeverRetries() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenReturn(response(200,"OK")); sender.sendVerificationCode(PHONE,"login",CODE); var req=ArgumentCaptor.forClass(SendSmsRequest.class); var runtime=ArgumentCaptor.forClass(RuntimeOptions.class); verify(client,times(1)).sendSmsWithOptions(req.capture(),runtime.capture()); assertThat(req.getValue().getPhoneNumbers()).isEqualTo(PHONE); assertThat(req.getValue().getSignName()).isEqualTo("测试签名"); assertThat(req.getValue().getTemplateCode()).isEqualTo("SMS_TEST_ONLY"); assertThat(req.getValue().getTemplateParam()).isEqualTo("{\"code\":\"652901\"}"); assertThat(runtime.getValue().getAutoretry()).isFalse(); assertThat(runtime.getValue().getConnectTimeout()).isEqualTo(3000); assertThat(runtime.getValue().getReadTimeout()).isEqualTo(5000); } @Test void eachExistingPurposeCanSend() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenReturn(response(200,"OK")); for(String purpose:new String[]{"login","password-set","password-reset","company-register"}) sender.sendVerificationCode(PHONE,purpose,CODE); verify(client,times(4)).sendSmsWithOptions(any(),any()); } @Test void providerBusinessRejectionNeverReturnsSuccess() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenReturn(response(200,"isv.BUSINESS_LIMIT_CONTROL")); assertSafeFailure(); } @Test void httpFailureEvenWithOkIsRejected() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenReturn(response(500,"OK")); assertSafeFailure(); } @Test void emptyResponseIsRejected() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenReturn(null); assertSafeFailure(); } @Test void emptyBodyIsRejected() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenReturn(new SendSmsResponse().setStatusCode(200)); assertSafeFailure(); } @Test void missingStatusIsRejected() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenReturn(new SendSmsResponse().setBody(new SendSmsResponseBody().setCode("OK"))); assertSafeFailure(); } @Test void exceptionContainingSecretsIsDiscardedAndNotRetried() throws Exception { when(client.sendSmsWithOptions(any(),any())).thenThrow(new java.net.SocketTimeoutException(PHONE+CODE+"test-secret-not-real")); assertSafeFailure(); verify(client,times(1)).sendSmsWithOptions(any(),any()); } @Test void invalidInputsNeverCallProvider() { for(String[] args:new String[][]{{null,"login",CODE},{"bad","login",CODE},{PHONE,null,CODE}, {PHONE,"anything",CODE},{PHONE,"login",null},{PHONE,"login","1234"},{PHONE,"login","\"malicious"}}) { assertThatThrownBy(()->sender.sendVerificationCode(args[0],args[1],args[2])).isInstanceOf(BusinessException.class); } verifyNoInteractions(client); } private void assertSafeFailure() { assertThatThrownBy(()->sender.sendVerificationCode(PHONE,"login",CODE)).isInstanceOf(BusinessException.class) .satisfies(error->{ assertThat(((BusinessException)error).getErrorCodeInt()).isEqualTo(ErrorCode.SMS_SEND_FAILED.getCode()); assertThat(error).hasNoCause().hasMessage("sms send failed"); }); } }