2424import org .junit .jupiter .api .Test ;
2525
2626import java .lang .reflect .Field ;
27+ import java .util .Arrays ;
2728import java .util .HashMap ;
2829import java .util .Map ;
2930
@@ -47,57 +48,57 @@ void setUp() {
4748 void testConfigurationValidation () throws Exception {
4849 // Test missing bootstrap servers
4950 assertThatThrownBy (
50- () ->
51- new FlussSinkBuilder <Order >()
52- .setDatabase ("testDb" )
53- .setTable ("testTable" )
54- .build ())
51+ () ->
52+ new FlussSinkBuilder <Order >()
53+ .setDatabase ("testDb" )
54+ .setTable ("testTable" )
55+ .build ())
5556 .isInstanceOf (NullPointerException .class )
5657 .hasMessageContaining ("BootstrapServers is required but not provided." );
5758
5859 // Test missing database
5960 assertThatThrownBy (
60- () ->
61- new FlussSinkBuilder <Order >()
62- .setBootstrapServers (bootstrapServers )
63- .setTable (tableName )
64- .setSerializationSchema (new OrderSerializationSchema ())
65- .build ())
61+ () ->
62+ new FlussSinkBuilder <Order >()
63+ .setBootstrapServers (bootstrapServers )
64+ .setTable (tableName )
65+ .setSerializationSchema (new OrderSerializationSchema ())
66+ .build ())
6667 .isInstanceOf (RuntimeException .class )
6768 .hasMessageContaining ("Database is required but not provided." );
6869
6970 // Test empty database
7071 assertThatThrownBy (
71- () ->
72- new FlussSinkBuilder <Order >()
73- .setBootstrapServers (bootstrapServers )
74- .setDatabase ("" )
75- .setTable (tableName )
76- .setSerializationSchema (new OrderSerializationSchema ())
77- .build ())
72+ () ->
73+ new FlussSinkBuilder <Order >()
74+ .setBootstrapServers (bootstrapServers )
75+ .setDatabase ("" )
76+ .setTable (tableName )
77+ .setSerializationSchema (new OrderSerializationSchema ())
78+ .build ())
7879 .isInstanceOf (IllegalArgumentException .class )
7980 .hasMessageContaining ("Database cannot be empty" );
8081
8182 // Test missing table name
8283 assertThatThrownBy (
83- () ->
84- new FlussSinkBuilder <Order >()
85- .setBootstrapServers (bootstrapServers )
86- .setDatabase ("testDb" )
87- .setSerializationSchema (new OrderSerializationSchema ())
88- .build ())
84+ () ->
85+ new FlussSinkBuilder <Order >()
86+ .setBootstrapServers (bootstrapServers )
87+ .setDatabase ("testDb" )
88+ .setSerializationSchema (new OrderSerializationSchema ())
89+ .build ())
8990 .isInstanceOf (NullPointerException .class )
9091 .hasMessageContaining ("Table name is required" );
9192
9293 // Test empty table name
9394 assertThatThrownBy (
94- () ->
95- new FlussSinkBuilder <Order >()
96- .setBootstrapServers (bootstrapServers )
97- .setDatabase ("testDb" )
98- .setTable ("" )
99- .setSerializationSchema (new OrderSerializationSchema ())
100- .build ())
95+ () ->
96+ new FlussSinkBuilder <Order >()
97+ .setBootstrapServers (bootstrapServers )
98+ .setDatabase ("testDb" )
99+ .setTable ("" )
100+ .setSerializationSchema (new OrderSerializationSchema ())
101+ .build ())
101102 .isInstanceOf (IllegalArgumentException .class )
102103 .hasMessageContaining ("Table name cannot be empty" );
103104 }
@@ -171,17 +172,62 @@ void testFluentChaining() {
171172 .setTable (tableName )
172173 .setOption ("key1" , "value1" )
173174 .setOptions (new HashMap <>())
174- .setShuffleByBucketId (false );
175+ .setShuffleByBucketId (false )
176+ .setPartialUpdateColumns ("id" , "price" );
175177
176178 // Verify the builder instance is returned
177179 assertThat (chainedBuilder ).isInstanceOf (FlussSinkBuilder .class );
178180 }
179181
182+ @ Test
183+ void testComputeTargetColumnIndexesFullUpdate () {
184+ int [] result =
185+ FlussSinkBuilder .computeTargetColumnIndexes (
186+ Arrays .asList ("id" , "name" , "price" ),
187+ Arrays .asList ("id" ),
188+ null );
189+ assertThat (result ).isNull ();
190+ }
191+
192+ @ Test
193+ void testComputeTargetColumnIndexesValidPartialIncludesPk () {
194+ int [] result =
195+ FlussSinkBuilder .computeTargetColumnIndexes (
196+ Arrays .asList ("id" , "name" , "price" , "ts" ),
197+ Arrays .asList ("id" ),
198+ Arrays .asList ("id" , "price" ));
199+ assertThat (result ).containsExactly (0 , 2 );
200+ }
201+
202+ @ Test
203+ void testComputeTargetColumnIndexesMissingPkThrows () {
204+ assertThatThrownBy (
205+ () ->
206+ FlussSinkBuilder .computeTargetColumnIndexes (
207+ Arrays .asList ("id" , "name" , "price" ),
208+ Arrays .asList ("id" ),
209+ Arrays .asList ("name" , "price" )))
210+ .isInstanceOf (IllegalArgumentException .class )
211+ .hasMessageContaining ("Partial updates must include all primary key columns" );
212+ }
213+
214+ @ Test
215+ void testComputeTargetColumnIndexesUnknownColumnThrows () {
216+ assertThatThrownBy (
217+ () ->
218+ FlussSinkBuilder .computeTargetColumnIndexes (
219+ Arrays .asList ("id" , "name" ),
220+ Arrays .asList ("id" ),
221+ Arrays .asList ("id" , "unknown" )))
222+ .isInstanceOf (IllegalArgumentException .class )
223+ .hasMessageContaining ("not found in table schema" );
224+ }
225+
180226 // Helper method to get private field values using reflection
181227 @ SuppressWarnings ("unchecked" )
182228 private <T > T getFieldValue (Object object , String fieldName ) throws Exception {
183229 Field field = FlussSinkBuilder .class .getDeclaredField (fieldName );
184230 field .setAccessible (true );
185231 return (T ) field .get (object );
186232 }
187- }
233+ }
0 commit comments