|
| 1 | +package com.baloise.confluence.digitalsignature.migration; |
| 2 | + |
| 3 | +import static com.atlassian.confluence.setup.bandana.ConfluenceBandanaContext.GLOBAL_CONTEXT; |
| 4 | +import static org.junit.jupiter.api.Assertions.*; |
| 5 | +import static org.mockito.ArgumentMatchers.any; |
| 6 | +import static org.mockito.ArgumentMatchers.eq; |
| 7 | +import static org.mockito.Mockito.*; |
| 8 | + |
| 9 | +import java.io.ByteArrayInputStream; |
| 10 | +import java.io.ByteArrayOutputStream; |
| 11 | +import java.io.BufferedReader; |
| 12 | +import java.io.IOException; |
| 13 | +import java.io.InputStreamReader; |
| 14 | +import java.nio.charset.StandardCharsets; |
| 15 | +import java.util.Date; |
| 16 | +import java.util.List; |
| 17 | +import java.util.Map; |
| 18 | +import java.util.zip.GZIPInputStream; |
| 19 | + |
| 20 | +import com.atlassian.bandana.BandanaManager; |
| 21 | +import com.atlassian.migration.app.gateway.AppCloudForgeMigrationGateway; |
| 22 | +import com.atlassian.migration.app.gateway.MigrationDetailsV1; |
| 23 | +import com.baloise.confluence.digitalsignature.Signature2; |
| 24 | +import com.google.gson.JsonObject; |
| 25 | +import com.google.gson.JsonParser; |
| 26 | + |
| 27 | +import org.junit.jupiter.api.BeforeEach; |
| 28 | +import org.junit.jupiter.api.Test; |
| 29 | + |
| 30 | +class DigitalSignatureMigrationListenerTest { |
| 31 | + |
| 32 | + private BandanaManager bandanaManager; |
| 33 | + private DigitalSignatureMigrationListener listener; |
| 34 | + |
| 35 | + @BeforeEach |
| 36 | + void setUp() { |
| 37 | + bandanaManager = mock(BandanaManager.class); |
| 38 | + listener = new DigitalSignatureMigrationListener(bandanaManager); |
| 39 | + } |
| 40 | + |
| 41 | + @Test |
| 42 | + void macroMapping_returnsCorrectMapping() { |
| 43 | + Map<String, String> mapping = listener.getServerToForgeMacroMapping(); |
| 44 | + assertEquals(1, mapping.size()); |
| 45 | + assertEquals( |
| 46 | + DigitalSignatureMigrationListener.FORGE_MACRO_KEY, |
| 47 | + mapping.get(DigitalSignatureMigrationListener.SERVER_MACRO_KEY) |
| 48 | + ); |
| 49 | + } |
| 50 | + |
| 51 | + @Test |
| 52 | + void toJsonLine_includesRequiredFields() { |
| 53 | + Signature2 sig = new Signature2(42L, "my body", "my title"); |
| 54 | + sig.getSignatures().put("alice", new Date(0)); |
| 55 | + sig.getMissingSignatures().add("bob"); |
| 56 | + sig.getNotify().add("carol"); |
| 57 | + |
| 58 | + String line = DigitalSignatureMigrationListener.toJsonLine(sig); |
| 59 | + JsonObject obj = new JsonParser().parse(line).getAsJsonObject(); |
| 60 | + |
| 61 | + assertAll( |
| 62 | + () -> assertEquals(sig.getHash(), obj.get("hash").getAsString()), |
| 63 | + () -> assertEquals(42L, obj.get("pageId").getAsLong()), |
| 64 | + () -> assertEquals("my title", obj.get("title").getAsString()), |
| 65 | + () -> assertEquals("my body", obj.get("body").getAsString()), |
| 66 | + () -> assertTrue(obj.has("signatures")), |
| 67 | + () -> assertTrue(obj.get("signatures").getAsJsonObject().has("alice")), |
| 68 | + // missingSignatures and notify must NOT appear in the export |
| 69 | + () -> assertFalse(obj.has("missingSignatures")), |
| 70 | + () -> assertFalse(obj.has("notify")) |
| 71 | + ); |
| 72 | + } |
| 73 | + |
| 74 | + @Test |
| 75 | + void onStartAppMigration_writesOneLinePerContract() throws Exception { |
| 76 | + Signature2 sig1 = new Signature2(10L, "body1", "title1"); |
| 77 | + sig1.getSignatures().put("user1", new Date(1000)); |
| 78 | + |
| 79 | + Signature2 sig2 = new Signature2(20L, "body2", "title2"); |
| 80 | + sig2.getSignatures().put("user2", new Date(2000)); |
| 81 | + sig2.getSignatures().put("user3", new Date(3000)); |
| 82 | + |
| 83 | + List<String> keys = List.of( |
| 84 | + sig1.getKey(), |
| 85 | + sig2.getKey(), |
| 86 | + "protected.somehash", // should be skipped |
| 87 | + "other.key" // should be skipped |
| 88 | + ); |
| 89 | + |
| 90 | + when(bandanaManager.getKeys(GLOBAL_CONTEXT)).thenReturn(keys); |
| 91 | + when(bandanaManager.getValue(eq(GLOBAL_CONTEXT), eq(sig1.getKey()))).thenReturn(Signature2.GSON.toJson(sig1, Signature2.class)); |
| 92 | + when(bandanaManager.getValue(eq(GLOBAL_CONTEXT), eq(sig2.getKey()))).thenReturn(Signature2.GSON.toJson(sig2, Signature2.class)); |
| 93 | + |
| 94 | + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
| 95 | + AppCloudForgeMigrationGateway gateway = mock(AppCloudForgeMigrationGateway.class); |
| 96 | + when(gateway.createAppData("signatures")).thenReturn(buffer); |
| 97 | + |
| 98 | + listener.onStartAppMigration(gateway, new MigrationDetailsV1()); |
| 99 | + |
| 100 | + verify(gateway).completeExport(); |
| 101 | + |
| 102 | + List<String> lines = gunzipLines(buffer.toByteArray()); |
| 103 | + assertEquals(2, lines.size(), "Expected one JSONL line per signature contract"); |
| 104 | + |
| 105 | + JsonObject first = new JsonParser().parse(lines.get(0)).getAsJsonObject(); |
| 106 | + JsonObject second = new JsonParser().parse(lines.get(1)).getAsJsonObject(); |
| 107 | + |
| 108 | + // Verify both contracts appear (order not guaranteed by Bandana) |
| 109 | + List<String> hashes = List.of( |
| 110 | + first.get("hash").getAsString(), |
| 111 | + second.get("hash").getAsString() |
| 112 | + ); |
| 113 | + assertTrue(hashes.contains(sig1.getHash())); |
| 114 | + assertTrue(hashes.contains(sig2.getHash())); |
| 115 | + } |
| 116 | + |
| 117 | + @Test |
| 118 | + void onStartAppMigration_skipNullEntries() throws Exception { |
| 119 | + String key = "signature.deadbeef"; |
| 120 | + when(bandanaManager.getKeys(GLOBAL_CONTEXT)).thenReturn(List.of(key)); |
| 121 | + // fromBandana returns null when key is not found in getKeys |
| 122 | + when(bandanaManager.getKeys(GLOBAL_CONTEXT)).thenReturn(List.of()); |
| 123 | + |
| 124 | + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
| 125 | + AppCloudForgeMigrationGateway gateway = mock(AppCloudForgeMigrationGateway.class); |
| 126 | + when(gateway.createAppData("signatures")).thenReturn(buffer); |
| 127 | + |
| 128 | + assertDoesNotThrow(() -> listener.onStartAppMigration(gateway, new MigrationDetailsV1())); |
| 129 | + verify(gateway).completeExport(); |
| 130 | + |
| 131 | + List<String> lines = gunzipLines(buffer.toByteArray()); |
| 132 | + assertEquals(0, lines.size()); |
| 133 | + } |
| 134 | + |
| 135 | + // ---- helpers ---- |
| 136 | + |
| 137 | + private static List<String> gunzipLines(byte[] compressed) throws IOException { |
| 138 | + try (GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(compressed)); |
| 139 | + BufferedReader reader = new BufferedReader(new InputStreamReader(gzip, StandardCharsets.UTF_8))) { |
| 140 | + return reader.lines().filter(l -> !l.isBlank()).toList(); |
| 141 | + } |
| 142 | + } |
| 143 | +} |
0 commit comments