Skip to content

Commit f72dc24

Browse files
vasiliy-mikhailovmsmygit
authored andcommitted
Add unit tests for TargetInsertStatement bind input validation and bind-index ordering
1 parent 76c5bf9 commit f72dc24

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

src/test/java/com/datastax/cdm/cql/statement/TargetInsertStatementTest.java

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222

2323
import org.junit.jupiter.api.BeforeEach;
2424
import org.junit.jupiter.api.Test;
25+
import org.mockito.ArgumentCaptor;
2526

2627
import com.datastax.cdm.cql.CommonMocks;
28+
import com.datastax.cdm.properties.KnownProperties;
2729
import com.datastax.oss.driver.api.core.cql.*;
2830
import com.datastax.oss.driver.api.core.type.DataTypes;
2931

@@ -343,4 +345,160 @@ public void bind_withNonEmptyMap_shouldCallSet() {
343345
verify(boundStatement, atLeastOnce()).set(anyInt(), any(), any(Class.class));
344346
}
345347

348+
// ---- bind(): input validation and bind-index ordering ----
349+
350+
/**
351+
* When TTL columns are enabled but no TTL value is supplied, bind() rejects the input with a RuntimeException
352+
* naming the missing TTL property.
353+
*/
354+
@Test
355+
public void bind_checkBindInputsThrowsWhenTtlMissing() {
356+
when(writetimeTTLFeature.isEnabled()).thenReturn(true);
357+
when(writetimeTTLFeature.hasTTLColumns()).thenReturn(true);
358+
targetInsertStatement = new TargetInsertStatement(propertyHelper, targetSession);
359+
360+
RuntimeException ex = assertThrows(RuntimeException.class,
361+
() -> targetInsertStatement.bind(originRow, targetRow, null, null, null, null));
362+
assertEquals(KnownProperties.ORIGIN_TTL_NAMES + " specified, but no TTL value was provided", ex.getMessage());
363+
}
364+
365+
/**
366+
* When writetime columns are enabled but no writetime value is supplied, bind() rejects the input with a
367+
* RuntimeException naming the missing writetime property.
368+
*/
369+
@Test
370+
public void bind_checkBindInputsThrowsWhenWriteTimeMissing() {
371+
when(writetimeTTLFeature.isEnabled()).thenReturn(true);
372+
when(writetimeTTLFeature.hasWritetimeColumns()).thenReturn(true);
373+
targetInsertStatement = new TargetInsertStatement(propertyHelper, targetSession);
374+
375+
RuntimeException ex = assertThrows(RuntimeException.class,
376+
() -> targetInsertStatement.bind(originRow, targetRow, null, null, null, null));
377+
assertEquals(KnownProperties.ORIGIN_WRITETIME_NAMES + " specified, but no WriteTime value was provided",
378+
ex.getMessage());
379+
}
380+
381+
/**
382+
* bind() binds every target column exactly once at sequential indices 0, 1, 2, ...
383+
*/
384+
@Test
385+
public void bind_bindsAllColumnsAtSequentialIndices() {
386+
targetInsertStatement = new TargetInsertStatement(propertyHelper, targetSession);
387+
388+
BoundStatement result = targetInsertStatement.bind(originRow, targetRow, null, null, null, null);
389+
assertNotNull(result);
390+
391+
ArgumentCaptor<Integer> indexCaptor = ArgumentCaptor.forClass(Integer.class);
392+
verify(boundStatement, times(targetColumnNames.size())).set(indexCaptor.capture(), any(), any(Class.class));
393+
394+
List<Integer> capturedIndices = indexCaptor.getAllValues();
395+
for (int i = 0; i < capturedIndices.size(); i++) {
396+
assertEquals(i, capturedIndices.get(i).intValue());
397+
}
398+
}
399+
400+
/**
401+
* Constant columns are not bound: bind() sets only the non-constant columns, at sequential indices starting from
402+
* zero.
403+
*/
404+
@Test
405+
public void bind_skipsConstantColumns() {
406+
commonSetup(false, true, false);
407+
targetInsertStatement = new TargetInsertStatement(propertyHelper, targetSession);
408+
409+
ArgumentCaptor<Integer> indexCaptor = ArgumentCaptor.forClass(Integer.class);
410+
411+
targetInsertStatement.bind(originRow, targetRow, null, null, null, null);
412+
413+
// Constant columns are skipped; only the remaining columns are bound.
414+
int expectedBindCount = targetColumnNames.size() - constantColumns.size();
415+
verify(boundStatement, times(expectedBindCount)).set(indexCaptor.capture(), any(), any(Class.class));
416+
417+
List<Integer> capturedIndices = indexCaptor.getAllValues();
418+
for (int i = 0; i < capturedIndices.size(); i++) {
419+
assertEquals(i, capturedIndices.get(i).intValue());
420+
}
421+
}
422+
423+
/**
424+
* With an explode-map column present, bind() sets every target column at sequential indices starting from zero.
425+
*/
426+
@Test
427+
public void bind_bindsExplodeMapColumnsAtSequentialIndices() {
428+
commonSetup(true, false, false);
429+
targetInsertStatement = new TargetInsertStatement(propertyHelper, targetSession);
430+
431+
ArgumentCaptor<Integer> indexCaptor = ArgumentCaptor.forClass(Integer.class);
432+
433+
targetInsertStatement.bind(originRow, targetRow, null, null, getSampleData(explodeMapKeyType),
434+
getSampleData(explodeMapValueType));
435+
436+
verify(boundStatement, times(targetColumnNames.size())).set(indexCaptor.capture(), any(), any(Class.class));
437+
List<Integer> capturedIndices = indexCaptor.getAllValues();
438+
for (int i = 0; i < capturedIndices.size(); i++) {
439+
assertEquals(i, capturedIndices.get(i).intValue());
440+
}
441+
}
442+
443+
/**
444+
* With both TTL and writetime enabled, bind() binds each target column at sequential indices and then appends the
445+
* TTL value (as an Integer) and the writetime value (as a Long) at the two trailing indices, in that order.
446+
*/
447+
@Test
448+
public void bind_appendsTtlAndWritetimeAtTrailingIndicesWithTypes() {
449+
when(writetimeTTLFeature.isEnabled()).thenReturn(true);
450+
when(writetimeTTLFeature.hasTTLColumns()).thenReturn(true);
451+
when(writetimeTTLFeature.hasWritetimeColumns()).thenReturn(true);
452+
targetInsertStatement = new TargetInsertStatement(propertyHelper, targetSession);
453+
454+
ArgumentCaptor<Integer> indexCaptor = ArgumentCaptor.forClass(Integer.class);
455+
ArgumentCaptor<Object> valueCaptor = ArgumentCaptor.forClass(Object.class);
456+
ArgumentCaptor<Class> classCaptor = ArgumentCaptor.forClass(Class.class);
457+
458+
Integer ttl = 7200;
459+
Long writeTime = 42L;
460+
461+
BoundStatement result = targetInsertStatement.bind(originRow, targetRow, ttl, writeTime, null, null);
462+
assertNotNull(result);
463+
464+
verify(boundStatement, times(targetColumnNames.size() + 2)).set(indexCaptor.capture(), valueCaptor.capture(),
465+
classCaptor.capture());
466+
467+
List<Integer> allIndices = indexCaptor.getAllValues();
468+
List<Object> allValues = valueCaptor.getAllValues();
469+
List<Class> allClasses = classCaptor.getAllValues();
470+
471+
// Columns are bound at sequential indices.
472+
for (int i = 0; i < targetColumnNames.size(); i++) {
473+
assertEquals(i, allIndices.get(i).intValue());
474+
}
475+
476+
// TTL is bound immediately after the columns, with its value and Integer type.
477+
int ttlIdx = targetColumnNames.size();
478+
assertEquals(ttlIdx, allIndices.get(ttlIdx).intValue());
479+
assertEquals(ttl, allValues.get(ttlIdx));
480+
assertEquals(Integer.class, allClasses.get(ttlIdx));
481+
482+
// WriteTime is bound at the next index, with its value and Long type.
483+
int wtIdx = targetColumnNames.size() + 1;
484+
assertEquals(wtIdx, allIndices.get(wtIdx).intValue());
485+
assertEquals(writeTime, allValues.get(wtIdx));
486+
assertEquals(Long.class, allClasses.get(wtIdx));
487+
}
488+
489+
/**
490+
* When converting an origin value throws, bind() wraps the failure in a RuntimeException whose message identifies
491+
* the value being bound.
492+
*/
493+
@Test
494+
public void bind_exceptionWithNonNullOriginValue() {
495+
when(targetTable.getCorrespondingIndex(0)).thenReturn(0);
496+
when(originTable.getAndConvertData(0, originRow)).thenThrow(new RuntimeException("binding error"));
497+
498+
RuntimeException ex = assertThrows(RuntimeException.class, () -> targetInsertStatement.bind(originRow,
499+
targetRow, 3600, 123456789L, getSampleData(explodeMapKeyType), getSampleData(explodeMapValueType)));
500+
501+
assertTrue(ex.getMessage().contains("Error trying to bind value"));
502+
}
503+
346504
}

0 commit comments

Comments
 (0)