|
1 | 1 | package org.openmrs.module.initializer.api; |
2 | 2 |
|
3 | 3 | import static org.hamcrest.CoreMatchers.is; |
| 4 | +import static org.hamcrest.CoreMatchers.not; |
4 | 5 | import static org.hamcrest.MatcherAssert.assertThat; |
5 | 6 | import static org.hamcrest.Matchers.containsInAnyOrder; |
6 | 7 | import static org.hamcrest.Matchers.empty; |
7 | 8 | import static org.openmrs.module.initializer.api.ConfigDirUtil.CHECKSUM_FILE_EXT; |
| 9 | +import static org.openmrs.module.initializer.api.ConfigDirUtil.ROW_CHECKSUM_FILE_EXT; |
| 10 | +import static org.openmrs.module.initializer.api.ConfigDirUtil.computeRowChecksum; |
8 | 11 | import static org.openmrs.module.initializer.api.ConfigDirUtil.getLocatedFilename; |
9 | 12 |
|
10 | 13 | import java.io.File; |
|
14 | 17 | import java.util.Arrays; |
15 | 18 | import java.util.Collection; |
16 | 19 | import java.util.HashMap; |
| 20 | +import java.util.HashSet; |
17 | 21 | import java.util.List; |
18 | 22 | import java.util.Map; |
| 23 | +import java.util.Set; |
19 | 24 | import java.util.stream.Collectors; |
20 | 25 |
|
21 | 26 | import org.apache.commons.codec.digest.DigestUtils; |
@@ -136,6 +141,100 @@ public void getFiles_shouldHandleChecksumsWhenNestedFiles() throws IOException { |
136 | 141 | } |
137 | 142 | } |
138 | 143 |
|
| 144 | + @Test |
| 145 | + public void computeRowChecksum_shouldBeStableUnderColumnReordering() { |
| 146 | + String[] header1 = { "uuid", "name", "description" }; |
| 147 | + String[] row1 = { "abc-123", "Acme", "A clinic" }; |
| 148 | + String[] header2 = { "name", "description", "uuid" }; |
| 149 | + String[] row2 = { "Acme", "A clinic", "abc-123" }; |
| 150 | + Assert.assertEquals(computeRowChecksum(header1, row1), computeRowChecksum(header2, row2)); |
| 151 | + } |
| 152 | + |
| 153 | + @Test |
| 154 | + public void computeRowChecksum_shouldChangeWhenAValueChanges() { |
| 155 | + String[] header = { "uuid", "name" }; |
| 156 | + String[] row1 = { "abc-123", "Acme" }; |
| 157 | + String[] row2 = { "abc-123", "Beta" }; |
| 158 | + assertThat(computeRowChecksum(header, row1), is(not(computeRowChecksum(header, row2)))); |
| 159 | + } |
| 160 | + |
| 161 | + @Test |
| 162 | + public void computeRowChecksum_shouldDistinguishAbsentFromPresentButEmpty() { |
| 163 | + // Adding a column — even when its cell is empty — must change the hash, because some line |
| 164 | + // processors (e.g. NestedConceptLineProcessor) treat a present-but-empty cell as a directive |
| 165 | + // to clear an existing field and treat an absent column as "leave the field alone". |
| 166 | + String[] header1 = { "uuid", "name" }; |
| 167 | + String[] row1 = { "abc-123", "Acme" }; |
| 168 | + String[] header2 = { "uuid", "name", "description" }; |
| 169 | + String[] row2 = { "abc-123", "Acme", null }; |
| 170 | + String[] row3 = { "abc-123", "Acme", "" }; |
| 171 | + assertThat(computeRowChecksum(header1, row1), is(not(computeRowChecksum(header2, row2)))); |
| 172 | + assertThat(computeRowChecksum(header1, row1), is(not(computeRowChecksum(header2, row3)))); |
| 173 | + // However null and empty values within an existing column should be treated identically, |
| 174 | + // since CsvParser normalizes blank cells to null on read. |
| 175 | + Assert.assertEquals(computeRowChecksum(header2, row2), computeRowChecksum(header2, row3)); |
| 176 | + } |
| 177 | + |
| 178 | + @Test |
| 179 | + public void computeRowChecksum_shouldChangeWhenAColumnIsRenamed() { |
| 180 | + String[] header1 = { "uuid", "name" }; |
| 181 | + String[] header2 = { "uuid", "label" }; |
| 182 | + String[] row = { "abc-123", "Acme" }; |
| 183 | + assertThat(computeRowChecksum(header1, row), is(not(computeRowChecksum(header2, row)))); |
| 184 | + } |
| 185 | + |
| 186 | + @Test |
| 187 | + public void rowChecksums_shouldRoundTripThroughDisk() throws IOException { |
| 188 | + String configDirPath = getClass().getClassLoader().getResource("org/openmrs/module/initializer/include").getPath(); |
| 189 | + String checksumsDirPath = Files.createTempDirectory("configuration_checksums_rows").toString(); |
| 190 | + String domain = "file_patterns"; |
| 191 | + |
| 192 | + ConfigDirUtil dirUtil = new ConfigDirUtil(configDirPath, checksumsDirPath, domain); |
| 193 | + File configFile = new File(Paths.get(configDirPath, domain, "diagnoses.csv").toString()); |
| 194 | + |
| 195 | + Set<String> hashes = new HashSet<>(); |
| 196 | + hashes.add("hash-a"); |
| 197 | + hashes.add("hash-b"); |
| 198 | + hashes.add("hash-c"); |
| 199 | + |
| 200 | + // Writing then reading should round-trip the set. |
| 201 | + dirUtil.writeRowChecksums(configFile, hashes); |
| 202 | + File rowsFile = Paths.get(checksumsDirPath, domain, |
| 203 | + getLocatedFilename(Paths.get(configDirPath, domain).toString(), configFile) + "." + ROW_CHECKSUM_FILE_EXT) |
| 204 | + .toFile(); |
| 205 | + assertThat(rowsFile.exists(), is(true)); |
| 206 | + assertThat(dirUtil.readRowChecksums(configFile), is(hashes)); |
| 207 | + |
| 208 | + // Writing an empty set should remove the file. |
| 209 | + dirUtil.writeRowChecksums(configFile, new HashSet<>()); |
| 210 | + assertThat(rowsFile.exists(), is(false)); |
| 211 | + |
| 212 | + // Reading when no file exists returns an empty set. |
| 213 | + assertThat(dirUtil.readRowChecksums(configFile), is(empty())); |
| 214 | + } |
| 215 | + |
| 216 | + @Test |
| 217 | + public void deleteRowChecksums_shouldRemoveTheRowChecksumFile() throws IOException { |
| 218 | + String configDirPath = getClass().getClassLoader().getResource("org/openmrs/module/initializer/include").getPath(); |
| 219 | + String checksumsDirPath = Files.createTempDirectory("configuration_checksums_rows_delete").toString(); |
| 220 | + String domain = "file_patterns"; |
| 221 | + |
| 222 | + ConfigDirUtil dirUtil = new ConfigDirUtil(configDirPath, checksumsDirPath, domain); |
| 223 | + File configFile = new File(Paths.get(configDirPath, domain, "diagnoses.csv").toString()); |
| 224 | + |
| 225 | + Set<String> hashes = new HashSet<>(); |
| 226 | + hashes.add("hash-a"); |
| 227 | + dirUtil.writeRowChecksums(configFile, hashes); |
| 228 | + |
| 229 | + File rowsFile = Paths.get(checksumsDirPath, domain, |
| 230 | + getLocatedFilename(Paths.get(configDirPath, domain).toString(), configFile) + "." + ROW_CHECKSUM_FILE_EXT) |
| 231 | + .toFile(); |
| 232 | + assertThat(rowsFile.exists(), is(true)); |
| 233 | + |
| 234 | + dirUtil.deleteRowChecksums(configFile); |
| 235 | + assertThat(rowsFile.exists(), is(false)); |
| 236 | + } |
| 237 | + |
139 | 238 | /* |
140 | 239 | * One of the CSV files has a non-parseable _order. |
141 | 240 | * The resulting exception is logged as an error. |
|
0 commit comments