From 1a2e15428098de939c329c58664680cd351e6efd Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Tue, 16 Sep 2025 21:24:50 +0300 Subject: [PATCH] Add repository delegation tests --- .../repository/DefaultHomeRepository.java | 15 ++- .../repository/DefaultHomeRepositoryTest.java | 121 +++++++++++++++--- 2 files changed, 118 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepository.java b/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepository.java index c1ebd017..f3cf6a93 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepository.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepository.java @@ -1,8 +1,12 @@ package com.d4rk.androidtutorials.java.data.repository; +import com.d4rk.androidtutorials.java.data.model.PromotedApp; import com.d4rk.androidtutorials.java.data.source.HomeLocalDataSource; import com.d4rk.androidtutorials.java.data.source.HomeRemoteDataSource; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Default implementation of {@link HomeRepository} combining local and remote sources. */ @@ -34,6 +38,15 @@ public String dailyTip() { @Override public void fetchPromotedApps(PromotedAppsCallback callback) { - remoteDataSource.fetchPromotedApps(callback::onResult); + remoteDataSource.fetchPromotedApps(new HomeRemoteDataSource.PromotedAppsCallback() { + private final AtomicBoolean dispatched = new AtomicBoolean(false); + + @Override + public void onResult(List apps) { + if (dispatched.compareAndSet(false, true)) { + callback.onResult(apps); + } + } + }); } } diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepositoryTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepositoryTest.java index 0205f46c..e01eff33 100644 --- a/app/src/test/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepositoryTest.java +++ b/app/src/test/java/com/d4rk/androidtutorials/java/data/repository/DefaultHomeRepositoryTest.java @@ -1,6 +1,7 @@ package com.d4rk.androidtutorials.java.data.repository; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import com.d4rk.androidtutorials.java.data.model.PromotedApp; @@ -9,58 +10,144 @@ import org.junit.Test; +import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class DefaultHomeRepositoryTest { + private static final List PROMOTED_APPS = + List.of(new PromotedApp("Name", "pkg", "icon")); + @Test - public void repositoryDelegatesToDataSources() { - List promoted = List.of(new PromotedApp("Name", "pkg", "icon")); - FakeHomeRemoteDataSource remote = new FakeHomeRemoteDataSource(promoted); - FakeHomeLocalDataSource local = new FakeHomeLocalDataSource(); + public void getPlayStoreUrlDelegatesToLocalSource() { + SpyHomeLocalDataSource local = new SpyHomeLocalDataSource(); + RecordingHomeRemoteDataSource remote = + new RecordingHomeRemoteDataSource(Collections.emptyList()); + DefaultHomeRepository repository = new DefaultHomeRepository(remote, local); + String result = repository.getPlayStoreUrl(); + + assertEquals("play", result); + assertTrue(local.playStoreUrlCalled); + assertFalse(local.appPlayStoreUrlCalled); + assertFalse(local.dailyTipCalled); + assertFalse(remote.fetchCalled); + } + + @Test + public void getAppPlayStoreUrlDelegatesToLocalSource() { + SpyHomeLocalDataSource local = new SpyHomeLocalDataSource(); + RecordingHomeRemoteDataSource remote = + new RecordingHomeRemoteDataSource(Collections.emptyList()); DefaultHomeRepository repository = new DefaultHomeRepository(remote, local); - assertEquals("play", repository.getPlayStoreUrl()); - assertEquals("play/pkg", repository.getAppPlayStoreUrl("pkg")); - assertEquals("tip", repository.dailyTip()); + String packageName = "pkg"; + String result = repository.getAppPlayStoreUrl(packageName); + + assertEquals("play/pkg", result); + assertTrue(local.appPlayStoreUrlCalled); + assertEquals(packageName, local.lastRequestedPackage); + assertFalse(local.playStoreUrlCalled); + assertFalse(local.dailyTipCalled); + assertFalse(remote.fetchCalled); + } + + @Test + public void dailyTipDelegatesToLocalSource() { + SpyHomeLocalDataSource local = new SpyHomeLocalDataSource(); + RecordingHomeRemoteDataSource remote = + new RecordingHomeRemoteDataSource(Collections.emptyList()); + DefaultHomeRepository repository = new DefaultHomeRepository(remote, local); + + String result = repository.dailyTip(); + + assertEquals("tip", result); + assertTrue(local.dailyTipCalled); + assertFalse(local.playStoreUrlCalled); + assertFalse(local.appPlayStoreUrlCalled); + assertFalse(remote.fetchCalled); + } + + @Test + public void fetchPromotedAppsDelegatesToRemoteSource() { + SpyHomeLocalDataSource local = new SpyHomeLocalDataSource(); + RecordingHomeRemoteDataSource remote = + new RecordingHomeRemoteDataSource(List.of(PROMOTED_APPS)); + DefaultHomeRepository repository = new DefaultHomeRepository(remote, local); AtomicReference> result = new AtomicReference<>(); repository.fetchPromotedApps(result::set); - assertTrue(remote.called); - assertEquals(promoted, result.get()); + + assertTrue(remote.fetchCalled); + assertEquals(PROMOTED_APPS, result.get()); + assertFalse(local.playStoreUrlCalled); + assertFalse(local.appPlayStoreUrlCalled); + assertFalse(local.dailyTipCalled); } - private static class FakeHomeLocalDataSource implements HomeLocalDataSource { + @Test + public void fetchPromotedAppsEmitsOnlyFirstRemoteResponse() { + SpyHomeLocalDataSource local = new SpyHomeLocalDataSource(); + List secondResponse = + List.of(new PromotedApp("Second", "second.pkg", "secondIcon")); + RecordingHomeRemoteDataSource remote = + new RecordingHomeRemoteDataSource(List.of(PROMOTED_APPS, secondResponse)); + DefaultHomeRepository repository = new DefaultHomeRepository(remote, local); + + AtomicInteger callbackCount = new AtomicInteger(); + AtomicReference> result = new AtomicReference<>(); + repository.fetchPromotedApps(apps -> { + callbackCount.incrementAndGet(); + result.set(apps); + }); + + assertTrue(remote.fetchCalled); + assertEquals(1, callbackCount.get()); + assertEquals(PROMOTED_APPS, result.get()); + } + + private static class SpyHomeLocalDataSource implements HomeLocalDataSource { + boolean playStoreUrlCalled; + boolean appPlayStoreUrlCalled; + boolean dailyTipCalled; + String lastRequestedPackage; + @Override public String getPlayStoreUrl() { + playStoreUrlCalled = true; return "play"; } @Override public String getAppPlayStoreUrl(String packageName) { + appPlayStoreUrlCalled = true; + lastRequestedPackage = packageName; return "play/" + packageName; } @Override public String getDailyTip() { + dailyTipCalled = true; return "tip"; } } - private static class FakeHomeRemoteDataSource implements HomeRemoteDataSource { - private final List apps; - boolean called = false; + private static class RecordingHomeRemoteDataSource implements HomeRemoteDataSource { + private final List> responses; + boolean fetchCalled; - FakeHomeRemoteDataSource(List apps) { - this.apps = apps; + RecordingHomeRemoteDataSource(List> responses) { + this.responses = responses; } @Override public void fetchPromotedApps(PromotedAppsCallback callback) { - called = true; - callback.onResult(apps); + fetchCalled = true; + for (List response : responses) { + callback.onResult(response); + } } } }