Skip to content

Commit 963ad67

Browse files
authored
Merge pull request #4 from antkorwin/feature/expected-data-set
Add ExpectedDataSet annotation
2 parents 8998467 + 63d2f0b commit 963ad67

36 files changed

+1945
-89
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.antkorwin.springtestmongo.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* You can use this annotation in tests
10+
* to check a state of the mongodb after the test execution.
11+
*
12+
* After test execution, all document collections will check
13+
* to match to expected data set in the selected file.
14+
*
15+
* @author Korovin Anatoliy
16+
*/
17+
@Retention(RetentionPolicy.RUNTIME)
18+
@Target(ElementType.METHOD)
19+
public @interface ExpectedMongoDataSet {
20+
21+
/**
22+
* Path to the file with an expected data set (after test execution)
23+
*
24+
* @return path to file with an expected data set
25+
*/
26+
String value();
27+
}

src/main/java/com/antkorwin/springtestmongo/internal/DataSet.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
*
99
* @author Korovin Anatoliy
1010
*/
11-
interface DataSet {
11+
public interface DataSet {
1212

1313
/**
1414
* Read the data set from some kind of source
1515
*
1616
* @return map with the data set,
1717
* map looks like this:
18-
* "org.package....FirstDocument" : [FirstDocument doc1, FirstDocument doc2, FirstDocument doc3],
19-
* "org.package....SecondDocument" : [SecondDocument Doc1, SecondDocument Doc2, SecondDocument Doc3]
18+
* "org.package....FirstDocument" : [{"id":"1", "field":"aaa"}, {"id":"2", "field":"bbb"}],
19+
* "org.package....SecondDocument" : [{"id":"1", "value":"123"}, {"id":"2", "value":"456"}]
2020
*/
21-
Map<String, List<?>> read();
21+
Map<String, List<Map<String, Object>>> read();
2222
}

src/main/java/com/antkorwin/springtestmongo/internal/JsonImport.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ class JsonImport implements DataSet {
2828
}
2929

3030
@Override
31-
public Map<String, List<?>> read() {
31+
public Map<String, List<Map<String, Object>>> read() {
3232

3333
String content = text.read();
3434
try {
35-
return objectMapper.readValue(content, new TypeReference<Map<String, List<?>>>() {
36-
});
35+
return objectMapper.readValue(content,
36+
new TypeReference<Map<String, List<Map<String, Object>>>>() {});
3737
} catch (IOException e) {
3838
e.printStackTrace();
3939
throw new InternalException(JSON_PARSING_ERROR, e);

src/main/java/com/antkorwin/springtestmongo/internal/MongoDataExport.java

+15-6
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import com.antkorwin.commonutils.exceptions.InternalException;
44
import com.antkorwin.commonutils.validation.Guard;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
56
import org.bson.Document;
67
import org.springframework.data.mongodb.core.MongoTemplate;
78

89
import java.util.Collections;
910
import java.util.HashMap;
1011
import java.util.List;
1112
import java.util.Map;
13+
import java.util.stream.Collectors;
1214

1315
import static com.antkorwin.springtestmongo.errorinfo.MongoDbErrorInfo.MONGO_TEMPLATE_IS_MANDATORY;
1416

@@ -20,15 +22,17 @@
2022
class MongoDataExport implements DataSet {
2123

2224
private final MongoTemplate mongoTemplate;
25+
private final ObjectMapper objectMapper;
2326

2427
MongoDataExport(MongoTemplate mongoTemplate) {
2528
Guard.check(mongoTemplate != null, InternalException.class, MONGO_TEMPLATE_IS_MANDATORY);
2629
this.mongoTemplate = mongoTemplate;
30+
this.objectMapper = new ObjectMapper();
2731
}
2832

2933
@Override
30-
public Map<String, List<?>> read() {
31-
Map<String, List<?>> map = new HashMap<>();
34+
public Map<String, List<Map<String, Object>>> read() {
35+
Map<String, List<Map<String, Object>>> map = new HashMap<>();
3236

3337
for (String name : mongoTemplate.getCollectionNames()) {
3438
map.put(getEntityClassName(name), getDataSet(name));
@@ -37,7 +41,7 @@ public Map<String, List<?>> read() {
3741
return map;
3842
}
3943

40-
private List<?> getDataSet(String collectionName) {
44+
private List<Map<String, Object>> getDataSet(String collectionName) {
4145

4246
Document first = mongoTemplate.getCollection(collectionName)
4347
.find(Document.class)
@@ -49,9 +53,14 @@ private List<?> getDataSet(String collectionName) {
4953
try {
5054
String className = (String) first.get("_class");
5155
Class<?> aClass = Class.forName(className);
52-
return mongoTemplate.findAll(aClass);
53-
}
54-
catch (ClassNotFoundException e) {
56+
57+
return mongoTemplate.findAll(aClass)
58+
.stream()
59+
.map(e -> objectMapper.convertValue(e, Map.class))
60+
.map(e -> (Map<String,Object>)e)
61+
.collect(Collectors.toList());
62+
63+
} catch (ClassNotFoundException e) {
5564
e.printStackTrace();
5665
throw new InternalException(e);
5766
}

src/main/java/com/antkorwin/springtestmongo/internal/MongoDbTest.java

+14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.antkorwin.commonutils.exceptions.InternalException;
44
import com.antkorwin.commonutils.validation.Guard;
5+
import com.antkorwin.springtestmongo.internal.expect.MatchDataSets;
6+
57
import org.springframework.data.mongodb.core.MongoTemplate;
68

79
import static com.antkorwin.springtestmongo.errorinfo.MongoDbErrorInfo.MONGO_TEMPLATE_IS_MANDATORY;
@@ -38,4 +40,16 @@ public void exportTo(String fileName) {
3840
public void importFrom(String fileName) {
3941
new MongoDataImport(mongoTemplate).importFrom(new JsonImport(new ImportFile(fileName)));
4042
}
43+
44+
/**
45+
* Check data in the mongodb,
46+
* try to match data from the DB to loaded from file data set.
47+
*
48+
* @param fileName path to file with an expected data set
49+
*/
50+
public void expect(String fileName) {
51+
DataSet dataSet = new JsonImport(new ImportFile(fileName));
52+
DataSet mongoData = new MongoDataExport(mongoTemplate);
53+
new MatchDataSets(mongoData, dataSet).check();
54+
}
4155
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.antkorwin.springtestmongo.internal.expect;
2+
3+
import com.antkorwin.commonutils.exceptions.InternalException;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.Map;
10+
import java.util.Set;
11+
import java.util.stream.Collectors;
12+
import java.util.stream.IntStream;
13+
14+
/**
15+
* Evaluate the IndexedGraph and
16+
* assert that all patterns applied to any data record.
17+
*/
18+
public class AssertGraph {
19+
20+
private final IndexedGraph indexGraph;
21+
private final ObjectMapper objectMapper = new ObjectMapper();
22+
private boolean failed = false;
23+
private List<String> errors = new ArrayList<>();
24+
25+
26+
public AssertGraph(Graph graph) {
27+
this.indexGraph = new IndexedGraph(graph);
28+
}
29+
30+
public void doAssert() {
31+
validateDataRecords(indexGraph.evaluateDataIndexes());
32+
validatePatterns(indexGraph.evaluatePatternIndexes());
33+
if (failed) {
34+
throw new Error("\nExpectedDataSet of " + indexGraph.getDocumentName() + " \n\n" +
35+
errors.stream().collect(Collectors.joining("\n")) + "\n");
36+
}
37+
}
38+
39+
private void validateDataRecords(Set<Integer> indexes) {
40+
if (indexes.size() != indexGraph.dataCount()) {
41+
42+
String notFoundDataRecords = IntStream.range(0, indexGraph.dataCount())
43+
.boxed()
44+
.filter(i -> !indexes.contains(i))
45+
.map(indexGraph::getDataRecord)
46+
.map(this::mapToString)
47+
.collect(Collectors.joining("\n"));
48+
49+
error("Not expected: \n" + notFoundDataRecords + "\n");
50+
}
51+
}
52+
53+
54+
private void validatePatterns(Set<Integer> indexes) {
55+
if (indexes.size() != indexGraph.patternCount()) {
56+
57+
String notFoundPatterns = IntStream.range(0, indexGraph.patternCount())
58+
.boxed()
59+
.filter(i -> !indexes.contains(i))
60+
.map(indexGraph::getPattern)
61+
.map(this::mapToString)
62+
.collect(Collectors.joining("\n"));
63+
64+
error("Expected but not found: \n" + notFoundPatterns + "\n");
65+
}
66+
}
67+
68+
private String mapToString(Map<String, Object> stringObjectMap) {
69+
try {
70+
return objectMapper.writeValueAsString(stringObjectMap);
71+
} catch (JsonProcessingException e) {
72+
e.printStackTrace();
73+
throw new InternalException(e);
74+
}
75+
}
76+
77+
private void error(String message) {
78+
failed = true;
79+
errors.add(message);
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.antkorwin.springtestmongo.internal.expect;
2+
3+
import java.util.Map;
4+
5+
/**
6+
* Created on 09.12.2018.
7+
*
8+
* Graph is a model to calculate the matching of data in
9+
* mongodb and patterns in provided data sets, for one
10+
* type of document collection.
11+
*
12+
* @author Korovin Anatoliy
13+
*/
14+
public interface Graph {
15+
16+
/**
17+
* convert graph to matrix
18+
*
19+
* @return matrix view of graph
20+
*/
21+
boolean[][] calculate();
22+
23+
/**
24+
* @return count of data records from mongodb
25+
*/
26+
int dataCount();
27+
28+
/**
29+
* @return count of patterns from data set file
30+
*/
31+
int patternCount();
32+
33+
/**
34+
* retrieve a data record by the index in graph
35+
*
36+
* @param index position of needed data record
37+
* @return data record from mongodb by the index in graph
38+
*/
39+
Map<String, Object> getDataRecord(int index);
40+
41+
/**
42+
* retrieve the pattern from data set file
43+
*
44+
* @param index position of this pattern in the graph
45+
* @return pattern object from a data set file by the index from the graph
46+
*/
47+
Map<String, Object> getPattern(int index);
48+
49+
/**
50+
* @return name of the mongodb document associated to this graph
51+
*/
52+
String getDocumentName();
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.antkorwin.springtestmongo.internal.expect;
2+
3+
import java.util.HashSet;
4+
import java.util.Map;
5+
import java.util.Set;
6+
7+
/**
8+
* Add in graph indexes of successful matched patterns
9+
* and data records.
10+
*/
11+
public class IndexedGraph implements Graph {
12+
13+
private final Graph graph;
14+
private boolean indexReady;
15+
private Set<Integer> patternIndexes;
16+
private Set<Integer> dataIndexes;
17+
18+
public IndexedGraph(Graph graph) {
19+
this.graph = graph;
20+
this.dataIndexes = new HashSet<>();
21+
this.patternIndexes = new HashSet<>();
22+
}
23+
24+
@Override
25+
public boolean[][] calculate() {
26+
return graph.calculate();
27+
}
28+
29+
@Override
30+
public int dataCount() {
31+
return graph.dataCount();
32+
}
33+
34+
@Override
35+
public int patternCount() {
36+
return graph.patternCount();
37+
}
38+
39+
@Override
40+
public Map<String, Object> getDataRecord(int index) {
41+
return graph.getDataRecord(index);
42+
}
43+
44+
@Override
45+
public Map<String, Object> getPattern(int index) {
46+
return graph.getPattern(index);
47+
}
48+
49+
@Override
50+
public String getDocumentName() {
51+
return graph.getDocumentName();
52+
}
53+
54+
/**
55+
* @return set with indexes of patterns used in this graph
56+
*/
57+
public Set<Integer> evaluatePatternIndexes() {
58+
if (!indexReady) {
59+
evaluateIndexes();
60+
}
61+
return patternIndexes;
62+
}
63+
64+
/**
65+
* @return set with indexes of data records used in this graph
66+
*/
67+
public Set<Integer> evaluateDataIndexes() {
68+
if (!indexReady) {
69+
evaluateIndexes();
70+
}
71+
return dataIndexes;
72+
}
73+
74+
/**
75+
* calculate all used indexes of patterns and
76+
* data records matched for this patterns
77+
*/
78+
private void evaluateIndexes() {
79+
80+
boolean[][] matrix = graph.calculate();
81+
82+
for (int i = 0; i < dataCount(); i++) {
83+
for (int j = 0; j < patternCount(); j++) {
84+
if (matrix[i][j]) {
85+
dataIndexes.add(i);
86+
patternIndexes.add(j);
87+
}
88+
}
89+
}
90+
indexReady = true;
91+
}
92+
}

0 commit comments

Comments
 (0)