View Javadoc
1   package de.dlr.shepard.migrations.neo4j;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertThrows;
5   import static org.neo4j.cypherdsl.core.Cypher.node;
6   
7   import com.opencsv.exceptions.CsvValidationException;
8   import de.dlr.shepard.common.neo4j.MigrationsRunner;
9   import de.dlr.shepard.context.collection.entities.Collection;
10  import java.io.IOException;
11  import java.sql.SQLException;
12  import java.util.Map;
13  import org.apache.commons.lang3.RandomStringUtils;
14  import org.junit.jupiter.api.MethodOrderer;
15  import org.junit.jupiter.api.Test;
16  import org.junit.jupiter.api.TestMethodOrder;
17  import org.neo4j.cypherdsl.core.Cypher;
18  import org.neo4j.cypherdsl.core.Node;
19  import org.neo4j.ogm.exception.CypherException;
20  
21  /**
22   * Test the database migrations with a focus on the Neo4j migrations, utilizing the neo4j-migrations package.
23   * This includes migrations which additionally rely on timescale data.
24   */
25  @TestMethodOrder(MethodOrderer.MethodName.class)
26  public class TestNeo4jMigrations {
27  
28    private static final String randomElement = RandomStringUtils.insecure().next(6, true, true);
29    private static final QueryHelper q = new QueryHelper();
30    private static final TestV11 testV11 = new TestV11();
31    private static final TestV13 testV13 = new TestV13();
32    private static final TestV14 testV14 = new TestV14();
33  
34    private static void testNodeMigrated(Node old, Node migrated) {
35      assertEquals(0, q.match(old).size());
36      assertEquals(1, q.match(migrated).size());
37    }
38  
39    private static void runMigrations(String targetVersion) {
40      new MigrationsRunner(targetVersion).apply();
41    }
42  
43    private static <K, V> void assertEqualsMaps(Map<K, V> expected, Map<K, V> actual) {
44      assertEquals(expected.keySet(), actual.keySet());
45    }
46  
47    @Test
48    public void testV09() {
49      var collectionWithBadAttributes = node("Collection").withProperties(
50        "name",
51        Cypher.literalOf(randomElement),
52        "attributes.a",
53        Cypher.literalOf(0),
54        "attributes.b.c",
55        Cypher.literalOf(1)
56      );
57      q.create(collectionWithBadAttributes);
58  
59      runMigrations("V9");
60  
61      var collectionWithGoodAttributes = node("Collection").withProperties(
62        "name",
63        Cypher.literalOf(randomElement),
64        "attributes||a",
65        Cypher.literalOf(0),
66        "attributes||b.c",
67        Cypher.literalOf(1)
68      );
69  
70      testNodeMigrated(collectionWithBadAttributes, collectionWithGoodAttributes);
71  
72      // test that the migration has not touched the attribute values
73      var migratedCollection = q.match(collectionWithGoodAttributes, Collection.class).getFirst();
74      assertEqualsMaps(Map.of("a", "0", "b.c", "1"), migratedCollection.getAttributes());
75    }
76  
77    @Test
78    public void testV10() {
79      var legacyAnnotation = node("SemanticAnnotation").withProperties(
80        "name",
81        Cypher.literalOf("prop-" + randomElement + "::" + "value-" + randomElement),
82        "propertyIRI",
83        Cypher.literalOf("piri"),
84        "valueIRI",
85        Cypher.literalOf("viri")
86      );
87      q.create(legacyAnnotation);
88  
89      runMigrations("V10");
90  
91      var migratedAnnotation = node("SemanticAnnotation").withProperties(
92        "propertyName",
93        Cypher.literalOf("prop-" + randomElement),
94        "valueName",
95        Cypher.literalOf("value-" + randomElement)
96      );
97  
98      testNodeMigrated(legacyAnnotation, migratedAnnotation);
99    }
100 
101   @Test
102   public void testV11_0_NoException() {
103     testV11.setupPreMigrationData();
104     testV11.runMigration();
105   }
106 
107   @Test
108   public void testV11_UniqueTimeseriesUntouched() {
109     testV11.assertUniqueTimeseriesUntouched();
110   }
111 
112   @Test
113   public void testV11_DuplicatesEliminated() {
114     testV11.assertDuplicatesEliminated();
115   }
116 
117   @Test
118   public void testV11_ReferencesMigrated() {
119     testV11.assertReferencesMigrated();
120   }
121 
122   @Test
123   public void testV12() {
124     testV11.cleanup();
125     var legacyReferencedTimeseries = node("Timeseries").withProperties("device", Cypher.literalOf("V12"));
126     q.create(legacyReferencedTimeseries);
127     runMigrations("V12");
128     var migratedTimeseries = node("TimeseriesTuple").withProperties("device", Cypher.literalOf("V12"));
129     testNodeMigrated(legacyReferencedTimeseries, migratedTimeseries);
130   }
131 
132   @Test
133   public void testV12_ConstraintHolds() {
134     var ts = node("TimeseriesTuple").withProperties(
135       "measurement",
136       Cypher.literalOf("V12"),
137       "device",
138       Cypher.literalOf("V12"),
139       "location",
140       Cypher.literalOf("V12"),
141       "symbolicName",
142       Cypher.literalOf("V12"),
143       "field",
144       Cypher.literalOf("V12")
145     );
146     q.create(ts);
147     assertThrows(CypherException.class, () -> q.create(ts));
148   }
149 
150   @Test
151   public void testV13_0_NoException() throws CsvValidationException, IOException, ClassNotFoundException {
152     testV13.setupPreMigrationData();
153     testV13.runMigration();
154   }
155 
156   @Test
157   public void testV13_TimeseriesPresentInGraphDb() {
158     testV13.assertTimeseriesPresentInGraphDb();
159   }
160 
161   @Test
162   public void testV13_MetadataDeletedInTimeseriesDb() throws SQLException, ClassNotFoundException {
163     testV13.assertMetadataDeletedInTimeseriesDb();
164   }
165 
166   @Test
167   public void testV13_TimeseriesDatapointsIntact() throws SQLException, ClassNotFoundException {
168     testV13.assertTimeseriesDatapointsIntact();
169   }
170 
171   @Test
172   public void testV13_NewTimeseriesMergedWithPreexisting() {
173     testV13.assertNewTimeseriesMergedWithPreexisting();
174   }
175 
176   @Test
177   public void testV14_0_NoException() {
178     testV14.setupPreMigrationData();
179     runMigrations("V14");
180   }
181 
182   @Test
183   public void testV14_SinglyAnnotatedTimeseriesMigrated() {
184     testV14.assertSinglyAnnotatedTimeseriesMigrated();
185   }
186 
187   @Test
188   public void testV14_DoublyAnnotatedTimeseriesMigrated() {
189     testV14.assertDoublyAnnotatedTimeseriesMigrated();
190   }
191 
192   @Test
193   public void testV14_LegacyAnnotatedTimeseriesDeleted() {
194     testV14.assertLegacyAnnotatedTimeseriesDeleted();
195   }
196 }