Skip to content

Commit 38bc686

Browse files
committed
utils coverage
1 parent bec32cc commit 38bc686

1 file changed

Lines changed: 155 additions & 68 deletions

File tree

src/test/java/org/folio/fqm/migration/MigrationUtilsTest.java

Lines changed: 155 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,20 @@
1010
import static org.junit.jupiter.api.Assertions.assertTrue;
1111
import static org.junit.jupiter.api.Assertions.fail;
1212

13+
import com.fasterxml.jackson.databind.ObjectMapper;
1314
import com.fasterxml.jackson.databind.node.TextNode;
15+
import java.io.IOException;
1416
import java.io.UncheckedIOException;
1517
import java.util.ArrayList;
1618
import java.util.List;
1719
import java.util.Map;
1820
import java.util.UUID;
21+
import lombok.SneakyThrows;
1922
import lombok.extern.log4j.Log4j2;
20-
import org.apache.commons.lang3.tuple.Pair;
21-
import org.apache.commons.lang3.tuple.Triple;
2223
import org.folio.fqm.exception.InvalidFqlException;
2324
import org.folio.fqm.migration.types.MigratableFqlFieldAndCondition;
2425
import org.folio.fqm.migration.types.SingleFieldMigrationResult;
26+
import org.folio.fqm.migration.warnings.RemovedFieldWarning;
2527
import org.junit.jupiter.api.Test;
2628
import org.junit.jupiter.params.ParameterizedTest;
2729
import org.junit.jupiter.params.provider.Arguments;
@@ -31,25 +33,23 @@
3133
class MigrationUtilsTest {
3234

3335
private static final UUID TEST_UUID = UUID.fromString("d686ef05-fb3d-5edc-a87f-c3001c579dfb");
36+
private static final UUID UUID_A = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
37+
private static final UUID UUID_B = UUID.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
38+
private static final UUID UUID_C = UUID.fromString("cccccccc-cccc-cccc-cccc-cccccccccccc");
3439
private static final Map<UUID, Map<String, UUID>> EMPTY_SOURCE_MAP = Map.of();
40+
private static final Map<UUID, Map<String, UUID>> DUMMY_SOURCE_MAP = Map.ofEntries(
41+
Map.entry(TEST_UUID, Map.of("sourceA", UUID_A, "sourceB", UUID_B)),
42+
Map.entry(UUID_B, Map.of("sourceC", UUID_C))
43+
);
3544

36-
static List<Arguments> functionCallTestCases() {
45+
static List<Arguments> migrateFqlFunctionCallTestCases() {
3746
return List.of(
38-
// query, list of expected field transformation calls from migrateFql,
39-
// list of field transformation calls from migrateAndReshapeFql
40-
Arguments.of("{}", List.of(), List.of()),
41-
Arguments.of("{\"_version\":\"1\"}", List.of(), List.of()),
47+
// query, list of expected field transformation calls from migrateFql
48+
Arguments.of("{}", List.of()),
49+
Arguments.of("{\"_version\":\"1\"}", List.of()),
4250
// basic single-field query
43-
Arguments.of(
44-
"{\"field\":{\"$eq\":\"foo\"}}",
45-
List.of(Pair.of("field", "{\"$eq\":\"foo\"}")),
46-
List.of(Triple.of("field", "$eq", "\"foo\""))
47-
),
48-
Arguments.of(
49-
"{\"_version\":\"1\", \"field\":{\"$eq\":\"foo\"}}",
50-
List.of(Pair.of("field", "{\"$eq\":\"foo\"}")),
51-
List.of(Triple.of("field", "$eq", "\"foo\""))
52-
),
51+
Arguments.of("{\"field\":{\"$eq\":\"foo\"}}", List.of(callWith("field", "$eq", "\"foo\""))),
52+
Arguments.of("{\"_version\":\"1\", \"field\":{\"$eq\":\"foo\"}}", List.of(callWith("field", "$eq", "\"foo\""))),
5353
// multi-field query, without $and
5454
Arguments.of(
5555
"""
@@ -60,14 +60,9 @@ static List<Arguments> functionCallTestCases() {
6060
}
6161
""",
6262
List.of(
63-
Pair.of("field1", "{\"$eq\":\"foo\"}"),
64-
Pair.of("field2", "{\"$le\":\"bar\"}"),
65-
Pair.of("field3", "{\"$ne\":\"baz\"}")
66-
),
67-
List.of(
68-
Triple.of("field1", "$eq", "\"foo\""),
69-
Triple.of("field2", "$le", "\"bar\""),
70-
Triple.of("field3", "$ne", "\"baz\"")
63+
callWith("field1", "$eq", "\"foo\""),
64+
callWith("field2", "$le", "\"bar\""),
65+
callWith("field3", "$ne", "\"baz\"")
7166
)
7267
),
7368
Arguments.of(
@@ -80,14 +75,9 @@ static List<Arguments> functionCallTestCases() {
8075
}
8176
""",
8277
List.of(
83-
Pair.of("field1", "{\"$eq\":\"foo\"}"),
84-
Pair.of("field2", "{\"$le\":\"bar\"}"),
85-
Pair.of("field3", "{\"$ne\":\"baz\"}")
86-
),
87-
List.of(
88-
Triple.of("field1", "$eq", "\"foo\""),
89-
Triple.of("field2", "$le", "\"bar\""),
90-
Triple.of("field3", "$ne", "\"baz\"")
78+
callWith("field1", "$eq", "\"foo\""),
79+
callWith("field2", "$le", "\"bar\""),
80+
callWith("field3", "$ne", "\"baz\"")
9181
)
9282
),
9383
// multi-operator single-field query
@@ -98,8 +88,7 @@ static List<Arguments> functionCallTestCases() {
9888
"$ge": 100
9989
}}
10090
""",
101-
List.of(Pair.of("field1", "{\"$le\":500,\"$ge\":100}")),
102-
List.of(Triple.of("field1", "$le", "500"), Triple.of("field1", "$ge", "100"))
91+
List.of(callWith("field1", "$le", "500"), callWith("field1", "$ge", "100"))
10392
),
10493
// query with $and
10594
Arguments.of(
@@ -109,8 +98,7 @@ static List<Arguments> functionCallTestCases() {
10998
{ "field": {"$ne": "bar"} }
11099
]}
111100
""",
112-
List.of(Pair.of("field", "{\"$ne\":\"foo\"}"), Pair.of("field", "{\"$ne\":\"bar\"}")),
113-
List.of(Triple.of("field", "$ne", "\"foo\""), Triple.of("field", "$ne", "\"bar\""))
101+
List.of(callWith("field", "$ne", "\"foo\""), callWith("field", "$ne", "\"bar\""))
114102
),
115103
// putting everything together
116104
Arguments.of(
@@ -133,56 +121,103 @@ static List<Arguments> functionCallTestCases() {
133121
}
134122
""",
135123
List.of(
136-
Pair.of("field1", "{\"$eq\":\"foo\"}"),
137-
Pair.of("field2", "{\"$le\":\"bar\"}"),
138-
Pair.of("field3", "{\"$ne\":\"baz\",\"$gt\":100}"),
139-
Pair.of("field4", "{\"$ne\":\"foo\"}"),
140-
Pair.of("field5", "{\"$ne\":\"bar\",\"$lt\":100}")
141-
),
124+
callWith("field1", "$eq", "\"foo\""),
125+
callWith("field2", "$le", "\"bar\""),
126+
callWith("field3", "$ne", "\"baz\""),
127+
callWith("field3", "$gt", "100"),
128+
callWith("field4", "$ne", "\"foo\""),
129+
callWith("field5", "$ne", "\"bar\""),
130+
callWith("field5", "$lt", "100")
131+
)
132+
),
133+
// basic nested source
134+
Arguments.of(
135+
"{\"sourceA.field\":{\"$eq\":\"foo\"}}",
136+
List.of(callWith("sourceA.field", "$eq", "\"foo\""), callWith(UUID_A, "sourceA.", "field", "$eq", "\"foo\""))
137+
),
138+
// doubly nested source
139+
Arguments.of(
140+
"{\"sourceB.sourceC.field\":{\"$eq\":\"foo\"}}",
141+
List.of(
142+
callWith(TEST_UUID, "", "sourceB.sourceC.field", "$eq", "\"foo\""),
143+
callWith(UUID_B, "sourceB.", "sourceC.field", "$eq", "\"foo\""),
144+
callWith(UUID_C, "sourceB.sourceC.", "field", "$eq", "\"foo\"")
145+
)
146+
),
147+
// no nested source match, so we can't traverse inside
148+
Arguments.of(
149+
"""
150+
{
151+
"nested.field":{"$eq":"foo"},
152+
"sourceB.garbage.field":{"$eq":"bar"}
153+
}
154+
""",
155+
List.of(
156+
callWith(TEST_UUID, "", "nested.field", "$eq", "\"foo\""),
157+
callWith(TEST_UUID, "", "sourceB.garbage.field", "$eq", "\"bar\""),
158+
callWith(UUID_B, "sourceB.", "garbage.field", "$eq", "\"bar\"")
159+
)
160+
),
161+
// early breaks. since the outer (composite'd) field was changed, we shouldn't keep traversing
162+
// (e.g. migration applies to composite specifically and/or has composite-specific logic)
163+
// these are configured to perform the $ACTION with exactly one outer source
164+
Arguments.of(
165+
"""
166+
{
167+
"sourceB.$REMOVE":{"$eq":"foo"},
168+
"sourceB.$CHANGE":{"$eq":"bar"},
169+
"sourceB.$ADD":{"$eq":"boo"},
170+
"sourceB.$BREAK":{"$eq":"baz"},
171+
"sourceB.sourceC.$WARN":{"$eq":"baz"}
172+
}
173+
""",
142174
List.of(
143-
Triple.of("field1", "$eq", "\"foo\""),
144-
Triple.of("field2", "$le", "\"bar\""),
145-
Triple.of("field3", "$ne", "\"baz\""),
146-
Triple.of("field3", "$gt", "100"),
147-
Triple.of("field4", "$ne", "\"foo\""),
148-
Triple.of("field5", "$ne", "\"bar\""),
149-
Triple.of("field5", "$lt", "100")
175+
callWith("sourceB.$REMOVE", "$eq", "\"foo\""),
176+
callWith("sourceB.$CHANGE", "$eq", "\"bar\""),
177+
callWith("sourceB.$ADD", "$eq", "\"boo\""),
178+
callWith("sourceB.$BREAK", "$eq", "\"baz\""),
179+
callWith("sourceB.sourceC.$WARN", "$eq", "\"baz\""),
180+
callWith(UUID_B, "sourceB.", "sourceC.$WARN", "$eq", "\"baz\"")
150181
)
151182
)
152183
);
153184
}
154185

