package com.suno.android.media.hooks import android.net.Uri import androidx.media3.common.Player import com.suno.android.common_analytics.hooks.HookAnalyticsManager import com.suno.android.common_analytics.hooks.HookAnalyticsSession import com.suno.android.common_analytics.hooks.HookClipAnalyticsManager import com.suno.android.common_core_utils.Id import com.suno.android.common_core_utils.SunoLogger import com.suno.android.common_core_utils.constants.SunoMediaType import com.suno.android.common_core_utils.model.Url import com.suno.android.common_core_utils.model.UserHandle import com.suno.android.common_data.mappers.clips.ClipStatus import com.suno.android.common_data.mappers.clips.LocalClipData import com.suno.android.media.player.SharedPlayer import io.mockk.Runs import io.mockk.every import io.mockk.just import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.slot import io.mockk.unmockkAll import io.mockk.verify import org.junit.After import org.junit.Before import org.junit.Test import kotlin.time.Duration.Companion.milliseconds class HookClipMediaManagerTest { private val sharedPlayer = mockk() private val hookClipAnalyticsManager = mockk(relaxed = true) private val hookAnalyticsManager = mockk(relaxed = true) private val player = mockk(relaxed = true) private val loggerFactory = mockk(relaxed = true) private lateinit var subject: HookClipMediaManager @Before fun setUp() { mockkStatic(Uri::class) every { Uri.parse(any()) } returns mockk() every { sharedPlayer.getInstance() } returns player every { sharedPlayer.releaseInstance() } just Runs subject = HookClipMediaManager( loggerFactory = loggerFactory, sharedPlayer = sharedPlayer, hookClipAnalyticsManager = hookClipAnalyticsManager, hookAnalyticsManager = hookAnalyticsManager, ) } @After fun tearDown() { unmockkAll() } @Test fun `when playClip called then tracks open clip player play song event`() { val clip = createTestClipData(clipId = "clip1") val hookSessionId = Id("hook-session-123") val listenerSlot = slot() every { player.addListener(capture(listenerSlot)) } just Runs every { hookAnalyticsManager.getCurrentSessionId() } returns hookSessionId subject.playClip(clip = clip) listenerSlot.captured.onIsPlayingChanged(true) verify { hookClipAnalyticsManager.trackOpenClipPlayerPlaySong( clip = clip, previousHookSessionId = hookSessionId, ) } } @Test fun `when playClip called multiple times then tracks open clip player play song event only once`() { val clip = createTestClipData(clipId = "clip1") val hookSessionId = Id("hook-session-123") val listenerSlot = slot() every { player.addListener(capture(listenerSlot)) } just Runs every { hookAnalyticsManager.getCurrentSessionId() } returns hookSessionId subject.playClip(clip = clip) listenerSlot.captured.onIsPlayingChanged(true) listenerSlot.captured.onIsPlayingChanged(false) subject.playClip(clip = clip) listenerSlot.captured.onIsPlayingChanged(true) verify(exactly = 1) { hookClipAnalyticsManager.trackOpenClipPlayerPlaySong( clip = clip, previousHookSessionId = hookSessionId, ) } } @Test fun `when release called then tracks close clip player pause song event with correct play duration`() { val clip = createTestClipData(clipId = "clip1") val hookSessionId = Id("hook-session-123") val listenerSlot = slot() every { player.addListener(capture(listenerSlot)) } just Runs every { player.currentPosition } returnsMany listOf(2000L, 7000L) every { hookAnalyticsManager.getCurrentSessionId() } returns hookSessionId subject.playClip(clip = clip) listenerSlot.captured.onIsPlayingChanged(true) subject.release() verify { hookClipAnalyticsManager.trackCloseClipPlayerPauseSong( clip = clip, playStartMillis = 2000.milliseconds, playEndMillis = 7000.milliseconds, playDurationMillis = 0.milliseconds, ) } } private fun createTestClipData( clipId: String = "test-clip-id", artistUserId: String = "artist-user-id", ): LocalClipData = LocalClipData( clipId = Id(clipId), mediaType = SunoMediaType.AUDIO, mediaUrl = Url("https://example.com/audio.mp3"), artistName = "Test Artist", artistUserId = Id(artistUserId), handle = UserHandle("@testartist"), artistAvatarUrl = Url("https://example.com/avatar.jpg"), nowPlayingTitle = "Test Song Title", albumImageUrl = Url("https://example.com/album.jpg"), videoCoverUrl = null, previewUrl = null, lyricsString = null, caption = null, captionMentions = null, isPublic = true, modelName = "test-model", majorModelVersion = "v100", reaction = null, prompt = "test prompt", gptPrompt = null, tags = "test,tags", displayTags = "test tags", status = ClipStatus.Complete, downloadDisabledReason = null, commentCount = 5, upvoteCount = 10, duration = null, playCount = 100, isFollowing = false, canRemix = true, isRemix = false, remixTask = null, ) }