|
|
@@ -0,0 +1,154 @@
|
|
|
+package com.ruoyi.app.push;
|
|
|
+
|
|
|
+import com.google.firebase.messaging.FirebaseMessaging;
|
|
|
+import com.google.firebase.messaging.FirebaseMessagingException;
|
|
|
+import com.google.firebase.messaging.Message;
|
|
|
+import com.google.firebase.messaging.MessagingErrorCode;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.junit.jupiter.api.io.TempDir;
|
|
|
+
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.util.EnumMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+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.any;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+class FcmPushSenderTest {
|
|
|
+ @TempDir
|
|
|
+ Path tempDir;
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void sendsEachRecipientThroughItsOwnProjectWithOriginalPayload() throws Exception {
|
|
|
+ BusinessPushProperties properties = nativeProperties();
|
|
|
+ StubSender sender = new StubSender(properties);
|
|
|
+ sender.initialize();
|
|
|
+
|
|
|
+ for (RecipientApp app : RecipientApp.values()) {
|
|
|
+ assertEquals(app.name().toLowerCase() + ".json", sender.loadedPaths.get(app));
|
|
|
+ String token = "native-token-" + app.name();
|
|
|
+ sender.send(app, token, "New order", "Order body",
|
|
|
+ "{\"ddId\":123,\"state\":2,\"type\":0,\"pushType\":\"order\"}");
|
|
|
+ org.mockito.ArgumentCaptor<Message> captured = org.mockito.ArgumentCaptor.forClass(Message.class);
|
|
|
+ verify(sender.messaging.get(app)).send(captured.capture());
|
|
|
+ Message message = captured.getValue();
|
|
|
+ assertEquals(token, field(message, "token"));
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<String, String> data = (Map<String, String>) field(message, "data");
|
|
|
+ assertEquals("New order", data.get("title"));
|
|
|
+ assertEquals("Order body", data.get("body"));
|
|
|
+ assertEquals("123", data.get("ddId"));
|
|
|
+ assertEquals("2", data.get("state"));
|
|
|
+ assertEquals("0", data.get("type"));
|
|
|
+ assertEquals("order", data.get("pushType"));
|
|
|
+ assertTrue(data.get("payload").contains("\"ddId\":123"));
|
|
|
+ assertFalse(data.values().contains("uni-cid"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void classifiesUnregisteredTokenWithoutIncludingTokenInError() throws Exception {
|
|
|
+ StubSender sender = new StubSender(nativeProperties());
|
|
|
+ sender.initialize();
|
|
|
+ FirebaseMessagingException failure = mock(FirebaseMessagingException.class);
|
|
|
+ when(failure.getMessagingErrorCode()).thenReturn(MessagingErrorCode.UNREGISTERED);
|
|
|
+ when(sender.messaging.get(RecipientApp.USER).send(any(Message.class))).thenThrow(failure);
|
|
|
+
|
|
|
+ IllegalStateException error = assertThrows(IllegalStateException.class,
|
|
|
+ () -> sender.send(RecipientApp.USER, "sensitive-native-token", "title", "body", "{}"));
|
|
|
+
|
|
|
+ assertTrue(error.getMessage().contains("UNREGISTERED"));
|
|
|
+ assertFalse(error.getMessage().contains("sensitive-native-token"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void classifiesProjectMismatchWithoutIncludingTokenInError() throws Exception {
|
|
|
+ StubSender sender = new StubSender(nativeProperties());
|
|
|
+ sender.initialize();
|
|
|
+ FirebaseMessagingException failure = mock(FirebaseMessagingException.class);
|
|
|
+ when(failure.getMessagingErrorCode()).thenReturn(MessagingErrorCode.SENDER_ID_MISMATCH);
|
|
|
+ when(sender.messaging.get(RecipientApp.MERCHANT).send(any(Message.class))).thenThrow(failure);
|
|
|
+
|
|
|
+ IllegalStateException error = assertThrows(IllegalStateException.class,
|
|
|
+ () -> sender.send(RecipientApp.MERCHANT, "sensitive-native-token", "title", "body", "{}"));
|
|
|
+
|
|
|
+ assertTrue(error.getMessage().contains("SENDER_ID_MISMATCH"));
|
|
|
+ assertFalse(error.getMessage().contains("sensitive-native-token"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void repeatedInitializationDoesNotReloadProjects() {
|
|
|
+ StubSender sender = new StubSender(nativeProperties());
|
|
|
+
|
|
|
+ sender.initialize();
|
|
|
+ sender.initialize();
|
|
|
+
|
|
|
+ assertEquals(3, sender.loadCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void invalidCredentialFileFailsNativeInitialization() throws Exception {
|
|
|
+ BusinessPushProperties properties = nativeProperties();
|
|
|
+ Path invalidFile = tempDir.resolve("invalid.json");
|
|
|
+ Files.writeString(invalidFile, "{}");
|
|
|
+ properties.getApp(RecipientApp.USER).setFcmCredentialsPath(invalidFile.toString());
|
|
|
+
|
|
|
+ IllegalStateException error = assertThrows(IllegalStateException.class,
|
|
|
+ () -> new FcmPushSender(properties).initialize());
|
|
|
+
|
|
|
+ assertTrue(error.getMessage().contains("USER"));
|
|
|
+ assertFalse(error.getMessage().contains(invalidFile.toString()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void uniModeDoesNotLoadCredentials() {
|
|
|
+ FcmPushSender sender = new FcmPushSender(new BusinessPushProperties());
|
|
|
+ sender.initialize();
|
|
|
+ sender.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ private BusinessPushProperties nativeProperties() {
|
|
|
+ BusinessPushProperties properties = new BusinessPushProperties();
|
|
|
+ properties.setMode(BusinessPushProperties.Mode.NATIVE);
|
|
|
+ for (RecipientApp app : RecipientApp.values()) {
|
|
|
+ BusinessPushProperties.AppProperties config = new BusinessPushProperties.AppProperties();
|
|
|
+ config.setFcmCredentialsPath(app.name().toLowerCase() + ".json");
|
|
|
+ properties.getApps().put(app, config);
|
|
|
+ }
|
|
|
+ return properties;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object field(Object object, String name) throws Exception {
|
|
|
+ Field field = object.getClass().getDeclaredField(name);
|
|
|
+ field.setAccessible(true);
|
|
|
+ return field.get(object);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class StubSender extends FcmPushSender {
|
|
|
+ private final Map<RecipientApp, FirebaseMessaging> messaging = new EnumMap<>(RecipientApp.class);
|
|
|
+ private final Map<RecipientApp, String> loadedPaths = new EnumMap<>(RecipientApp.class);
|
|
|
+ private int loadCount;
|
|
|
+
|
|
|
+ StubSender(BusinessPushProperties properties) {
|
|
|
+ super(properties);
|
|
|
+ for (RecipientApp app : RecipientApp.values()) {
|
|
|
+ messaging.put(app, mock(FirebaseMessaging.class));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected FirebaseMessaging createMessaging(RecipientApp app, String credentialsPath) {
|
|
|
+ loadCount++;
|
|
|
+ loadedPaths.put(app, credentialsPath);
|
|
|
+ return messaging.get(app);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|