package com.ruoyi.cms.service; import com.ruoyi.common.exception.ServiceException; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class WeChatMiniProgramQrCodeServiceTest { @Test void generatesPermanentOutdoorFairCodeAndCachesImage() { WeChatMiniProgramApiClient client = mock(WeChatMiniProgramApiClient.class); when(client.getAccessToken("app-id", "app-secret")) .thenReturn(new WeChatMiniProgramApiClient.AccessTokenResult("token-1", 7200)); when(client.getUnlimitedQrCode("token-1", "fairId=12", WeChatMiniProgramQrCodeService.OUTDOOR_FAIR_PAGE)) .thenReturn(new WeChatMiniProgramApiClient.QrCodeImage( "image-content".getBytes(StandardCharsets.UTF_8), "image/png")); WeChatMiniProgramQrCodeService service = new WeChatMiniProgramQrCodeService( client, "app-id", "app-secret", () -> 1_000L); Map first = service.generateOutdoorFairQrCode(12L); Map second = service.generateOutdoorFairQrCode(12L); assertEquals(12L, first.get("fairId")); assertEquals("fairId=12", first.get("scene")); assertEquals("packageA/pages/outdoorFair/detail", first.get("page")); assertTrue((Boolean) first.get("permanent")); assertFalse((Boolean) first.get("rotationRequired")); assertTrue(String.valueOf(first.get("imageData")).startsWith("data:image/png;base64,")); assertEquals(first.get("imageData"), second.get("imageData")); verify(client, times(1)).getAccessToken("app-id", "app-secret"); verify(client, times(1)).getUnlimitedQrCode( "token-1", "fairId=12", WeChatMiniProgramQrCodeService.OUTDOOR_FAIR_PAGE); } @Test void refreshesAccessTokenBeforeItExpires() { AtomicLong now = new AtomicLong(1_000L); WeChatMiniProgramApiClient client = mock(WeChatMiniProgramApiClient.class); when(client.getAccessToken("app-id", "app-secret")) .thenReturn(new WeChatMiniProgramApiClient.AccessTokenResult("token-1", 7200)) .thenReturn(new WeChatMiniProgramApiClient.AccessTokenResult("token-2", 7200)); when(client.getUnlimitedQrCode(anyString(), anyString(), anyString())) .thenReturn(new WeChatMiniProgramApiClient.QrCodeImage(new byte[]{1, 2, 3}, "image/png")); WeChatMiniProgramQrCodeService service = new WeChatMiniProgramQrCodeService( client, "app-id", "app-secret", now::get); service.generateOutdoorFairQrCode(1L); now.addAndGet(7_000_000L); service.generateOutdoorFairQrCode(2L); verify(client, times(2)).getAccessToken("app-id", "app-secret"); verify(client).getUnlimitedQrCode( "token-1", "fairId=1", WeChatMiniProgramQrCodeService.OUTDOOR_FAIR_PAGE); verify(client).getUnlimitedQrCode( "token-2", "fairId=2", WeChatMiniProgramQrCodeService.OUTDOOR_FAIR_PAGE); } @Test void rejectsMissingConfigurationWithoutCallingWechat() { WeChatMiniProgramApiClient client = mock(WeChatMiniProgramApiClient.class); WeChatMiniProgramQrCodeService service = new WeChatMiniProgramQrCodeService( client, "", "", () -> 1_000L); ServiceException exception = assertThrows( ServiceException.class, () -> service.generateOutdoorFairQrCode(1L)); assertEquals("微信小程序配置不完整,请联系管理员", exception.getMessage()); } }