Skip to content

Commit 4cea3ac

Browse files
authored
chore: add AuditEntryExpectation.compareWith() (#756)
1 parent 85cb196 commit 4cea3ac

File tree

1 file changed

+113
-45
lines changed

1 file changed

+113
-45
lines changed

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

Lines changed: 113 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@
2222
import io.flamingock.internal.common.core.audit.AuditEntry;
2323
import io.flamingock.support.stages.ThenStage;
2424
import io.flamingock.support.stages.WhenStage;
25+
import io.flamingock.support.validation.error.FieldMismatchError;
2526

2627
import java.lang.annotation.Annotation;
2728
import java.lang.reflect.Method;
2829
import java.time.LocalDateTime;
30+
import java.util.ArrayList;
31+
import java.util.List;
32+
import java.util.Objects;
2933

3034
import static io.flamingock.internal.common.core.audit.AuditEntry.Status.APPLIED;
3135
import static io.flamingock.internal.common.core.audit.AuditEntry.Status.FAILED;
@@ -162,8 +166,6 @@ public static AuditEntryExpectation ROLLBACK_FAILED(String expectedChangeId) {
162166
return new AuditEntryExpectation(expectedChangeId, ROLLBACK_FAILED);
163167
}
164168

165-
// ==================== Class-Based Factory Methods ====================
166-
167169
/**
168170
* Creates an expectation for a successfully applied change by extracting
169171
* metadata from the change class annotations.
@@ -296,8 +298,6 @@ private static String findMethodName(Class<?> changeClass, AuditEntry.Status sta
296298
annotationClass.getSimpleName()));
297299
}
298300

299-
// ==================== Identity Fields ====================
300-
301301
/**
302302
* Sets the expected execution ID for verification.
303303
*
@@ -324,8 +324,6 @@ public AuditEntryExpectation withStageId(String stageId) {
324324
return this;
325325
}
326326

327-
// ==================== Metadata Fields ====================
328-
329327
/**
330328
* Sets the expected author of the change.
331329
*
@@ -368,8 +366,6 @@ public AuditEntryExpectation withTimestampBetween(LocalDateTime after, LocalDate
368366
return this;
369367
}
370368

371-
// ==================== Execution Fields ====================
372-
373369
/**
374370
* Sets the expected fully-qualified class name.
375371
*
@@ -409,8 +405,6 @@ public AuditEntryExpectation withMetadata(Object metadata) {
409405
return this;
410406
}
411407

412-
// ==================== Performance Fields ====================
413-
414408
/**
415409
* Sets the expected execution duration in milliseconds.
416410
*
@@ -437,8 +431,6 @@ public AuditEntryExpectation withExecutionHostname(String hostname) {
437431
return this;
438432
}
439433

440-
// ==================== Error Fields ====================
441-
442434
/**
443435
* Sets the expected error trace for failed changes.
444436
*
@@ -453,8 +445,6 @@ public AuditEntryExpectation withErrorTrace(String errorTrace) {
453445
return this;
454446
}
455447

456-
// ==================== Target System Fields ====================
457-
458448
/**
459449
* Sets the expected target system identifier.
460450
*
@@ -466,50 +456,128 @@ public AuditEntryExpectation withTargetSystemId(String targetSystemId) {
466456
return this;
467457
}
468458

469-
// ==================== Getters (for verification logic) ====================
459+
/**
460+
* Compares this expectation against an actual audit entry.
461+
*
462+
* <p>Returns a list of field mismatches (empty if all expected fields match).
463+
* Only fields with non-null expected values are verified, except for
464+
* {@code changeId} and {@code status} which are always verified.</p>
465+
*
466+
* <p>Timestamp verification supports two modes:</p>
467+
* <ul>
468+
* <li>Exact match: when {@code expectedCreatedAt} is set</li>
469+
* <li>Range match: when {@code timestampAfter} and/or {@code timestampBefore} are set</li>
470+
* </ul>
471+
*
472+
* @param actual the actual audit entry to compare against
473+
* @return list of field mismatch errors (empty if all match)
474+
*/
475+
public List<FieldMismatchError> compareWith(AuditEntry actual) {
476+
List<FieldMismatchError> errors = new ArrayList<>();
470477

471-
/** Returns the expected execution ID. */
472-
public String getExpectedExecutionId() { return expectedExecutionId; }
478+
// Required fields - always verified
479+
if (!expectedChangeId.equals(actual.getTaskId())) {
480+
errors.add(new FieldMismatchError("changeId", expectedChangeId, actual.getTaskId()));
481+
}
473482

474-
/** Returns the expected stage ID. */
475-
public String getExpectedStageId() { return expectedStageId; }
483+
if (expectedState != actual.getState()) {
484+
errors.add(new FieldMismatchError("status",
485+
expectedState.name(),
486+
actual.getState() != null ? actual.getState().name() : null));
487+
}
476488

477-
/** Returns the expected change ID. */
478-
public String getExpectedChangeId() { return expectedChangeId; }
489+
// Optional fields - verified when non-null
490+
if (expectedExecutionId != null && !expectedExecutionId.equals(actual.getExecutionId())) {
491+
errors.add(new FieldMismatchError("executionId", expectedExecutionId, actual.getExecutionId()));
492+
}
479493

480-
/** Returns the expected author. */
481-
public String getExpectedAuthor() { return expectedAuthor; }
494+
if (expectedStageId != null && !expectedStageId.equals(actual.getStageId())) {
495+
errors.add(new FieldMismatchError("stageId", expectedStageId, actual.getStageId()));
496+
}
482497

483-
/** Returns the expected creation timestamp. */
484-
public LocalDateTime getExpectedCreatedAt() { return expectedCreatedAt; }
498+
if (expectedAuthor != null && !expectedAuthor.equals(actual.getAuthor())) {
499+
errors.add(new FieldMismatchError("author", expectedAuthor, actual.getAuthor()));
500+
}
485501

486-
/** Returns the expected audit entry status. */
487-
public AuditEntry.Status getExpectedState() { return expectedState; }
502+
if (expectedClassName != null && !expectedClassName.equals(actual.getClassName())) {
503+
errors.add(new FieldMismatchError("className", expectedClassName, actual.getClassName()));
504+
}
488505

489-
/** Returns the expected class name. */
490-
public String getExpectedClassName() { return expectedClassName; }
506+
if (expectedMethodName != null && !expectedMethodName.equals(actual.getMethodName())) {
507+
errors.add(new FieldMismatchError("methodName", expectedMethodName, actual.getMethodName()));
508+
}
491509

492-
/** Returns the expected method name. */
493-
public String getExpectedMethodName() { return expectedMethodName; }
510+
if (expectedMetadata != null && !Objects.equals(expectedMetadata, actual.getMetadata())) {
511+
errors.add(new FieldMismatchError("metadata",
512+
String.valueOf(expectedMetadata),
513+
String.valueOf(actual.getMetadata())));
514+
}
494515

495-
/** Returns the expected metadata. */
496-
public Object getExpectedMetadata() { return expectedMetadata; }
516+
if (expectedExecutionMillis != null && expectedExecutionMillis != actual.getExecutionMillis()) {
517+
errors.add(new FieldMismatchError("executionMillis",
518+
String.valueOf(expectedExecutionMillis),
519+
String.valueOf(actual.getExecutionMillis())));
520+
}
497521

498-
/** Returns the expected execution duration in milliseconds. */
499-
public Long getExpectedExecutionMillis() { return expectedExecutionMillis; }
522+
if (expectedExecutionHostname != null && !expectedExecutionHostname.equals(actual.getExecutionHostname())) {
523+
errors.add(new FieldMismatchError("executionHostname", expectedExecutionHostname, actual.getExecutionHostname()));
524+
}
500525

501-
/** Returns the expected execution hostname. */
502-
public String getExpectedExecutionHostname() { return expectedExecutionHostname; }
526+
if (expectedErrorTrace != null && !expectedErrorTrace.equals(actual.getErrorTrace())) {
527+
errors.add(new FieldMismatchError("errorTrace", expectedErrorTrace, actual.getErrorTrace()));
528+
}
503529

504-
/** Returns the expected error trace. */
505-
public String getExpectedErrorTrace() { return expectedErrorTrace; }
530+
if (expectedTargetSystemId != null && !expectedTargetSystemId.equals(actual.getTargetSystemId())) {
531+
errors.add(new FieldMismatchError("targetSystemId", expectedTargetSystemId, actual.getTargetSystemId()));
532+
}
506533

507-
/** Returns the expected target system ID. */
508-
public String getExpectedTargetSystemId() { return expectedTargetSystemId; }
534+
errors.addAll(compareTimestamp(actual));
509535

510-
/** Returns the lower bound for timestamp range verification. */
511-
public LocalDateTime getTimestampAfter() { return timestampAfter; }
536+
return errors;
537+
}
538+
539+
private List<FieldMismatchError> compareTimestamp(AuditEntry actual) {
540+
List<FieldMismatchError> errors = new ArrayList<>();
541+
if (expectedCreatedAt != null) {
542+
// Exact match mode
543+
if (!expectedCreatedAt.equals(actual.getCreatedAt())) {
544+
errors.add(new FieldMismatchError("createdAt",
545+
expectedCreatedAt.toString(),
546+
actual.getCreatedAt() != null ? actual.getCreatedAt().toString() : null));
547+
}
548+
} else if (timestampAfter != null || timestampBefore != null) {
549+
// Range match mode
550+
LocalDateTime actualTimestamp = actual.getCreatedAt();
551+
if (actualTimestamp == null) {
552+
errors.add(new FieldMismatchError("createdAt",
553+
formatTimestampRange(),
554+
null));
555+
} else {
556+
boolean afterOk = timestampAfter == null ||
557+
actualTimestamp.isAfter(timestampAfter) ||
558+
actualTimestamp.isEqual(timestampAfter);
559+
boolean beforeOk = timestampBefore == null ||
560+
actualTimestamp.isBefore(timestampBefore) ||
561+
actualTimestamp.isEqual(timestampBefore);
562+
563+
if (!afterOk || !beforeOk) {
564+
errors.add(new FieldMismatchError("createdAt",
565+
formatTimestampRange(),
566+
actualTimestamp.toString()));
567+
}
568+
}
569+
}
570+
return errors;
571+
}
572+
573+
private String formatTimestampRange() {
574+
if (timestampAfter != null && timestampBefore != null) {
575+
return String.format("between %s and %s", timestampAfter, timestampBefore);
576+
} else if (timestampAfter != null) {
577+
return String.format("after %s", timestampAfter);
578+
} else {
579+
return String.format("before %s", timestampBefore);
580+
}
581+
}
512582

513-
/** Returns the upper bound for timestamp range verification. */
514-
public LocalDateTime getTimestampBefore() { return timestampBefore; }
515583
}

0 commit comments

Comments
 (0)