Skip to content

Commit 6725349

Browse files
feat(test-support): add thenExpectAuditSequenceStrict(#762)
1 parent a1c50b3 commit 6725349

File tree

3 files changed

+381
-7
lines changed

3 files changed

+381
-7
lines changed

core/flamingock-test-support/src/main/java/io/flamingock/support/validation/impl/AuditEntryExpectation.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
* against expected definitions. Users should not interact with this class directly;
3131
* they should use {@link AuditEntryDefinition} instead.</p>
3232
*/
33-
class AuditEntryExpectation {
33+
public class AuditEntryExpectation {
3434

3535
private final AuditEntryDefinition definition;
3636

37-
AuditEntryExpectation(AuditEntryDefinition definition) {
37+
public AuditEntryExpectation(AuditEntryDefinition definition) {
3838
this.definition = definition;
3939
}
4040

@@ -48,7 +48,7 @@ class AuditEntryExpectation {
4848
* @param actual the actual audit entry to compare against
4949
* @return list of field mismatch errors (empty if all match)
5050
*/
51-
List<FieldMismatchError> compareWith(AuditEntry actual) {
51+
public List<FieldMismatchError> compareWith(AuditEntry actual) {
5252
List<FieldMismatchError> errors = new ArrayList<>();
5353

5454
// Required fields - always verified

core/flamingock-test-support/src/main/java/io/flamingock/support/validation/impl/AuditSequenceStrictValidator.java

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,111 @@
1515
*/
1616
package io.flamingock.support.validation.impl;
1717

18+
import io.flamingock.internal.common.core.audit.AuditEntry;
1819
import io.flamingock.internal.common.core.audit.AuditReader;
1920
import io.flamingock.support.domain.AuditEntryDefinition;
2021
import io.flamingock.support.validation.SimpleValidator;
21-
import io.flamingock.support.validation.error.ValidationResult;
22+
import io.flamingock.support.validation.error.*;
2223

24+
import java.util.ArrayList;
2325
import java.util.Arrays;
2426
import java.util.List;
2527
import java.util.stream.Collectors;
2628

29+
/**
30+
* Validator that performs strict sequence validation of audit entries.
31+
*
32+
* <p>This validator verifies that the actual audit entries match the expected
33+
* sequence exactly, both in count and in field values. Checking:</p>
34+
* <ul>
35+
* <li>Exact count match between expected and actual entries</li>
36+
* <li>Strict field-by-field validation for each entry at each index</li>
37+
* <li>Order preservation (expected[0] must match actual[0], etc.)</li>
38+
* </ul>
39+
*/
2740
public class AuditSequenceStrictValidator implements SimpleValidator {
2841

2942
private static final String VALIDATOR_NAME = "Audit Sequence (Strict)";
3043

3144
private final AuditReader auditReader;
3245
private final List<AuditEntryExpectation> expectations;
33-
46+
private final List<AuditEntry> actualEntries;
3447

3548
public AuditSequenceStrictValidator(AuditReader auditReader, AuditEntryDefinition... definitions) {
3649
this.auditReader = auditReader;
3750
this.expectations = Arrays.stream(definitions)
3851
.map(AuditEntryExpectation::new)
3952
.collect(Collectors.toList());
53+
this.actualEntries = auditReader.getAuditHistory();
54+
}
55+
56+
/**
57+
* Internal constructor for direct list initialization (used by tests).
58+
*/
59+
AuditSequenceStrictValidator(List<AuditEntryDefinition> expectedDefinitions, List<AuditEntry> actualEntries, AuditReader auditReader) {
60+
this.expectations = expectedDefinitions != null
61+
? expectedDefinitions.stream()
62+
.map(AuditEntryExpectation::new)
63+
.collect(Collectors.toList())
64+
: new ArrayList<>();
65+
this.auditReader = auditReader;
66+
this.actualEntries = actualEntries != null ? actualEntries : new ArrayList<>();
4067
}
4168

4269
@Override
4370
public ValidationResult validate() {
44-
// TODO: Implement actual validation logic
45-
return ValidationResult.success(VALIDATOR_NAME);
71+
List<ValidationError> allErrors = new ArrayList<>();
72+
73+
int expectedSize = expectations.size();
74+
int actualSize = actualEntries.size();
75+
76+
if (expectedSize != actualSize) {
77+
allErrors.add(new CountMismatchError(getExpectedChangeIds(), getActualChangeIds()));
78+
}
79+
80+
allErrors.addAll(getValidationErrors(expectations, actualEntries));
81+
82+
if (allErrors.isEmpty()) {
83+
return ValidationResult.success(VALIDATOR_NAME);
84+
}
85+
86+
return ValidationResult.failure(VALIDATOR_NAME, allErrors.toArray(new ValidationError[0]));
87+
}
88+
89+
private static List<ValidationError> getValidationErrors(List<AuditEntryExpectation> expectedEntries, List<AuditEntry> actualEntries) {
90+
List<ValidationError> allErrors = new ArrayList<>();
91+
if (expectedEntries.isEmpty()) {
92+
return allErrors;
93+
}
94+
int actualSize = actualEntries.size();
95+
int limit = Math.max(expectedEntries.size(), actualSize);
96+
97+
for (int i = 0; i < limit; i++) {
98+
AuditEntryExpectation expected = i < expectedEntries.size() ? expectedEntries.get(i) : null;
99+
AuditEntry actual = i < actualEntries.size() ? actualEntries.get(i) : null;
100+
if( expected != null && actual != null) {
101+
allErrors.addAll(expected.compareWith(actual));
102+
} else if( expected != null) {
103+
AuditEntryDefinition def = expected.getDefinition();
104+
allErrors.add(new MissingEntryError(i, def.getChangeId(), def.getState()));
105+
} else {
106+
assert actual != null;
107+
allErrors.add(new UnexpectedEntryError(i, actual.getTaskId(), actual.getState()));
108+
}
109+
110+
}
111+
return allErrors;
112+
}
113+
114+
private List<String> getExpectedChangeIds() {
115+
return expectations.stream()
116+
.map(exp -> exp.getDefinition().getChangeId())
117+
.collect(Collectors.toList());
118+
}
119+
120+
private List<String> getActualChangeIds() {
121+
return actualEntries.stream()
122+
.map(AuditEntry::getTaskId)
123+
.collect(Collectors.toList());
46124
}
47125
}

0 commit comments

Comments
 (0)