diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 15f428a8..a9d8f0ac 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -198,7 +198,7 @@ dependencies { implementation(libs.google.exoplayer.mediasession) // Spotify SDK - implementation(files("./lib/spotify-app-remote-release-0.7.2.aar")) + api(project(":spotify-app-remote")) // Networking and parsing implementation(libs.jsoup) diff --git a/app/src/androidTest/java/org/listenbrainz/android/UserPagesTest.kt b/app/src/androidTest/java/org/listenbrainz/android/UserPagesTest.kt new file mode 100644 index 00000000..b03b7583 --- /dev/null +++ b/app/src/androidTest/java/org/listenbrainz/android/UserPagesTest.kt @@ -0,0 +1,182 @@ +package org.listenbrainz.android + +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performScrollToIndex +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestDispatcher +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.listenbrainz.android.ui.screens.profile.BaseProfileScreen +import org.listenbrainz.android.ui.theme.ListenBrainzTheme +import org.listenbrainz.android.viewmodel.FeedViewModel +import org.listenbrainz.android.viewmodel.ListensViewModel +import org.listenbrainz.android.viewmodel.SocialViewModel +import org.listenbrainz.android.viewmodel.UserViewModel +import org.listenbrainz.sharedtest.mocks.MockAppPreferences +import org.listenbrainz.sharedtest.mocks.MockFeedRepository +import org.listenbrainz.sharedtest.mocks.MockListensRepository +import org.listenbrainz.sharedtest.mocks.MockRemotePlaybackHandler +import org.listenbrainz.sharedtest.mocks.MockSocialRepository +import org.listenbrainz.sharedtest.mocks.MockSocketRepository +import org.listenbrainz.sharedtest.mocks.MockUserRepository +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUsername + +@RunWith(AndroidJUnit4::class) +@HiltAndroidTest +class UserPagesTest { + + @get:Rule(order = 0) + var hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val rule = createComposeRule() + + private lateinit var viewModel: UserViewModel + private lateinit var feedViewModel: FeedViewModel + private lateinit var listensViewModel: ListensViewModel + private lateinit var socialViewModel: SocialViewModel + + @Before + fun setup() { + val testDispatcher: TestDispatcher = StandardTestDispatcher() + viewModel = UserViewModel( + MockAppPreferences(), + MockUserRepository(), + MockListensRepository(), + MockSocialRepository(), + testDispatcher + ) + feedViewModel = FeedViewModel( + feedRepository = MockFeedRepository(), + socialRepository = MockSocialRepository(), + listensRepository = MockListensRepository(), + appPreferences = MockAppPreferences(), + remotePlaybackHandler = MockRemotePlaybackHandler(), + testDispatcher, + testDispatcher + ) + listensViewModel = ListensViewModel( + repository = MockListensRepository(), + appPreferences = MockAppPreferences(), + socketRepository = MockSocketRepository(), + remotePlaybackHandler = MockRemotePlaybackHandler(), + testDispatcher + ) + socialViewModel = SocialViewModel( + repository = MockSocialRepository(), + appPreferences = MockAppPreferences(), + remotePlaybackHandler = MockRemotePlaybackHandler(), + testDispatcher + ) + + rule.setContent { + runBlocking { + viewModel.getUserDataFromRemote(testUsername) + testDispatcher.scheduler.advanceUntilIdle() + } + val uiState by viewModel.uiState.collectAsState() + ListenBrainzTheme { + Scaffold { + it -> + BaseProfileScreen( + username = testUsername, + snackbarState = remember { + SnackbarHostState() + }, + uiState = uiState, + onFollowClick = {}, + onUnfollowClick = {}, + goToUserProfile = { /*TODO*/ }, + viewModel = viewModel, + feedViewModel = feedViewModel, + socialViewModel = socialViewModel, + listensViewModel = listensViewModel, + ) + } + + } + } + } + + @Test + fun allTabsExistenceTest() { + // Verify that all tabs ("Listens", "Stats", "Taste") exist on the main screen + rule.onNodeWithText("Listens").assertExists() + rule.onNodeWithText("Stats").assertExists() + rule.onNodeWithText("Taste").assertExists() + } + + @Test + fun listensTabScreenFlowTest() { + // Navigate to the "Listens" tab + rule.onNodeWithText("Listens").performClick() + + // Verify that key elements on the "Listens" screen are present + rule.onNodeWithText("You have listened to").assertExists() + rule.onNodeWithText("Recent Listens").assertExists() + rule.onNodeWithText("Followers").assertExists() + + // Locate the scrollable container for the "Listens" screen + val scrollableContainer = rule.onNodeWithTag("listensScreenScrollableContainer") + + // Scroll to a specific index to ensure additional content is loaded + scrollableContainer.performScrollToIndex(10) + + // Verify that more elements are present after scrolling + rule.onNodeWithText("Similar Users").assertExists() + } + + @Test + fun statsTabScreenFlowTest() { + // Navigate to the "Stats" tab + rule.onNodeWithText("Stats").performClick() + + // Verify that key elements on the "Stats" screen are present + rule.onNodeWithText("Global").assertExists() + rule.onNodeWithText("This Week").assertExists() + rule.onNodeWithText("This Month").assertExists() + rule.onNodeWithText("This Year").assertExists() + + // Locate the scrollable container for the "Stats" screen + val scrollableContainer = rule.onNodeWithTag("statsScreenScrollableContainer") + + // Scroll to a specific index to ensure additional content is loaded + scrollableContainer.performScrollToIndex(2) + + // Verify that more elements are present after scrolling + rule.onNodeWithText("Artists").assertExists() + rule.onNodeWithText("Albums").assertExists() + rule.onNodeWithText("Songs").assertExists() + + // Scroll further to check the presence of additional content + scrollableContainer.performScrollToIndex(3) + rule.onNodeWithText("Load More").assertExists() + } + + @Test + fun tasteTabScreenFlowTest() { + // Navigate to the "Taste" tab + rule.onNodeWithText("Taste").performClick() + + // Verify that key elements on the "Taste" screen are present + rule.onNodeWithText("Loved").assertExists() + rule.onNodeWithText("Hated").assertExists() + rule.onNodeWithText("Pins").assertExists() + } + +} diff --git a/app/src/main/java/org/listenbrainz/android/model/PinnedRecording.kt b/app/src/main/java/org/listenbrainz/android/model/PinnedRecording.kt index 2aa06bf5..2b596fe2 100644 --- a/app/src/main/java/org/listenbrainz/android/model/PinnedRecording.kt +++ b/app/src/main/java/org/listenbrainz/android/model/PinnedRecording.kt @@ -2,6 +2,10 @@ package org.listenbrainz.android.model import com.google.gson.annotations.SerializedName +data class CurrentPins( + @SerializedName("pinned_recording") val pinnedRecording: PinnedRecording? = null +) + data class PinnedRecording( @SerializedName("created" ) val created: Float? = null, @SerializedName("row_id" ) val rowId: Int? = null, diff --git a/app/src/main/java/org/listenbrainz/android/repository/user/UserRepository.kt b/app/src/main/java/org/listenbrainz/android/repository/user/UserRepository.kt index 3d625c65..571486df 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/user/UserRepository.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/user/UserRepository.kt @@ -1,5 +1,6 @@ package org.listenbrainz.android.repository.user +import org.listenbrainz.android.model.CurrentPins import org.listenbrainz.android.model.Listens import org.listenbrainz.android.model.PinnedRecording import org.listenbrainz.android.model.user.AllPinnedRecordings @@ -14,7 +15,7 @@ import org.listenbrainz.android.util.Resource interface UserRepository { suspend fun fetchUserListenCount (username: String?) : Resource suspend fun fetchUserSimilarity(username: String? , otherUserName: String?) : Resource - suspend fun fetchUserCurrentPins(username: String?) : Resource + suspend fun fetchUserCurrentPins(username: String?) : Resource suspend fun fetchUserPins(username: String?) : Resource //TODO: Move to artists VM once implemented suspend fun getTopArtists(username: String?, rangeString: String = "all_time", count: Int = 25): Resource diff --git a/app/src/main/java/org/listenbrainz/android/repository/user/UserRepositoryImpl.kt b/app/src/main/java/org/listenbrainz/android/repository/user/UserRepositoryImpl.kt index 45b67023..1b96747f 100644 --- a/app/src/main/java/org/listenbrainz/android/repository/user/UserRepositoryImpl.kt +++ b/app/src/main/java/org/listenbrainz/android/repository/user/UserRepositoryImpl.kt @@ -1,5 +1,6 @@ package org.listenbrainz.android.repository.user +import org.listenbrainz.android.model.CurrentPins import org.listenbrainz.android.model.Listens import org.listenbrainz.android.model.PinnedRecording import org.listenbrainz.android.model.ResponseError @@ -29,7 +30,7 @@ class UserRepositoryImpl @Inject constructor( service.getUserSimilarity(username,otherUserName) } - override suspend fun fetchUserCurrentPins(username: String?): Resource = parseResponse { + override suspend fun fetchUserCurrentPins(username: String?): Resource = parseResponse { if(username.isNullOrEmpty()) return ResponseError.BAD_REQUEST.asResource() service.getUserCurrentPins(username) } diff --git a/app/src/main/java/org/listenbrainz/android/service/UserService.kt b/app/src/main/java/org/listenbrainz/android/service/UserService.kt index 82f01a2d..641932a7 100644 --- a/app/src/main/java/org/listenbrainz/android/service/UserService.kt +++ b/app/src/main/java/org/listenbrainz/android/service/UserService.kt @@ -1,7 +1,7 @@ package org.listenbrainz.android.service +import org.listenbrainz.android.model.CurrentPins import org.listenbrainz.android.model.Listens -import org.listenbrainz.android.model.PinnedRecording import org.listenbrainz.android.model.user.AllPinnedRecordings import org.listenbrainz.android.model.user.TopAlbums import org.listenbrainz.android.model.user.TopArtists @@ -22,7 +22,7 @@ interface UserService { suspend fun getUserSimilarity(@Path("user_name") username: String? , @Path("other_user_name") otherUserName: String?) : Response @GET("{user_name}/pins/current") - suspend fun getUserCurrentPins(@Path("user_name") username: String?) : Response + suspend fun getUserCurrentPins(@Path("user_name") username: String?) : Response @GET("{user_name}/pins") suspend fun getUserPins(@Path("user_name") username: String?) : Response diff --git a/app/src/main/java/org/listenbrainz/android/ui/components/ListenCardSmall.kt b/app/src/main/java/org/listenbrainz/android/ui/components/ListenCardSmall.kt index e8e27015..328e473d 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/components/ListenCardSmall.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/components/ListenCardSmall.kt @@ -2,6 +2,7 @@ package org.listenbrainz.android.ui.components import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.annotation.DrawableRes +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -13,6 +14,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -22,6 +24,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.FilterQuality import androidx.compose.ui.layout.ContentScale @@ -51,6 +54,7 @@ fun ListenCardSmall( trackName: String, artistName: String, coverArtUrl: String?, + listenCount: Int? = null, @DrawableRes errorAlbumArt: Int = R.drawable.ic_coverartarchive_logo_no_text, enableDropdownIcon: Boolean = false, onDropdownIconClick: () -> Unit = {}, @@ -100,16 +104,28 @@ fun ListenCardSmall( Spacer(modifier = Modifier.width(ListenBrainzTheme.paddings.coverArtAndTextGap)) TitleAndSubtitle(modifier = Modifier.padding(end = 6.dp), title = trackName, subtitle = artistName, titleColor = titleColor, subtitleColor = subtitleColor) - } - + Box( modifier = modifier .fillMaxWidth(1f - mainContentFraction) .align(Alignment.CenterEnd), contentAlignment = Alignment.Center ) { - + if(listenCount != null){ + Box( + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .background(ListenBrainzTheme.colorScheme.followerChipSelected) + .padding(6.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = listenCount.toString(), + color = Color.Black + ) + } + } // Trailing content if (enableTrailingContent) { trailingContent( diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/BaseProfileScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/BaseProfileScreen.kt index 669f4f27..9530cde7 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/BaseProfileScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/BaseProfileScreen.kt @@ -55,17 +55,23 @@ import org.listenbrainz.android.ui.theme.lb_orange import org.listenbrainz.android.ui.theme.lb_purple import org.listenbrainz.android.ui.theme.new_app_bg_light import org.listenbrainz.android.util.Constants -import org.listenbrainz.android.viewmodel.ProfileViewModel +import org.listenbrainz.android.viewmodel.FeedViewModel +import org.listenbrainz.android.viewmodel.ListensViewModel +import org.listenbrainz.android.viewmodel.SocialViewModel +import org.listenbrainz.android.viewmodel.UserViewModel @Composable fun BaseProfileScreen( username: String?, snackbarState: SnackbarHostState, - viewModel: ProfileViewModel = hiltViewModel(), + viewModel: UserViewModel = hiltViewModel(), uiState: ProfileUiState, onFollowClick: (String) -> Unit, onUnfollowClick: (String) -> Unit, goToUserProfile: () -> Unit, + feedViewModel: FeedViewModel = hiltViewModel(), + listensViewModel: ListensViewModel = hiltViewModel(), + socialViewModel: SocialViewModel = hiltViewModel() ){ val currentTab : MutableState = remember { mutableStateOf(ProfileScreenTab.LISTENS) } @@ -102,12 +108,15 @@ fun BaseProfileScreen( Spacer(modifier = Modifier.width(ListenBrainzTheme.paddings.chipsHorizontal / 2)) repeat(5) { position -> when(position){ - 0 -> Box(modifier = Modifier.padding(ListenBrainzTheme.paddings.chipsHorizontal,) .clip(shape = RoundedCornerShape(4.dp)).background( - when(uiState.isSelf){ - true -> lb_purple - false -> lb_orange - } - )) { + 0 -> Box(modifier = Modifier + .padding(ListenBrainzTheme.paddings.chipsHorizontal,) + .clip(shape = RoundedCornerShape(4.dp)) + .background( + when (uiState.isSelf) { + true -> lb_purple + false -> lb_orange + } + )) { Row (modifier = Modifier.padding(end = 8.dp, top = when(uiState.isSelf){ true -> 4.dp false -> 0.dp @@ -201,23 +210,36 @@ fun BaseProfileScreen( when(currentTab.value) { ProfileScreenTab.LISTENS -> ListensScreen( scrollRequestState = false, - profileViewModel = viewModel, + userViewModel = viewModel, onScrollToTop = {}, snackbarState = snackbarState, - username = username + username = username, + feedViewModel = feedViewModel, + socialViewModel = socialViewModel, + viewModel = listensViewModel ) ProfileScreenTab.STATS -> StatsScreen( - username = username + username = username, + snackbarState = snackbarState, + socialViewModel = socialViewModel, + viewModel = viewModel, + feedViewModel = feedViewModel ) ProfileScreenTab.TASTE -> TasteScreen( snackbarState = snackbarState, + socialViewModel = socialViewModel, + feedViewModel = feedViewModel, + viewModel = viewModel ) else -> ListensScreen( scrollRequestState = false, - profileViewModel = viewModel, + userViewModel = viewModel, onScrollToTop = {}, snackbarState = snackbarState, username = username, + feedViewModel = feedViewModel, + socialViewModel = socialViewModel, + viewModel = listensViewModel ) } diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt index d0515be7..12b885b7 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/ProfileScreen.kt @@ -37,12 +37,12 @@ import com.airbnb.lottie.compose.LottieConstants import com.airbnb.lottie.compose.rememberLottieComposition import org.listenbrainz.android.R import org.listenbrainz.android.util.Constants.Strings.STATUS_LOGGED_IN -import org.listenbrainz.android.viewmodel.ProfileViewModel +import org.listenbrainz.android.viewmodel.UserViewModel @Composable fun ProfileScreen( context: Context = LocalContext.current, - viewModel: ProfileViewModel = hiltViewModel(), + viewModel: UserViewModel = hiltViewModel(), scrollRequestState: Boolean, onScrollToTop: (suspend () -> Unit) -> Unit, username: String?, diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/listens/ListensScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/listens/ListensScreen.kt index 1c1278f0..69a75256 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/listens/ListensScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/listens/ListensScreen.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.UriHandler +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextAlign @@ -53,7 +54,6 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel import kotlinx.coroutines.launch import org.listenbrainz.android.R import org.listenbrainz.android.model.Listen @@ -83,22 +83,22 @@ import org.listenbrainz.android.ui.theme.lb_purple_night import org.listenbrainz.android.util.Utils.getCoverArtUrl import org.listenbrainz.android.viewmodel.FeedViewModel import org.listenbrainz.android.viewmodel.ListensViewModel -import org.listenbrainz.android.viewmodel.ProfileViewModel import org.listenbrainz.android.viewmodel.SocialViewModel +import org.listenbrainz.android.viewmodel.UserViewModel @Composable fun ListensScreen( - viewModel: ListensViewModel = hiltViewModel(), - profileViewModel: ProfileViewModel, - socialViewModel: SocialViewModel = hiltViewModel(), - feedViewModel : FeedViewModel = hiltViewModel(), + viewModel: ListensViewModel, + userViewModel: UserViewModel, + socialViewModel: SocialViewModel, + feedViewModel : FeedViewModel, scrollRequestState: Boolean, onScrollToTop: (suspend () -> Unit) -> Unit, snackbarState : SnackbarHostState, username: String?, ) { - val uiState by profileViewModel.uiState.collectAsState() + val uiState by userViewModel.uiState.collectAsState() val preferencesUiState by viewModel.preferencesUiState.collectAsState() val socialUiState by socialViewModel.uiState.collectAsState() val feedUiState by feedViewModel.uiState.collectAsState() @@ -158,10 +158,10 @@ fun ListensScreen( it, status -> if(!username.isNullOrEmpty()) { if(!status){ - profileViewModel.followUser(it) + userViewModel.followUser(it) } else{ - profileViewModel.unfollowUser(it) + userViewModel.unfollowUser(it) } } } @@ -241,7 +241,7 @@ fun ListensScreen( } AnimatedVisibility(visible = !uiState.listensTabUiState.isLoading) { - LazyColumn(state = listState) { + LazyColumn(state = listState, modifier = Modifier.testTag("listensScreenScrollableContainer")) { item { SongsListened(username = username, listenCount = uiState.listensTabUiState.listenCount, isSelf = uiState.isSelf) } diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/stats/StatsScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/stats/StatsScreen.kt index 421a48c8..956c0bd3 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/stats/StatsScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/stats/StatsScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ElevatedSuggestionChip import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SuggestionChipDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -30,15 +31,20 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.platform.UriHandler +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottomAxis import com.patrykandpatrick.vico.compose.cartesian.axis.rememberStartAxis @@ -51,23 +57,48 @@ import com.patrykandpatrick.vico.core.cartesian.data.columnSeries import com.patrykandpatrick.vico.core.cartesian.layer.ColumnCartesianLayer import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.listenbrainz.android.R +import org.listenbrainz.android.model.AdditionalInfo +import org.listenbrainz.android.model.MbidMapping +import org.listenbrainz.android.model.Metadata +import org.listenbrainz.android.model.SocialUiState +import org.listenbrainz.android.model.TrackMetadata +import org.listenbrainz.android.model.feed.ReviewEntityType +import org.listenbrainz.android.ui.components.ErrorBar import org.listenbrainz.android.ui.components.ListenCardSmall +import org.listenbrainz.android.ui.components.SuccessBar +import org.listenbrainz.android.ui.components.dialogs.Dialog +import org.listenbrainz.android.ui.components.dialogs.rememberDialogsState +import org.listenbrainz.android.ui.screens.feed.FeedUiState +import org.listenbrainz.android.ui.screens.feed.SocialDropdown import org.listenbrainz.android.ui.screens.profile.ProfileUiState +import org.listenbrainz.android.ui.screens.profile.listens.Dialogs +import org.listenbrainz.android.ui.screens.profile.listens.ListenDialogBundleKeys import org.listenbrainz.android.ui.screens.profile.listens.LoadMoreButton import org.listenbrainz.android.ui.theme.ListenBrainzTheme import org.listenbrainz.android.ui.theme.app_bg_secondary_dark import org.listenbrainz.android.ui.theme.lb_purple_night import org.listenbrainz.android.util.Utils.getCoverArtUrl -import org.listenbrainz.android.viewmodel.ProfileViewModel +import org.listenbrainz.android.viewmodel.FeedViewModel +import org.listenbrainz.android.viewmodel.SocialViewModel +import org.listenbrainz.android.viewmodel.UserViewModel @Composable fun StatsScreen( username: String?, - viewModel: ProfileViewModel = hiltViewModel(), + viewModel: UserViewModel, + socialViewModel: SocialViewModel, + feedViewModel : FeedViewModel, + snackbarState : SnackbarHostState, ) { val uiState by viewModel.uiState.collectAsState() + val socialUiState by socialViewModel.uiState.collectAsState() + val feedUiState by feedViewModel.uiState.collectAsState() + val dropdownItemIndex: MutableState = rememberSaveable { + mutableStateOf(null) + } val statsRangeState: MutableState = remember { mutableStateOf(StatsRange.THIS_WEEK) } @@ -94,53 +125,48 @@ fun StatsScreen( }, fetchTopSongs = { viewModel.getUserTopSongs(it) + }, + dropdownItemIndex = dropdownItemIndex, + feedUiState = feedUiState, + playListen = { + socialViewModel.playListen(it) + }, + snackbarState = snackbarState, + socialUiState = socialUiState, + onRecommend = {metadata -> + socialViewModel.recommend(metadata) + dropdownItemIndex.value = null + }, + onErrorShown = { + socialViewModel.clearErrorFlow() + }, + onMessageShown = { + socialViewModel.clearMsgFlow() + }, + onPin = { + metadata, blurbContent -> socialViewModel.pin(metadata , blurbContent) + dropdownItemIndex.value = null + }, + searchUsers = { + query -> feedViewModel.searchUser(query) + }, + isCritiqueBrainzLinked = { + feedViewModel.isCritiqueBrainzLinked() + }, + onReview = { + type, blurbContent, rating, locale, metadata -> socialViewModel.review(metadata , type , blurbContent , rating , locale) + }, + onPersonallyRecommend = { + metadata, users, blurbContent -> socialViewModel.personallyRecommend(metadata, users, blurbContent) } ) } - -fun weekFormatter ( - value: Int -): String { - val weekDay = when(value){ - 0 -> "Mon" - 1 -> "Tue" - 2 -> "Wed" - 3 -> "Thu" - 4 -> "Fri" - 5 -> "Sat" - 6 -> "Sun" - else -> "" - } - return weekDay -} - -fun yearFormatter( - value: Int -): String { - val month = when(value){ - 0 -> "Jan" - 1 -> "Feb" - 2 -> "Mar" - 3 -> "Apr" - 4 -> "May" - 5 -> "Jun" - 6 -> "Jul" - 7 -> "Aug" - 8 -> "Sep" - 9 -> "Oct" - 10 -> "Nov" - 11 -> "Dec" - else -> "" - } - return month -} - - @Composable fun StatsScreen( username: String?, uiState: ProfileUiState, + uriHandler: UriHandler = LocalUriHandler.current, statsRangeState: StatsRange, setStatsRange: (StatsRange) -> Unit, userGlobalState: UserGlobal, @@ -148,24 +174,37 @@ fun StatsScreen( fetchTopArtists: suspend (String?) -> Unit, fetchTopAlbums: suspend (String?) -> Unit, fetchTopSongs: suspend (String?) -> Unit, + dropdownItemIndex : MutableState, + feedUiState: FeedUiState, + playListen: (TrackMetadata) -> Unit, + snackbarState: SnackbarHostState, + socialUiState: SocialUiState, + onRecommend : (metadata : Metadata) -> Unit, + onErrorShown : () -> Unit, + onMessageShown : () -> Unit, + onPin : (metadata : Metadata , blurbContent : String) -> Unit, + searchUsers: (String) -> Unit, + isCritiqueBrainzLinked: suspend () -> Boolean?, + onReview: (type: ReviewEntityType, blurbContent: String, rating: Int?, locale: String, metadata: Metadata) -> Unit, + onPersonallyRecommend: (metadata: Metadata, users: List, blurbContent: String) -> Unit, ) { - val currentTabSelection: MutableState = remember { mutableStateOf(CategoryState.ARTISTS) } - val artistsCollapseState: MutableState = remember { mutableStateOf(true) } - val albumsCollapseState: MutableState = remember { mutableStateOf(true) } - val songsCollapseState: MutableState = remember { mutableStateOf(true) } + val dialogsState = rememberDialogsState() + val scope = rememberCoroutineScope() + val context = LocalContext.current + when(currentTabSelection.value){ CategoryState.ARTISTS -> { if(uiState.statsTabUIState.topArtists == null){ @@ -206,7 +245,7 @@ fun StatsScreen( false -> uiState.statsTabUIState.topSongs?.get(statsRangeState)?.payload?.recordings ?: listOf() } - LazyColumn { + LazyColumn (modifier = Modifier.testTag("statsScreenScrollableContainer")) { item { RangeBar( statsRangeState = statsRangeState, @@ -287,7 +326,8 @@ fun StatsScreen( .padding(start = 11.dp, end = 11.dp) .height(250.dp) .clip(RoundedCornerShape(10.dp)) - .background(Color(0xFFe0e5de)), + .background(Color(0xFFe0e5de)) + .testTag("listeningActivityChart"), chart = rememberCartesianChart( rememberColumnCartesianLayer( columnProvider = columnProvider, @@ -398,9 +438,21 @@ fun StatsScreen( } else{ Column (horizontalAlignment = Alignment.CenterHorizontally) { - topAlbums.map { - topAlbum -> - ListenCardSmall(trackName = topAlbum.releaseName ?: "", artistName = topAlbum.artistName ?: "", coverArtUrl = getCoverArtUrl(topAlbum.caaReleaseMbid, topAlbum.caaId), modifier = Modifier.padding(top = 10.dp, bottom = 10.dp, end = 10.dp), color = app_bg_secondary_dark, titleColor = ListenBrainzTheme.colorScheme.followerChipSelected, subtitleColor = ListenBrainzTheme.colorScheme.listenText.copy(alpha = 0.7f)) { + topAlbums.mapIndexed { + index, topAlbum -> + ListenCardSmall( + trackName = topAlbum.releaseName ?: "", + artistName = topAlbum.artistName ?: "", + coverArtUrl = getCoverArtUrl(topAlbum.caaReleaseMbid, topAlbum.caaId), + modifier = Modifier.padding(top = 10.dp, bottom = 10.dp, end = 10.dp), + color = app_bg_secondary_dark, + titleColor = ListenBrainzTheme.colorScheme.followerChipSelected, + subtitleColor = ListenBrainzTheme.colorScheme.listenText.copy(alpha = 0.7f), + enableTrailingContent = true, + listenCount = topAlbum.listenCount, + enableDropdownIcon = true + ) + { } } @@ -419,10 +471,73 @@ fun StatsScreen( } else{ Column (horizontalAlignment = Alignment.CenterHorizontally) { - topSongs.map { - topSong -> - ListenCardSmall(trackName = topSong.trackName ?: "", artistName = topSong.artistName ?: "", coverArtUrl = getCoverArtUrl(topSong.caaReleaseMbid, topSong.caaId), modifier = Modifier.padding(top = 10.dp, bottom = 10.dp, end = 10.dp), color = app_bg_secondary_dark, titleColor = ListenBrainzTheme.colorScheme.followerChipSelected, subtitleColor = ListenBrainzTheme.colorScheme.listenText.copy(alpha = 0.7f),) { - + topSongs.mapIndexed { + index, topSong -> + val metadata = Metadata(trackMetadata = TrackMetadata( + artistName = topSong.artistName ?: "", + releaseName = topSong.releaseName, + trackName = topSong.trackName ?: "", + mbidMapping = MbidMapping( + artistMbids = topSong.artistMbids ?: listOf(), + recordingName = topSong.releaseName ?: "", + caaId = topSong.caaId, + caaReleaseMbid = topSong.caaReleaseMbid, + recordingMbid = topSong.recordingMbid + ), + additionalInfo = AdditionalInfo() + )) + ListenCardSmall( + trackName = topSong.trackName ?: "", + artistName = topSong.artistName ?: "", + coverArtUrl = getCoverArtUrl(topSong.caaReleaseMbid, topSong.caaId), + modifier = Modifier.padding(top = 10.dp, bottom = 10.dp, end = 10.dp), + color = app_bg_secondary_dark, + titleColor = ListenBrainzTheme.colorScheme.followerChipSelected, + subtitleColor = ListenBrainzTheme.colorScheme.listenText.copy(alpha = 0.7f), + enableDropdownIcon = true, + onDropdownIconClick = { + dropdownItemIndex.value = index + }, + dropDown = { + SocialDropdown( + isExpanded = dropdownItemIndex.value == index, + onDismiss = { + dropdownItemIndex.value = null + }, + metadata = metadata, + onRecommend = { onRecommend(metadata) }, + onPersonallyRecommend = { + dialogsState.activateDialog(Dialog.PERSONAL_RECOMMENDATION , ListenDialogBundleKeys.listenDialogBundle(0, index)) + dropdownItemIndex.value = null + }, + onReview = { + dialogsState.activateDialog(Dialog.REVIEW , ListenDialogBundleKeys.listenDialogBundle(0, index)) + dropdownItemIndex.value = null + }, + onPin = { + dialogsState.activateDialog(Dialog.PIN , ListenDialogBundleKeys.listenDialogBundle(0, index)) + dropdownItemIndex.value = null + }, + onOpenInMusicBrainz = { + try { + uriHandler.openUri("https://musicbrainz.org/recording/${metadata.trackMetadata?.mbidMapping?.recordingMbid}") + } + catch(e : Error) { + scope.launch { + snackbarState.showSnackbar(context.getString(R.string.err_generic_toast)) + } + } + dropdownItemIndex.value = null + } + ) + }, + enableTrailingContent = true, + listenCount = topSong.listenCount + ) { + val trackMetadata = metadata.trackMetadata + if(trackMetadata != null){ + playListen(trackMetadata) + } } } Spacer(modifier = Modifier.height(10.dp)) @@ -438,6 +553,25 @@ fun StatsScreen( } } } + + ErrorBar(error = socialUiState.error, onErrorShown = onErrorShown ) + SuccessBar(resId = socialUiState.successMsgId, onMessageShown = onMessageShown, snackbarState = snackbarState) + + Dialogs( + deactivateDialog = { + dialogsState.deactivateDialog() + }, + currentDialog = dialogsState.currentDialog, + currentIndex = dialogsState.metadata?.getInt(ListenDialogBundleKeys.EVENT_INDEX.name), + listens = uiState.listensTabUiState.recentListens ?: listOf(), + onPin = {metadata, blurbContent -> onPin(metadata, blurbContent)}, + searchUsers = { query -> searchUsers(query) }, + feedUiState = feedUiState, + isCritiqueBrainzLinked = isCritiqueBrainzLinked, + onReview = {type, blurbContent, rating, locale, metadata -> onReview(type, blurbContent, rating, locale, metadata) }, + onPersonallyRecommend = {metadata, users, blurbContent -> onPersonallyRecommend(metadata, users, blurbContent)}, + snackbarState = snackbarState, + socialUiState = socialUiState) } @Composable diff --git a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/taste/TasteScreen.kt b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/taste/TasteScreen.kt index 45617c08..bbd71a87 100644 --- a/app/src/main/java/org/listenbrainz/android/ui/screens/profile/taste/TasteScreen.kt +++ b/app/src/main/java/org/listenbrainz/android/ui/screens/profile/taste/TasteScreen.kt @@ -38,7 +38,6 @@ import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel import kotlinx.coroutines.launch import org.listenbrainz.android.R import org.listenbrainz.android.model.Metadata @@ -61,14 +60,14 @@ import org.listenbrainz.android.ui.theme.ListenBrainzTheme import org.listenbrainz.android.ui.theme.lb_purple_night import org.listenbrainz.android.util.Utils.getCoverArtUrl import org.listenbrainz.android.viewmodel.FeedViewModel -import org.listenbrainz.android.viewmodel.ProfileViewModel import org.listenbrainz.android.viewmodel.SocialViewModel +import org.listenbrainz.android.viewmodel.UserViewModel @Composable fun TasteScreen( - viewModel: ProfileViewModel = hiltViewModel(), - socialViewModel: SocialViewModel = hiltViewModel(), - feedViewModel : FeedViewModel = hiltViewModel(), + viewModel: UserViewModel, + socialViewModel: SocialViewModel, + feedViewModel : FeedViewModel, snackbarState : SnackbarHostState, ) { val uiState by viewModel.uiState.collectAsState() diff --git a/app/src/main/java/org/listenbrainz/android/viewmodel/ProfileViewModel.kt b/app/src/main/java/org/listenbrainz/android/viewmodel/UserViewModel.kt similarity index 92% rename from app/src/main/java/org/listenbrainz/android/viewmodel/ProfileViewModel.kt rename to app/src/main/java/org/listenbrainz/android/viewmodel/UserViewModel.kt index 8b09f7bd..85f0f8df 100644 --- a/app/src/main/java/org/listenbrainz/android/viewmodel/ProfileViewModel.kt +++ b/app/src/main/java/org/listenbrainz/android/viewmodel/UserViewModel.kt @@ -29,7 +29,7 @@ import org.listenbrainz.android.util.Constants.Strings.STATUS_LOGGED_OUT import javax.inject.Inject @HiltViewModel -class ProfileViewModel @Inject constructor( +class UserViewModel @Inject constructor( val appPreferences: AppPreferences, private val userRepository: UserRepository, private val listensRepository: ListensRepository, @@ -126,7 +126,7 @@ class ProfileViewModel @Inject constructor( } val followingCount = following?.size val similarUsers = socialRepository.getSimilarUsers(username).data?.payload - val currentPins = userRepository.fetchUserCurrentPins(username).data + val currentPins = userRepository.fetchUserCurrentPins(username).data?.pinnedRecording val compatibility = if (username != appPreferences.username.get()) userRepository.fetchUserSimilarity( appPreferences.username.get(), @@ -152,48 +152,6 @@ class ProfileViewModel @Inject constructor( listenStateFlow.emit(listensTabState) } - private fun extractDayAndMonth(timeRange: String): Pair { - val monthOrder = mapOf( - "January" to 1, - "February" to 2, - "March" to 3, - "April" to 4, - "May" to 5, - "June" to 6, - "July" to 7, - "August" to 8, - "September" to 9, - "October" to 10, - "November" to 11, - "December" to 12 - ) - val parts = timeRange.split(" ") - val month = parts[1] - val day = parts[0].toIntOrNull() ?: 0 - return Pair(day, monthOrder[month] ?: 0) - } - - private fun extractMonthAndYear(timeRange: String): Pair { - val monthOrder = mapOf( - "January" to 1, - "February" to 2, - "March" to 3, - "April" to 4, - "May" to 5, - "June" to 6, - "July" to 7, - "August" to 8, - "September" to 9, - "October" to 10, - "November" to 11, - "December" to 12 - ) - val parts = timeRange.split(" ") - val month = parts[0] - val year = parts[1].toIntOrNull() ?: 0 - return Pair(monthOrder[month] ?: 0, year) - } - private suspend fun getUserStatsData(inputUsername: String?) { val userThisWeekListeningActivity = userRepository.getUserListeningActivity(inputUsername, StatsRange.THIS_WEEK.apiIdenfier).data?.payload?.listeningActivity ?: listOf() val userThisMonthListeningActivity = userRepository.getUserListeningActivity(inputUsername, StatsRange.THIS_MONTH.apiIdenfier).data?.payload?.listeningActivity ?: listOf() diff --git a/app/src/test/java/org/listenbrainz/android/user/UserRepositoryTest.kt b/app/src/test/java/org/listenbrainz/android/user/UserRepositoryTest.kt new file mode 100644 index 00000000..826c662b --- /dev/null +++ b/app/src/test/java/org/listenbrainz/android/user/UserRepositoryTest.kt @@ -0,0 +1,457 @@ +package org.listenbrainz.android.user + +import kotlinx.coroutines.test.runTest +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import okhttp3.mockwebserver.RecordedRequest +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.listenbrainz.android.model.ResponseError +import org.listenbrainz.android.repository.user.UserRepository +import org.listenbrainz.android.repository.user.UserRepositoryImpl +import org.listenbrainz.android.service.UserService +import org.listenbrainz.android.util.Resource +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.listenCountTestData +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testSomeOtherUser +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUserDNE +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUsername +import org.listenbrainz.sharedtest.utils.ResourceString.all_pins +import org.listenbrainz.sharedtest.utils.ResourceString.current_pins +import org.listenbrainz.sharedtest.utils.ResourceString.globalListeningActivity +import org.listenbrainz.sharedtest.utils.ResourceString.listenCount +import org.listenbrainz.sharedtest.utils.ResourceString.loved_hated_songs +import org.listenbrainz.sharedtest.utils.ResourceString.similarUser +import org.listenbrainz.sharedtest.utils.ResourceString.similarUserError +import org.listenbrainz.sharedtest.utils.ResourceString.similarUserErrorString +import org.listenbrainz.sharedtest.utils.ResourceString.topAlbums +import org.listenbrainz.sharedtest.utils.ResourceString.topSongs +import org.listenbrainz.sharedtest.utils.ResourceString.top_artists +import org.listenbrainz.sharedtest.utils.ResourceString.userListeningActivity +import org.listenbrainz.sharedtest.utils.ResourceString.user_does_not_exist_error +import org.listenbrainz.sharedtest.utils.RetrofitUtils + +class UserRepositoryTest { + + private lateinit var webServer: MockWebServer + private lateinit var repository: UserRepository + + @Before + fun setUp() { + webServer = MockWebServer() + webServer.dispatcher = object: Dispatcher() { + override fun dispatch(request: RecordedRequest): MockResponse { + return when(request.path){ + // Listen Count + "/user/$testUsername/listen-count" -> { + MockResponse().setResponseCode(200).setBody( + listenCount + ) + } + + "/user/$testUserDNE/listen-count" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + // Similar Users + "/user/$testUsername/similar-to/$testSomeOtherUser" -> { + MockResponse().setResponseCode(200).setBody( + similarUser + ) + } + + "/user/$testUsername/similar-to/$testUsername" -> { + MockResponse().setResponseCode(404).setBody( + similarUserError + ) + } + + // Current pins + "/$testUsername/pins/current" -> { + MockResponse().setResponseCode(200).setBody( + current_pins + ) + } + + "/$testUserDNE/pins/current" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + // All Pins + "/$testUsername/pins" -> { + MockResponse().setResponseCode(200).setBody( + all_pins + ) + } + + "/$testUserDNE/pins" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + // Artists + "/stats/user/$testUsername/artists?range=all_time&count=25" -> { + MockResponse().setResponseCode(200).setBody( + top_artists + ) + } + + "/stats/user/$testUserDNE/artists?range=all_time&count=25" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + // Loved Hated Songs + "/feedback/user/$testUsername/get-feedback?metadata=true" -> { + MockResponse().setResponseCode(200).setBody( + loved_hated_songs + ) + } + + "/feedback/user/$testUserDNE/get-feedback?metadata=true" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + // Listening Activity + "/stats/user/$testUsername/listening-activity?range=all_time" -> { + MockResponse().setResponseCode(200).setBody( + userListeningActivity + ) + } + + "/stats/user/$testUserDNE/listening-activity?range=all_time" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + // Global Listening Activity + "/stats/sitewide/listening-activity?range=all_time" -> { + MockResponse().setResponseCode(200).setBody( + globalListeningActivity + ) + } + + // Top Albums + "/stats/user/$testUsername/releases?range=all_time" -> { + MockResponse().setResponseCode(200).setBody( + topAlbums + ) + } + + "/stats/user/$testUserDNE/releases?range=all_time" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + // Top Songs + "/stats/user/$testUsername/recordings?range=all_time" -> { + MockResponse().setResponseCode(200).setBody( + topSongs + ) + } + + "/stats/user/$testUserDNE/recordings?range=all_time" -> { + MockResponse().setResponseCode(404).setBody( + user_does_not_exist_error + ) + } + + else -> { + MockResponse().setResponseCode(404) + } + } + } + + } + webServer.start() + val service = RetrofitUtils.createTestService(UserService::class.java, webServer.url("/")) + repository = UserRepositoryImpl(service) + } + + @After + fun teardown() { + webServer.close() + } + @Test + fun `fetch listen count for existing user`() = runTest { + // Fetch listen count for a user known to exist + val result = repository.fetchUserListenCount(testUsername) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Verify that the listen count matches the expected data + assertEquals(listenCountTestData.payload.count, result.data?.payload?.count) + } + + @Test + fun `fetch listen count for non-existing user`() = runTest { + // Attempt to fetch listen count for a user that does not exist + val result = repository.fetchUserListenCount(testUserDNE) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + @Test + fun `fetch similar users for valid comparison`() = runTest { + // Fetch similar users for a valid comparison between two different users + val result = repository.fetchUserSimilarity(testUsername, testSomeOtherUser) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify that the similar user's username matches the expected value + assertEquals("jivteshs20", result.data?.userSimilarity?.username) + } + + @Test + fun `fetch similar users for invalid comparison`() = runTest { + // Attempt to fetch similar users for a comparison with the same user + val result = repository.fetchUserSimilarity(testUsername, testUsername) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the error message matches the expected value for invalid comparison + assertEquals(similarUserErrorString, result.error?.actualResponse) + } + + @Test + fun `fetch current pins for existing user`() = runTest { + // Fetch current pins for a user known to exist + val result = repository.fetchUserCurrentPins(testUsername) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify that the pinned recording's creation time and blurb content match the expected values + assertEquals(1.72335654E9f, result.data?.pinnedRecording?.created) + assertEquals("Noice", result.data?.pinnedRecording?.blurbContent) + } + + @Test + fun `fetch current pins for non-existing user`() = runTest { + // Attempt to fetch current pins for a user that does not exist + val result = repository.fetchUserCurrentPins(testUserDNE) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + @Test + fun `fetch all pins for existing user`() = runTest { + // Fetch all pins for a user known to exist + val result = repository.fetchUserPins(testUsername) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify the total count and check the first pinned recording's MSID and username + assertEquals(12, result.data?.count) + assertEquals("6f4a50ca-b636-4c0b-a6a0-5b84451ab014", result.data?.pinnedRecordings?.get(0)?.recordingMsid) + assertEquals(testUsername, result.data?.userName) + } + + @Test + fun `fetch all pins for non-existing user`() = runTest { + // Attempt to fetch all pins for a user that does not exist + val result = repository.fetchUserPins(testUserDNE) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + @Test + fun `fetch top artists for existing user`() = runTest { + // Fetch top artists for a user known to exist + val result = repository.getTopArtists(testUsername) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify that the first artist's name and the total count of top artists match the expected values + assertEquals("Karan Aujla", result.data?.payload?.artists?.get(0)?.artistName) + assertEquals(25, result.data?.payload?.count) + } + + @Test + fun `fetch top artists for non-existing user`() = runTest { + // Attempt to fetch top artists for a user that does not exist + val result = repository.getTopArtists(testUserDNE) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + @Test + fun `fetch loved and hated songs for existing user`() = runTest { + // Fetch loved and hated songs for a user known to exist + val result = repository.getUserFeedback(testUsername, null) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify the count of feedback and the name of the first loved song + assertEquals(25, result.data?.count) + assertEquals("Calling", result.data?.feedback?.get(0)?.trackMetadata?.trackName) + + // Verify that the feedback user ID matches the expected username + assertEquals(testUsername, result?.data?.feedback?.get(2)?.userId) + } + + @Test + fun `fetch loved and hated songs for non-existing user`() = runTest { + // Attempt to fetch loved and hated songs for a user that does not exist + val result = repository.getUserFeedback(testUserDNE, null) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + @Test + fun `fetch user listening activity for existing user`() = runTest { + // Fetch user listening activity for a user known to exist + val result = repository.getUserListeningActivity(testUsername) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify the user ID and listen count for a specific activity + assertEquals(testUsername, result.data?.payload?.userId) + assertEquals(2826, result.data?.payload?.listeningActivity?.get(21)?.listenCount) + } + + @Test + fun `fetch user listening activity for non-existing user`() = runTest { + // Attempt to fetch user listening activity for a user that does not exist + val result = repository.getUserListeningActivity(testUserDNE) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + @Test + fun `fetch global listening activity`() = runTest { + // Fetch global listening activity, which is not user-specific + val result = repository.getGlobalListeningActivity() + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify the listen count for a specific activity and ensure userId is null for global data + assertEquals(4499100, result.data?.payload?.listeningActivity?.get(3)?.listenCount) + assertNull(result.data?.payload?.userId) + } + + @Test + fun `fetch top albums for existing user`() = runTest { + // Fetch top albums for a user known to exist + val result = repository.getTopAlbums(testUsername) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify the release name of the third album and the username associated with the data + assertEquals("Small Circle", result.data?.payload?.releases?.get(2)?.releaseName) + assertEquals(testUsername, result.data?.payload?.userId) + } + + @Test + fun `fetch top albums for non-existing user`() = runTest { + // Attempt to fetch top albums for a user that does not exist + val result = repository.getTopAlbums(testUserDNE) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + @Test + fun `fetch top songs for existing user`() = runTest { + // Fetch top songs for a user known to exist + val result = repository.getTopSongs(testUsername) + + // Assert that the operation was successful + assertEquals(Resource.Status.SUCCESS, result.status) + + // Ensure that the result data is not null + assertNotNull(result.data) + + // Verify the user ID and the name of the top song + assertEquals(testUsername, result.data?.payload?.userId) + assertEquals("Small Circle", result.data?.payload?.recordings?.get(0)?.releaseName) + } + + @Test + fun `fetch top songs for non-existing user`() = runTest { + // Attempt to fetch top songs for a user that does not exist + val result = repository.getTopSongs(testUserDNE) + + // Assert that the operation failed + assertEquals(Resource.Status.FAILED, result.status) + + // Verify that the correct error is returned for a non-existent user + assertEquals(ResponseError.DOES_NOT_EXIST, result.error) + } + + +} \ No newline at end of file diff --git a/app/src/test/java/org/listenbrainz/android/user/UserViewModelTest.kt b/app/src/test/java/org/listenbrainz/android/user/UserViewModelTest.kt new file mode 100644 index 00000000..b0559c66 --- /dev/null +++ b/app/src/test/java/org/listenbrainz/android/user/UserViewModelTest.kt @@ -0,0 +1,67 @@ +package org.listenbrainz.android.user + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Before +import org.junit.Test +import org.listenbrainz.android.ui.screens.profile.stats.StatsRange +import org.listenbrainz.android.ui.screens.profile.stats.UserGlobal +import org.listenbrainz.android.viewmodel.UserViewModel +import org.listenbrainz.sharedtest.mocks.MockAppPreferences +import org.listenbrainz.sharedtest.mocks.MockListensRepository +import org.listenbrainz.sharedtest.mocks.MockSocialRepository +import org.listenbrainz.sharedtest.mocks.MockUserRepository +import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUsername + +class UserViewModelTest { + private lateinit var viewModel: UserViewModel + @OptIn(ExperimentalCoroutinesApi::class) + @Before + fun setup(){ + Dispatchers.setMain(StandardTestDispatcher()) + viewModel = UserViewModel(MockAppPreferences(), MockUserRepository(), MockListensRepository(), MockSocialRepository(), Dispatchers.Default) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun getDataTest() = runTest { + // Start the data retrieval process using the ViewModel + viewModel.getUserDataFromRemote(testUsername) + + // Ensure all coroutines and background tasks are completed before assertions + advanceUntilIdle() + + // Check that the statsTabUIState is not null, meaning data has been loaded + assertNotNull(viewModel.uiState.value.statsTabUIState) + + // Check that the tasteTabUIState is not null, indicating successful data load + assertNotNull(viewModel.uiState.value.tasteTabUIState) + + // Ensure the listensTabUiState is not null after data retrieval + assertNotNull(viewModel.uiState.value.listensTabUiState) + + // Assert that the data loaded belongs to the user themselves + assertEquals(true, viewModel.uiState.value.isSelf) + + // Verify the number of similar users loaded is as expected (3 in this case) + assertEquals(3, viewModel.uiState.value.listensTabUiState.similarUsers?.size) + + // Confirm the listen count is accurate after data loading + assertEquals(3252, viewModel.uiState.value.listensTabUiState.listenCount) + + // Validate that the first follower's username is correctly loaded + assertEquals("jivteshs20", viewModel.uiState.value.listensTabUiState.followers?.get(0)?.first) + + // Check that the correct MusicBrainz Identifier (MBID) for a loved song is loaded + assertEquals("6b08f3d4-0d56-406c-b628-d0afe2ad5d44", viewModel.uiState.value.tasteTabUIState.lovedSongs?.feedback?.get(0)?.recordingMBID) + + // Ensure that the user listening activity data for all time is loaded with the correct size + assertEquals(23, viewModel.uiState.value.statsTabUIState.userListeningActivity.get(Pair(UserGlobal.USER, StatsRange.ALL_TIME))?.size) + } +} diff --git a/settings.gradle b/settings.gradle index 479c0fef..1ed457e9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,3 +17,4 @@ dependencyResolutionManagement { include(":app") include(":sharedTest") +include(":spotify-app-remote") diff --git a/sharedTest/build.gradle.kts b/sharedTest/build.gradle.kts index 988a96db..2ed17476 100644 --- a/sharedTest/build.gradle.kts +++ b/sharedTest/build.gradle.kts @@ -48,6 +48,9 @@ dependencies { implementation(libs.okhttp) implementation(libs.retrofit.converter.gson) + //Spotify SDK for mocking remotePlaybackHandler + api(project(":spotify-app-remote")) + // Testing implementation(libs.junit) implementation(libs.mockwebserver) diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockFeedRepository.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockFeedRepository.kt new file mode 100644 index 00000000..e4a4be98 --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockFeedRepository.kt @@ -0,0 +1,59 @@ +package org.listenbrainz.sharedtest.mocks + +import org.listenbrainz.android.model.SocialResponse +import org.listenbrainz.android.model.feed.FeedData +import org.listenbrainz.android.model.feed.FeedEventDeletionData +import org.listenbrainz.android.model.feed.FeedEventVisibilityData +import org.listenbrainz.android.repository.feed.FeedRepository +import org.listenbrainz.android.util.Resource + +class MockFeedRepository : FeedRepository { + override suspend fun getFeedEvents( + username: String?, + maxTs: Int?, + minTs: Int?, + count: Int + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun getFeedFollowListens( + username: String?, + maxTs: Int?, + minTs: Int?, + count: Int + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun getFeedSimilarListens( + username: String?, + maxTs: Int?, + minTs: Int?, + count: Int + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun deleteEvent( + username: String?, + data: FeedEventDeletionData + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun hideEvent( + username: String?, + data: FeedEventVisibilityData + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun unhideEvent( + username: String?, + data: FeedEventVisibilityData + ): Resource { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockListensRepository.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockListensRepository.kt new file mode 100644 index 00000000..4cfd4a84 --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockListensRepository.kt @@ -0,0 +1,54 @@ +package org.listenbrainz.sharedtest.mocks + +import android.graphics.drawable.Drawable +import org.listenbrainz.android.model.CoverArt +import org.listenbrainz.android.model.ListenBrainzExternalServices +import org.listenbrainz.android.model.ListenSubmitBody +import org.listenbrainz.android.model.Listens +import org.listenbrainz.android.model.PostResponse +import org.listenbrainz.android.model.ResponseError +import org.listenbrainz.android.model.TokenValidation +import org.listenbrainz.android.repository.listens.ListensRepository +import org.listenbrainz.android.util.Resource +import org.listenbrainz.sharedtest.testdata.ListensRepositoryTestData.listensTestData + +class MockListensRepository : ListensRepository { + override suspend fun fetchUserListens(username: String?): Resource { + return if(username.isNullOrEmpty()){ + ResponseError.DOES_NOT_EXIST.asResource() + } else{ + Resource(Resource.Status.SUCCESS, listensTestData) + } + } + + override suspend fun fetchCoverArt(mbid: String): Resource { + TODO("Not yet implemented") + } + + override suspend fun validateToken(token: String): Resource { + TODO("Not yet implemented") + } + + override fun getPackageIcon(packageName: String): Drawable? { + TODO("Not yet implemented") + } + + override fun getPackageLabel(packageName: String): String { + TODO("Not yet implemented") + } + + override suspend fun submitListen( + token: String, + body: ListenSubmitBody + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun getLinkedServices( + token: String?, + username: String? + ): Resource { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockRemotePlaybackHandler.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockRemotePlaybackHandler.kt new file mode 100644 index 00000000..7de7f700 --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockRemotePlaybackHandler.kt @@ -0,0 +1,59 @@ +package org.listenbrainz.sharedtest.mocks + +import com.spotify.protocol.types.PlayerContext +import com.spotify.protocol.types.PlayerState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import org.listenbrainz.android.model.ListenBitmap +import org.listenbrainz.android.model.ResponseError +import org.listenbrainz.android.repository.remoteplayer.RemotePlaybackHandler +import org.listenbrainz.android.util.Resource + + +class MockRemotePlaybackHandler : RemotePlaybackHandler { + override suspend fun searchYoutubeMusicVideoId( + trackName: String, + artist: String + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun playOnYoutube(getYoutubeMusicVideoId: suspend () -> Resource): Resource { + TODO("Not yet implemented") + } + + override suspend fun connectToSpotify(onError: (ResponseError) -> Unit) { + TODO("Not yet implemented") + } + + override suspend fun disconnectSpotify() { + TODO("Not yet implemented") + } + + override suspend fun fetchSpotifyTrackCoverArt(playerState: PlayerState?): ListenBitmap { + TODO("Not yet implemented") + } + + override fun playUri(trackId: String, onFailure: () -> Unit) { + TODO("Not yet implemented") + } + + override fun play(onPlay: () -> Unit) { + TODO("Not yet implemented") + } + + override fun pause(onPause: () -> Unit) { + TODO("Not yet implemented") + } + + override fun getPlayerState(): Flow { + return flow { + + } + } + + override fun getPlayerContext(): Flow { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockSocialRepository.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockSocialRepository.kt new file mode 100644 index 00000000..a6e4f513 --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockSocialRepository.kt @@ -0,0 +1,79 @@ +package org.listenbrainz.sharedtest.mocks + +import org.listenbrainz.android.model.PinData +import org.listenbrainz.android.model.RecommendationData +import org.listenbrainz.android.model.ResponseError +import org.listenbrainz.android.model.Review +import org.listenbrainz.android.model.SearchResult +import org.listenbrainz.android.model.SimilarUserData +import org.listenbrainz.android.model.SocialData +import org.listenbrainz.android.model.SocialResponse +import org.listenbrainz.android.model.feed.FeedEvent +import org.listenbrainz.android.repository.social.SocialRepository +import org.listenbrainz.android.util.Resource +import org.listenbrainz.sharedtest.testdata.SocialRepositoryTestData.testFollowersSuccessData +import org.listenbrainz.sharedtest.testdata.SocialRepositoryTestData.testFollowingSuccessData +import org.listenbrainz.sharedtest.testdata.SocialRepositoryTestData.testSimilarUserSuccessData + +class MockSocialRepository : SocialRepository { + override suspend fun getFollowers(username: String?): Resource { + return if(username.isNullOrEmpty()){ + ResponseError.DOES_NOT_EXIST.asResource() + } + else{ + Resource(Resource.Status.SUCCESS, testFollowersSuccessData) + } + } + + override suspend fun getFollowing(username: String): Resource { + return Resource(Resource.Status.SUCCESS, testFollowingSuccessData) + } + + override suspend fun followUser(username: String): Resource { + TODO("Not yet implemented") + } + + override suspend fun unfollowUser(username: String): Resource { + TODO("Not yet implemented") + } + + override suspend fun getSimilarUsers(username: String): Resource { + return Resource(Resource.Status.SUCCESS, testSimilarUserSuccessData) + } + + override suspend fun searchUser(username: String): Resource { + TODO("Not yet implemented") + } + + override suspend fun postPersonalRecommendation( + username: String?, + data: RecommendationData + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun postRecommendationToAll( + username: String?, + data: RecommendationData + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun postReview(username: String?, data: Review): Resource { + TODO("Not yet implemented") + } + + override suspend fun pin( + recordingMsid: String?, + recordingMbid: String?, + blurbContent: String?, + pinnedUntil: Int + ): Resource { + TODO("Not yet implemented") + } + + override suspend fun deletePin(id: Int): Resource { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockSocketRepository.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockSocketRepository.kt new file mode 100644 index 00000000..5d2e7835 --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockSocketRepository.kt @@ -0,0 +1,13 @@ +package org.listenbrainz.sharedtest.mocks + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import org.listenbrainz.android.model.Listen +import org.listenbrainz.android.repository.socket.SocketRepository + +class MockSocketRepository : SocketRepository { + override fun listen(username: String): Flow { + return flow { } + } + +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockUserRepository.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockUserRepository.kt new file mode 100644 index 00000000..3e01e1b2 --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/mocks/MockUserRepository.kt @@ -0,0 +1,80 @@ +package org.listenbrainz.sharedtest.mocks + +import org.listenbrainz.android.model.CurrentPins +import org.listenbrainz.android.model.Listens +import org.listenbrainz.android.model.user.AllPinnedRecordings +import org.listenbrainz.android.model.user.TopAlbums +import org.listenbrainz.android.model.user.TopArtists +import org.listenbrainz.android.model.user.TopSongs +import org.listenbrainz.android.model.user.UserFeedback +import org.listenbrainz.android.model.user.UserListeningActivity +import org.listenbrainz.android.model.user.UserSimilarityPayload +import org.listenbrainz.android.repository.user.UserRepository +import org.listenbrainz.android.util.Resource +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.allPinsTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.currentPinsTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.globalListeningActivityTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.listenCountTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.listeningActivityTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.lovedHatedSongsTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.topAlbumsTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.topArtistsTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.topSongsTestData +import org.listenbrainz.sharedtest.testdata.UserRepositoryTestData.userSimilarityTestData + +class MockUserRepository : UserRepository { + override suspend fun fetchUserListenCount(username: String?): Resource { + return Resource(Resource.Status.SUCCESS, listenCountTestData) + } + + override suspend fun fetchUserSimilarity( + username: String?, + otherUserName: String? + ): Resource { + return Resource(Resource.Status.SUCCESS, userSimilarityTestData) + } + + override suspend fun fetchUserCurrentPins(username: String?): Resource { + return Resource(Resource.Status.SUCCESS, currentPinsTestData) + } + + override suspend fun fetchUserPins(username: String?): Resource { + return Resource(Resource.Status.SUCCESS, allPinsTestData) + } + + override suspend fun getTopArtists( + username: String?, + rangeString: String, + count: Int + ): Resource { + return Resource(Resource.Status.SUCCESS, topArtistsTestData) + } + + override suspend fun getUserFeedback(username: String?, score: Int?): Resource { + return Resource(Resource.Status.SUCCESS, lovedHatedSongsTestData) + } + + override suspend fun getUserListeningActivity( + username: String?, + rangeString: String + ): Resource { + return Resource(Resource.Status.SUCCESS, listeningActivityTestData) + } + + override suspend fun getGlobalListeningActivity(rangeString: String): Resource { + return Resource(Resource.Status.SUCCESS, globalListeningActivityTestData) + } + + override suspend fun getTopAlbums( + username: String?, + rangeString: String, + count: Int + ): Resource { + return Resource(Resource.Status.SUCCESS, topAlbumsTestData) + } + + override suspend fun getTopSongs(username: String?, rangeString: String): Resource { + return Resource(Resource.Status.SUCCESS, topSongsTestData) + } + +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/testdata/ListensRepositoryTestData.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/testdata/ListensRepositoryTestData.kt new file mode 100644 index 00000000..62e94f04 --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/testdata/ListensRepositoryTestData.kt @@ -0,0 +1,10 @@ +package org.listenbrainz.sharedtest.testdata + +import com.google.gson.Gson +import org.listenbrainz.android.model.Listens +import org.listenbrainz.sharedtest.utils.ResourceString.listens + +object ListensRepositoryTestData { + val listensTestData : Listens + get() = Gson().fromJson(listens, Listens::class.java) +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/testdata/UserRepositoryTestData.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/testdata/UserRepositoryTestData.kt new file mode 100644 index 00000000..ce7f4d1c --- /dev/null +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/testdata/UserRepositoryTestData.kt @@ -0,0 +1,64 @@ +package org.listenbrainz.sharedtest.testdata + +import com.google.gson.Gson +import org.listenbrainz.android.model.CurrentPins +import org.listenbrainz.android.model.Listens +import org.listenbrainz.android.model.Payload +import org.listenbrainz.android.model.user.AllPinnedRecordings +import org.listenbrainz.android.model.user.TopAlbums +import org.listenbrainz.android.model.user.TopArtists +import org.listenbrainz.android.model.user.TopSongs +import org.listenbrainz.android.model.user.UserFeedback +import org.listenbrainz.android.model.user.UserListeningActivity +import org.listenbrainz.android.model.user.UserSimilarity +import org.listenbrainz.android.model.user.UserSimilarityPayload +import org.listenbrainz.sharedtest.utils.ResourceString.all_pins +import org.listenbrainz.sharedtest.utils.ResourceString.current_pins +import org.listenbrainz.sharedtest.utils.ResourceString.globalListeningActivity +import org.listenbrainz.sharedtest.utils.ResourceString.loved_hated_songs +import org.listenbrainz.sharedtest.utils.ResourceString.topAlbums +import org.listenbrainz.sharedtest.utils.ResourceString.topSongs +import org.listenbrainz.sharedtest.utils.ResourceString.top_artists +import org.listenbrainz.sharedtest.utils.ResourceString.userListeningActivity + +object UserRepositoryTestData { + val listenCountTestData: Listens = Listens( + payload = Payload( + count = 3252, + latest_listen_ts = 0, + listens = listOf(), + user_id = "Jasjeet" + ) + ) + + val userSimilarityTestData: UserSimilarityPayload = UserSimilarityPayload( + userSimilarity = UserSimilarity( + similarity = 0.2580655f, + username = "Shubhi" + ) + ) + + val currentPinsTestData: CurrentPins? + get() = Gson().fromJson(current_pins, CurrentPins::class.java) + + val allPinsTestData: AllPinnedRecordings? + get() = Gson().fromJson(all_pins, AllPinnedRecordings::class.java) + + val topArtistsTestData: TopArtists? + get() = Gson().fromJson(top_artists, TopArtists::class.java) + + val lovedHatedSongsTestData: UserFeedback? + get() = Gson().fromJson(loved_hated_songs, UserFeedback::class.java) + + val listeningActivityTestData: UserListeningActivity? + get() = Gson().fromJson(userListeningActivity, UserListeningActivity::class.java) + + val globalListeningActivityTestData: UserListeningActivity? + get() = Gson().fromJson(globalListeningActivity, UserListeningActivity::class.java) + + val topAlbumsTestData: TopAlbums? + get() = Gson().fromJson(topAlbums, TopAlbums::class.java) + + val topSongsTestData: TopSongs? + get() = Gson().fromJson(topSongs, TopSongs::class.java) +} \ No newline at end of file diff --git a/sharedTest/src/main/java/org/listenbrainz/sharedtest/utils/ResourceString.kt b/sharedTest/src/main/java/org/listenbrainz/sharedtest/utils/ResourceString.kt index b261f2b4..bdac44ee 100644 --- a/sharedTest/src/main/java/org/listenbrainz/sharedtest/utils/ResourceString.kt +++ b/sharedTest/src/main/java/org/listenbrainz/sharedtest/utils/ResourceString.kt @@ -76,6 +76,56 @@ object ResourceString { val yim_data by lazy { EntityTestUtils.loadResourceAsString("yim_data.json") } + + val current_pins by lazy { + EntityTestUtils.loadResourceAsString("current_pins.json") + } + + val all_pins by lazy { + EntityTestUtils.loadResourceAsString("pins.json") + } + + val top_artists by lazy { + EntityTestUtils.loadResourceAsString("top_artists.json") + } + + val loved_hated_songs by lazy { + EntityTestUtils.loadResourceAsString("loved_hated_songs.json") + } + + val userListeningActivity by lazy { + EntityTestUtils.loadResourceAsString("listening_activity.json") + } + + val globalListeningActivity by lazy { + EntityTestUtils.loadResourceAsString("global_listening_activity.json") + } + + val topAlbums by lazy { + EntityTestUtils.loadResourceAsString("top_albums.json") + } + + val topSongs by lazy { + EntityTestUtils.loadResourceAsString("top_songs.json") + } + + const val similarUserErrorString = "Similar-to user not found" + + val similarUserError by lazy { + EntityTestUtils.loadResourceAsString("similar_user_error.json") + } + + val listenCount by lazy { + EntityTestUtils.loadResourceAsString("listen_count.json") + } + + val listens by lazy { + EntityTestUtils.loadResourceAsString("listens.json") + } + + val similarUser by lazy { + EntityTestUtils.loadResourceAsString("similar_user.json") + } fun String.toClass(): T { return Gson().fromJson(this, object: TypeToken() {}.type) diff --git a/sharedTest/src/main/resources/current_pins.json b/sharedTest/src/main/resources/current_pins.json new file mode 100644 index 00000000..f809c450 --- /dev/null +++ b/sharedTest/src/main/resources/current_pins.json @@ -0,0 +1 @@ +{"pinned_recording":{"blurb_content":"Noice","created":1723356490,"pinned_until":1723961286,"recording_mbid":null,"recording_msid":"cbf5a3fb-6823-4a94-a211-cb811d99e59d","row_id":2270,"track_metadata":{"additional_info":{"recording_msid":"cbf5a3fb-6823-4a94-a211-cb811d99e59d"},"artist_name":"*NSYNC","release_name":"No Strings Attached","track_name":"Bye Bye Bye - From Deadpool and Wolverine Soundtrack"}},"user_name":"pranavkonidena"} diff --git a/sharedTest/src/main/resources/global_listening_activity.json b/sharedTest/src/main/resources/global_listening_activity.json new file mode 100644 index 00000000..1e4e4a71 --- /dev/null +++ b/sharedTest/src/main/resources/global_listening_activity.json @@ -0,0 +1 @@ +{"payload":{"from_ts":1009843200,"last_updated":1723276132,"listening_activity":[{"from_ts":1009843200,"listen_count":0,"time_range":"2002","to_ts":1041379199},{"from_ts":1041379200,"listen_count":0,"time_range":"2003","to_ts":1072915199},{"from_ts":1072915200,"listen_count":0,"time_range":"2004","to_ts":1104537599},{"from_ts":1104537600,"listen_count":4499100,"time_range":"2005","to_ts":1136073599},{"from_ts":1136073600,"listen_count":14968386,"time_range":"2006","to_ts":1167609599},{"from_ts":1167609600,"listen_count":23196904,"time_range":"2007","to_ts":1199145599},{"from_ts":1199145600,"listen_count":29162509,"time_range":"2008","to_ts":1230767999},{"from_ts":1230768000,"listen_count":35131923,"time_range":"2009","to_ts":1262303999},{"from_ts":1262304000,"listen_count":38607004,"time_range":"2010","to_ts":1293839999},{"from_ts":1293840000,"listen_count":40623372,"time_range":"2011","to_ts":1325375999},{"from_ts":1325376000,"listen_count":42543270,"time_range":"2012","to_ts":1356998399},{"from_ts":1356998400,"listen_count":44428432,"time_range":"2013","to_ts":1388534399},{"from_ts":1388534400,"listen_count":46991101,"time_range":"2014","to_ts":1420070399},{"from_ts":1420070400,"listen_count":47381647,"time_range":"2015","to_ts":1451606399},{"from_ts":1451606400,"listen_count":48389126,"time_range":"2016","to_ts":1483228799},{"from_ts":1483228800,"listen_count":55776319,"time_range":"2017","to_ts":1514764799},{"from_ts":1514764800,"listen_count":60656566,"time_range":"2018","to_ts":1546300799},{"from_ts":1546300800,"listen_count":61831361,"time_range":"2019","to_ts":1577836799},{"from_ts":1577836800,"listen_count":68597734,"time_range":"2020","to_ts":1609459199},{"from_ts":1609459200,"listen_count":74473231,"time_range":"2021","to_ts":1640995199},{"from_ts":1640995200,"listen_count":79513237,"time_range":"2022","to_ts":1672531199},{"from_ts":1672531200,"listen_count":78953885,"time_range":"2023","to_ts":1704067199},{"from_ts":1704067200,"listen_count":43640832,"time_range":"2024","to_ts":1735689599}],"range":"all_time","to_ts":1723248018}} diff --git a/sharedTest/src/main/resources/listen_count.json b/sharedTest/src/main/resources/listen_count.json new file mode 100644 index 00000000..786573da --- /dev/null +++ b/sharedTest/src/main/resources/listen_count.json @@ -0,0 +1 @@ +{"payload":{"count":3252}} \ No newline at end of file diff --git a/sharedTest/src/main/resources/listening_activity.json b/sharedTest/src/main/resources/listening_activity.json new file mode 100644 index 00000000..15691c62 --- /dev/null +++ b/sharedTest/src/main/resources/listening_activity.json @@ -0,0 +1 @@ +{"payload":{"from_ts":1009843200,"last_updated":1723274592,"listening_activity":[{"from_ts":1009843200,"listen_count":0,"time_range":"2002","to_ts":1041379199},{"from_ts":1041379200,"listen_count":0,"time_range":"2003","to_ts":1072915199},{"from_ts":1072915200,"listen_count":0,"time_range":"2004","to_ts":1104537599},{"from_ts":1104537600,"listen_count":0,"time_range":"2005","to_ts":1136073599},{"from_ts":1136073600,"listen_count":0,"time_range":"2006","to_ts":1167609599},{"from_ts":1167609600,"listen_count":0,"time_range":"2007","to_ts":1199145599},{"from_ts":1199145600,"listen_count":0,"time_range":"2008","to_ts":1230767999},{"from_ts":1230768000,"listen_count":0,"time_range":"2009","to_ts":1262303999},{"from_ts":1262304000,"listen_count":0,"time_range":"2010","to_ts":1293839999},{"from_ts":1293840000,"listen_count":0,"time_range":"2011","to_ts":1325375999},{"from_ts":1325376000,"listen_count":0,"time_range":"2012","to_ts":1356998399},{"from_ts":1356998400,"listen_count":0,"time_range":"2013","to_ts":1388534399},{"from_ts":1388534400,"listen_count":0,"time_range":"2014","to_ts":1420070399},{"from_ts":1420070400,"listen_count":0,"time_range":"2015","to_ts":1451606399},{"from_ts":1451606400,"listen_count":0,"time_range":"2016","to_ts":1483228799},{"from_ts":1483228800,"listen_count":0,"time_range":"2017","to_ts":1514764799},{"from_ts":1514764800,"listen_count":0,"time_range":"2018","to_ts":1546300799},{"from_ts":1546300800,"listen_count":0,"time_range":"2019","to_ts":1577836799},{"from_ts":1577836800,"listen_count":0,"time_range":"2020","to_ts":1609459199},{"from_ts":1609459200,"listen_count":0,"time_range":"2021","to_ts":1640995199},{"from_ts":1640995200,"listen_count":6,"time_range":"2022","to_ts":1672531199},{"from_ts":1672531200,"listen_count":2826,"time_range":"2023","to_ts":1704067199},{"from_ts":1704067200,"listen_count":418,"time_range":"2024","to_ts":1735689599}],"range":"all_time","to_ts":1723248018,"user_id":"Jasjeet"}} diff --git a/sharedTest/src/main/resources/listens.json b/sharedTest/src/main/resources/listens.json new file mode 100644 index 00000000..1369dbe3 --- /dev/null +++ b/sharedTest/src/main/resources/listens.json @@ -0,0 +1 @@ +{"payload":{"count":25,"latest_listen_ts":1723267385,"listens":[{"inserted_at":1723267446,"listened_at":1723267385,"recording_msid":"e3e2b4ef-eac8-46c3-89f9-85ce31c82404","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"e3e2b4ef-eac8-46c3-89f9-85ce31c82404","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Apurva","track_name":"RPReplay_Final1723230584"},"user_name":"Jasjeet"},{"inserted_at":1723144630,"listened_at":1723144571,"recording_msid":"0d3baf58-8315-4836-9ad3-2fc31821668b","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"0d3baf58-8315-4836-9ad3-2fc31821668b","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Srikanth","track_name":"RPReplay_Final1723141917"},"user_name":"Jasjeet"},{"inserted_at":1722833349,"listened_at":1722833327,"recording_msid":"9b10be53-16cc-4aa7-9c18-2a09bad819fc","track_metadata":{"additional_info":{"duration_ms":32966,"media_player":"Slack","recording_msid":"9b10be53-16cc-4aa7-9c18-2a09bad819fc","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Srikanth","track_name":"RPReplay_Final1722682110"},"user_name":"Jasjeet"},{"inserted_at":1722693745,"listened_at":1722693685,"recording_msid":"21a2a5d7-4a29-4361-8d1b-728b93aea251","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"21a2a5d7-4a29-4361-8d1b-728b93aea251","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Srikanth","track_name":"screen-20240803-185944.mp4"},"user_name":"Jasjeet"},{"inserted_at":1722318031,"listened_at":1722318030,"recording_msid":"d7f2d8c6-6818-4d9a-b10e-22e5787ae661","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"d7f2d8c6-6818-4d9a-b10e-22e5787ae661","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Lost Frequencies & Calum Scott","mbid_mapping":{"artist_mbids":["ea7260de-e1b1-43f1-bb11-f78274a36308","68fdc9cc-1094-48bb-942f-66492343e41c"],"artists":[{"artist_credit_name":"Lost Frequencies","artist_mbid":"ea7260de-e1b1-43f1-bb11-f78274a36308","join_phrase":" & "},{"artist_credit_name":"Calum Scott","artist_mbid":"68fdc9cc-1094-48bb-942f-66492343e41c","join_phrase":""}],"caa_id":38775931183,"caa_release_mbid":"cb090bf6-ade9-4c6a-81b8-91d04dedc866","recording_mbid":"fa2605de-3e06-4038-9337-daec9ca302e5","recording_name":"Where Are You Now","release_mbid":"3d6b4d5b-ecd2-4baa-a2a5-7b0e735da202"},"track_name":"Where Are You Now"},"user_name":"Jasjeet"},{"inserted_at":1722095358,"listened_at":1722095295,"recording_msid":"a04fa8dd-776f-4e90-a4d4-4595cad7eeeb","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"a04fa8dd-776f-4e90-a4d4-4595cad7eeeb","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Jass","track_name":"RPReplay_Final1722095096"},"user_name":"Jasjeet"},{"inserted_at":1721920480,"listened_at":1721920420,"recording_msid":"097728c0-7a48-4073-acbe-899f4d3c5b6b","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"097728c0-7a48-4073-acbe-899f4d3c5b6b","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Jass","track_name":"RPReplay_Final1721916666"},"user_name":"Jasjeet"},{"inserted_at":1721800380,"listened_at":1721800382,"recording_msid":"2950fbc6-0b2c-4549-a65c-dc418bb5656e","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"2950fbc6-0b2c-4549-a65c-dc418bb5656e","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Portugal. The Man","mbid_mapping":{"artist_mbids":["3599a39e-4e10-4cb5-90d4-c8a015ebc73b"],"artists":[{"artist_credit_name":"Portugal. The Man","artist_mbid":"3599a39e-4e10-4cb5-90d4-c8a015ebc73b","join_phrase":""}],"caa_id":37576308867,"caa_release_mbid":"3ba8912d-46c5-4c66-bc5e-e33c84ecd118","recording_mbid":"ded77223-a7a3-4a91-a8e6-4cbdf8c84865","recording_name":"People Say","release_mbid":"3ba8912d-46c5-4c66-bc5e-e33c84ecd118"},"release_name":"Social Cues","track_name":"People Say"},"user_name":"Jasjeet"},{"inserted_at":1721795579,"listened_at":1721795520,"recording_msid":"6c694af0-a6cd-4a8f-869e-a15743e0f556","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"6c694af0-a6cd-4a8f-869e-a15743e0f556","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Srikanth","track_name":"Record_2024-07-24-06-41-35_ca37809c94ede43fb59780785174bf2c.mp4"},"user_name":"Jasjeet"},{"inserted_at":1721714031,"listened_at":1721714031,"recording_msid":"ab6b34bb-c28c-46ae-b596-b5f99734e485","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"ab6b34bb-c28c-46ae-b596-b5f99734e485","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Jack \u00dc","mbid_mapping":{"artist_mbids":["8a40cfa4-e190-4133-a9af-e668c7d5f6f3","42c8f9b1-60b5-4b0e-9036-f9a9596695c1"],"artists":[{"artist_credit_name":"Jack \u00dc","artist_mbid":"8a40cfa4-e190-4133-a9af-e668c7d5f6f3","join_phrase":" feat. "},{"artist_credit_name":"Kai","artist_mbid":"42c8f9b1-60b5-4b0e-9036-f9a9596695c1","join_phrase":""}],"caa_id":12067718557,"caa_release_mbid":"e0ceba78-8ecb-46c4-bfd4-c100a5ba888c","recording_mbid":"27795d1e-c9bf-4036-bb1a-11d3e696e16a","recording_name":"Mind","release_mbid":"6844eac2-4772-42ea-8946-06cf049dab2b"},"track_name":"Mind (feat. Alessia De Gasperis)"},"user_name":"Jasjeet"},{"inserted_at":1721713836,"listened_at":1721713835,"recording_msid":"0a1dc120-36d5-4302-9624-9d31ee99f37f","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"0a1dc120-36d5-4302-9624-9d31ee99f37f","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"THYPONYX","track_name":"THYPONYX - Seven Nation Army"},"user_name":"Jasjeet"},{"inserted_at":1721664791,"listened_at":1721664731,"recording_msid":"711109c7-7d4c-4d7d-93cd-7c3077366e68","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"711109c7-7d4c-4d7d-93cd-7c3077366e68","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Ayesha Pradhan","track_name":"Screen_Recording_20240721_121100_Ultrahuman.mp4"},"user_name":"Jasjeet"},{"inserted_at":1721664236,"listened_at":1721664176,"recording_msid":"711109c7-7d4c-4d7d-93cd-7c3077366e68","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"711109c7-7d4c-4d7d-93cd-7c3077366e68","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Ayesha Pradhan","track_name":"Screen_Recording_20240721_121100_Ultrahuman.mp4"},"user_name":"Jasjeet"},{"inserted_at":1721626987,"listened_at":1721626986,"recording_msid":"146bb92a-d34c-4187-b2e9-29bb3179d1e1","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"146bb92a-d34c-4187-b2e9-29bb3179d1e1","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Charlie Puth","mbid_mapping":{"artist_mbids":["525f1f1c-03f0-4bc8-8dfd-e7521f87631b"],"artists":[{"artist_credit_name":"Charlie Puth","artist_mbid":"525f1f1c-03f0-4bc8-8dfd-e7521f87631b","join_phrase":""}],"caa_id":19827744116,"caa_release_mbid":"3b8ce7ae-ff41-4dc6-a694-2e2b2edc24ae","recording_mbid":"19b6d048-f981-40e9-8235-a5acf969e5df","recording_name":"Attention","release_mbid":"8004db5a-0947-4745-b391-3bcc6960411d"},"track_name":"Attention"},"user_name":"Jasjeet"},{"inserted_at":1721626693,"listened_at":1721626691,"recording_msid":"0f837ca8-4610-4306-b36c-c95d17661e55","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"0f837ca8-4610-4306-b36c-c95d17661e55","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Sean Paul","mbid_mapping":{"artist_mbids":["c3da3346-2643-48a7-93cd-011f6834b3d7","6f1a58bf-9b1b-49cf-a44a-6cefad7ae04f"],"artists":[{"artist_credit_name":"Sean Paul","artist_mbid":"c3da3346-2643-48a7-93cd-011f6834b3d7","join_phrase":" ft. "},{"artist_credit_name":"Dua Lipa","artist_mbid":"6f1a58bf-9b1b-49cf-a44a-6cefad7ae04f","join_phrase":""}],"caa_id":20271298649,"caa_release_mbid":"33eed724-cd55-465a-9f50-b4afe3c7728e","recording_mbid":"7b00dbe9-53ea-41b3-877b-31503eb2fd75","recording_name":"No Lie","release_mbid":"cfdb91a1-693f-41a9-b9fc-f08d650a268e"},"track_name":"No Lie (feat. Dua Lipa)"},"user_name":"Jasjeet"},{"inserted_at":1721626556,"listened_at":1721626420,"recording_msid":"55e9adb0-3ed1-4bf1-b8ab-2a28e451f26f","track_metadata":{"additional_info":{"duration_ms":270442,"media_player":"YouTube Music","recording_msid":"55e9adb0-3ed1-4bf1-b8ab-2a28e451f26f","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Mark Ronson","mbid_mapping":{"artist_mbids":["c3c82bdc-d9e7-4836-9746-c24ead47ca19","afb680f2-b6eb-4cd7-a70b-a63b25c763d5"],"artists":[{"artist_credit_name":"Mark Ronson","artist_mbid":"c3c82bdc-d9e7-4836-9746-c24ead47ca19","join_phrase":" feat. "},{"artist_credit_name":"Bruno Mars","artist_mbid":"afb680f2-b6eb-4cd7-a70b-a63b25c763d5","join_phrase":""}],"caa_id":19394943642,"caa_release_mbid":"0577f225-9866-4fe2-9c23-e52c37186a99","recording_mbid":"38e06968-af06-44ea-9eba-a1a275832767","recording_name":"Uptown Funk","release_mbid":"04ea8e96-ef0e-441c-9594-7128addc3951"},"track_name":"Uptown Funk (Official Video) (feat. Bruno Mars)"},"user_name":"Jasjeet"},{"inserted_at":1721379271,"listened_at":1721379242,"recording_msid":"b28cf8a9-d57c-46aa-90cd-5ae74cd648df","track_metadata":{"additional_info":{"duration_ms":54187,"media_player":"Slack","recording_msid":"b28cf8a9-d57c-46aa-90cd-5ae74cd648df","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Ayush","track_name":"WhatsApp Video 2024-07-18 at 21.45.18.mp4"},"user_name":"Jasjeet"},{"inserted_at":1721369443,"listened_at":1721369442,"recording_msid":"24271df0-b683-4ec8-bf47-9f680cfba289","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"24271df0-b683-4ec8-bf47-9f680cfba289","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Gotye","release_name":"Future Nostalgia","track_name":"Somebody That I Used To Know (feat. Kimbra)"},"user_name":"Jasjeet"},{"inserted_at":1721369215,"listened_at":1721369212,"recording_msid":"0b8287c3-8b99-4635-b4da-6002f20718f8","track_metadata":{"additional_info":{"media_player":"YouTube Music","recording_msid":"0b8287c3-8b99-4635-b4da-6002f20718f8","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Arctic Monkeys","mbid_mapping":{"artist_mbids":["ada7a83c-e3e1-40f1-93f9-3e73dbc9298a"],"artists":[{"artist_credit_name":"Arctic Monkeys","artist_mbid":"ada7a83c-e3e1-40f1-93f9-3e73dbc9298a","join_phrase":""}],"caa_id":11863856066,"caa_release_mbid":"c7858e6b-9232-4c01-a703-35e60d3f7ec3","recording_mbid":"f1e57531-e0df-4b3e-938f-1ae30c5b1a11","recording_name":"Do I Wanna Know?","release_mbid":"55171afe-440e-4c63-947c-e49074f3d5b5"},"track_name":"Do I Wanna Know?"},"user_name":"Jasjeet"},{"inserted_at":1721364931,"listened_at":1721364869,"recording_msid":"8aa61f93-ebeb-46d7-a51b-e46337705757","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"8aa61f93-ebeb-46d7-a51b-e46337705757","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Shivani","track_name":"Screen_Recording_20240719_102206_Ultrahuman.mp4"},"user_name":"Jasjeet"},{"inserted_at":1721364864,"listened_at":1721364825,"recording_msid":"6df1064e-3fb4-4888-8c10-54857a335f1c","track_metadata":{"additional_info":{"duration_ms":74441,"media_player":"Slack","recording_msid":"6df1064e-3fb4-4888-8c10-54857a335f1c","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Srikanth","track_name":"screen-20240719-102031.mp4"},"user_name":"Jasjeet"},{"inserted_at":1721236521,"listened_at":1721236461,"recording_msid":"0408a748-656f-44a4-951d-08bbc822212b","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"0408a748-656f-44a4-951d-08bbc822212b","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Malay Pandey","track_name":"668c76196406f6459cdf1b8c-Screen_Recording_20240709_012733_One_UI_Home.mp4"},"user_name":"Jasjeet"},{"inserted_at":1720870225,"listened_at":1720870164,"recording_msid":"81a518a3-9597-40a1-9fa1-a1d4345efc8f","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"81a518a3-9597-40a1-9fa1-a1d4345efc8f","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Trishala","track_name":"5212316802803753137_a8b5aefd-e639-4836-a25a-6dc3746edc27.mov"},"user_name":"Jasjeet"},{"inserted_at":1720861247,"listened_at":1720861187,"recording_msid":"b9b0bbe8-2beb-48de-98ea-9c19b6574ff1","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"b9b0bbe8-2beb-48de-98ea-9c19b6574ff1","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Srikanth","track_name":"screen-20240713-142204.mp4"},"user_name":"Jasjeet"},{"inserted_at":1720861179,"listened_at":1720861114,"recording_msid":"b9b0bbe8-2beb-48de-98ea-9c19b6574ff1","track_metadata":{"additional_info":{"media_player":"Slack","recording_msid":"b9b0bbe8-2beb-48de-98ea-9c19b6574ff1","submission_client":"ListenBrainz Android","submission_client_version":"2.6.1"},"artist_name":"Srikanth","track_name":"screen-20240713-142204.mp4"},"user_name":"Jasjeet"}],"oldest_listen_ts":1670056445,"user_id":"jasjeet"}} diff --git a/sharedTest/src/main/resources/loved_hated_songs.json b/sharedTest/src/main/resources/loved_hated_songs.json new file mode 100644 index 00000000..b7a5b23b --- /dev/null +++ b/sharedTest/src/main/resources/loved_hated_songs.json @@ -0,0 +1 @@ +{"count":25,"feedback":[{"created":1709235151,"recording_mbid":"6b08f3d4-0d56-406c-b628-d0afe2ad5d44","recording_msid":"d7d1de71-f60c-4fed-8028-7efa1f4aa52e","score":1,"track_metadata":{"additional_info":{"recording_msid":"d7d1de71-f60c-4fed-8028-7efa1f4aa52e"},"artist_name":"Metro Boomin, Swae Lee & NAV feat. A Boogie Wit da Hoodie","mbid_mapping":{"artist_mbids":["59db3d82-86ea-451f-881f-dffc8ec387c9","a17ce9c3-8f6f-4dcc-a1b7-2a04ab9e31f0","418fe547-4bec-48fe-8df0-ed25e27f4570","c1708d03-8a66-46eb-848e-fe0d233ffb39"],"artists":[{"artist_credit_name":"Metro Boomin","artist_mbid":"59db3d82-86ea-451f-881f-dffc8ec387c9","join_phrase":", "},{"artist_credit_name":"Swae Lee","artist_mbid":"a17ce9c3-8f6f-4dcc-a1b7-2a04ab9e31f0","join_phrase":" & "},{"artist_credit_name":"NAV","artist_mbid":"418fe547-4bec-48fe-8df0-ed25e27f4570","join_phrase":" feat. "},{"artist_credit_name":"A Boogie Wit da Hoodie","artist_mbid":"c1708d03-8a66-46eb-848e-fe0d233ffb39","join_phrase":""}],"caa_id":35816827255,"caa_release_mbid":"72a73259-c593-4503-a889-9bf1b5e10874","recording_mbid":"6b08f3d4-0d56-406c-b628-d0afe2ad5d44","release_mbid":"5e38a701-16fa-4c2e-b43a-cf59cee97157"},"release_name":"METRO BOOMIN PRESENTS SPIDER\u2010MAN: ACROSS THE SPIDER\u2010VERSE: SOUNDTRACK FROM AND INSPIRED BY THE MOTION PICTURE","track_name":"Calling"},"user_id":"Jasjeet"},{"created":1709235045,"recording_mbid":"a24f67ff-a81e-4a95-817d-c0b4e5af3e6b","recording_msid":null,"score":1,"track_metadata":{"artist_name":"Kanye West feat. Pusha T","mbid_mapping":{"artist_mbids":["164f0d73-1234-4e2c-8743-d77bf2191051","14ecd19f-7121-4192-8549-e5056241a42f"],"artists":[{"artist_credit_name":"Kanye West","artist_mbid":"164f0d73-1234-4e2c-8743-d77bf2191051","join_phrase":" feat. "},{"artist_credit_name":"Pusha T","artist_mbid":"14ecd19f-7121-4192-8549-e5056241a42f","join_phrase":""}],"caa_id":38000784873,"caa_release_mbid":"ef748325-cb18-46c6-b0fd-7276bd6f4337","recording_mbid":"a24f67ff-a81e-4a95-817d-c0b4e5af3e6b","release_mbid":"cd474271-bd7a-4d53-ba9e-e9d5bd7958cb"},"release_name":"My Beautiful Dark Twisted Fantasy","track_name":"Runaway"},"user_id":"Jasjeet"},{"created":1705662797,"recording_mbid":"03c4adcf-2f31-42d7-bcd5-056d68066f90","recording_msid":null,"score":1,"track_metadata":{"artist_name":"Mac Miller","mbid_mapping":{"artist_mbids":["a0e8a1b1-5f8f-475a-a253-17415c17d0ff"],"artists":[{"artist_credit_name":"Mac Miller","artist_mbid":"a0e8a1b1-5f8f-475a-a253-17415c17d0ff","join_phrase":""}],"caa_id":20706929859,"caa_release_mbid":"6db76635-a3cd-4933-85e9-e29cbbc38c1c","recording_mbid":"03c4adcf-2f31-42d7-bcd5-056d68066f90","release_mbid":"62bd84b5-1c38-4d27-9176-ea0716d51064"},"release_name":"Swimming","track_name":"Self Care"},"user_id":"Jasjeet"},{"created":1705659281,"recording_mbid":null,"recording_msid":"c49769d0-6e7f-4603-84e0-ff8ba9debdd0","score":1,"track_metadata":{"additional_info":{"recording_msid":"c49769d0-6e7f-4603-84e0-ff8ba9debdd0"},"artist_name":"Seren","track_name":"Travis Scott - My Eyes (Best Part Extended)"},"user_id":"Jasjeet"},{"created":1704548910,"recording_mbid":"9fb62336-68b7-43fa-a220-e7f3637c7cc5","recording_msid":"27bdfc3c-f0ff-4f81-995b-280a1514be16","score":1,"track_metadata":{"additional_info":{"recording_msid":"27bdfc3c-f0ff-4f81-995b-280a1514be16"},"artist_name":"Liana Flores","mbid_mapping":{"artist_mbids":["9f93c34a-2dc2-400e-ada5-937c3efc3437"],"artists":[{"artist_credit_name":"Liana Flores","artist_mbid":"9f93c34a-2dc2-400e-ada5-937c3efc3437","join_phrase":""}],"caa_id":30013330623,"caa_release_mbid":"7afdc460-27eb-444e-bffd-6977f6335597","recording_mbid":"9fb62336-68b7-43fa-a220-e7f3637c7cc5","release_mbid":"7afdc460-27eb-444e-bffd-6977f6335597"},"release_name":"recently","track_name":"rises the moon"},"user_id":"Jasjeet"},{"created":1704547584,"recording_mbid":"a4fc3d78-6520-4663-82bd-10393bb0af2b","recording_msid":null,"score":1,"track_metadata":{"artist_name":"The Drums","mbid_mapping":{"artist_mbids":["c3ecef5d-84f3-4942-8c24-0a626343c3f4"],"artists":[{"artist_credit_name":"The Drums","artist_mbid":"c3ecef5d-84f3-4942-8c24-0a626343c3f4","join_phrase":""}],"caa_id":4031185360,"caa_release_mbid":"dfffc568-7e2c-44e8-80e6-458f2d9aa319","recording_mbid":"a4fc3d78-6520-4663-82bd-10393bb0af2b","release_mbid":"b6b21d16-021f-48fe-a575-c46320cf3107"},"release_name":"Portamento","track_name":"Money"},"user_id":"Jasjeet"},{"created":1704402369,"recording_mbid":"2f14ac58-d62d-4e45-a5e1-6504d1366831","recording_msid":"3ed4f41f-07ba-4cc5-ae95-b95409e028d8","score":1,"track_metadata":{"additional_info":{"recording_msid":"3ed4f41f-07ba-4cc5-ae95-b95409e028d8"},"artist_name":"Aaron May","mbid_mapping":{"artist_mbids":["9f3225ad-e686-4538-8cbe-99406c4a4caa"],"artists":[{"artist_credit_name":"Aaron May","artist_mbid":"9f3225ad-e686-4538-8cbe-99406c4a4caa","join_phrase":""}],"caa_id":22549299616,"caa_release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4","recording_mbid":"2f14ac58-d62d-4e45-a5e1-6504d1366831","release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4"},"release_name":"Chase","track_name":"I'm Good Luv, Enjoy."},"user_id":"Jasjeet"},{"created":1702622193,"recording_mbid":"c703515e-92ba-4d8f-8b8e-8fa85a480038","recording_msid":"9d3390f4-9f21-4be5-8c08-cae287e4c189","score":1,"track_metadata":{"additional_info":{"recording_msid":"9d3390f4-9f21-4be5-8c08-cae287e4c189"},"artist_name":"Rosa Walton as Hallie Coggins","mbid_mapping":{"artist_mbids":["9a6273b2-72fa-47a4-a48b-073d6cbe037d","0ebdc5f5-0a11-415a-b6f3-efe0d36c6e9d"],"artists":[{"artist_credit_name":"Rosa Walton","artist_mbid":"9a6273b2-72fa-47a4-a48b-073d6cbe037d","join_phrase":" as "},{"artist_credit_name":"Hallie Coggins","artist_mbid":"0ebdc5f5-0a11-415a-b6f3-efe0d36c6e9d","join_phrase":""}],"caa_id":28105410573,"caa_release_mbid":"53002383-0ea4-44a4-97e1-b5d7d25e91a4","recording_mbid":"c703515e-92ba-4d8f-8b8e-8fa85a480038","release_mbid":"53002383-0ea4-44a4-97e1-b5d7d25e91a4"},"release_name":"Cyberpunk 2077: Radio, Vol. 2 (Original Soundtrack)","track_name":"I Really Want to Stay at Your House"},"user_id":"Jasjeet"},{"created":1702565277,"recording_mbid":"ab100b50-31eb-4a71-98d8-52a5e4e1304e","recording_msid":null,"score":1,"track_metadata":{"artist_name":"Dawid Podsiad\u0142o","mbid_mapping":{"artist_mbids":["68ffdf6b-001c-41ed-aa3a-20ec0128f627"],"artists":[{"artist_credit_name":"Dawid Podsiad\u0142o","artist_mbid":"68ffdf6b-001c-41ed-aa3a-20ec0128f627","join_phrase":""}],"caa_id":35924146102,"caa_release_mbid":"50274be9-730c-4642-8ede-25442d58867c","recording_mbid":"ab100b50-31eb-4a71-98d8-52a5e4e1304e","release_mbid":"cb64ddc6-dc64-4f31-b503-f977077c57d3"},"release_name":"Lata Dwudzieste z kawa\u0142kiem","track_name":"Let You Down"},"user_id":"Jasjeet"},{"created":1702402222,"recording_mbid":null,"recording_msid":"9c844e8e-b085-4954-bc0b-7651b1939e79","score":1,"track_metadata":{"additional_info":{"recording_msid":"9c844e8e-b085-4954-bc0b-7651b1939e79"},"artist_name":"Ammy Virk, Jaymeet and Rony Ajnali","release_name":"Late Checkout","track_name":"Memories - Ammy Virk (Official Song) Layers"},"user_id":"Jasjeet"},{"created":1702402221,"recording_mbid":null,"recording_msid":"17c684ff-6812-43f0-b53c-be23cf59fce1","score":1,"track_metadata":{"additional_info":{"recording_msid":"17c684ff-6812-43f0-b53c-be23cf59fce1"},"artist_name":"Mickey Singh and Jay Skilly","release_name":"The Phantom Files (From Cyberpunk 2077)","track_name":"Cruise Control"},"user_id":"Jasjeet"},{"created":1702402120,"recording_mbid":"dbad831a-7a9d-416e-9ef0-11e740fef6a0","recording_msid":null,"score":1,"track_metadata":{"artist_name":"Men I Trust","mbid_mapping":{"artist_mbids":["25ab7547-a4d2-480b-b028-c7f3497bc858"],"artists":[{"artist_credit_name":"Men I Trust","artist_mbid":"25ab7547-a4d2-480b-b028-c7f3497bc858","join_phrase":""}],"caa_id":26978507653,"caa_release_mbid":"0633a08c-660e-4992-ba7b-a62bcf82f43b","recording_mbid":"dbad831a-7a9d-416e-9ef0-11e740fef6a0","release_mbid":"c1bc7be3-7eab-4055-a260-d809487a6eb8"},"release_name":"Oncle Jazz","track_name":"Show Me How"},"user_id":"Jasjeet"},{"created":1702396242,"recording_mbid":"15c5d5a6-9ae9-4727-a413-d48c57e867d9","recording_msid":"ac081ab5-5702-4eb1-bd4d-cdc0504703c7","score":1,"track_metadata":{"additional_info":{"recording_msid":"ac081ab5-5702-4eb1-bd4d-cdc0504703c7"},"artist_name":"Namakopuri as Us Cracks","mbid_mapping":{"artist_mbids":["1b7d53f5-1746-420b-b23d-e6b7c41a0b37","07ee1730-aee1-4c90-a225-be7b3edc94be"],"artists":[{"artist_credit_name":"Namakopuri","artist_mbid":"1b7d53f5-1746-420b-b23d-e6b7c41a0b37","join_phrase":" as "},{"artist_credit_name":"Us Cracks","artist_mbid":"07ee1730-aee1-4c90-a225-be7b3edc94be","join_phrase":""}],"caa_id":28105410573,"caa_release_mbid":"53002383-0ea4-44a4-97e1-b5d7d25e91a4","recording_mbid":"15c5d5a6-9ae9-4727-a413-d48c57e867d9","release_mbid":"53002383-0ea4-44a4-97e1-b5d7d25e91a4"},"release_name":"Cyberpunk 2077: Radio, Vol. 2 (Original Soundtrack)","track_name":"PonPon Shit"},"user_id":"Jasjeet"},{"created":1702387013,"recording_mbid":null,"recording_msid":"0e10f95e-dc5a-42ea-a70d-5a274f8683ff","score":1,"track_metadata":{"additional_info":{"recording_msid":"0e10f95e-dc5a-42ea-a70d-5a274f8683ff"},"artist_name":"Semwal and Agaazz","release_name":"Hass Hass","track_name":"Dil Da Pecha"},"user_id":"Jasjeet"},{"created":1702369018,"recording_mbid":null,"recording_msid":"62b5f8da-45df-4cbf-87db-a9a7bfc7d906","score":1,"track_metadata":{"additional_info":{"recording_msid":"62b5f8da-45df-4cbf-87db-a9a7bfc7d906"},"artist_name":"Mickey Singh and Jay Skilly","release_name":"Infinity","track_name":"Cruise Control"},"user_id":"Jasjeet"},{"created":1701932614,"recording_mbid":"13c81871-58de-4c1c-bb43-f83e7b6032a7","recording_msid":"0f2644de-9cd7-4811-b638-770aeb516a89","score":1,"track_metadata":{"additional_info":{"recording_msid":"0f2644de-9cd7-4811-b638-770aeb516a89"},"artist_name":"Dawid Podsiad\u0142o & P.T. Adamczyk","mbid_mapping":{"artist_mbids":["68ffdf6b-001c-41ed-aa3a-20ec0128f627","bc1285ea-31c8-4f50-847e-b66244a142b3"],"artists":[{"artist_credit_name":"Dawid Podsiad\u0142o","artist_mbid":"68ffdf6b-001c-41ed-aa3a-20ec0128f627","join_phrase":" & "},{"artist_credit_name":"P.T. Adamczyk","artist_mbid":"bc1285ea-31c8-4f50-847e-b66244a142b3","join_phrase":""}],"caa_id":35924146102,"caa_release_mbid":"50274be9-730c-4642-8ede-25442d58867c","recording_mbid":"13c81871-58de-4c1c-bb43-f83e7b6032a7","release_mbid":"cb64ddc6-dc64-4f31-b503-f977077c57d3"},"release_name":"Lata Dwudzieste z kawa\u0142kiem","track_name":"Phantom Liberty"},"user_id":"Jasjeet"},{"created":1701932501,"recording_mbid":"7a8949b0-3abf-4f0d-b14c-dd7d4f635966","recording_msid":"f984f2d0-cfc6-4205-9838-891f4bfbff3a","score":1,"track_metadata":{"additional_info":{"recording_msid":"f984f2d0-cfc6-4205-9838-891f4bfbff3a"},"artist_name":"Idris Elba","mbid_mapping":{"artist_mbids":["b3c7c6d6-5907-45a8-b589-1ec2572be4fd"],"artists":[{"artist_credit_name":"Idris Elba","artist_mbid":"b3c7c6d6-5907-45a8-b589-1ec2572be4fd","join_phrase":""}],"caa_id":36853564662,"caa_release_mbid":"77ef1c1d-e3fe-4f54-ba43-4cdc1243ea6c","recording_mbid":"7a8949b0-3abf-4f0d-b14c-dd7d4f635966","release_mbid":"77ef1c1d-e3fe-4f54-ba43-4cdc1243ea6c"},"release_name":"The Phantom Files (From Cyberpunk 2077)","track_name":"Choke Hold"},"user_id":"Jasjeet"},{"created":1700751665,"recording_mbid":"d2ed35bd-9771-496f-9c48-ba9e7c462da2","recording_msid":"3cedd757-0142-4708-852d-cf6e2bd35750","score":1,"track_metadata":{"additional_info":{"recording_msid":"3cedd757-0142-4708-852d-cf6e2bd35750"},"artist_name":"Neeraj Shridhar, Raman Mahadevan, Pervez Quadir & Loy Mendonsa","mbid_mapping":{"artist_mbids":["db7fb490-d527-447f-9d40-ed91c479d5ee","b63974b0-43ef-4b72-b4be-7b7eb96025bf","c967a8f1-2852-4416-bf46-9e041b6594ea","5fa5c9fc-9d9c-4c0f-ad27-10333051084e"],"artists":[{"artist_credit_name":"Neeraj Shridhar","artist_mbid":"db7fb490-d527-447f-9d40-ed91c479d5ee","join_phrase":", "},{"artist_credit_name":"Raman Mahadevan","artist_mbid":"b63974b0-43ef-4b72-b4be-7b7eb96025bf","join_phrase":", "},{"artist_credit_name":"Pervez Quadir","artist_mbid":"c967a8f1-2852-4416-bf46-9e041b6594ea","join_phrase":" & "},{"artist_credit_name":"Loy Mendonsa","artist_mbid":"5fa5c9fc-9d9c-4c0f-ad27-10333051084e","join_phrase":""}],"recording_mbid":"d2ed35bd-9771-496f-9c48-ba9e7c462da2","release_mbid":"69587b26-f597-48e5-844c-3823d3ff8f26"},"release_name":"2007 It's Rocking","track_name":"Heyy Babyy (Heyy Babyy)"},"user_id":"Jasjeet"},{"created":1700588943,"recording_mbid":"da07eaed-4eda-44bc-8eea-4713e832aa2e","recording_msid":"891ed674-1c1f-4c3a-919d-1af3438fbd5a","score":1,"track_metadata":{"additional_info":{"recording_msid":"891ed674-1c1f-4c3a-919d-1af3438fbd5a"},"artist_name":"Diljit Dosanjh & Sia","mbid_mapping":{"artist_mbids":["f931c961-b647-4861-be8c-f47d84a4de51","2f548675-008d-4332-876c-108b0c7ab9c5"],"artists":[{"artist_credit_name":"Diljit Dosanjh","artist_mbid":"f931c961-b647-4861-be8c-f47d84a4de51","join_phrase":" & "},{"artist_credit_name":"Sia","artist_mbid":"2f548675-008d-4332-876c-108b0c7ab9c5","join_phrase":""}],"caa_id":37082867811,"caa_release_mbid":"308fbfe2-6327-439e-b209-f3b021e8795c","recording_mbid":"da07eaed-4eda-44bc-8eea-4713e832aa2e","release_mbid":"308fbfe2-6327-439e-b209-f3b021e8795c"},"release_name":"Hass Hass","track_name":"Hass Hass"},"user_id":"Jasjeet"},{"created":1689852464,"recording_mbid":"eb8d7744-a329-4740-94b9-ac575e979d5c","recording_msid":"2059fd62-165e-4f52-a041-125e6090785e","score":1,"track_metadata":{"additional_info":{"recording_msid":"2059fd62-165e-4f52-a041-125e6090785e"},"artist_name":"Metro Boomin & Future feat. Don Toliver","mbid_mapping":{"artist_mbids":["59db3d82-86ea-451f-881f-dffc8ec387c9","48262e82-db9f-4a92-b650-dfef979b73ec","a0723a3c-4135-438e-85c5-012712144ede"],"artists":[{"artist_credit_name":"Metro Boomin","artist_mbid":"59db3d82-86ea-451f-881f-dffc8ec387c9","join_phrase":" & "},{"artist_credit_name":"Future","artist_mbid":"48262e82-db9f-4a92-b650-dfef979b73ec","join_phrase":" feat. "},{"artist_credit_name":"Don Toliver","artist_mbid":"a0723a3c-4135-438e-85c5-012712144ede","join_phrase":""}],"caa_id":34245737801,"caa_release_mbid":"2f58792e-a91a-4038-8828-c70e072492fd","recording_mbid":"eb8d7744-a329-4740-94b9-ac575e979d5c","release_mbid":"067d55ab-5eb1-4a1b-96ae-8c67f1f50441"},"release_name":"HEROES & VILLAINS","track_name":"Too Many Nights"},"user_id":"Jasjeet"},{"created":1689852459,"recording_mbid":null,"recording_msid":"4d60c545-b353-4b63-9c91-d98bd781ddcf","score":1,"track_metadata":null,"user_id":"Jasjeet"},{"created":1685986475,"recording_mbid":null,"recording_msid":"9656809d-b8b9-4e4c-93eb-c9965fb53f20","score":1,"track_metadata":null,"user_id":"Jasjeet"},{"created":1685912721,"recording_mbid":"01552e9a-11b0-4b5d-bcd5-cd15850ebd29","recording_msid":"34e3e63b-9e4b-460a-b2c6-9628bf96273c","score":1,"track_metadata":{"additional_info":{"recording_msid":"34e3e63b-9e4b-460a-b2c6-9628bf96273c"},"artist_name":"Halsey","mbid_mapping":{"artist_mbids":["3377f3bb-60fc-4403-aea9-7e800612e060"],"artists":[{"artist_credit_name":"Halsey","artist_mbid":"3377f3bb-60fc-4403-aea9-7e800612e060","join_phrase":""}],"caa_id":33498041604,"caa_release_mbid":"2ec31e00-de8e-4181-8d6d-4ff6d8cb3a19","recording_mbid":"01552e9a-11b0-4b5d-bcd5-cd15850ebd29","release_mbid":"911dfcb8-90b8-41fe-a8b5-d1b4dfdba503"},"release_name":"\u30de\u30cb\u30c3\u30af","track_name":"Without Me"},"user_id":"Jasjeet"},{"created":1683483053,"recording_mbid":"75cd34ef-70fd-46b4-a1a6-1d3db062b363","recording_msid":"8dfdf578-35c2-436c-8f47-081cac3028f0","score":1,"track_metadata":{"additional_info":{"recording_msid":"8dfdf578-35c2-436c-8f47-081cac3028f0"},"artist_name":"Yo Yo Honey Singh","mbid_mapping":{"artist_mbids":["0dc9c4bc-8bcc-42f1-9033-bec41160377f"],"artists":[{"artist_credit_name":"Yo Yo Honey Singh","artist_mbid":"0dc9c4bc-8bcc-42f1-9033-bec41160377f","join_phrase":""}],"caa_id":13965559599,"caa_release_mbid":"c30eb11a-9c23-4983-9e8f-3bff16b239a9","recording_mbid":"75cd34ef-70fd-46b4-a1a6-1d3db062b363","release_mbid":"c30eb11a-9c23-4983-9e8f-3bff16b239a9"},"release_name":"Zorawar","track_name":"Call Aundi"},"user_id":"Jasjeet"},{"created":1683366612,"recording_mbid":"75a38235-5db3-4545-bd4c-e4de9baee068","recording_msid":"0a95a65b-94a5-4f6a-a7ef-51d0455e4ad4","score":1,"track_metadata":{"additional_info":{"recording_msid":"0a95a65b-94a5-4f6a-a7ef-51d0455e4ad4"},"artist_name":"Playboi Carti","mbid_mapping":{"artist_mbids":["2baf3276-ed6a-4349-8d2e-f4601e7b2167"],"artists":[{"artist_credit_name":"Playboi Carti","artist_mbid":"2baf3276-ed6a-4349-8d2e-f4601e7b2167","join_phrase":""}],"recording_mbid":"75a38235-5db3-4545-bd4c-e4de9baee068","release_mbid":"b2d63750-05c8-4fa0-be7c-f7c3a4737a4a"},"release_name":"Magnolia","track_name":"Magnolia"},"user_id":"Jasjeet"}],"offset":0,"total_count":36} diff --git a/sharedTest/src/main/resources/pins.json b/sharedTest/src/main/resources/pins.json new file mode 100644 index 00000000..c74b2082 --- /dev/null +++ b/sharedTest/src/main/resources/pins.json @@ -0,0 +1 @@ +{"count":12,"offset":0,"pinned_recordings":[{"blurb_content":"","created":1720693976,"pinned_until":1721298776,"recording_mbid":null,"recording_msid":"6f4a50ca-b636-4c0b-a6a0-5b84451ab014","row_id":2136,"track_metadata":{"additional_info":{"recording_msid":"6f4a50ca-b636-4c0b-a6a0-5b84451ab014"},"artist_name":"WEEDIAN","release_name":"Trip to California (Stoner Edition)","track_name":"Mesmer - Wings of Evil"}},{"blurb_content":"","created":1720593845,"pinned_until":1720693976,"recording_mbid":"9a67108f-ded9-4653-a51a-43ccb873d32c","recording_msid":"33b761de-ad44-4ee7-ab20-b28118ed9f67","row_id":2116,"track_metadata":{"additional_info":{"recording_msid":"33b761de-ad44-4ee7-ab20-b28118ed9f67"},"artist_name":"Karkara","mbid_mapping":{"artist_mbids":["1bdc2715-507c-41cb-9e06-021a8ecd6c8e"],"artists":[{"artist_credit_name":"Karkara","artist_mbid":"1bdc2715-507c-41cb-9e06-021a8ecd6c8e","join_phrase":""}],"caa_id":38158274590,"caa_release_mbid":"d5406f53-0b82-45cb-8a78-468fc343748f","recording_mbid":"9a67108f-ded9-4653-a51a-43ccb873d32c","release_mbid":"d5406f53-0b82-45cb-8a78-468fc343748f"},"release_name":"All Is Dust","track_name":"On Edge"}},{"blurb_content":"","created":1710955582,"pinned_until":1711560384,"recording_mbid":"8fe7ac15-a253-4f29-9e81-2363200af18d","recording_msid":"c361edf3-1c86-48b9-aab2-755c4fb8dce8","row_id":1698,"track_metadata":{"additional_info":{"recording_msid":"c361edf3-1c86-48b9-aab2-755c4fb8dce8"},"artist_name":"Arjan Dhillon","mbid_mapping":{"artist_mbids":["9b34cd6a-b874-4e75-bec8-1c174fe295d0"],"artists":[{"artist_credit_name":"Arjan Dhillon","artist_mbid":"9b34cd6a-b874-4e75-bec8-1c174fe295d0","join_phrase":""}],"caa_id":37928515775,"caa_release_mbid":"d360fa73-e2be-4cd8-82d2-73eb61cadd51","recording_mbid":"8fe7ac15-a253-4f29-9e81-2363200af18d","release_mbid":"d360fa73-e2be-4cd8-82d2-73eb61cadd51"},"release_name":"Chobar","track_name":"Woah"}},{"blurb_content":null,"created":1704539945,"pinned_until":1705144745,"recording_mbid":"2f14ac58-d62d-4e45-a5e1-6504d1366831","recording_msid":"65971184-31f4-4ca3-924b-29e8ae072bc5","row_id":1438,"track_metadata":{"additional_info":{"recording_msid":"65971184-31f4-4ca3-924b-29e8ae072bc5"},"artist_name":"Aaron May","mbid_mapping":{"artist_mbids":["9f3225ad-e686-4538-8cbe-99406c4a4caa"],"artists":[{"artist_credit_name":"Aaron May","artist_mbid":"9f3225ad-e686-4538-8cbe-99406c4a4caa","join_phrase":""}],"caa_id":22549299616,"caa_release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4","recording_mbid":"2f14ac58-d62d-4e45-a5e1-6504d1366831","release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4"},"release_name":"Chase","track_name":"I'm Good Luv, Enjoy."}},{"blurb_content":"Good anime good outro good game","created":1702565315,"pinned_until":1703170115,"recording_mbid":"ab100b50-31eb-4a71-98d8-52a5e4e1304e","recording_msid":"726eab7a-5a51-4d7d-aa5e-ca5e33ced3a1","row_id":1370,"track_metadata":{"additional_info":{"recording_msid":"726eab7a-5a51-4d7d-aa5e-ca5e33ced3a1"},"artist_name":"Dawid Podsiad\u0142o","mbid_mapping":{"artist_mbids":["68ffdf6b-001c-41ed-aa3a-20ec0128f627"],"artists":[{"artist_credit_name":"Dawid Podsiad\u0142o","artist_mbid":"68ffdf6b-001c-41ed-aa3a-20ec0128f627","join_phrase":""}],"caa_id":35924146102,"caa_release_mbid":"50274be9-730c-4642-8ede-25442d58867c","recording_mbid":"ab100b50-31eb-4a71-98d8-52a5e4e1304e","release_mbid":"cb64ddc6-dc64-4f31-b503-f977077c57d3"},"release_name":"Lata Dwudzieste z kawa\u0142kiem","track_name":"Let You Down"}},{"blurb_content":"Vibe","created":1702397156,"pinned_until":1702565315,"recording_mbid":"15c5d5a6-9ae9-4727-a413-d48c57e867d9","recording_msid":"ac081ab5-5702-4eb1-bd4d-cdc0504703c7","row_id":1363,"track_metadata":{"additional_info":{"recording_msid":"ac081ab5-5702-4eb1-bd4d-cdc0504703c7"},"artist_name":"Namakopuri as Us Cracks","mbid_mapping":{"artist_mbids":["1b7d53f5-1746-420b-b23d-e6b7c41a0b37","07ee1730-aee1-4c90-a225-be7b3edc94be"],"artists":[{"artist_credit_name":"Namakopuri","artist_mbid":"1b7d53f5-1746-420b-b23d-e6b7c41a0b37","join_phrase":" as "},{"artist_credit_name":"Us Cracks","artist_mbid":"07ee1730-aee1-4c90-a225-be7b3edc94be","join_phrase":""}],"caa_id":28105410573,"caa_release_mbid":"53002383-0ea4-44a4-97e1-b5d7d25e91a4","recording_mbid":"15c5d5a6-9ae9-4727-a413-d48c57e867d9","release_mbid":"53002383-0ea4-44a4-97e1-b5d7d25e91a4"},"release_name":"Cyberpunk 2077: Radio, Vol. 2 (Original Soundtrack)","track_name":"PonPon Shit"}},{"blurb_content":"new pin","created":1692526823,"pinned_until":1692910066,"recording_mbid":"b990f72b-ec15-4c6f-801c-b990f2d711e7","recording_msid":null,"row_id":1089,"track_metadata":{"artist_name":"BigXthaPlug","mbid_mapping":{"artist_mbids":["d25f6294-686a-4569-b1b9-fe64bfef2519"],"artists":[{"artist_credit_name":"BigXthaPlug","artist_mbid":"d25f6294-686a-4569-b1b9-fe64bfef2519","join_phrase":""}],"caa_id":34865132955,"caa_release_mbid":"829adf9e-773a-4026-b674-fffa1fd141bb","recording_mbid":"b990f72b-ec15-4c6f-801c-b990f2d711e7","release_mbid":"829adf9e-773a-4026-b674-fffa1fd141bb"},"release_name":"AMAR","track_name":"Levels"}},{"blurb_content":"Wow..","created":1692526658,"pinned_until":1692526823,"recording_mbid":"43e2cac8-ac33-428e-9ad9-79827d4a8b34","recording_msid":null,"row_id":1088,"track_metadata":null},{"blurb_content":"Wow..","created":1692526234,"pinned_until":1692526658,"recording_mbid":null,"recording_msid":"40ef0ae1-5626-43eb-838f-1b34187519bf","row_id":1087,"track_metadata":null},{"blurb_content":null,"created":1683483528,"pinned_until":1684088328,"recording_mbid":"75cd34ef-70fd-46b4-a1a6-1d3db062b363","recording_msid":"8dfdf578-35c2-436c-8f47-081cac3028f0","row_id":856,"track_metadata":{"additional_info":{"recording_msid":"8dfdf578-35c2-436c-8f47-081cac3028f0"},"artist_name":"Yo Yo Honey Singh","mbid_mapping":{"artist_mbids":["0dc9c4bc-8bcc-42f1-9033-bec41160377f"],"artists":[{"artist_credit_name":"Yo Yo Honey Singh","artist_mbid":"0dc9c4bc-8bcc-42f1-9033-bec41160377f","join_phrase":""}],"caa_id":13965559599,"caa_release_mbid":"c30eb11a-9c23-4983-9e8f-3bff16b239a9","recording_mbid":"75cd34ef-70fd-46b4-a1a6-1d3db062b363","release_mbid":"c30eb11a-9c23-4983-9e8f-3bff16b239a9"},"release_name":"Zorawar","track_name":"Call Aundi"}},{"blurb_content":null,"created":1683366660,"pinned_until":1683483528,"recording_mbid":"650aa31f-cf18-4239-ac59-9cb9cca92d4d","recording_msid":"f564e069-46b8-43fc-ba95-d5f615c4a2cd","row_id":855,"track_metadata":{"additional_info":{"recording_msid":"f564e069-46b8-43fc-ba95-d5f615c4a2cd"},"artist_name":"Tegi Pannu & JJ Esko","mbid_mapping":{"artist_mbids":["092552b9-ea6d-4ead-b424-cf8b07da5877","81035abf-09f7-4cbe-a785-4510dbd7970c"],"artists":[{"artist_credit_name":"Tegi Pannu","artist_mbid":"092552b9-ea6d-4ead-b424-cf8b07da5877","join_phrase":" & "},{"artist_credit_name":"JJ Esko","artist_mbid":"81035abf-09f7-4cbe-a785-4510dbd7970c","join_phrase":""}],"caa_id":34147069927,"caa_release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea","recording_mbid":"650aa31f-cf18-4239-ac59-9cb9cca92d4d","release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea"},"release_name":"Disturbing The Peace","track_name":"Hold You Down"}},{"blurb_content":null,"created":1683190944,"pinned_until":1683366660,"recording_mbid":"75a38235-5db3-4545-bd4c-e4de9baee068","recording_msid":"0a95a65b-94a5-4f6a-a7ef-51d0455e4ad4","row_id":851,"track_metadata":{"additional_info":{"recording_msid":"0a95a65b-94a5-4f6a-a7ef-51d0455e4ad4"},"artist_name":"Playboi Carti","mbid_mapping":{"artist_mbids":["2baf3276-ed6a-4349-8d2e-f4601e7b2167"],"artists":[{"artist_credit_name":"Playboi Carti","artist_mbid":"2baf3276-ed6a-4349-8d2e-f4601e7b2167","join_phrase":""}],"recording_mbid":"75a38235-5db3-4545-bd4c-e4de9baee068","release_mbid":"b2d63750-05c8-4fa0-be7c-f7c3a4737a4a"},"release_name":"Magnolia","track_name":"Magnolia"}}],"total_count":12,"user_name":"Jasjeet"} diff --git a/sharedTest/src/main/resources/similar_user.json b/sharedTest/src/main/resources/similar_user.json new file mode 100644 index 00000000..2594d566 --- /dev/null +++ b/sharedTest/src/main/resources/similar_user.json @@ -0,0 +1 @@ +{"payload":{"similarity":0.592778543651705,"user_name":"jivteshs20"}} \ No newline at end of file diff --git a/sharedTest/src/main/resources/similar_user_error.json b/sharedTest/src/main/resources/similar_user_error.json new file mode 100644 index 00000000..f788e421 --- /dev/null +++ b/sharedTest/src/main/resources/similar_user_error.json @@ -0,0 +1 @@ +{"code":404,"error":"Similar-to user not found"} \ No newline at end of file diff --git a/sharedTest/src/main/resources/similar_users_response.json b/sharedTest/src/main/resources/similar_users_response.json index 9d8b0324..31e26bc2 100644 --- a/sharedTest/src/main/resources/similar_users_response.json +++ b/sharedTest/src/main/resources/similar_users_response.json @@ -1 +1 @@ -{"payload":[{"similarity":0.592778543651705,"user_name":"jivteshs20"},{"similarity":0.2367332837331803,"user_name":"akshaaatt"},{"similarity":0.18639954307331036,"user_name":"lucifer"}]} +{"payload":[{"similarity":0.592778543651705,"user_name":"jivteshs20"},{"similarity":0.2367332837331803,"user_name":"akshaaatt"},{"similarity":0.18639954307331036,"user_name":"lucifer"}]} \ No newline at end of file diff --git a/sharedTest/src/main/resources/top_albums.json b/sharedTest/src/main/resources/top_albums.json new file mode 100644 index 00000000..b4564d19 --- /dev/null +++ b/sharedTest/src/main/resources/top_albums.json @@ -0,0 +1 @@ +{"payload":{"count":25,"from_ts":1009843200,"last_updated":1723268864,"offset":0,"range":"all_time","releases":[{"artist_mbids":["4a779683-5404-4b90-a0d7-242495158265"],"artist_name":"Karan Aujla","artists":[{"artist_credit_name":"Karan Aujla","artist_mbid":"4a779683-5404-4b90-a0d7-242495158265","join_phrase":""}],"caa_id":32050224646,"caa_release_mbid":"bdd4aa78-9caf-40e4-a698-6639f96571eb","listen_count":156,"release_mbid":"bdd4aa78-9caf-40e4-a698-6639f96571eb","release_name":"B.T.F.U"},{"artist_mbids":["9b34cd6a-b874-4e75-bec8-1c174fe295d0"],"artist_name":"Arjan Dhillon","artists":[{"artist_credit_name":"Arjan Dhillon","artist_mbid":"9b34cd6a-b874-4e75-bec8-1c174fe295d0","join_phrase":""}],"caa_id":31811400978,"caa_release_mbid":"d8c0026f-e10c-494d-b66c-2085062b8c2e","listen_count":150,"release_mbid":"d8c0026f-e10c-494d-b66c-2085062b8c2e","release_name":"Awara"},{"artist_mbids":[],"artist_name":"Ekam Sudhar, Manni Sandhu, Rav Hanjra","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":127,"release_mbid":null,"release_name":"Small Circle"},{"artist_mbids":["d0a37e1c-a9c3-4966-b630-984c33b087df"],"artist_name":"Eifi","artists":[{"artist_credit_name":"Eifi","artist_mbid":"d0a37e1c-a9c3-4966-b630-984c33b087df","join_phrase":""}],"caa_id":35241947664,"caa_release_mbid":"1dd36e0a-16e8-4486-b062-00891253abb0","listen_count":103,"release_mbid":"1dd36e0a-16e8-4486-b062-00891253abb0","release_name":"Old School Vibe"},{"artist_mbids":["8837b875-3689-4646-b3fd-d8b53815c7a8"],"artist_name":"Lil Uzi Vert","artists":[{"artist_credit_name":"Lil Uzi Vert","artist_mbid":"8837b875-3689-4646-b3fd-d8b53815c7a8","join_phrase":""}],"caa_id":35008042732,"caa_release_mbid":"de152c73-f234-4456-a032-cac36b5c006d","listen_count":99,"release_mbid":"de152c73-f234-4456-a032-cac36b5c006d","release_name":"Watch This (ARIZONATEARS pluggnb remix)"},{"artist_mbids":["092552b9-ea6d-4ead-b424-cf8b07da5877"],"artist_name":"Tegi Pannu","artists":[{"artist_credit_name":"Tegi Pannu","artist_mbid":"092552b9-ea6d-4ead-b424-cf8b07da5877","join_phrase":""}],"caa_id":34147069927,"caa_release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea","listen_count":77,"release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea","release_name":"Disturbing The Peace"},{"artist_mbids":[],"artist_name":"Amantej Hundal","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":74,"release_mbid":null,"release_name":"Underrated"},{"artist_mbids":["63aa26c3-d59b-4da4-84ac-716b54f1ef4d"],"artist_name":"Tame Impala","artists":[{"artist_credit_name":"Tame Impala","artist_mbid":"63aa26c3-d59b-4da4-84ac-716b54f1ef4d","join_phrase":""}],"caa_id":25489083340,"caa_release_mbid":"b9410b9a-cb64-41ac-b863-fa95f8eb51ea","listen_count":64,"release_mbid":"b9410b9a-cb64-41ac-b863-fa95f8eb51ea","release_name":"Currents"},{"artist_mbids":["9537e089-79dd-43d7-9cf3-f6d51f6864fc"],"artist_name":"The Landers","artists":[{"artist_credit_name":"The Landers","artist_mbid":"9537e089-79dd-43d7-9cf3-f6d51f6864fc","join_phrase":""}],"caa_id":32015349705,"caa_release_mbid":"c7f3577a-8e75-4fc7-9498-1010054af117","listen_count":63,"release_mbid":"c7f3577a-8e75-4fc7-9498-1010054af117","release_name":"Gustakhiyan"},{"artist_mbids":["4a779683-5404-4b90-a0d7-242495158265","3ea12c3c-8596-4d70-b327-208b0a459a97"],"artist_name":"Karan Aujla, Ikky","artists":[{"artist_credit_name":"Karan Aujla","artist_mbid":"4a779683-5404-4b90-a0d7-242495158265","join_phrase":", "},{"artist_credit_name":"Ikky","artist_mbid":"3ea12c3c-8596-4d70-b327-208b0a459a97","join_phrase":""}],"caa_id":34792503592,"caa_release_mbid":"1390f1b7-7851-48ae-983d-eb8a48f78048","listen_count":63,"release_mbid":"1390f1b7-7851-48ae-983d-eb8a48f78048","release_name":"Four You"},{"artist_mbids":["381086ea-f511-4aba-bdf9-71c753dc5077"],"artist_name":"Kendrick Lamar","artists":[{"artist_credit_name":"Kendrick Lamar","artist_mbid":"381086ea-f511-4aba-bdf9-71c753dc5077","join_phrase":""}],"caa_id":2361576294,"caa_release_mbid":"e1d99364-1ad9-4f4d-9505-2242eff10a44","listen_count":51,"release_mbid":"e1d99364-1ad9-4f4d-9505-2242eff10a44","release_name":"good kid, m.A.A.d city"},{"artist_mbids":[],"artist_name":"R Nait","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":47,"release_mbid":null,"release_name":"Toy"},{"artist_mbids":[],"artist_name":"Arjan Dhillon","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":45,"release_mbid":null,"release_name":"Night Out"},{"artist_mbids":[],"artist_name":"Arjan Dhillon","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":43,"release_mbid":null,"release_name":"Kise Naal Ni Bolda"},{"artist_mbids":["610d38e7-f5b5-4ec8-b10d-17387ee11bdd","878391e2-c5e7-4b51-bbc1-236326fa810c"],"artist_name":"Amrit Maan & The PropheC","artists":[{"artist_credit_name":"Amrit Maan","artist_mbid":"610d38e7-f5b5-4ec8-b10d-17387ee11bdd","join_phrase":" & "},{"artist_credit_name":"The PropheC","artist_mbid":"878391e2-c5e7-4b51-bbc1-236326fa810c","join_phrase":""}],"caa_id":33469880728,"caa_release_mbid":"eb0701cd-5d83-4c23-ab7c-df4cc9dbb639","listen_count":40,"release_mbid":"eb0701cd-5d83-4c23-ab7c-df4cc9dbb639","release_name":"My Moon"},{"artist_mbids":["7e1cf02b-cf78-4803-b496-930c38649223"],"artist_name":"Jassa Dhillon","artists":[{"artist_credit_name":"Jassa Dhillon","artist_mbid":"7e1cf02b-cf78-4803-b496-930c38649223","join_phrase":""}],"caa_id":31988019523,"caa_release_mbid":"101876a6-8e7d-4512-97d5-a0ff08f22f53","listen_count":40,"release_mbid":"101876a6-8e7d-4512-97d5-a0ff08f22f53","release_name":"Love War"},{"artist_mbids":["9e975e9b-7538-4acd-ba91-8e5fd5df6b6e"],"artist_name":"Kid Bloom","artists":[{"artist_credit_name":"Kid Bloom","artist_mbid":"9e975e9b-7538-4acd-ba91-8e5fd5df6b6e","join_phrase":""}],"caa_id":null,"caa_release_mbid":null,"listen_count":36,"release_mbid":"75169a25-004e-40f5-aaa7-78d2de09eb28","release_name":"Electric U -Single"},{"artist_mbids":["bce6d667-cde8-485e-b078-c0a05adea36d"],"artist_name":"ScHoolboy Q","artists":[{"artist_credit_name":"ScHoolboy Q","artist_mbid":"bce6d667-cde8-485e-b078-c0a05adea36d","join_phrase":""}],"caa_id":32363103828,"caa_release_mbid":"9fc29035-a4c5-4e7c-b984-a52e9aa63ad8","listen_count":36,"release_mbid":"9fc29035-a4c5-4e7c-b984-a52e9aa63ad8","release_name":"CrasH Talk"},{"artist_mbids":[],"artist_name":"NIJJAR","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":34,"release_mbid":null,"release_name":"Chobbar"},{"artist_mbids":["ada7a83c-e3e1-40f1-93f9-3e73dbc9298a"],"artist_name":"Arctic Monkeys","artists":[{"artist_credit_name":"Arctic Monkeys","artist_mbid":"ada7a83c-e3e1-40f1-93f9-3e73dbc9298a","join_phrase":""}],"caa_id":32131848311,"caa_release_mbid":"55171afe-440e-4c63-947c-e49074f3d5b5","listen_count":33,"release_mbid":"55171afe-440e-4c63-947c-e49074f3d5b5","release_name":"AM"},{"artist_mbids":["2338cede-17d7-40ef-9ea1-a16416908eeb"],"artist_name":"Sports","artists":[{"artist_credit_name":"Sports","artist_mbid":"2338cede-17d7-40ef-9ea1-a16416908eeb","join_phrase":""}],"caa_id":16665200572,"caa_release_mbid":"afea971b-c6ed-4ff8-9552-b47b8569c751","listen_count":32,"release_mbid":"afea971b-c6ed-4ff8-9552-b47b8569c751","release_name":"Naked All The Time"},{"artist_mbids":["9f3225ad-e686-4538-8cbe-99406c4a4caa"],"artist_name":"Aaron May","artists":[{"artist_credit_name":"Aaron May","artist_mbid":"9f3225ad-e686-4538-8cbe-99406c4a4caa","join_phrase":""}],"caa_id":22549299616,"caa_release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4","listen_count":31,"release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4","release_name":"Chase"},{"artist_mbids":["3538581d-2e3f-40ac-98fe-fd434f29d04d"],"artist_name":"Nirvair Pannu","artists":[{"artist_credit_name":"Nirvair Pannu","artist_mbid":"3538581d-2e3f-40ac-98fe-fd434f29d04d","join_phrase":""}],"caa_id":33181866663,"caa_release_mbid":"9a2d9b48-146f-4980-bdc1-891bc0b598b1","listen_count":29,"release_mbid":"9a2d9b48-146f-4980-bdc1-891bc0b598b1","release_name":"Click"},{"artist_mbids":[],"artist_name":"Navaan Sandhu","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":28,"release_mbid":null,"release_name":"Relentless"},{"artist_mbids":["9b34cd6a-b874-4e75-bec8-1c174fe295d0"],"artist_name":"Arjan Dhillon","artists":[{"artist_credit_name":"Arjan Dhillon","artist_mbid":"9b34cd6a-b874-4e75-bec8-1c174fe295d0","join_phrase":""}],"caa_id":33843554085,"caa_release_mbid":"6c7dc4d8-7505-49ef-88d6-86a3b7fbb7eb","listen_count":26,"release_mbid":"6c7dc4d8-7505-49ef-88d6-86a3b7fbb7eb","release_name":"Jalwa"}],"to_ts":1723248018,"total_release_count":638,"user_id":"Jasjeet"}} diff --git a/sharedTest/src/main/resources/top_artists.json b/sharedTest/src/main/resources/top_artists.json new file mode 100644 index 00000000..7d698b1b --- /dev/null +++ b/sharedTest/src/main/resources/top_artists.json @@ -0,0 +1 @@ +{"payload":{"artists":[{"artist_mbid":"4a779683-5404-4b90-a0d7-242495158265","artist_name":"Karan Aujla","listen_count":263},{"artist_mbid":"9b34cd6a-b874-4e75-bec8-1c174fe295d0","artist_name":"Arjan Dhillon","listen_count":188},{"artist_mbid":null,"artist_name":"Ekam Sudhar, Manni Sandhu, Rav Hanjra","listen_count":127},{"artist_mbid":null,"artist_name":"Arjan Dhillon","listen_count":127},{"artist_mbid":"092552b9-ea6d-4ead-b424-cf8b07da5877","artist_name":"Tegi Pannu","listen_count":114},{"artist_mbid":"8837b875-3689-4646-b3fd-d8b53815c7a8","artist_name":"Lil Uzi Vert","listen_count":104},{"artist_mbid":"63aa26c3-d59b-4da4-84ac-716b54f1ef4d","artist_name":"Tame Impala","listen_count":103},{"artist_mbid":"d0a37e1c-a9c3-4966-b630-984c33b087df","artist_name":"Eifi","listen_count":103},{"artist_mbid":null,"artist_name":"Amantej Hundal","listen_count":75},{"artist_mbid":"3ea12c3c-8596-4d70-b327-208b0a459a97","artist_name":"Ikky","listen_count":65},{"artist_mbid":"381086ea-f511-4aba-bdf9-71c753dc5077","artist_name":"Kendrick Lamar","listen_count":64},{"artist_mbid":"9537e089-79dd-43d7-9cf3-f6d51f6864fc","artist_name":"The Landers","listen_count":63},{"artist_mbid":"7e1cf02b-cf78-4803-b496-930c38649223","artist_name":"Jassa Dhillon","listen_count":54},{"artist_mbid":"119a6864-622b-4e6c-8aab-a422080530c6","artist_name":"Sidhu Moose Wala","listen_count":53},{"artist_mbid":"9880800a-f6f8-4f8f-a00e-b4b664776c0c","artist_name":"Jay Rock","listen_count":52},{"artist_mbid":"878391e2-c5e7-4b51-bbc1-236326fa810c","artist_name":"The PropheC","listen_count":49},{"artist_mbid":null,"artist_name":"R Nait","listen_count":49},{"artist_mbid":"610d38e7-f5b5-4ec8-b10d-17387ee11bdd","artist_name":"Amrit Maan","listen_count":48},{"artist_mbid":null,"artist_name":"Navaan Sandhu","listen_count":40},{"artist_mbid":"6ae6bde5-3727-4202-9298-e2f3de2235cd","artist_name":"Manni Sandhu","listen_count":38},{"artist_mbid":"ada7a83c-e3e1-40f1-93f9-3e73dbc9298a","artist_name":"Arctic Monkeys","listen_count":38},{"artist_mbid":"bce6d667-cde8-485e-b078-c0a05adea36d","artist_name":"ScHoolboy Q","listen_count":36},{"artist_mbid":"9e975e9b-7538-4acd-ba91-8e5fd5df6b6e","artist_name":"Kid Bloom","listen_count":36},{"artist_mbid":"83fd7495-a02f-4254-9cfb-6753cb87c73e","artist_name":"Prem Dhillon","listen_count":34},{"artist_mbid":null,"artist_name":"NIJJAR","listen_count":34}],"count":25,"from_ts":1009843200,"last_updated":1723268083,"offset":0,"range":"all_time","to_ts":1723248018,"total_artist_count":617,"user_id":"Jasjeet"}} diff --git a/sharedTest/src/main/resources/top_songs.json b/sharedTest/src/main/resources/top_songs.json new file mode 100644 index 00000000..dc1e0bd5 --- /dev/null +++ b/sharedTest/src/main/resources/top_songs.json @@ -0,0 +1 @@ +{"payload":{"count":25,"from_ts":1009843200,"last_updated":1723271769,"offset":0,"range":"all_time","recordings":[{"artist_mbids":[],"artist_name":"Ekam Sudhar, Manni Sandhu, Rav Hanjra","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":127,"recording_mbid":null,"release_mbid":null,"release_name":"Small Circle","track_name":"Small Circle"},{"artist_mbids":["d0a37e1c-a9c3-4966-b630-984c33b087df"],"artist_name":"Eifi","artists":[{"artist_credit_name":"Eifi","artist_mbid":"d0a37e1c-a9c3-4966-b630-984c33b087df","join_phrase":""}],"caa_id":35241947664,"caa_release_mbid":"1dd36e0a-16e8-4486-b062-00891253abb0","listen_count":103,"recording_mbid":"5279bc04-6b83-4897-814d-4f604fedd79c","release_mbid":"1dd36e0a-16e8-4486-b062-00891253abb0","release_name":"Old School Vibe","track_name":"Old School Vibe"},{"artist_mbids":["8837b875-3689-4646-b3fd-d8b53815c7a8"],"artist_name":"Lil Uzi Vert","artists":[{"artist_credit_name":"Lil Uzi Vert","artist_mbid":"8837b875-3689-4646-b3fd-d8b53815c7a8","join_phrase":""}],"caa_id":35008042732,"caa_release_mbid":"de152c73-f234-4456-a032-cac36b5c006d","listen_count":99,"recording_mbid":"296c6296-570c-471b-ace2-bd1d0f6bc3f6","release_mbid":"de152c73-f234-4456-a032-cac36b5c006d","release_name":"Watch This (ARIZONATEARS pluggnb remix)","track_name":"Watch This (ARIZONATEARS pluggnb remix)"},{"artist_mbids":[],"artist_name":"Amantej Hundal","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":74,"recording_mbid":null,"release_mbid":null,"release_name":"Underrated","track_name":"Still Standing"},{"artist_mbids":["9537e089-79dd-43d7-9cf3-f6d51f6864fc"],"artist_name":"The Landers","artists":[{"artist_credit_name":"The Landers","artist_mbid":"9537e089-79dd-43d7-9cf3-f6d51f6864fc","join_phrase":""}],"caa_id":32015349705,"caa_release_mbid":"c7f3577a-8e75-4fc7-9498-1010054af117","listen_count":63,"recording_mbid":"0f0ed9f3-6960-4b8d-b8bf-ae7d52ec4385","release_mbid":"c7f3577a-8e75-4fc7-9498-1010054af117","release_name":"Gustakhiyan","track_name":"Gustakhiyan"},{"artist_mbids":["63aa26c3-d59b-4da4-84ac-716b54f1ef4d"],"artist_name":"Tame Impala","artists":[{"artist_credit_name":"Tame Impala","artist_mbid":"63aa26c3-d59b-4da4-84ac-716b54f1ef4d","join_phrase":""}],"caa_id":25489083340,"caa_release_mbid":"b9410b9a-cb64-41ac-b863-fa95f8eb51ea","listen_count":50,"recording_mbid":"f078699c-9b68-4040-9ba4-b1e4f7d86598","release_mbid":"b9410b9a-cb64-41ac-b863-fa95f8eb51ea","release_name":"Currents","track_name":"New Person, Same Old Mistakes"},{"artist_mbids":["381086ea-f511-4aba-bdf9-71c753dc5077","9880800a-f6f8-4f8f-a00e-b4b664776c0c"],"artist_name":"Kendrick Lamar featuring Jay Rock","artists":[{"artist_credit_name":"Kendrick Lamar","artist_mbid":"381086ea-f511-4aba-bdf9-71c753dc5077","join_phrase":" featuring "},{"artist_credit_name":"Jay Rock","artist_mbid":"9880800a-f6f8-4f8f-a00e-b4b664776c0c","join_phrase":""}],"caa_id":2361576294,"caa_release_mbid":"e1d99364-1ad9-4f4d-9505-2242eff10a44","listen_count":50,"recording_mbid":"c294757b-0430-4eb4-ae7a-6abcffb87405","release_mbid":"e1d99364-1ad9-4f4d-9505-2242eff10a44","release_name":"good kid, m.A.A.d city","track_name":"Money Trees"},{"artist_mbids":[],"artist_name":"R Nait","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":47,"recording_mbid":null,"release_mbid":null,"release_name":"Toy","track_name":"Toy"},{"artist_mbids":["4a779683-5404-4b90-a0d7-242495158265"],"artist_name":"Karan Aujla","artists":[{"artist_credit_name":"Karan Aujla","artist_mbid":"4a779683-5404-4b90-a0d7-242495158265","join_phrase":""}],"caa_id":32050224646,"caa_release_mbid":"bdd4aa78-9caf-40e4-a698-6639f96571eb","listen_count":47,"recording_mbid":"4fd3a889-6d06-47c5-99c2-756783f3d34e","release_mbid":"bdd4aa78-9caf-40e4-a698-6639f96571eb","release_name":"B.T.F.U","track_name":"Addi Sunni"},{"artist_mbids":[],"artist_name":"Arjan Dhillon","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":45,"recording_mbid":null,"release_mbid":null,"release_name":"Night Out","track_name":"Night Out"},{"artist_mbids":[],"artist_name":"Arjan Dhillon","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":43,"recording_mbid":null,"release_mbid":null,"release_name":"Kise Naal Ni Bolda","track_name":"Kise Naal Ni Bolda"},{"artist_mbids":["9b34cd6a-b874-4e75-bec8-1c174fe295d0"],"artist_name":"Arjan Dhillon","artists":[{"artist_credit_name":"Arjan Dhillon","artist_mbid":"9b34cd6a-b874-4e75-bec8-1c174fe295d0","join_phrase":""}],"caa_id":31811400978,"caa_release_mbid":"d8c0026f-e10c-494d-b66c-2085062b8c2e","listen_count":43,"recording_mbid":"8ff7f33a-d307-4f6f-ab7e-ddfd853a9ee8","release_mbid":"d8c0026f-e10c-494d-b66c-2085062b8c2e","release_name":"Awara","track_name":"Dope"},{"artist_mbids":["610d38e7-f5b5-4ec8-b10d-17387ee11bdd","878391e2-c5e7-4b51-bbc1-236326fa810c"],"artist_name":"Amrit Maan & The PropheC","artists":[{"artist_credit_name":"Amrit Maan","artist_mbid":"610d38e7-f5b5-4ec8-b10d-17387ee11bdd","join_phrase":" & "},{"artist_credit_name":"The PropheC","artist_mbid":"878391e2-c5e7-4b51-bbc1-236326fa810c","join_phrase":""}],"caa_id":33469880728,"caa_release_mbid":"eb0701cd-5d83-4c23-ab7c-df4cc9dbb639","listen_count":40,"recording_mbid":"440111a5-c53e-4263-9921-8c96fe92a035","release_mbid":"eb0701cd-5d83-4c23-ab7c-df4cc9dbb639","release_name":"My Moon","track_name":"My Moon"},{"artist_mbids":["092552b9-ea6d-4ead-b424-cf8b07da5877"],"artist_name":"Tegi Pannu","artists":[{"artist_credit_name":"Tegi Pannu","artist_mbid":"092552b9-ea6d-4ead-b424-cf8b07da5877","join_phrase":""}],"caa_id":34147069927,"caa_release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea","listen_count":38,"recording_mbid":"f0fd8820-2d6e-4c70-a9a0-79187d55c79a","release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea","release_name":"Disturbing The Peace","track_name":"Mood Swings"},{"artist_mbids":["9e975e9b-7538-4acd-ba91-8e5fd5df6b6e"],"artist_name":"Kid Bloom","artists":[{"artist_credit_name":"Kid Bloom","artist_mbid":"9e975e9b-7538-4acd-ba91-8e5fd5df6b6e","join_phrase":""}],"caa_id":null,"caa_release_mbid":null,"listen_count":36,"recording_mbid":"224a60b4-a861-4862-8a00-eb33c15267f2","release_mbid":"75169a25-004e-40f5-aaa7-78d2de09eb28","release_name":"Electric U -Single","track_name":"Electric U"},{"artist_mbids":["7e1cf02b-cf78-4803-b496-930c38649223"],"artist_name":"Jassa Dhillon","artists":[{"artist_credit_name":"Jassa Dhillon","artist_mbid":"7e1cf02b-cf78-4803-b496-930c38649223","join_phrase":""}],"caa_id":31988019523,"caa_release_mbid":"101876a6-8e7d-4512-97d5-a0ff08f22f53","listen_count":35,"recording_mbid":"8d3e9d59-78b8-4d25-8a2b-79290aa398fc","release_mbid":"101876a6-8e7d-4512-97d5-a0ff08f22f53","release_name":"Love War","track_name":"Shadow"},{"artist_mbids":["bce6d667-cde8-485e-b078-c0a05adea36d"],"artist_name":"ScHoolboy Q","artists":[{"artist_credit_name":"ScHoolboy Q","artist_mbid":"bce6d667-cde8-485e-b078-c0a05adea36d","join_phrase":""}],"caa_id":32363103828,"caa_release_mbid":"9fc29035-a4c5-4e7c-b984-a52e9aa63ad8","listen_count":34,"recording_mbid":"e5645698-84ef-4312-9a75-ad2fd981a1d8","release_mbid":"9fc29035-a4c5-4e7c-b984-a52e9aa63ad8","release_name":"CrasH Talk","track_name":"CrasH"},{"artist_mbids":[],"artist_name":"NIJJAR","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":34,"recording_mbid":null,"release_mbid":null,"release_name":"Chobbar","track_name":"Chobbar"},{"artist_mbids":["092552b9-ea6d-4ead-b424-cf8b07da5877","81035abf-09f7-4cbe-a785-4510dbd7970c"],"artist_name":"Tegi Pannu & JJ Esko","artists":[{"artist_credit_name":"Tegi Pannu","artist_mbid":"092552b9-ea6d-4ead-b424-cf8b07da5877","join_phrase":" & "},{"artist_credit_name":"JJ Esko","artist_mbid":"81035abf-09f7-4cbe-a785-4510dbd7970c","join_phrase":""}],"caa_id":34147069927,"caa_release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea","listen_count":33,"recording_mbid":"650aa31f-cf18-4239-ac59-9cb9cca92d4d","release_mbid":"75e7ef49-6a09-4856-987b-0cbefa1ba9ea","release_name":"Disturbing The Peace","track_name":"Hold You Down"},{"artist_mbids":["2338cede-17d7-40ef-9ea1-a16416908eeb"],"artist_name":"Sports","artists":[{"artist_credit_name":"Sports","artist_mbid":"2338cede-17d7-40ef-9ea1-a16416908eeb","join_phrase":""}],"caa_id":16665200572,"caa_release_mbid":"afea971b-c6ed-4ff8-9552-b47b8569c751","listen_count":32,"recording_mbid":"537f2df8-d1a6-4e22-9a6a-18393cdbb988","release_mbid":"afea971b-c6ed-4ff8-9552-b47b8569c751","release_name":"Naked All The Time","track_name":"You Are The Right One"},{"artist_mbids":["4a779683-5404-4b90-a0d7-242495158265","3ea12c3c-8596-4d70-b327-208b0a459a97"],"artist_name":"Karan Aujla, Ikky","artists":[{"artist_credit_name":"Karan Aujla","artist_mbid":"4a779683-5404-4b90-a0d7-242495158265","join_phrase":", "},{"artist_credit_name":"Ikky","artist_mbid":"3ea12c3c-8596-4d70-b327-208b0a459a97","join_phrase":""}],"caa_id":34792503592,"caa_release_mbid":"1390f1b7-7851-48ae-983d-eb8a48f78048","listen_count":31,"recording_mbid":"34c208ee-2de7-4d38-b47e-907074866dd3","release_mbid":"1390f1b7-7851-48ae-983d-eb8a48f78048","release_name":"Four You","track_name":"52 Bars"},{"artist_mbids":["9f3225ad-e686-4538-8cbe-99406c4a4caa"],"artist_name":"Aaron May","artists":[{"artist_credit_name":"Aaron May","artist_mbid":"9f3225ad-e686-4538-8cbe-99406c4a4caa","join_phrase":""}],"caa_id":22549299616,"caa_release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4","listen_count":29,"recording_mbid":"2f14ac58-d62d-4e45-a5e1-6504d1366831","release_mbid":"ff0e48f3-83f6-4923-b5fa-a3c39f484da4","release_name":"Chase","track_name":"I'm Good Luv, Enjoy."},{"artist_mbids":["ada7a83c-e3e1-40f1-93f9-3e73dbc9298a"],"artist_name":"Arctic Monkeys","artists":[{"artist_credit_name":"Arctic Monkeys","artist_mbid":"ada7a83c-e3e1-40f1-93f9-3e73dbc9298a","join_phrase":""}],"caa_id":32131848311,"caa_release_mbid":"55171afe-440e-4c63-947c-e49074f3d5b5","listen_count":27,"recording_mbid":"adadc047-e040-45d5-b020-0603db0d69dc","release_mbid":"55171afe-440e-4c63-947c-e49074f3d5b5","release_name":"AM","track_name":"Snap Out of It"},{"artist_mbids":[],"artist_name":"Seren","artists":null,"caa_id":null,"caa_release_mbid":null,"listen_count":26,"recording_mbid":null,"release_mbid":null,"release_name":null,"track_name":"Travis Scott - My Eyes (Best Part Extended)"},{"artist_mbids":["39d82be4-4dd4-4bda-b485-fc84c7c72938","fc63d806-ca89-4ea3-a404-ee6de695743f"],"artist_name":"Trix & Flix feat. Shaggy","artists":[{"artist_credit_name":"Trix & Flix","artist_mbid":"39d82be4-4dd4-4bda-b485-fc84c7c72938","join_phrase":" feat. "},{"artist_credit_name":"Shaggy","artist_mbid":"fc63d806-ca89-4ea3-a404-ee6de695743f","join_phrase":""}],"caa_id":38114265515,"caa_release_mbid":"0a6443f6-5f09-45db-a1cf-eae3991eeb27","listen_count":25,"recording_mbid":"e7098517-c096-4c36-968f-f014549baddd","release_mbid":"912ca54e-76a6-4c61-8746-62ef479a7a63","release_name":"Intoxication","track_name":"Like a Superstar (radio edit)"}],"to_ts":1723248018,"total_recording_count":884,"user_id":"Jasjeet"}} diff --git a/spotify-app-remote/build.gradle.kts b/spotify-app-remote/build.gradle.kts new file mode 100644 index 00000000..f479d2bf --- /dev/null +++ b/spotify-app-remote/build.gradle.kts @@ -0,0 +1,2 @@ +configurations.maybeCreate("default") +artifacts.add("default", file("spotify-app-remote-release-0.7.2.aar")) \ No newline at end of file diff --git a/app/lib/spotify-app-remote-release-0.7.2.aar b/spotify-app-remote/spotify-app-remote-release-0.7.2.aar similarity index 100% rename from app/lib/spotify-app-remote-release-0.7.2.aar rename to spotify-app-remote/spotify-app-remote-release-0.7.2.aar