blob: 77eaa3dbc8266866d4e090a631754b21438c7bb1 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.adservices.service.measurement;
import static com.android.adservices.service.measurement.SystemHealthParams.MAX_TRIGGER_REGISTERS_PER_DESTINATION;
import static com.android.adservices.service.measurement.attribution.TriggerContentProvider.TRIGGER_URI;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyShort;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.adservices.measurement.RegistrationRequest;
import android.adservices.measurement.WebSourceParams;
import android.adservices.measurement.WebSourceRegistrationRequest;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.RemoteException;
import androidx.test.core.app.ApplicationProvider;
import com.android.adservices.data.DbTestUtil;
import com.android.adservices.data.enrollment.EnrollmentDao;
import com.android.adservices.data.measurement.DatastoreException;
import com.android.adservices.data.measurement.DatastoreManager;
import com.android.adservices.data.measurement.IMeasurementDao;
import com.android.adservices.data.measurement.ITransaction;
import com.android.adservices.data.measurement.MeasurementTables;
import com.android.adservices.data.measurement.SQLDatastoreManager;
import com.android.adservices.data.measurement.SqliteObjectMapperAccessor;
import com.android.adservices.service.Flags;
import com.android.adservices.service.FlagsFactory;
import com.android.adservices.service.enrollment.EnrollmentData;
import com.android.adservices.service.measurement.registration.AsyncSourceFetcher;
import com.android.adservices.service.measurement.registration.AsyncTriggerFetcher;
import com.android.adservices.service.measurement.registration.EnqueueAsyncRegistration;
import com.android.adservices.service.measurement.reporting.DebugReportApi;
import com.android.adservices.service.measurement.util.AsyncFetchStatus;
import com.android.adservices.service.measurement.util.AsyncRedirect;
import com.android.adservices.service.measurement.util.UnsignedLong;
import com.android.adservices.service.stats.AdServicesLogger;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.google.common.truth.Truth;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.net.ssl.HttpsURLConnection;
/** Unit tests for {@link AsyncRegistrationQueueRunnerTest} */
public class AsyncRegistrationQueueRunnerTest {
private static final Context sDefaultContext = ApplicationProvider.getApplicationContext();
private static final boolean DEFAULT_AD_ID_PERMISSION = false;
private static final String DEFAULT_ENROLLMENT_ID = "enrollment_id";
private static final Uri DEFAULT_REGISTRANT = Uri.parse("android-app://com.registrant");
private static final Uri DEFAULT_VERIFIED_DESTINATION = Uri.parse("android-app://com.example");
private static final String SDK_PACKAGE_NAME = "sdk.package.name";
private static final Uri APP_TOP_ORIGIN =
Uri.parse("android-app://" + sDefaultContext.getPackageName());
private static final Uri WEB_TOP_ORIGIN = WebUtil.validUri("https://example.test");
private static final Uri REGISTRATION_URI = WebUtil.validUri("https://foo.test/bar?ad=134");
private static final String LIST_TYPE_REDIRECT_URI_1 = WebUtil.validUrl("https://foo.test");
private static final String LIST_TYPE_REDIRECT_URI_2 = WebUtil.validUrl("https://bar.test");
private static final String LOCATION_TYPE_REDIRECT_URI = WebUtil.validUrl("https://baz.test");
private static final Uri WEB_DESTINATION = WebUtil.validUri("https://web-destination.test");
private static final Uri APP_DESTINATION = Uri.parse("android-app://com.app_destination");
private static final Source SOURCE_1 =
SourceFixture.getValidSourceBuilder()
.setEventId(new UnsignedLong(1L))
.setPublisher(APP_TOP_ORIGIN)
.setAppDestinations(List.of(Uri.parse("android-app://com.destination1")))
.setWebDestinations(List.of(WebUtil.validUri("https://web-destination1.test")))
.setEnrollmentId(DEFAULT_ENROLLMENT_ID)
.setRegistrant(Uri.parse("android-app://com.example"))
.setEventTime(new Random().nextLong())
.setExpiryTime(8640000010L)
.setPriority(100L)
.setSourceType(Source.SourceType.EVENT)
.setAttributionMode(Source.AttributionMode.TRUTHFULLY)
.setDebugKey(new UnsignedLong(47823478789L))
.build();
private static final Uri DEFAULT_WEB_DESTINATION =
WebUtil.validUri("https://def-web-destination.test");
private static final Uri ALT_WEB_DESTINATION =
WebUtil.validUri("https://alt-web-destination.test");
private static final Uri ALT_APP_DESTINATION =
Uri.parse("android-app://com.alt-app_destination");
private static final String DEFAULT_REGISTRATION = WebUtil.validUrl("https://foo.test");
private static final Uri DEFAULT_OS_DESTINATION =
Uri.parse("android-app://com.def-os-destination");
private static final WebSourceParams DEFAULT_REGISTRATION_PARAM_LIST =
new WebSourceParams.Builder(Uri.parse(DEFAULT_REGISTRATION))
.setDebugKeyAllowed(true)
.build();
private static final Trigger TRIGGER =
TriggerFixture.getValidTriggerBuilder()
.setAttributionDestination(APP_DESTINATION)
.setDestinationType(EventSurfaceType.APP)
.build();
private AsyncSourceFetcher mAsyncSourceFetcher;
private AsyncTriggerFetcher mAsyncTriggerFetcher;
private Source mMockedSource;
@Mock HttpsURLConnection mUrlConnection1;
@Mock HttpsURLConnection mUrlConnection2;
@Mock private IMeasurementDao mMeasurementDao;
@Mock private Trigger mMockedTrigger;
@Mock private ITransaction mTransaction;
@Mock private EnrollmentDao mEnrollmentDao;
@Mock private ContentResolver mContentResolver;
@Mock private ContentProviderClient mMockContentProviderClient;
@Mock private DebugReportApi mDebugReportApi;
@Mock HttpsURLConnection mUrlConnection;
@Mock Flags mFlags;
@Mock AdServicesLogger mLogger;
private MockitoSession mStaticMockSession;
private static EnrollmentData getEnrollment(String enrollmentId) {
return new EnrollmentData.Builder().setEnrollmentId(enrollmentId).build();
}
class FakeDatastoreManager extends DatastoreManager {
@Override
public ITransaction createNewTransaction() {
return mTransaction;
}
@Override
public IMeasurementDao getMeasurementDao() {
return mMeasurementDao;
}
}
@After
public void cleanup() {
SQLiteDatabase db = DbTestUtil.getDbHelperForTest().getWritableDatabase();
emptyTables(db);
mStaticMockSession.finishMocking();
}
@Before
public void before() throws RemoteException {
mStaticMockSession =
ExtendedMockito.mockitoSession()
.spyStatic(FlagsFactory.class)
.strictness(Strictness.WARN)
.startMocking();
ExtendedMockito.doReturn(FlagsFactory.getFlagsForTest()).when(FlagsFactory::getFlags);
mAsyncSourceFetcher = spy(new AsyncSourceFetcher(sDefaultContext));
mAsyncTriggerFetcher = spy(new AsyncTriggerFetcher(sDefaultContext));
mMockedSource = spy(SourceFixture.getValidSource());
MockitoAnnotations.initMocks(this);
when(mEnrollmentDao.getEnrollmentDataFromMeasurementUrl(any()))
.thenReturn(getEnrollment(DEFAULT_ENROLLMENT_ID));
when(mContentResolver.acquireContentProviderClient(TRIGGER_URI))
.thenReturn(mMockContentProviderClient);
when(mMockContentProviderClient.insert(any(), any())).thenReturn(TRIGGER_URI);
}
@Test
public void test_runAsyncRegistrationQueueWorker_appSource_success() throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
WebUtil.validUri("https://example.test/sF1"),
WebUtil.validUri("https://example.test/sF2")));
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
Source.FakeReport sf = new Source.FakeReport(
new UnsignedLong(1L), 1L, List.of(WebUtil.validUri("https://example.test/sF")));
List<Source.FakeReport> eventReportList = Collections.singletonList(sf);
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, times(1)).insertSource(any(Source.class));
verify(mMeasurementDao, times(2)).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
// Tests for redirect types
@Test
public void runAsyncRegistrationQueueWorker_appSource_defaultRegistration_redirectTypeList()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource();
Answer<Optional<Source>> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
Uri.parse(LIST_TYPE_REDIRECT_URI_1),
Uri.parse(LIST_TYPE_REDIRECT_URI_2)));
asyncRedirect.setRedirectType(AsyncRegistration.RedirectType.NONE);
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
List<Source.FakeReport> eventReportList = Collections.singletonList(
new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION)));
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, times(1)).insertSource(any(Source.class));
verify(mMeasurementDao, times(2)).insertAsyncRegistration(
asyncRegistrationArgumentCaptor.capture());
// Assert 'none' type redirect and values
Assert.assertEquals(2, asyncRegistrationArgumentCaptor.getAllValues().size());
AsyncRegistration asyncReg1 = asyncRegistrationArgumentCaptor.getAllValues().get(0);
Assert.assertEquals(Uri.parse(LIST_TYPE_REDIRECT_URI_1), asyncReg1.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.NONE, asyncReg1.getRedirectType());
Assert.assertEquals(1, asyncReg1.getRedirectCount());
AsyncRegistration asyncReg2 = asyncRegistrationArgumentCaptor.getAllValues().get(1);
Assert.assertEquals(Uri.parse(LIST_TYPE_REDIRECT_URI_2), asyncReg2.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.NONE, asyncReg2.getRedirectType());
Assert.assertEquals(1, asyncReg2.getRedirectCount());
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void runAsyncRegistrationQueueWorker_appSource_defaultRegistration_redirectTypeLocation()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource();
Answer<Optional<Source>> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
Uri.parse(LOCATION_TYPE_REDIRECT_URI)));
asyncRedirect.setRedirectType(AsyncRegistration.RedirectType.DAISY_CHAIN);
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
List<Source.FakeReport> eventReportList = Collections.singletonList(
new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION)));
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, times(1)).insertSource(any(Source.class));
verify(mMeasurementDao, times(1)).insertAsyncRegistration(
asyncRegistrationArgumentCaptor.capture());
// Assert 'location' type redirect and value
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0);
Assert.assertEquals(Uri.parse(LOCATION_TYPE_REDIRECT_URI), asyncReg.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.DAISY_CHAIN, asyncReg.getRedirectType());
Assert.assertEquals(1, asyncReg.getRedirectCount());
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void runAsyncRegistrationQueueWorker_appSource_middleRegistration_redirectTypeLocation()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(
AsyncRegistration.RedirectType.DAISY_CHAIN, 3);
Answer<Optional<Source>> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
Uri.parse(LOCATION_TYPE_REDIRECT_URI)));
asyncRedirect.setRedirectType(AsyncRegistration.RedirectType.DAISY_CHAIN);
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
List<Source.FakeReport> eventReportList = Collections.singletonList(
new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION)));
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, times(1)).insertSource(any(Source.class));
verify(mMeasurementDao, times(1)).insertAsyncRegistration(
asyncRegistrationArgumentCaptor.capture());
// Assert 'location' type redirect and value
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0);
Assert.assertEquals(Uri.parse(LOCATION_TYPE_REDIRECT_URI), asyncReg.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.DAISY_CHAIN, asyncReg.getRedirectType());
Assert.assertEquals(4, asyncReg.getRedirectCount());
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void runAsyncRegistrationQueueWorker_appTrigger_defaultRegistration_redirectTypeList()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger();
Answer<Optional<Trigger>> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
Uri.parse(LIST_TYPE_REDIRECT_URI_1),
Uri.parse(LIST_TYPE_REDIRECT_URI_2)));
asyncRedirect.setRedirectType(AsyncRegistration.RedirectType.NONE);
return Optional.of(mMockedTrigger);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, times(2)).insertAsyncRegistration(
asyncRegistrationArgumentCaptor.capture());
// Assert 'none' type redirect and values
Assert.assertEquals(2, asyncRegistrationArgumentCaptor.getAllValues().size());
AsyncRegistration asyncReg1 = asyncRegistrationArgumentCaptor.getAllValues().get(0);
Assert.assertEquals(Uri.parse(LIST_TYPE_REDIRECT_URI_1), asyncReg1.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.NONE, asyncReg1.getRedirectType());
Assert.assertEquals(1, asyncReg1.getRedirectCount());
AsyncRegistration asyncReg2 = asyncRegistrationArgumentCaptor.getAllValues().get(1);
Assert.assertEquals(Uri.parse(LIST_TYPE_REDIRECT_URI_2), asyncReg2.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.NONE, asyncReg2.getRedirectType());
Assert.assertEquals(1, asyncReg2.getRedirectCount());
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void runAsyncRegistrationQueueWorker_appTrigger_defaultReg_redirectTypeLocation()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger();
Answer<Optional<Trigger>> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
Uri.parse(LOCATION_TYPE_REDIRECT_URI)));
asyncRedirect.setRedirectType(AsyncRegistration.RedirectType.DAISY_CHAIN);
return Optional.of(mMockedTrigger);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, times(1)).insertAsyncRegistration(
asyncRegistrationArgumentCaptor.capture());
// Assert 'location' type redirect and values
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0);
Assert.assertEquals(Uri.parse(LOCATION_TYPE_REDIRECT_URI), asyncReg.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.DAISY_CHAIN, asyncReg.getRedirectType());
Assert.assertEquals(1, asyncReg.getRedirectCount());
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void runAsyncRegistrationQueueWorker_appTrigger_middleRegistration_redirectTypeLocation()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(
AsyncRegistration.RedirectType.DAISY_CHAIN, 4);
Answer<Optional<Trigger>> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
Uri.parse(LOCATION_TYPE_REDIRECT_URI)));
asyncRedirect.setRedirectType(AsyncRegistration.RedirectType.DAISY_CHAIN);
return Optional.of(mMockedTrigger);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, times(1)).insertAsyncRegistration(
asyncRegistrationArgumentCaptor.capture());
// Assert 'location' type redirect and values
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0);
Assert.assertEquals(Uri.parse(LOCATION_TYPE_REDIRECT_URI), asyncReg.getRegistrationUri());
Assert.assertEquals(AsyncRegistration.RedirectType.DAISY_CHAIN, asyncReg.getRedirectType());
Assert.assertEquals(5, asyncReg.getRedirectCount());
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
// End tests for redirect types
@Test
public void test_runAsyncRegistrationQueueWorker_appSource_noRedirects_success()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
Source.FakeReport sf = new Source.FakeReport(
new UnsignedLong(1L), 1L, List.of(WebUtil.validUri("https://example.test/sF")));
List<Source.FakeReport> eventReportList = Collections.singletonList(sf);
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, times(1)).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appSource_adTechUnavailable()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SERVER_UNAVAILABLE);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
WebUtil.validUri("https://example.test/sF1"),
WebUtil.validUri("https://example.test/sF2")));
return Optional.empty();
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
Source.FakeReport sf = new Source.FakeReport(
new UnsignedLong(1L), 1L, List.of(WebUtil.validUri("https://example.test/sF")));
List<Source.FakeReport> eventReportList = Collections.singletonList(sf);
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, never()).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appSource_NetworkError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.NETWORK_ERROR);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
WebUtil.validUri("https://example.test/sF1"),
WebUtil.validUri("https://example.test/sF2")));
return Optional.empty();
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
Source.FakeReport sf = new Source.FakeReport(
new UnsignedLong(1L), 1L, List.of(WebUtil.validUri("https://example.test/sF")));
List<Source.FakeReport> eventReportList = Collections.singletonList(sf);
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, never()).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appSource_parsingError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.PARSING_ERROR);
return Optional.empty();
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, never()).updateRetryCount(any());
verify(mMeasurementDao, never()).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, never()).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appTrigger_success()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
WebUtil.validUri("https://example.test/sF1"),
WebUtil.validUri("https://example.test/sF2")));
return Optional.of(mMockedTrigger);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, times(2)).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appTrigger_noRedirects_success()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
return Optional.of(mMockedTrigger);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appTrigger_adTechUnavailable()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SERVER_UNAVAILABLE);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
WebUtil.validUri("https://example.test/sF1"),
WebUtil.validUri("https://example.test/sF2")));
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appTrigger_networkError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.NETWORK_ERROR);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
WebUtil.validUri("https://example.test/sF1"),
WebUtil.validUri("https://example.test/sF2")));
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_appTrigger_parsingError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.PARSING_ERROR);
AsyncRedirect asyncRedirect = invocation.getArgument(2);
asyncRedirect.addToRedirects(List.of(
WebUtil.validUri("https://example.test/sF1"),
WebUtil.validUri("https://example.test/sF2")));
return Optional.empty();
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, never()).updateRetryCount(any());
verify(mMeasurementDao, never()).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webSource_success() throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
return Optional.of(mMockedSource);
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
Source.FakeReport sf = new Source.FakeReport(
new UnsignedLong(1L), 1L, List.of(WebUtil.validUri("https://example.test/sF")));
List<Source.FakeReport> eventReportList = Collections.singletonList(sf);
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, times(1)).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webSource_adTechUnavailable()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SERVER_UNAVAILABLE);
return Optional.empty();
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
Source.FakeReport sf = new Source.FakeReport(
new UnsignedLong(1L), 1L, List.of(WebUtil.validUri("https://example.test/sF")));
List<Source.FakeReport> eventReportList = Collections.singletonList(sf);
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, never()).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webSource_NetworkError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.NETWORK_ERROR);
return Optional.empty();
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, never()).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webSource_parsingError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebSource();
Answer<?> answerAsyncSourceFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.PARSING_ERROR);
return Optional.empty();
};
doAnswer(answerAsyncSourceFetcher)
.when(mAsyncSourceFetcher)
.fetchSource(any(), any(), any());
Source.FakeReport sf = new Source.FakeReport(
new UnsignedLong(1L), 1L, List.of(WebUtil.validUri("https://example.test/sF")));
List<Source.FakeReport> eventReportList = Collections.singletonList(sf);
when(mMockedSource.assignAttributionModeAndGenerateFakeReports())
.thenReturn(eventReportList);
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncSourceFetcher, times(1))
.fetchSource(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, never()).updateRetryCount(any());
verify(mMeasurementDao, never()).insertEventReport(any(EventReport.class));
verify(mMeasurementDao, never()).insertSource(any(Source.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webTrigger_success()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SUCCESS);
return Optional.of(mMockedTrigger);
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, times(1)).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webTrigger_adTechUnavailable()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.SERVER_UNAVAILABLE);
return Optional.empty();
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webTrigger_networkError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.NETWORK_ERROR);
return Optional.empty();
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor =
ArgumentCaptor.forClass(AsyncRegistration.class);
verify(mMeasurementDao, times(1))
.updateRetryCount(asyncRegistrationArgumentCaptor.capture());
Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size());
Assert.assertEquals(
1, asyncRegistrationArgumentCaptor.getAllValues().get(0).getRetryCount());
verify(mMeasurementDao, never()).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).deleteAsyncRegistration(any(String.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
}
@Test
public void test_runAsyncRegistrationQueueWorker_webTrigger_parsingError()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
AsyncRegistration validAsyncRegistration = createAsyncRegistrationForWebTrigger();
Answer<?> answerAsyncTriggerFetcher =
invocation -> {
AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1);
asyncFetchStatus.setStatus(AsyncFetchStatus.ResponseStatus.PARSING_ERROR);
return Optional.empty();
};
doAnswer(answerAsyncTriggerFetcher)
.when(mAsyncTriggerFetcher)
.fetchTrigger(any(), any(), any());
when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyShort(), any()))
.thenReturn(validAsyncRegistration);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(1L, (short) 5);
// Assertions
verify(mAsyncTriggerFetcher, times(1))
.fetchTrigger(any(AsyncRegistration.class), any(AsyncFetchStatus.class), any());
verify(mMeasurementDao, never()).updateRetryCount(any());
verify(mMeasurementDao, never()).insertTrigger(any(Trigger.class));
verify(mMeasurementDao, never()).insertAsyncRegistration(any(AsyncRegistration.class));
verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class));
}
@Test
public void insertSource_withFakeReportsFalseAppAttribution_accountsForFakeReportAttribution()
throws DatastoreException {
// Setup
int fakeReportsCount = 2;
Source source =
spy(
SourceFixture.getValidSourceBuilder()
.setAppDestinations(
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS)
.setWebDestinations(null)
.build());
List<Source.FakeReport> fakeReports =
createFakeReports(
source,
fakeReportsCount,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS);
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
Answer<?> falseAttributionAnswer =
(arg) -> {
source.setAttributionMode(Source.AttributionMode.FALSELY);
return fakeReports;
};
doAnswer(falseAttributionAnswer).when(source).assignAttributionModeAndGenerateFakeReports();
ArgumentCaptor<Attribution> attributionRateLimitArgCaptor =
ArgumentCaptor.forClass(Attribution.class);
// Execution
asyncRegistrationQueueRunner.insertSourceFromTransaction(source, mMeasurementDao);
// Assertion
verify(mMeasurementDao).insertSource(source);
verify(mMeasurementDao, times(2)).insertEventReport(any());
verify(mMeasurementDao).insertAttribution(attributionRateLimitArgCaptor.capture());
assertEquals(
new Attribution.Builder()
.setDestinationOrigin(source.getAppDestinations().get(0).toString())
.setDestinationSite(source.getAppDestinations().get(0).toString())
.setEnrollmentId(source.getEnrollmentId())
.setSourceOrigin(source.getPublisher().toString())
.setSourceSite(source.getPublisher().toString())
.setRegistrant(source.getRegistrant().toString())
.setTriggerTime(source.getEventTime())
.build(),
attributionRateLimitArgCaptor.getValue());
}
@Test
public void insertSource_withFakeReportsFalseWebAttribution_accountsForFakeReportAttribution()
throws DatastoreException {
// Setup
int fakeReportsCount = 2;
Source source =
spy(
SourceFixture.getValidSourceBuilder()
.setAppDestinations(null)
.setWebDestinations(
SourceFixture.ValidSourceParams.WEB_DESTINATIONS)
.build());
List<Source.FakeReport> fakeReports =
createFakeReports(
source, fakeReportsCount, SourceFixture.ValidSourceParams.WEB_DESTINATIONS);
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
Answer<?> falseAttributionAnswer =
(arg) -> {
source.setAttributionMode(Source.AttributionMode.FALSELY);
return fakeReports;
};
doAnswer(falseAttributionAnswer).when(source).assignAttributionModeAndGenerateFakeReports();
ArgumentCaptor<Attribution> attributionRateLimitArgCaptor =
ArgumentCaptor.forClass(Attribution.class);
// Execution
asyncRegistrationQueueRunner.insertSourceFromTransaction(source, mMeasurementDao);
// Assertion
verify(mMeasurementDao).insertSource(source);
verify(mMeasurementDao, times(2)).insertEventReport(any());
verify(mMeasurementDao).insertAttribution(attributionRateLimitArgCaptor.capture());
assertEquals(
new Attribution.Builder()
.setDestinationOrigin(source.getWebDestinations().get(0).toString())
.setDestinationSite(source.getWebDestinations().get(0).toString())
.setEnrollmentId(source.getEnrollmentId())
.setSourceOrigin(source.getPublisher().toString())
.setSourceSite(source.getPublisher().toString())
.setRegistrant(source.getRegistrant().toString())
.setTriggerTime(source.getEventTime())
.build(),
attributionRateLimitArgCaptor.getValue());
}
@Test
public void insertSource_withFalseAppAndWebAttribution_accountsForFakeReportAttribution()
throws DatastoreException {
// Setup
int fakeReportsCount = 2;
Source source =
spy(
SourceFixture.getValidSourceBuilder()
.setAppDestinations(
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS)
.setWebDestinations(
SourceFixture.ValidSourceParams.WEB_DESTINATIONS)
.build());
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
List<Source.FakeReport> fakeReports =
createFakeReports(
source,
fakeReportsCount,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS);
Answer<?> falseAttributionAnswer =
(arg) -> {
source.setAttributionMode(Source.AttributionMode.FALSELY);
return fakeReports;
};
ArgumentCaptor<Attribution> attributionRateLimitArgCaptor =
ArgumentCaptor.forClass(Attribution.class);
doAnswer(falseAttributionAnswer).when(source).assignAttributionModeAndGenerateFakeReports();
// Execution
asyncRegistrationQueueRunner.insertSourceFromTransaction(source, mMeasurementDao);
// Assertion
verify(mMeasurementDao).insertSource(source);
verify(mMeasurementDao, times(2)).insertEventReport(any());
verify(mMeasurementDao, times(2))
.insertAttribution(attributionRateLimitArgCaptor.capture());
assertEquals(
new Attribution.Builder()
.setDestinationOrigin(source.getAppDestinations().get(0).toString())
.setDestinationSite(source.getAppDestinations().get(0).toString())
.setEnrollmentId(source.getEnrollmentId())
.setSourceOrigin(source.getPublisher().toString())
.setSourceSite(source.getPublisher().toString())
.setRegistrant(source.getRegistrant().toString())
.setTriggerTime(source.getEventTime())
.build(),
attributionRateLimitArgCaptor.getAllValues().get(0));
assertEquals(
new Attribution.Builder()
.setDestinationOrigin(source.getWebDestinations().get(0).toString())
.setDestinationSite(source.getWebDestinations().get(0).toString())
.setEnrollmentId(source.getEnrollmentId())
.setSourceOrigin(source.getPublisher().toString())
.setSourceSite(source.getPublisher().toString())
.setRegistrant(source.getRegistrant().toString())
.setTriggerTime(source.getEventTime())
.build(),
attributionRateLimitArgCaptor.getAllValues().get(1));
}
@Test
public void insertSource_withFakeReportsNeverAppAttribution_accountsForFakeReportAttribution()
throws DatastoreException {
// Setup
Source source =
spy(
SourceFixture.getValidSourceBuilder()
.setAppDestinations(
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS)
.setWebDestinations(null)
.build());
List<Source.FakeReport> fakeReports = Collections.emptyList();
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
getSpyAsyncRegistrationQueueRunner();
Answer<?> neverAttributionAnswer =
(arg) -> {
source.setAttributionMode(Source.AttributionMode.NEVER);
return fakeReports;
};
doAnswer(neverAttributionAnswer).when(source).assignAttributionModeAndGenerateFakeReports();
ArgumentCaptor<Attribution> attributionRateLimitArgCaptor =
ArgumentCaptor.forClass(Attribution.class);
// Execution
asyncRegistrationQueueRunner.insertSourceFromTransaction(source, mMeasurementDao);
// Assertion
verify(mMeasurementDao).insertSource(source);
verify(mMeasurementDao, never()).insertEventReport(any());
verify(mMeasurementDao).insertAttribution(attributionRateLimitArgCaptor.capture());
assertEquals(
new Attribution.Builder()
.setDestinationOrigin(source.getAppDestinations().get(0).toString())
.setDestinationSite(source.getAppDestinations().get(0).toString())
.setEnrollmentId(source.getEnrollmentId())
.setSourceOrigin(source.getPublisher().toString())
.setSourceSite(source.getPublisher().toString())
.setRegistrant(source.getRegistrant().toString())
.setTriggerTime(source.getEventTime())
.build(),
attributionRateLimitArgCaptor.getValue());
}
@Test
public void testRegister_registrationTypeSource_sourceFetchSuccess() throws DatastoreException {
// setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
// Execution
when(mMeasurementDao.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(0));
when(mMeasurementDao.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(0));
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assertions
verify(mMeasurementDao, times(2))
.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong());
verify(mMeasurementDao, times(2))
.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong());
}
@Test
public void testRegister_registrationTypeSource_exceedsPrivacyParam_destination()
throws DatastoreException {
// setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
// Execution
when(mMeasurementDao.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(100));
when(mMeasurementDao.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(0));
boolean status =
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assert
assertFalse(status);
verify(mMeasurementDao, times(1))
.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong());
verify(mMeasurementDao, never())
.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong());
}
@Test
public void testRegister_registrationTypeSource_exceedsMaxSourcesLimit()
throws DatastoreException {
// setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
// Execution
doReturn((long) SystemHealthParams.getMaxSourcesPerPublisher())
.when(mMeasurementDao)
.getNumSourcesPerPublisher(any(), anyInt());
boolean status =
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assert
assertFalse(status);
verify(mMeasurementDao, times(1)).getNumSourcesPerPublisher(any(), anyInt());
}
@Test
public void testRegister_registrationTypeSource_exceedsPrivacyParam_adTech()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
// Execution
when(mMeasurementDao.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(0));
when(mMeasurementDao.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(100));
boolean status =
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assert
assertFalse(status);
verify(mMeasurementDao, times(1))
.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong());
verify(mMeasurementDao, times(1))
.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong());
}
@Test
public void testRegisterWebSource_exceedsPrivacyParam_destination()
throws RemoteException, DatastoreException {
// setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
// Execution
when(mMeasurementDao.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(100));
when(mMeasurementDao.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(0));
boolean status =
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assert
assertFalse(status);
verify(mMockContentProviderClient, never()).insert(any(), any());
verify(mMeasurementDao, times(1))
.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong());
verify(mMeasurementDao, never())
.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong());
}
@Test
public void testRegisterWebSource_exceedsPrivacyParam_adTech() throws DatastoreException {
// setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
// Execution
when(mMeasurementDao.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(0));
when(mMeasurementDao.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong()))
.thenReturn(Integer.valueOf(100));
boolean status =
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assert
assertFalse(status);
verify(mMeasurementDao, times(1))
.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource(
any(), anyInt(), any(), any(), anyInt(), anyLong(), anyLong());
verify(mMeasurementDao, times(1))
.countDistinctEnrollmentsPerPublisherXDestinationInSource(
any(), anyInt(), any(), any(), anyLong(), anyLong());
}
@Test
public void testRegisterWebSource_exceedsMaxSourcesLimit() throws DatastoreException {
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
doReturn((long) SystemHealthParams.getMaxSourcesPerPublisher())
.when(mMeasurementDao)
.getNumSourcesPerPublisher(any(), anyInt());
// Execution
boolean status =
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assertions
assertFalse(status);
}
@Test
public void testRegisterWebSource_LimitsMaxSources_ForWebPublisher_WitheTLDMatch()
throws DatastoreException {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
doReturn((long) SystemHealthParams.getMaxSourcesPerPublisher())
.when(mMeasurementDao)
.getNumSourcesPerPublisher(any(), anyInt());
// Execution
boolean status =
asyncRegistrationQueueRunner.isSourceAllowedToInsert(
SOURCE_1,
SOURCE_1.getPublisher(),
EventSurfaceType.APP,
mMeasurementDao,
mDebugReportApi);
// Assertions
assertFalse(status);
}
@Test
public void testRegisterTrigger_belowSystemHealthLimits_success() throws Exception {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
when(mMeasurementDao.getNumTriggersPerDestination(APP_DESTINATION, EventSurfaceType.APP))
.thenReturn(0L);
Truth.assertThat(
AsyncRegistrationQueueRunner.isTriggerAllowedToInsert(
mMeasurementDao, TRIGGER))
.isTrue();
}
@Test
public void testRegisterTrigger_atSystemHealthLimits_success() throws Exception {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
when(mMeasurementDao.getNumTriggersPerDestination(APP_DESTINATION, EventSurfaceType.APP))
.thenReturn(MAX_TRIGGER_REGISTERS_PER_DESTINATION - 1L);
Truth.assertThat(
AsyncRegistrationQueueRunner.isTriggerAllowedToInsert(
mMeasurementDao, TRIGGER))
.isTrue();
}
@Test
public void testRegisterTrigger_overSystemHealthLimits_failure() throws Exception {
// Setup
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
when(mMeasurementDao.getNumTriggersPerDestination(APP_DESTINATION, EventSurfaceType.APP))
.thenReturn(MAX_TRIGGER_REGISTERS_PER_DESTINATION);
Truth.assertThat(
AsyncRegistrationQueueRunner.isTriggerAllowedToInsert(
mMeasurementDao, TRIGGER))
.isFalse();
}
@Test
public void testRegisterAppSource_redirectOverridesOsButNotWebDestination()
throws DatastoreException, IOException {
// Setup
RegistrationRequest request = buildRequest(DEFAULT_REGISTRATION);
AsyncSourceFetcher mFetcher = spy(new AsyncSourceFetcher(mEnrollmentDao, mFlags, mLogger));
doReturn(mUrlConnection1).when(mFetcher).openUrl(new URL(DEFAULT_REGISTRATION));
doReturn(mUrlConnection2).when(mFetcher).openUrl(new URL("https://foo-redirect.test"));
when(mUrlConnection1.getResponseCode()).thenReturn(200);
when(mUrlConnection2.getResponseCode()).thenReturn(200);
when(mUrlConnection1.getHeaderFields())
.thenReturn(
Map.of(
"Attribution-Reporting-Register-Source",
List.of(
"{\n"
+ " \"priority\": \"123\",\n"
+ " \"expiry\": \"456789\",\n"
+ " \"source_event_id\": \"987654321\",\n"
+ " \"destination\": \""
+ DEFAULT_OS_DESTINATION
+ "\",\n"
+ "\"web_destination\": \""
+ DEFAULT_WEB_DESTINATION
+ "\""
+ "}"),
"Attribution-Reporting-Redirect",
List.of("https://foo-redirect.test")));
when(mUrlConnection2.getHeaderFields())
.thenReturn(
Map.of(
"Attribution-Reporting-Register-Source",
List.of(
"{\n"
+ " \"priority\": \"321\",\n"
+ " \"expiry\": \"987654\",\n"
+ " \"source_event_id\": \"123456789\",\n"
+ " \"destination\": \""
+ ALT_APP_DESTINATION
+ "\",\n"
+ "\"web_destination\": \""
+ ALT_WEB_DESTINATION
+ "\""
+ "}")));
DatastoreManager datastoreManager =
spy(new SQLDatastoreManager(DbTestUtil.getDbHelperForTest()));
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
datastoreManager,
mDebugReportApi));
ArgumentCaptor<DatastoreManager.ThrowingCheckedConsumer> consumerArgCaptor =
ArgumentCaptor.forClass(DatastoreManager.ThrowingCheckedConsumer.class);
EnqueueAsyncRegistration.appSourceOrTriggerRegistrationRequest(
request,
/* adIdPermission */ true,
APP_TOP_ORIGIN,
/* requestTime */ 100,
Source.SourceType.NAVIGATION,
mEnrollmentDao,
datastoreManager);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(2L, (short) 5);
// Assertion
verify(datastoreManager, times(3)).runInTransaction(consumerArgCaptor.capture());
consumerArgCaptor.getValue().accept(mMeasurementDao);
try (Cursor cursor =
DbTestUtil.getDbHelperForTest()
.getReadableDatabase()
.query(
MeasurementTables.SourceContract.TABLE,
null,
null,
null,
null,
null,
null)) {
Assert.assertTrue(cursor.moveToNext());
Source source = SqliteObjectMapperAccessor.constructSourceFromCursor(cursor);
assertEquals(new UnsignedLong(987654321L), source.getEventId());
assertEquals(DEFAULT_WEB_DESTINATION, source.getWebDestinations().get(0));
assertEquals(DEFAULT_OS_DESTINATION, source.getAppDestinations().get(0));
Assert.assertTrue(cursor.moveToNext());
source = SqliteObjectMapperAccessor.constructSourceFromCursor(cursor);
assertEquals(new UnsignedLong(123456789L), source.getEventId());
assertEquals(ALT_WEB_DESTINATION, source.getWebDestinations().get(0));
assertEquals(DEFAULT_OS_DESTINATION, source.getAppDestinations().get(0));
}
}
@Test
public void testRegisterWebSource_failsWebAndOsDestinationVerification()
throws DatastoreException, IOException {
// Setup
AsyncSourceFetcher mFetcher = spy(new AsyncSourceFetcher(mEnrollmentDao, mFlags, mLogger));
WebSourceRegistrationRequest request =
buildWebSourceRegistrationRequest(
Collections.singletonList(DEFAULT_REGISTRATION_PARAM_LIST),
WEB_TOP_ORIGIN.toString(),
DEFAULT_OS_DESTINATION,
DEFAULT_WEB_DESTINATION);
doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(DEFAULT_REGISTRATION));
when(mUrlConnection.getResponseCode()).thenReturn(200);
when(mUrlConnection.getHeaderFields())
.thenReturn(
Map.of(
"Attribution-Reporting-Register-Source",
List.of(
"{\n"
+ " \"destination\": \""
+ ALT_APP_DESTINATION
+ "\",\n"
+ " \"priority\": \"123\",\n"
+ " \"expiry\": \"456789\",\n"
+ " \"source_event_id\": \"987654321\",\n"
+ "\"web_destination\": \""
+ ALT_WEB_DESTINATION
+ "\""
+ "}")));
DatastoreManager datastoreManager =
spy(new SQLDatastoreManager(DbTestUtil.getDbHelperForTest()));
AsyncRegistrationQueueRunner asyncRegistrationQueueRunner =
spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
datastoreManager,
mDebugReportApi));
ArgumentCaptor<DatastoreManager.ThrowingCheckedConsumer> consumerArgCaptor =
ArgumentCaptor.forClass(DatastoreManager.ThrowingCheckedConsumer.class);
EnqueueAsyncRegistration.webSourceRegistrationRequest(
request,
DEFAULT_AD_ID_PERMISSION,
APP_TOP_ORIGIN,
100,
Source.SourceType.NAVIGATION,
mEnrollmentDao,
datastoreManager);
// Execution
asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(2L, (short) 5);
// Assertion
verify(datastoreManager, times(2)).runInTransaction(consumerArgCaptor.capture());
consumerArgCaptor.getValue().accept(mMeasurementDao);
try (Cursor cursor =
DbTestUtil.getDbHelperForTest()
.getReadableDatabase()
.query(
MeasurementTables.SourceContract.TABLE,
null,
null,
null,
null,
null,
null)) {
Assert.assertFalse(cursor.moveToNext());
}
}
private RegistrationRequest buildRequest(String registrationUri) {
return new RegistrationRequest.Builder(
RegistrationRequest.REGISTER_SOURCE,
Uri.parse(registrationUri),
sDefaultContext.getAttributionSource().getPackageName(),
SDK_PACKAGE_NAME)
.build();
}
private WebSourceRegistrationRequest buildWebSourceRegistrationRequest(
List<WebSourceParams> sourceParamsList,
String topOrigin,
Uri appDestination,
Uri webDestination) {
WebSourceRegistrationRequest.Builder webSourceRegistrationRequestBuilder =
new WebSourceRegistrationRequest.Builder(sourceParamsList, Uri.parse(topOrigin))
.setAppDestination(appDestination);
if (webDestination != null) {
webSourceRegistrationRequestBuilder.setWebDestination(webDestination);
}
return webSourceRegistrationRequestBuilder.build();
}
private List<Source.FakeReport> createFakeReports(
Source source, int count, List<Uri> destinations) {
return IntStream.range(0, count)
.mapToObj(
x ->
new Source.FakeReport(
new UnsignedLong(0L),
source.getReportingTimeForNoising(0),
destinations))
.collect(Collectors.toList());
}
private static AsyncRegistration createAsyncRegistrationForAppSource() {
return createAsyncRegistrationForAppSource(AsyncRegistration.RedirectType.ANY, 0);
}
private static AsyncRegistration createAsyncRegistrationForAppSource(
@AsyncRegistration.RedirectType int redirectType, int redirectCount) {
return new AsyncRegistration.Builder()
.setId(UUID.randomUUID().toString())
.setEnrollmentId(DEFAULT_ENROLLMENT_ID)
.setRegistrationUri(REGISTRATION_URI)
// null .setWebDestination(webDestination)
// null .setOsDestination(osDestination)
.setRegistrant(DEFAULT_REGISTRANT)
// null .setVerifiedDestination(null)
.setTopOrigin(APP_TOP_ORIGIN)
.setType(AsyncRegistration.RegistrationType.APP_SOURCE.ordinal())
.setSourceType(Source.SourceType.EVENT)
.setRequestTime(System.currentTimeMillis())
.setRetryCount(0)
.setLastProcessingTime(System.currentTimeMillis())
.setRedirectType(redirectType)
.setRedirectCount(redirectCount)
.setDebugKeyAllowed(true)
.build();
}
private static AsyncRegistration createAsyncRegistrationForAppTrigger() {
return createAsyncRegistrationForAppTrigger(AsyncRegistration.RedirectType.ANY, 0);
}
private static AsyncRegistration createAsyncRegistrationForAppTrigger(
@AsyncRegistration.RedirectType int redirectType, int redirectCount) {
return new AsyncRegistration.Builder()
.setId(UUID.randomUUID().toString())
.setEnrollmentId(DEFAULT_ENROLLMENT_ID)
.setRegistrationUri(REGISTRATION_URI)
// null .setWebDestination(webDestination)
// null .setOsDestination(osDestination)
.setRegistrant(DEFAULT_REGISTRANT)
// null .setVerifiedDestination(null)
.setTopOrigin(APP_TOP_ORIGIN)
.setType(AsyncRegistration.RegistrationType.APP_TRIGGER.ordinal())
// null .setSourceType(null)
.setRequestTime(System.currentTimeMillis())
.setRetryCount(0)
.setLastProcessingTime(System.currentTimeMillis())
.setRedirectType(redirectType)
.setRedirectCount(redirectCount)
.setDebugKeyAllowed(true)
.build();
}
private static AsyncRegistration createAsyncRegistrationForWebSource() {
return new AsyncRegistration.Builder()
.setId(UUID.randomUUID().toString())
.setEnrollmentId(DEFAULT_ENROLLMENT_ID)
.setRegistrationUri(REGISTRATION_URI)
.setWebDestination(WEB_DESTINATION)
.setOsDestination(APP_DESTINATION)
.setRegistrant(DEFAULT_REGISTRANT)
.setVerifiedDestination(DEFAULT_VERIFIED_DESTINATION)
.setTopOrigin(WEB_TOP_ORIGIN)
.setType(AsyncRegistration.RegistrationType.WEB_SOURCE.ordinal())
.setSourceType(Source.SourceType.EVENT)
.setRequestTime(System.currentTimeMillis())
.setRetryCount(0)
.setLastProcessingTime(System.currentTimeMillis())
.setRedirectType(AsyncRegistration.RedirectType.NONE)
.setDebugKeyAllowed(true)
.build();
}
private static AsyncRegistration createAsyncRegistrationForWebTrigger() {
return new AsyncRegistration.Builder()
.setId(UUID.randomUUID().toString())
.setEnrollmentId(DEFAULT_ENROLLMENT_ID)
.setRegistrationUri(REGISTRATION_URI)
// null .setWebDestination(webDestination)
// null .setOsDestination(osDestination)
.setRegistrant(DEFAULT_REGISTRANT)
// null .setVerifiedDestination(null)
.setTopOrigin(WEB_TOP_ORIGIN)
.setType(AsyncRegistration.RegistrationType.WEB_TRIGGER.ordinal())
// null .setSourceType(null)
.setRequestTime(System.currentTimeMillis())
.setRetryCount(0)
.setLastProcessingTime(System.currentTimeMillis())
.setRedirectType(AsyncRegistration.RedirectType.NONE)
.setDebugKeyAllowed(true)
.build();
}
private AsyncRegistrationQueueRunner getSpyAsyncRegistrationQueueRunner() {
return spy(
new AsyncRegistrationQueueRunner(
mContentResolver,
mAsyncSourceFetcher,
mAsyncTriggerFetcher,
mEnrollmentDao,
new FakeDatastoreManager(),
mDebugReportApi));
}
private static void emptyTables(SQLiteDatabase db) {
db.delete("msmt_source", null, null);
db.delete("msmt_trigger", null, null);
db.delete("msmt_event_report", null, null);
db.delete("msmt_attribution", null, null);
db.delete("msmt_aggregate_report", null, null);
db.delete("enrollment_data", null, null);
db.delete("msmt_async_registration_contract", null, null);
}
}