155186
@ParameterizedTest
156-
@MethodSource("functionCallTestCases")
157-
void testMigrateAndReshapeFqlFunctionCalls(
158-
String query,
159-
List<Pair<String, String>> unused,
160-
List<Triple<String, String, String>> fieldArguments
161-
) {
162-
List<Triple<String, String, String>> fieldArgumentsLeftToGet = new ArrayList<>(fieldArguments);
187+
@MethodSource("migrateFqlFunctionCallTestCases")
188+
void testMigrateFqlFunctionCalls(String query, List<MigratableFqlFieldAndCondition> fieldArguments) {
189+
List<MigratableFqlFieldAndCondition> fieldArgumentsLeftToGet = new ArrayList<>(fieldArguments);
163190

164191
MigrationUtils.migrateFql(
165192
TEST_UUID,
166193
query,
167-
original -> {
194+
(MigratableFqlFieldAndCondition original) -> {
195+
assertThat(original.entityTypeId(), is(notNullValue()));
196+
assertThat(original.fieldPrefix(), is(notNullValue()));
168197
assertThat(original.field(), is(notNullValue()));
169198
assertThat(original.operator(), is(notNullValue()));
170199
assertThat(original.value(), is(notNullValue()));
171200

172-
Triple<String, String, String> actual = Triple.of(
173-
original.field(),
174-
original.operator(),
175-
original.value().toString()
176-
);
177-
if (fieldArgumentsLeftToGet.contains(actual)) {
178-
fieldArgumentsLeftToGet.remove(actual);
201+
if (fieldArgumentsLeftToGet.contains(original)) {
202+
fieldArgumentsLeftToGet.remove(original);
179203
} else {
180-
fail("Unexpected field transformation call: " + actual.getLeft() + " -> " + actual.getRight());
204+
fail("Unexpected field transformation call: " + original);
181205
}
182206

183-
return SingleFieldMigrationResult.removed();
207+
if (original.field().matches("^\\w+\\.\\$REMOVE.*$")) {
208+
return SingleFieldMigrationResult.removed();
209+
} else if (original.field().matches("^\\w+\\.\\$CHANGE.*$")) {
210+
return SingleFieldMigrationResult.withField(original.withField("new_field"));
211+
} else if (original.field().matches("^\\w+\\.\\$ADD.*$")) {
212+
return new SingleFieldMigrationResult<>(List.of(original, original), List.of(), false);
213+
} else if (original.field().matches("^\\w+\\.\\$BREAK.*$")) {
214+
return SingleFieldMigrationResult.noop(original).withHadBreakingChange(true);
215+
} else if (original.field().matches("^\\w+\\.\\$WARN.*$")) {
216+
return SingleFieldMigrationResult.noop(original).withWarnings(List.of(RemovedFieldWarning.builder().build()));
217+
}
218+
return SingleFieldMigrationResult.noop(original);
184219
},
185-
EMPTY_SOURCE_MAP
220+
DUMMY_SOURCE_MAP
186221
);
187222

188223
assertThat(fieldArgumentsLeftToGet, is(empty()));
@@ -255,6 +290,43 @@ void testReshapeWithMultipleFields() {
255290
);
256291
}
257292

293+
@Test
294+
void testReshapeWithNestedSourceField() {
295+
assertThat(
296+
MigrationUtils
297+
.migrateFql(
298+
TEST_UUID,
299+
"{\"_version\":\"old\",\"sourceB.sourceC.field\":{\"$eq\": 123}}",
300+
original -> {
301+
if (original.equals(callWith("sourceB.sourceC.field", "$eq", "123"))) {
302+
return SingleFieldMigrationResult.noop(original); // noop
303+
} else if (original.equals(callWith(UUID_B, "sourceB.", "sourceC.field", "$eq", "123"))) {
304+
// change is triggered on sourceB level, replacing sourceC.field with these.
305+
// sourceC will no longer be referenced directly in the resulting query
306+
return new SingleFieldMigrationResult<>(
307+
List.of(
308+
original.withField("new1").withOperator("$a").withValue(new TextNode("aaa")),
309+
original.withField("new2").withOperator("$b").withValue(new TextNode("bbb"))
310+
),
311+
List.of(),
312+
false
313+
);
314+
} else {
315+
fail("Unexpected field transformation call: " + original);
316+
return null;
317+
}
318+
},
319+
DUMMY_SOURCE_MAP
320+
)
321+
.result(),
322+
is(
323+
equalTo(
324+
"{\"_version\":\"old\",\"$and\":[{\"sourceB.new1\":{\"$a\":\"aaa\"}},{\"sourceB.new2\":{\"$b\":\"bbb\"}}]}"
325+
)
326+
)
327+
);
328+
}
329+
258330
@Test
259331
void testInvalidJson() {
260332
assertThrows(
@@ -328,4 +400,19 @@ void testSingleSegment() {
328400
assertTrue(MigrationUtils.compareVersions("b", "a") > 0);
329401
assertEquals(0, MigrationUtils.compareVersions("x", "x"));
330402
}
403+
404+
private static MigratableFqlFieldAndCondition callWith(String field, String op, String value) {
405+
return callWith(TEST_UUID, "", field, op, value);
406+
}
407+
408+
@SneakyThrows(IOException.class)
409+
private static MigratableFqlFieldAndCondition callWith(
410+
UUID entityId,
411+
String fieldPrefix,
412+
String field,
413+
String op,
414+
String value
415+
) {
416+
return new MigratableFqlFieldAndCondition(entityId, fieldPrefix, field, op, new ObjectMapper().readTree(value));
417+
}
331418
}

0 commit comments

Comments
 (0)