package com.getbase.android.db.fluentsqlite; import static com.getbase.android.db.fluentsqlite.Expressions.arg; import static com.getbase.android.db.fluentsqlite.Expressions.column; import static com.getbase.android.db.fluentsqlite.Query.select; import static com.getbase.android.db.fluentsqlite.Update.update; import static com.google.common.truth.Truth.assertThat; import static org.fest.assertions.api.ANDROID.assertThat; import static org.fest.assertions.api.android.content.ContentValuesEntry.entry; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteStatement; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) public class UpdateTest { @Mock private SQLiteDatabase mDb; @Mock private SQLiteStatement mStatement; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mDb.compileStatement(anyString())).thenReturn(mStatement); } @Test public void shouldUpdateCorrectTableWhenDoingSimpleUpdate() throws Exception { update() .table("test") .value("num", 666) .where("num=?", 0) .perform(mDb); verify(mDb).update( eq("test"), any(ContentValues.class), anyString(), any(String[].class) ); } @Test public void shouldPassCorrectValuesWhenDoingSimpleUpdate() throws Exception { update() .table("test") .value("num", 666) .where("num=?", 0) .perform(mDb); ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class); verify(mDb).update( anyString(), contentValuesArgument.capture(), anyString(), any(String[].class) ); assertThat(contentValuesArgument.getValue()).contains(entry("num", 666)); } @Test public void shouldUseCorrectSelectionAndArgsWhenDoingSimpleUpdate() throws Exception { update() .table("test") .value("num", 666) .where("num=?", 0) .perform(mDb); verify(mDb).update( anyString(), any(ContentValues.class), eq("(num=?)"), eq(new String[] { "0" }) ); } @Test public void shouldConcatenateSelectionAndArgs() throws Exception { update() .table("test") .value("num", 666) .where("num=?", 0) .where("t=?", "test") .perform(mDb); verify(mDb).update( anyString(), any(ContentValues.class), eq("(num=?) AND (t=?)"), eq(new String[] { "0", "test" }) ); } @Test public void shouldBuildSelectionFromExpression() throws Exception { update() .table("test") .value("num", 666) .where(column("num").eq().arg(), 0) .perform(mDb); verify(mDb).update( anyString(), any(ContentValues.class), eq("(num == ?)"), eq(new String[] { "0" }) ); } @Test public void shouldUseSQLiteStatementWhenColumnExpressionIsUsed() throws Exception { update() .table("test") .setColumn("num", "666") .perform(mDb); verify(mDb, never()).update(anyString(), any(ContentValues.class), anyString(), any(String[].class)); verify(mDb).compileStatement(anyString()); } @Test public void shouldCopyColumnExpressionsDirectlyIntoStatement() throws Exception { update() .table("test") .setColumn("num", "666") .perform(mDb); verify(mDb).compileStatement(eq("UPDATE test SET num=(666)")); } @Test public void shouldBuildColumnExpressionsWithSelection() throws Exception { update() .table("test") .setColumn("num", "666") .where("t=?", "test") .perform(mDb); verify(mDb).compileStatement(eq("UPDATE test SET num=(666) WHERE (t=?)")); } @Test public void shouldBuildColumnExpressionsFromExpression() throws Exception { update() .table("test") .setColumn("num", Expressions.literal(666)) .perform(mDb); verify(mDb).compileStatement(eq("UPDATE test SET num=(666)")); } @Test public void shouldPassContentValuesArgsAsBoundArgsWhenCustomColumnExpressionsIsUsed() throws Exception { update() .table("test") .setColumn("num", "666") .value("t", "666") .perform(mDb); verify(mDb).compileStatement(eq("UPDATE test SET num=(666), t=?")); } @Test public void shouldOverrideContentValuesAddedEarlierWithCustomColumnExpressionForTheSameColumn() throws Exception { update() .table("test") .value("num", "667") .value("t", "666") .setColumn("num", "666") .perform(mDb); verify(mDb).compileStatement(eq("UPDATE test SET num=(666), t=?")); } @Test public void shouldOverrideCustomColumnExpressionAddedEarlierWithContentValuesForTheSameColumn() throws Exception { update() .table("test") .setColumn("t", "666") .setColumn("num", "666") .value("num", "667") .perform(mDb); verify(mDb).compileStatement(eq("UPDATE test SET t=(666), num=?")); } @Test public void shouldRevertToSimpleUpdateWhenAllCustomColumnExpressionsAreOverridden() throws Exception { update() .table("test") .setColumn("num", "666") .value("num", "667") .value("t", "666") .perform(mDb); verify(mDb).update(anyString(), any(ContentValues.class), anyString(), any(String[].class)); verify(mDb, never()).compileStatement(anyString()); } @Test public void shouldNotModifyPassedContentValues() throws Exception { ContentValues values = new ContentValues(); update() .table("A") .values(values) .value("key", "value"); assertThat(values.containsKey("key")).isFalse(); ContentValues valuesToConcatenate = new ContentValues(); valuesToConcatenate.put("another_key", "another_value"); update() .table("A") .values(values) .values(valuesToConcatenate); assertThat(values.containsKey("another_key")).isFalse(); } @Test public void shouldBuildInsertWithConcatenatedContentValues() throws Exception { ContentValues firstValues = new ContentValues(); firstValues.put("col1", "val1"); ContentValues secondValues = new ContentValues(); secondValues.put("col2", "val2"); update() .table("A") .values(firstValues) .values(secondValues) .perform(mDb); ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class); verify(mDb).update( anyString(), contentValuesArgument.capture(), anyString(), any(String[].class) ); assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"), entry("col2", "val2")); } @Test public void shouldPerformInsertWithContentValuesOverriddenBySingleValue() throws Exception { ContentValues values = new ContentValues(); values.put("col1", "val1"); values.put("col2", "val2"); update() .table("A") .values(values) .value("col2", null) .perform(mDb); ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class); verify(mDb).update( anyString(), contentValuesArgument.capture(), anyString(), any(String[].class) ); assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"), entry("col2", null)); } @Test public void shouldPerformInsertWithContentValuesOverriddenByOtherContentValues() throws Exception { ContentValues firstValues = new ContentValues(); firstValues.put("col1", "val1"); firstValues.put("col2", "val2"); ContentValues secondValues = new ContentValues(); secondValues.putNull("col2"); secondValues.put("col3", "val3"); update() .table("A") .values(firstValues) .values(secondValues) .perform(mDb); ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class); verify(mDb).update( anyString(), contentValuesArgument.capture(), anyString(), any(String[].class) ); assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"), entry("col3", "val3"), entry("col2", null)); } @Test(expected = IllegalArgumentException.class) public void shouldRejectColumnExpressionWithUnboundArgsPlaceholders() throws Exception { update().table("A").setColumn("id", arg()); } @Test public void shouldUseBoundArgsFromColumnExpressions() throws Exception { update() .table("test") .setColumn("col_a", column("col_b").in(select().column("id").from("B").where("status=?", "new").build())) .perform(mDb); verify(mDb).compileStatement(eq("UPDATE test SET col_a=(col_b IN (SELECT id FROM B WHERE (status=?)))")); verify(mStatement).bindString(eq(1), eq("new")); } @Test(expected = IllegalArgumentException.class) public void shouldRejectSelectionWithExpressionWithTooManyArgsPlaceholders() throws Exception { update().table("A").value("col1", "val1").where(column("col2").eq().arg()); } @Test(expected = IllegalArgumentException.class) public void shouldRejectSelectionWithExpressionWithTooFewArgsPlaceholders() throws Exception { update().table("A").value("col1", "val1").where(column("col2").eq().arg(), 1, 2); } @Test public void shouldBuildSelectionFromExpressionWithArgsPlaceholders() throws Exception { update() .table("A") .value("col1", "val1") .where(column("col2").eq().arg(), "val2") .perform(mDb); verify(mDb).update( anyString(), any(ContentValues.class), eq("(col2 == ?)"), eq(new String[] { "val2" }) ); } @Test public void shouldBuildSelectionFromExpressionWithBoundArgs() throws Exception { update() .table("A") .value("col1", "val1") .where(column("col2").in(select().column("id").from("B").where("status=?", "new").build())) .perform(mDb); verify(mDb).update( anyString(), any(ContentValues.class), anyString(), eq(new String[] { "new" }) ); } @Test public void shouldNotUseBoundArgsFromColumnExpressionsOverriddenByContentValues() throws Exception { update() .table("test") .setColumn("col_a", column("col_b").in(select().column("id").from("B").where("status=?", "new").build())) .value("col_a", 666) .perform(mDb); verify(mDb).update( anyString(), any(ContentValues.class), anyString(), eq(new String[0]) ); } @Test public void shouldOverrideBoundArgsFromColumnExpressionsIfTheExpressionForTheSameColumnIsSpecifiedTwice() throws Exception { update() .table("test") .setColumn("col_a", column("col_b").in(select().column("id").from("B").where("status=?", "new").build())) .setColumn("col_a", column("col_b").in(select().column("id").from("B").where("status=?", "old").build())) .perform(mDb); verify(mStatement).bindString(eq(1), eq("old")); } @Test public void shouldOverrideBoundArgsFromColumnExpressionsWithSimpleColumnExpression() throws Exception { update() .table("test") .setColumn("col_a", column("col_b").in(select().column("id").from("B").where("status=?", "new").build())) .setColumn("col_a", "666") .perform(mDb); verify(mStatement).executeUpdateDelete(); verify(mStatement).close(); verifyNoMoreInteractions(mStatement); } @Test public void shouldAllowUsingNullArgumentsForSelection() throws Exception { update() .table("table_a") .where("col_a IS NULL", (Object[]) null) .perform(mDb); verify(mDb).update(eq("table_a"), any(ContentValues.class), eq("(col_a IS NULL)"), eq(new String[0])); } @Test public void shouldAllowUsingNullArgumentsForSelectionWithExpression() throws Exception { update() .table("table_a") .where(column("col_a").is().nul(), (Object[]) null) .perform(mDb); verify(mDb).update(eq("table_a"), any(ContentValues.class), eq("(col_a IS NULL)"), eq(new String[0])); } @Test public void shouldAllowUsingNullSelectionWithNullArguments() throws Exception { update() .table("table_a") .where((String) null) .perform(mDb); } @Test(expected = IllegalArgumentException.class) public void shouldNotAllowUsingNullSelectionWithArguments() throws Exception { update() .table("table_a") .where((String) null, "I shall fail") .perform(mDb); } }