package com.getbase.android.db.provider;
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.isNotNull;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.*;
import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
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.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ProviderActionsTest {
private static final Uri TEST_URI = Uri.parse("content://authority/people");
@Mock
private ContentResolver contentResolverMock;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void shouldPassNullsEverywhere() throws Exception {
ProviderAction.query(TEST_URI)
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), eq((String[]) null), eq((String) null), eq((String[]) null), eq((String) null));
}
@Test
public void shouldUseProjectionWhenQuery() throws Exception {
ProviderAction.query(TEST_URI)
.projection("COL1")
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), eq(new String[] { "COL1" }), eq((String) null), eq((String[]) null), eq((String) null));
}
@Test
public void shouldAppendProjection() throws Exception {
ProviderAction.query(TEST_URI)
.projection("COL1")
.projection("COL2")
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), eq(new String[] { "COL1", "COL2" }), eq((String) null), eq((String[]) null), eq((String) null));
}
@Test
public void shouldConcatenateSelectionProperlyWhenQuerying() throws Exception {
ProviderAction.query(TEST_URI)
.where("COL1 = ?", "arg")
.where("COL2 = ?", "arg2")
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), eq((String[]) null), eq("(COL1 = ?) AND (COL2 = ?)"), eq(new String[] { "arg", "arg2" }), eq((String) null));
}
@Test
public void shouldAddParenthesesForEachWhereWhenQuerying() throws Exception {
ProviderAction.query(TEST_URI)
.where("COL1 = ? OR COL1 = ?", "arg", "argh")
.where("COL2 = ?", "arg2")
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), eq((String[]) null), eq("(COL1 = ? OR COL1 = ?) AND (COL2 = ?)"), eq(new String[] { "arg", "argh", "arg2" }), eq((String) null));
}
@Test
public void shouldAddParenthesesForEachWhereWhenDeleting() throws Exception {
ProviderAction.delete(TEST_URI)
.where("COL1 = ? OR COL1 = ?", "arg", "argh")
.where("COL2 = ?", "arg2")
.perform(contentResolverMock);
verify(contentResolverMock).delete(eq(TEST_URI), eq("(COL1 = ? OR COL1 = ?) AND (COL2 = ?)"), eq(new String[] { "arg", "argh", "arg2" }));
}
@Test
public void shouldAddParenthesesForEachWhereWhenUpdating() throws Exception {
ProviderAction.update(TEST_URI)
.where("COL1 = ? OR COL1 = ?", "arg", "argh")
.where("COL2 = ?", "arg2")
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), any(ContentValues.class), eq("(COL1 = ? OR COL1 = ?) AND (COL2 = ?)"), eq(new String[] { "arg", "argh", "arg2" }));
}
@Test
public void shouldUseOrderBy() throws Exception {
ProviderAction.query(TEST_URI)
.orderBy("COL1 DESC")
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), eq((String[]) null), eq((String) null), eq((String[]) null), eq("COL1 DESC"));
}
@Test
public void shouldPerformProperInsert() throws Exception {
ContentValues values = new ContentValues();
values.put("asdf", "value");
ProviderAction.insert(TEST_URI)
.values(values)
.perform(contentResolverMock);
verify(contentResolverMock).insert(eq(TEST_URI), eq(values));
}
@Test
public void shouldPerformInsertWithSingleValue() throws Exception {
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.insert(TEST_URI)
.value("col1", "val1")
.perform(contentResolverMock);
verify(contentResolverMock).insert(eq(TEST_URI), contentValuesArgument.capture());
assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"));
}
@Test
public void insertShouldNotModifyPassedContentValues() throws Exception {
ContentValues values = new ContentValues();
ProviderAction.insert(TEST_URI)
.values(values)
.value("key", "value")
.perform(contentResolverMock);
assertThat(values.containsKey("key")).isFalse();
ContentValues valuesToConcatenate = new ContentValues();
valuesToConcatenate.put("another_key", "another_value");
ProviderAction.insert(TEST_URI)
.values(values)
.values(valuesToConcatenate)
.perform(contentResolverMock);
assertThat(values.containsKey("another_key")).isFalse();
}
@Test
public void shouldPerformInsertWithConcatenatedContentValues() throws Exception {
ContentValues firstValues = new ContentValues();
firstValues.put("col1", "val1");
ContentValues secondValues = new ContentValues();
secondValues.put("col2", "val2");
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.insert(TEST_URI)
.values(firstValues)
.values(secondValues)
.perform(contentResolverMock);
verify(contentResolverMock).insert(eq(TEST_URI), contentValuesArgument.capture());
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");
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.insert(TEST_URI)
.values(values)
.value("col2", null)
.perform(contentResolverMock);
verify(contentResolverMock).insert(eq(TEST_URI), contentValuesArgument.capture());
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");
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.insert(TEST_URI)
.values(firstValues)
.values(secondValues)
.perform(contentResolverMock);
verify(contentResolverMock).insert(eq(TEST_URI), contentValuesArgument.capture());
assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"), entry("col3", "val3"), entry("col2", null));
}
@Test(expected = IllegalArgumentException.class)
public void shouldRejectInsertWithSingleValueOfUnsupportedType() throws Exception {
ProviderAction.insert(TEST_URI).value("col1", new Object());
}
@Test
public void shouldPerformUpdateWithValues() throws Exception {
ContentValues values = new ContentValues();
values.put("col1", "val1");
ProviderAction.update(TEST_URI)
.values(values)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), eq(values), eq((String) null), eq((String[]) null));
}
@Test
public void shouldPerformUpdateWithSingleValue() throws Exception {
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.update(TEST_URI)
.value("col1", "val1")
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), contentValuesArgument.capture(), eq((String) null), eq((String[]) null));
assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"));
}
@Test
public void shouldPerformUpdateWithConcatenatedContentValues() throws Exception {
ContentValues firstValues = new ContentValues();
firstValues.put("col1", "val1");
ContentValues secondValues = new ContentValues();
secondValues.put("col2", "val2");
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.update(TEST_URI)
.values(firstValues)
.values(secondValues)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), contentValuesArgument.capture(), eq((String) null), eq((String[]) null));
assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"), entry("col2", "val2"));
}
@Test
public void shouldPerformUpdateWithContentValuesOverriddenBySingleValue() throws Exception {
ContentValues values = new ContentValues();
values.put("col1", "val1");
values.put("col2", "val2");
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.update(TEST_URI)
.values(values)
.value("col2", null)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), contentValuesArgument.capture(), eq((String) null), eq((String[]) null));
assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"), entry("col2", null));
}
@Test
public void shouldPerformUpdateWithContentValuesOverriddenByOtherContentValues() 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");
ArgumentCaptor<ContentValues> contentValuesArgument = ArgumentCaptor.forClass(ContentValues.class);
ProviderAction.update(TEST_URI)
.values(firstValues)
.values(secondValues)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), contentValuesArgument.capture(), eq((String) null), eq((String[]) null));
assertThat(contentValuesArgument.getValue()).contains(entry("col1", "val1"), entry("col3", "val3"), entry("col2", null));
}
@Test(expected = IllegalArgumentException.class)
public void shouldRejectUpdateWithSingleValueOfUnsupportedType() throws Exception {
ProviderAction.update(TEST_URI).value("col1", new Object());
}
@Test
public void shouldPerformUpdateWithSelectionAndSelectionArgs() throws Exception {
ContentValues values = new ContentValues();
values.put("col1", "val1");
ProviderAction.update(TEST_URI)
.values(values)
.where("col2 = ?", "blah")
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), eq(values), eq("(col2 = ?)"), eq(new String[] { "blah" }));
}
@Test
public void updateShouldNotModifyPassedContentValues() throws Exception {
ContentValues values = new ContentValues();
ProviderAction.update(TEST_URI)
.values(values)
.value("key", "value")
.perform(contentResolverMock);
assertThat(values.containsKey("key")).isFalse();
ContentValues valuesToConcatenate = new ContentValues();
valuesToConcatenate.put("another_key", "another_value");
ProviderAction.update(TEST_URI)
.values(values)
.values(valuesToConcatenate)
.perform(contentResolverMock);
assertThat(values.containsKey("another_key")).isFalse();
}
@Test
public void shouldPerformDeleteOnUri() throws Exception {
ProviderAction.delete(TEST_URI).perform(contentResolverMock);
verify(contentResolverMock).delete(eq(TEST_URI), eq((String) null), eq((String[]) null));
}
@Test
public void shouldCareAboutSelectionAndSelectionArgsWhenDeleting() throws Exception {
ProviderAction.delete(TEST_URI)
.where("col1 = ?", "val1")
.perform(contentResolverMock);
verify(contentResolverMock).delete(eq(TEST_URI), eq("(col1 = ?)"), eq(new String[] { "val1" }));
}
@Test
public void shouldBeAbleToUseNonStringObjectsInSelectionArgs() throws Exception {
ProviderAction.query(TEST_URI)
.where("col1 > ?", 18)
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), eq((String[]) null), eq("(col1 > ?)"), eq(new String[] { "18" }), eq((String) null));
}
@Test
public void shouldBeAbleToCreateASelectionWithWhereIn() throws Exception {
final List<?> inSet = Lists.newArrayList(1L, "two", 3L);
ProviderAction.query(TEST_URI)
.whereIn("col1", inSet)
.perform(contentResolverMock);
final String expectedSelection = "(" + "col1 IN (" + Joiner.on(",").join(Collections2.transform(inSet, Utils.toEscapedSqlFunction())) + ")" + ")";
verify(contentResolverMock).query(eq(TEST_URI),
eq((String[]) null),
eq(expectedSelection),
eq((String[]) null),
eq((String) null));
}
@Test
public void shouldBeAbleToCreateAnUpdateWithWhereIn() throws Exception {
final List<Object> inSet = Lists.<Object>newArrayList(1L, "two", 3L);
ProviderAction.update(TEST_URI)
.whereIn("col1", inSet)
.perform(contentResolverMock);
final String expectedSelection = "(" + "col1 IN (" + Joiner.on(",").join(Collections2.transform(inSet, Utils.toEscapedSqlFunction())) + ")" + ")";
verify(contentResolverMock).update(eq(TEST_URI),
any(ContentValues.class),
eq(expectedSelection),
eq((String[]) null));
}
@Test
public void shouldBeAbleToCreateADeleteWithWhereIn() throws Exception {
final List<Object> inSet = Lists.<Object>newArrayList(1L, "two", 3L);
ProviderAction.delete(TEST_URI)
.whereIn("col1", inSet)
.perform(contentResolverMock);
final String expectedSelection = "(" + "col1 IN (" + Joiner.on(",").join(Collections2.transform(inSet, Utils.toEscapedSqlFunction())) + ")" + ")";
verify(contentResolverMock).delete(eq(TEST_URI),
eq(expectedSelection),
eq((String[]) null));
}
@Test
public void shouldAlwaysPassNonNullContentValuesOnInsert() throws Exception {
ProviderAction.insert(TEST_URI)
.perform(contentResolverMock);
verify(contentResolverMock).insert(eq(TEST_URI), isNotNull(ContentValues.class));
}
@Test
public void shouldAlwaysPassNonNullContentValuesOnUpdate() throws Exception {
ProviderAction.update(TEST_URI)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), isNotNull(ContentValues.class), isNull(String.class), isNull(String[].class));
}
@Test
public void shouldAllowUsingNullSelectionOnQuery() throws Exception {
ProviderAction.query(TEST_URI)
.where(null)
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), isNull(String[].class), isNull(String.class), isNull(String[].class), isNull(String.class));
}
@Test
public void shouldAllowUsingNullSelectionOnUpdate() throws Exception {
ProviderAction.update(TEST_URI)
.where(null)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), any(ContentValues.class), isNull(String.class), isNull(String[].class));
}
@Test
public void shouldAllowUsingNullSelectionOnDelete() throws Exception {
ProviderAction.delete(TEST_URI)
.where(null)
.perform(contentResolverMock);
verify(contentResolverMock).delete(eq(TEST_URI), isNull(String.class), isNull(String[].class));
}
private static final Object[] NULL_ARGS = null;
@Test
public void shouldAllowUsingNullArgumentsForSelectionOnQuery() throws Exception {
ProviderAction.query(TEST_URI)
.where("col1 IS NULL", NULL_ARGS)
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), isNull(String[].class), eq("(col1 IS NULL)"), isNull(String[].class), isNull(String.class));
}
@Test
public void shouldAllowUsingNullArgumentsForSelectionOnUpdate() throws Exception {
ProviderAction.update(TEST_URI)
.where("col1 IS NULL", NULL_ARGS)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), any(ContentValues.class), eq("(col1 IS NULL)"), isNull(String[].class));
}
@Test
public void shouldAllowUsingNullArgumentsForSelectionOnDelete() throws Exception {
ProviderAction.delete(TEST_URI)
.where("col1 IS NULL", NULL_ARGS)
.perform(contentResolverMock);
verify(contentResolverMock).delete(eq(TEST_URI), eq("(col1 IS NULL)"), isNull(String[].class));
}
@Test
public void shouldAllowUsingNullSelectionAndArgumentsOnQuery() throws Exception {
ProviderAction.query(TEST_URI)
.where(null, NULL_ARGS)
.perform(contentResolverMock);
verify(contentResolverMock).query(eq(TEST_URI), isNull(String[].class), isNull(String.class), isNull(String[].class), isNull(String.class));
}
@Test
public void shouldAllowUsingNullSelectionAndArgumentsOnUpdate() throws Exception {
ProviderAction.update(TEST_URI)
.where(null, NULL_ARGS)
.perform(contentResolverMock);
verify(contentResolverMock).update(eq(TEST_URI), any(ContentValues.class), isNull(String.class), isNull(String[].class));
}
@Test
public void shouldAllowUsingNullSelectionAndArgumentsOnDelete() throws Exception {
ProviderAction.delete(TEST_URI)
.where(null, NULL_ARGS)
.perform(contentResolverMock);
verify(contentResolverMock).delete(eq(TEST_URI), isNull(String.class), isNull(String[].class));
}
@Test(expected = IllegalArgumentException.class)
public void shouldNotAllowUsingNonNullArgumentsWithNullSelectionOnQuery() throws Exception {
ProviderAction.query(TEST_URI)
.where(null, "arg1");
}
@Test(expected = IllegalArgumentException.class)
public void shouldNotAllowUsingNonNullArgumentsWithNullSelectionOnUpdate() throws Exception {
ProviderAction.update(TEST_URI)
.where(null, "arg1");
}
@Test(expected = IllegalArgumentException.class)
public void shouldNotAllowUsingNonNullArgumentsWithNullSelectionOnDelete() throws Exception {
ProviderAction.delete(TEST_URI)
.where(null, "arg1");
}
}