1+ /*
2+ * Copyright 2025 Flamingock (https://www.flamingock.io)
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+ package io .flamingock .support .domain ;
17+
18+ import io .flamingock .internal .common .core .audit .AuditEntry ;
19+ import io .flamingock .internal .common .core .audit .AuditTxType ;
20+
21+ import java .time .LocalDateTime ;
22+ import java .util .List ;
23+
24+ import static org .junit .jupiter .api .Assertions .*;
25+
26+ /**
27+ * Comprehensive assertion utilities for AuditEntry testing.
28+ * Provides systematic verification of all audit entry fields to ensure
29+ * complete correctness of audit persistence across different storage implementations.
30+ *
31+ * <p><strong>Usage Examples:</strong></p>
32+ * <pre>{@code
33+ * // Complete audit entry verification
34+ * AuditEntryExpectation expected = auditEntry()
35+ * .withTaskId("test-change")
36+ * .withState(EXECUTED)
37+ * .withTxType(TX_SHARED);
38+ *
39+ * AuditEntryAssertions.assertAuditEntry(actualEntry, expected);
40+ *
41+ * // Quick individual field verification
42+ * AuditEntryAssertions.assertTransactionFields(entry, TX_SHARED, "custom-target-system");
43+ *
44+ * // Multiple entries verification
45+ * AuditEntryAssertions.assertAuditSequence(entries,
46+ * auditEntry().withState(STARTED),
47+ * auditEntry().withState(APPLIED));
48+ * }</pre>
49+ */
50+ public class AuditEntryAssertions {
51+
52+ /**
53+ * Comprehensive verification of all audit entry fields against expectations.
54+ * This is the primary method for complete audit entry validation.
55+ *
56+ * @param actual the actual audit entry to verify
57+ * @param expected the expected values
58+ */
59+ public static void assertAuditEntry (AuditEntry actual , AuditEntryExpectation expected ) {
60+ assertNotNull (actual , "Audit entry should not be null" );
61+
62+ // Identity fields
63+ assertEquals (expected .getExpectedExecutionId (), actual .getExecutionId (),
64+ "ExecutionId mismatch" );
65+
66+
67+ assertEquals (expected .getExpectedStageId (), actual .getStageId (),
68+ "StageId mismatch" );
69+
70+
71+ assertEquals (expected .getExpectedTaskId (), actual .getTaskId (),
72+ "TaskId mismatch" );
73+
74+
75+ // Metadata fields
76+ assertEquals (expected .getExpectedAuthor (), actual .getAuthor (),
77+ "Author mismatch" );
78+
79+
80+ // Timestamp verification (flexible)
81+ assertNotNull (actual .getCreatedAt (), "CreatedAt should not be null" );
82+
83+ if (expected .getExpectedCreatedAt () != null ) {
84+ assertEquals (expected .getExpectedCreatedAt (), actual .getCreatedAt (),
85+ "CreatedAt exact match failed" );
86+ } else if (expected .getTimestampAfter () != null && expected .getTimestampBefore () != null ) {
87+ assertTrue (actual .getCreatedAt ().isAfter (expected .getTimestampAfter ()) ||
88+ actual .getCreatedAt ().isEqual (expected .getTimestampAfter ()),
89+ "CreatedAt should be after " + expected .getTimestampAfter ());
90+ assertTrue (actual .getCreatedAt ().isBefore (expected .getTimestampBefore ()) ||
91+ actual .getCreatedAt ().isEqual (expected .getTimestampBefore ()),
92+ "CreatedAt should be before " + expected .getTimestampBefore ());
93+ }
94+
95+
96+ // State fields
97+ if (expected .getExpectedState () != null ) {
98+ assertEquals (expected .getExpectedState (), actual .getState (),
99+ "State mismatch" );
100+ }
101+
102+ if (expected .getExpectedType () != null ) {
103+ assertEquals (expected .getExpectedType (), actual .getType (),
104+ "ExecutionType mismatch" );
105+ }
106+
107+ // Execution fields
108+ if (expected .getExpectedClassName () != null ) {
109+ assertEquals (expected .getExpectedClassName (), actual .getClassName (),
110+ "ClassName mismatch" );
111+ }
112+
113+ if (expected .getExpectedMethodName () != null ) {
114+ assertEquals (expected .getExpectedMethodName (), actual .getMethodName (),
115+ "MethodName mismatch" );
116+ }
117+
118+ if (expected .getExpectedMetadata () != null ) {
119+ assertEquals (expected .getExpectedMetadata (), actual .getMetadata (),
120+ "Metadata mismatch" );
121+ }
122+
123+ // Performance fields
124+ assertEquals (expected .getExpectedExecutionMillis ().longValue (), actual .getExecutionMillis (),
125+ "ExecutionMillis mismatch" );
126+
127+
128+ assertEquals (expected .getExpectedExecutionHostname (), actual .getExecutionHostname (),
129+ "ExecutionHostname mismatch" );
130+
131+
132+ // Error fields
133+ if (expected .getExpectedErrorTrace () != null ) {
134+ assertEquals (expected .getExpectedErrorTrace (), actual .getErrorTrace (),
135+ "ErrorTrace mismatch" );
136+ }
137+
138+ // System fields
139+ if (expected .getExpectedSystemChange () != null ) {
140+ assertEquals (expected .getExpectedSystemChange (), actual .getSystemChange (),
141+ "SystemChange mismatch" );
142+ }
143+
144+ // Transaction fields
145+ if (expected .getExpectedTxType () != null ) {
146+ assertEquals (expected .getExpectedTxType (), actual .getTxType (),
147+ "TxType mismatch" );
148+ }
149+
150+ if (expected .getExpectedTargetSystemId () != null ) {
151+ assertEquals (expected .getExpectedTargetSystemId (), actual .getTargetSystemId (),
152+ "TargetSystemId mismatch" );
153+ }
154+ }
155+
156+ /**
157+ * Quick verification of basic audit entry identity fields.
158+ *
159+ * @param entry the audit entry to verify
160+ * @param expectedTaskId expected task ID
161+ * @param expectedAuthor expected author
162+ * @param expectedState expected audit entry state
163+ */
164+ //TODO add author check, when CodeChangeTestDefinition adds it
165+ public static void assertBasicFields (AuditEntry entry , String expectedTaskId , String expectedAuthor ,
166+ AuditEntry .Status expectedState ) {
167+ assertNotNull (entry , "Audit entry should not be null" );
168+ assertEquals (expectedTaskId , entry .getTaskId (), "TaskId mismatch" );
169+ // assertEquals(expectedAuthor, entry.getAuthor(), "Author mismatch");
170+ assertEquals (expectedState , entry .getState (), "State mismatch" );
171+ }
172+
173+ /**
174+ * Quick verification of execution-related fields.
175+ *
176+ * @param entry the audit entry to verify
177+ * @param expectedClassName expected class name
178+ * @param expectedMethodName expected method name
179+ * @param expectedType expected execution type
180+ */
181+ public static void assertExecutionFields (AuditEntry entry , String expectedClassName ,
182+ String expectedMethodName , AuditEntry .ExecutionType expectedType ) {
183+ assertNotNull (entry , "Audit entry should not be null" );
184+ assertEquals (expectedClassName , entry .getClassName (), "ClassName mismatch" );
185+ assertEquals (expectedMethodName , entry .getMethodName (), "MethodName mismatch" );
186+ assertEquals (expectedType , entry .getType (), "ExecutionType mismatch" );
187+ }
188+
189+ /**
190+ * Quick verification of transaction-related fields.
191+ *
192+ * @param entry the audit entry to verify
193+ * @param expectedTxType expected transaction type
194+ * @param expectedTargetSystemId expected target system ID
195+ */
196+ public static void assertTransactionFields (AuditEntry entry , AuditTxType expectedTxType ,
197+ String expectedTargetSystemId ) {
198+ assertNotNull (entry , "Audit entry should not be null" );
199+ assertEquals (expectedTxType , entry .getTxType (), "TxType mismatch" );
200+ assertEquals (expectedTargetSystemId , entry .getTargetSystemId (), "TargetSystemId mismatch" );
201+ }
202+
203+ /**
204+ * Quick verification of timing and performance fields.
205+ *
206+ * @param entry the audit entry to verify
207+ * @param timestampAfter expected timestamp should be after this time (can be null)
208+ * @param timestampBefore expected timestamp should be before this time (can be null)
209+ */
210+ public static void assertTimingFields (AuditEntry entry , LocalDateTime timestampAfter ,
211+ LocalDateTime timestampBefore ) {
212+ assertNotNull (entry , "Audit entry should not be null" );
213+ assertNotNull (entry .getCreatedAt (), "CreatedAt should not be null" );
214+
215+ if (timestampAfter != null ) {
216+ assertTrue (entry .getCreatedAt ().isAfter (timestampAfter ) ||
217+ entry .getCreatedAt ().isEqual (timestampAfter ),
218+ "CreatedAt should be after " + timestampAfter );
219+ }
220+
221+ if (timestampBefore != null ) {
222+ assertTrue (entry .getCreatedAt ().isBefore (timestampBefore ) ||
223+ entry .getCreatedAt ().isEqual (timestampBefore ),
224+ "CreatedAt should be before " + timestampBefore );
225+ }
226+
227+ assertTrue (entry .getExecutionMillis () >= 0 , "ExecutionMillis should be non-negative" );
228+ }
229+
230+ /**
231+ * Quick verification of system-related fields.
232+ *
233+ * @param entry the audit entry to verify
234+ * @param expectedSystemChange expected system change flag
235+ * @param expectedHostname expected execution hostname (can be null)
236+ */
237+ public static void assertSystemFields (AuditEntry entry , boolean expectedSystemChange ,
238+ String expectedHostname ) {
239+ assertNotNull (entry , "Audit entry should not be null" );
240+ assertEquals (expectedSystemChange , entry .getSystemChange (), "SystemChange mismatch" );
241+
242+ if (expectedHostname != null ) {
243+ assertEquals (expectedHostname , entry .getExecutionHostname (), "ExecutionHostname mismatch" );
244+ }
245+ }
246+
247+ /**
248+ * Verification of multiple audit entries in sequence.
249+ * Useful for testing complete execution flows (STARTED to APPLIED, etc.)
250+ *
251+ * @param actualEntries list of actual audit entries from the system
252+ * @param expectedEntries expected audit entry expectations to verify against
253+ */
254+ public static void assertAuditSequence (List <AuditEntry > actualEntries ,
255+ AuditEntryExpectation ... expectedEntries ) {
256+ assertNotNull (actualEntries , "Audit entries list should not be null" );
257+ assertEquals (expectedEntries .length , actualEntries .size (),
258+ "Expected " + expectedEntries .length + " audit entries, but got " + actualEntries .size ());
259+
260+ for (int i = 0 ; i < expectedEntries .length ; i ++) {
261+ assertAuditEntry (actualEntries .get (i ), expectedEntries [i ]);
262+ }
263+ }
264+
265+ /**
266+ * Verification that all entries in a sequence have the same core identity fields.
267+ * Useful for verifying that STARTED and APPLIED entries belong to the same execution.
268+ *
269+ * @param entries list of audit entries to verify belong to same execution
270+ */
271+ public static void assertSameExecution (List <AuditEntry > entries ) {
272+ assertNotNull (entries , "Audit entries list should not be null" );
273+ assertTrue (entries .size () >= 2 , "Need at least 2 entries to verify same execution" );
274+
275+ String expectedExecutionId = entries .get (0 ).getExecutionId ();
276+ String expectedStageId = entries .get (0 ).getStageId ();
277+ String expectedTaskId = entries .get (0 ).getTaskId ();
278+
279+ for (AuditEntry entry : entries ) {
280+ assertEquals (expectedExecutionId , entry .getExecutionId (),
281+ "All entries should have same executionId" );
282+ assertEquals (expectedStageId , entry .getStageId (),
283+ "All entries should have same stageId" );
284+ assertEquals (expectedTaskId , entry .getTaskId (),
285+ "All entries should have same taskId" );
286+ }
287+ }
288+
289+ /**
290+ * Quick verification that critical audit fields are not null/empty.
291+ * Useful for basic audit entry completeness checks.
292+ *
293+ * @param entry the audit entry to verify for completeness
294+ */
295+ public static void assertAuditEntryCompleteness (AuditEntry entry ) {
296+ assertNotNull (entry , "Audit entry should not be null" );
297+
298+ // Critical fields that should never be null/empty
299+ assertNotNull (entry .getExecutionId (), "ExecutionId should not be null" );
300+ assertFalse (entry .getExecutionId ().trim ().isEmpty (), "ExecutionId should not be empty" );
301+
302+ assertNotNull (entry .getStageId (), "StageId should not be null" );
303+ assertFalse (entry .getStageId ().trim ().isEmpty (), "StageId should not be empty" );
304+
305+ assertNotNull (entry .getTaskId (), "TaskId should not be null" );
306+ assertFalse (entry .getTaskId ().trim ().isEmpty (), "TaskId should not be empty" );
307+
308+ assertNotNull (entry .getAuthor (), "Author should not be null" );
309+ assertFalse (entry .getAuthor ().trim ().isEmpty (), "Author should not be empty" );
310+
311+ assertNotNull (entry .getCreatedAt (), "CreatedAt should not be null" );
312+ assertNotNull (entry .getState (), "State should not be null" );
313+ assertNotNull (entry .getType (), "ExecutionType should not be null" );
314+ assertNotNull (entry .getTxType (), "TxType should not be null" );
315+
316+ // Fields that can be null but if present should not be empty strings
317+ if (entry .getClassName () != null ) {
318+ assertFalse (entry .getClassName ().trim ().isEmpty (), "ClassName should not be empty if present" );
319+ }
320+
321+ if (entry .getMethodName () != null ) {
322+ assertFalse (entry .getMethodName ().trim ().isEmpty (), "MethodName should not be empty if present" );
323+ }
324+
325+ if (entry .getTargetSystemId () != null ) {
326+ assertFalse (entry .getTargetSystemId ().trim ().isEmpty (), "TargetSystemId should not be empty if present" );
327+ }
328+ }
329+ }
0 commit comments