11/*
2- * Copyright 2023 Flamingock (https://www.flamingock.io)
2+ * Copyright 2025 Flamingock (https://www.flamingock.io)
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1818import io .flamingock .api .annotations .EnableFlamingock ;
1919import io .flamingock .api .annotations .Stage ;
2020import io .flamingock .community .dynamodb .driver .DynamoDBAuditStore ;
21- import io .flamingock .importer .dynamodb .MongockDynamoDBAuditEntry ;
22- import io .flamingock .internal .common .core .audit .AuditEntry ;
21+ import io .flamingock .core .kit .TestKit ;
22+ import io .flamingock .core .kit .audit .AuditTestHelper ;
23+ import io .flamingock .dynamodb .kit .DynamoDBTestKit ;
2324import io .flamingock .internal .common .core .error .FlamingockException ;
24- import io .flamingock .internal .core .builder .FlamingockFactory ;
2525import io .flamingock .internal .core .runner .Runner ;
2626import io .flamingock .support .mongock .annotations .MongockSupport ;
27+ import io .flamingock .targetsystem .dynamodb .DynamoDBTargetSystem ;
28+ import org .junit .jupiter .api .AfterEach ;
2729import org .junit .jupiter .api .Assertions ;
28- import org .junit .jupiter .api .BeforeAll ;
2930import org .junit .jupiter .api .BeforeEach ;
3031import org .junit .jupiter .api .Disabled ;
3132import org .junit .jupiter .api .Test ;
3637import software .amazon .awssdk .auth .credentials .StaticCredentialsProvider ;
3738import software .amazon .awssdk .regions .Region ;
3839import software .amazon .awssdk .services .dynamodb .DynamoDbClient ;
39- import software .amazon .awssdk .services .dynamodb .model .ScanRequest ;
40- import software .amazon .awssdk .services .dynamodb .model .ScanResponse ;
40+ import software .amazon .awssdk .services .dynamodb .model .AttributeDefinition ;
41+ import software .amazon .awssdk .services .dynamodb .model .BillingMode ;
42+ import software .amazon .awssdk .services .dynamodb .model .CreateTableRequest ;
43+ import software .amazon .awssdk .services .dynamodb .model .DescribeTableRequest ;
44+ import software .amazon .awssdk .services .dynamodb .model .DescribeTableResponse ;
45+ import software .amazon .awssdk .services .dynamodb .model .KeySchemaElement ;
46+ import software .amazon .awssdk .services .dynamodb .model .KeyType ;
47+ import software .amazon .awssdk .services .dynamodb .model .ResourceInUseException ;
48+ import software .amazon .awssdk .services .dynamodb .model .ScalarAttributeType ;
4149
4250import java .net .URI ;
43- import java .time .Instant ;
44- import java .util .Arrays ;
45- import java .util .HashMap ;
46- import java .util .List ;
4751
52+ import static io .flamingock .core .kit .audit .AuditEntryExpectation .APPLIED ;
53+ import static io .flamingock .core .kit .audit .AuditEntryExpectation .STARTED ;
54+ import static io .flamingock .internal .common .core .metadata .Constants .DEFAULT_MONGOCK_ORIGIN ;
4855import static io .flamingock .internal .util .constants .CommunityPersistenceConstants .DEFAULT_AUDIT_STORE_NAME ;
56+ import static io .flamingock .internal .util .constants .CommunityPersistenceConstants .DEFAULT_LOCK_STORE_NAME ;
4957import static org .junit .jupiter .api .Assertions .assertEquals ;
5058import static org .junit .jupiter .api .Assertions .assertTrue ;
5159
@@ -58,16 +66,13 @@ public class DynamoDBImporterTest {
5866 public static final GenericContainer <?> dynamoDBContainer = new GenericContainer <>("amazon/dynamodb-local:latest" )
5967 .withExposedPorts (8000 );
6068
61- public static final String MONGOCK_CHANGE_LOGS = "mongockChangeLogs" ;
62-
63-
6469 private static DynamoDbClient client ;
65- private DynamoDBTestHelper mongockChangeLogsHelper ;
66-
67- @ BeforeAll
68- static void beforeAll () {
69- dynamoDBContainer .start ();
70+ private DynamoDBMongockTestHelper mongockTestHelper ;
71+ private TestKit testKit ;
72+ private AuditTestHelper auditHelper ;
7073
74+ @ BeforeEach
75+ void setUp () {
7176 String endpoint = String .format ("http://%s:%d" ,
7277 dynamoDBContainer .getHost (),
7378 dynamoDBContainer .getMappedPort (8000 ));
@@ -80,112 +85,152 @@ static void beforeAll() {
8085 )
8186 )
8287 .build ();
88+
89+ // Create Mongock origin table for migration
90+ createMongockTable (DEFAULT_MONGOCK_ORIGIN );
91+
92+ // Create Flamingock audit and lock tables
93+ createAuditTable (DEFAULT_AUDIT_STORE_NAME );
94+ createLockTable (DEFAULT_LOCK_STORE_NAME );
95+
96+ mongockTestHelper = new DynamoDBMongockTestHelper (client , DEFAULT_MONGOCK_ORIGIN );
97+
98+ // Initialize TestKit for unified testing
99+ testKit = DynamoDBTestKit .create (client , new DynamoDBAuditStore (client ));
100+ auditHelper = testKit .getAuditHelper ();
83101 }
84102
85- @ BeforeEach
86- void setUp () {
87- mongockChangeLogsHelper = new DynamoDBTestHelper (client , MONGOCK_CHANGE_LOGS );
88- mongockChangeLogsHelper .ensureTableExists ();
89- mongockChangeLogsHelper .resetTable ();
103+ private void createMongockTable (String tableName ) {
104+ try {
105+ client .createTable (CreateTableRequest .builder ()
106+ .tableName (tableName )
107+ .keySchema (
108+ KeySchemaElement .builder ()
109+ .attributeName ("executionId" )
110+ .keyType (KeyType .HASH )
111+ .build (),
112+ KeySchemaElement .builder ()
113+ .attributeName ("changeId" )
114+ .keyType (KeyType .RANGE )
115+ .build ()
116+ )
117+ .attributeDefinitions (
118+ AttributeDefinition .builder ()
119+ .attributeName ("executionId" )
120+ .attributeType (ScalarAttributeType .S )
121+ .build (),
122+ AttributeDefinition .builder ()
123+ .attributeName ("changeId" )
124+ .attributeType (ScalarAttributeType .S )
125+ .build ()
126+ )
127+ .billingMode (BillingMode .PAY_PER_REQUEST )
128+ .build ());
129+ } catch (ResourceInUseException ignored ) {
130+ // Table already exists, ignore
131+ }
132+ }
90133
91- new DynamoDBTestHelper (client , DEFAULT_AUDIT_STORE_NAME ).ensureTableExists ();
92- new DynamoDBTestHelper (client , DEFAULT_AUDIT_STORE_NAME ).resetTable ();
134+ private void createAuditTable (String tableName ) {
135+ try {
136+ client .createTable (CreateTableRequest .builder ()
137+ .tableName (tableName )
138+ .keySchema (
139+ KeySchemaElement .builder ()
140+ .attributeName ("partitionKey" )
141+ .keyType (KeyType .HASH )
142+ .build ()
143+ )
144+ .attributeDefinitions (
145+ AttributeDefinition .builder ()
146+ .attributeName ("partitionKey" )
147+ .attributeType (ScalarAttributeType .S )
148+ .build ()
149+ )
150+ .billingMode (BillingMode .PAY_PER_REQUEST )
151+ .build ());
152+ } catch (ResourceInUseException ignored ) {
153+ // Table already exists, ignore
154+ }
155+ }
156+
157+ private void createLockTable (String tableName ) {
158+ try {
159+ client .createTable (CreateTableRequest .builder ()
160+ .tableName (tableName )
161+ .keySchema (
162+ KeySchemaElement .builder ()
163+ .attributeName ("partitionKey" )
164+ .keyType (KeyType .HASH )
165+ .build ()
166+ )
167+ .attributeDefinitions (
168+ AttributeDefinition .builder ()
169+ .attributeName ("partitionKey" )
170+ .attributeType (ScalarAttributeType .S )
171+ .build ()
172+ )
173+ .billingMode (BillingMode .PAY_PER_REQUEST )
174+ .build ());
175+ } catch (ResourceInUseException ignored ) {
176+ // Table already exists, ignore
177+ }
178+ }
179+
180+ @ AfterEach
181+ void tearDown () {
182+ // DynamoDB local doesn't need explicit cleanup between tests
183+ // Tables are automatically cleaned by Testcontainers on restart
184+ client .close ();
93185 }
94186
95187 @ Test
96- @ Disabled ("restore when https://trello.com/c/4gEQ8Wb4/458-mongock-legacy-targetsystem done" )
97188 void testImportDynamoDBChangeLogs () {
98- List <MongockDynamoDBAuditEntry > entries = Arrays .asList (
99- new MongockDynamoDBAuditEntry (
100- "exec-1" ,
101- "client-initializer" ,
102- "author1" ,
103- String .valueOf (Instant .now ().toEpochMilli ()),
104- "EXECUTED" ,
105- "EXECUTION" ,
106- "io.flamingock.changelog.Class1" ,
107- "method1" ,
108- new HashMap <String , String >() {{
109- put ("meta1" , "value1" );
110- }}.toString (),
111- 123L ,
112- "host1" ,
113- null ,
114- true
115- ),
116- new MongockDynamoDBAuditEntry (
117- "exec-1" ,
118- "client-updater" ,
119- "author1" ,
120- String .valueOf (Instant .now ().toEpochMilli ()),
121- "EXECUTED" ,
122- "EXECUTION" ,
123- "io.flamingock.changelog.Class2" ,
124- "method1" ,
125- new HashMap <String , String >() {{
126- put ("meta1" , "value1" );
127- }}.toString (),
128- 123L ,
129- "host1" ,
130- null ,
131- true
132- )
133- );
189+ // Setup Mongock entries
190+ mongockTestHelper .setupBasicScenario ();
134191
135- mongockChangeLogsHelper . insertChangeEntries ( entries );
192+ DynamoDBTargetSystem dynamodbTargetSystem = new DynamoDBTargetSystem ( "dynamodb-target-system" , client );
136193
137- Runner flamingock = FlamingockFactory . getCommunityBuilder ()
138- .setAuditStore ( new DynamoDBAuditStore ( client ) )
194+ Runner flamingock = testKit . createBuilder ()
195+ .addTargetSystem ( dynamodbTargetSystem )
139196 .build ();
140197
141198 flamingock .run ();
142199
143- List <AuditEntry > auditLog = new DynamoDBTestHelper (client , DEFAULT_AUDIT_STORE_NAME ).getAuditEntriesSorted ();
144- assertEquals (6 , auditLog .size ());
145-
146- for (AuditEntry entry : auditLog ) {
147- System .out .println ("executionId: " + entry .getExecutionId ());
148- System .out .println ("stageId: " + entry .getStageId ());
149- System .out .println ("taskId: " + entry .getTaskId ());
150- System .out .println ("author: " + entry .getAuthor ());
151- System .out .println ("createdAt: " + entry .getCreatedAt ());
152- System .out .println ("state: " + entry .getState ());
153- System .out .println ("type: " + entry .getType ());
154- System .out .println ("className: " + entry .getClassName ());
155- System .out .println ("methodName: " + entry .getMethodName ());
156- System .out .println ("executionMillis: " + entry .getExecutionMillis ());
157- System .out .println ("executionHostname: " + entry .getExecutionHostname ());
158- System .out .println ("metadata: " + entry .getMetadata ());
159- System .out .println ("systemChange: " + entry .getSystemChange ());
160- System .out .println ("errorTrace: " + entry .getErrorTrace ());
161- System .out .println ("txStrategy: " + entry .getTxType ());
162- System .out .println ("targetSystemId: " + entry .getTargetSystemId ());
163- System .out .println ("order: " + entry .getOrder ());
164- System .out .println ("-----" );
165- }
166-
167- AuditEntry entry1 = auditLog .stream ()
168- .filter (e -> "client-updater" .equals (e .getTaskId ()))
169- .findFirst ()
170- .orElseThrow (() -> new AssertionError ("Entry with changeId 'client-updater' not found" ));
171-
200+ // Verify audit sequence: 9 total entries
201+ // Legacy imports only show APPLIED (imported from Mongock), new changes show STARTED+APPLIED
202+ auditHelper .verifyAuditSequenceStrict (
203+ // Legacy imports from Mongock (APPLIED only - no STARTED for imported changes)
204+ APPLIED ("system-change-00001_before" ),
205+ APPLIED ("system-change-00001" ),
206+ APPLIED ("client-initializer_before" ),
207+ APPLIED ("client-initializer" ),
208+ APPLIED ("client-updater" ),
209+
210+ // System stage - actual system importer change
211+ STARTED ("migration-mongock-to-flamingock-community" ),
212+ APPLIED ("migration-mongock-to-flamingock-community" ),
213+
214+ // Application stage - new change created by code
215+ STARTED ("create-users-table" ),
216+ APPLIED ("create-users-table" )
217+ );
172218
173- assertEquals ("client-updater" , entry1 .getTaskId ());
174- assertEquals ("author1" , entry1 .getAuthor ());
175- assertEquals ("exec-1" , entry1 .getExecutionId ());
176- assertTrue (entry1 .getSystemChange ());
219+ // Validate actual table creation
220+ assertTrue (client .listTables ().tableNames ().contains ("users" ), "Users table should exist" );
177221
178- ScanResponse scanResponse = client .scan (
179- ScanRequest .builder ().tableName (DEFAULT_AUDIT_STORE_NAME ).build ()
222+ // Verify table structure
223+ DescribeTableResponse tableDescription = client .describeTable (
224+ DescribeTableRequest .builder ().tableName ("users" ).build ()
180225 );
181- assertTrue (scanResponse .count () > 0 , "Audit table should not be empty" );
226+ assertEquals ("email" , tableDescription .table ().keySchema ().get (0 ).attributeName ());
227+ assertEquals (KeyType .HASH , tableDescription .table ().keySchema ().get (0 ).keyType ());
182228 }
183229
184230 @ Test
185231 @ Disabled ("restore when https://trello.com/c/4gEQ8Wb4/458-mongock-legacy-targetsystem done" )
186232 void failIfEmptyOrigin () {
187- Runner flamingock = FlamingockFactory .getCommunityBuilder ()
188- .setAuditStore (new DynamoDBAuditStore (client ))
233+ Runner flamingock = testKit .createBuilder ()
189234 .build ();
190235
191236 Assertions .assertThrows (FlamingockException .class , flamingock ::run );
0 commit comments