Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ internal class LoginUserOperationExecutor(
var identities = mapOf<String, String>()
var subscriptions = mapOf<String, SubscriptionObject>()
val properties = mutableMapOf<String, String>()
properties["timezone_id"] = TimeUtils.getTimeZoneId()!!
properties["timezone_id"] = TimeUtils.getTimeZoneId()
properties["language"] = _languageContext.language

if (createUserOperation.externalId != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.onesignal.common

import android.os.Build
import br.com.colman.kotest.android.extensions.robolectric.RobolectricTest
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldNotBeEmpty
import io.kotest.matchers.string.shouldNotContain
import java.time.ZoneId
import java.util.TimeZone

@RobolectricTest
class TimeUtilsTest : FunSpec({

test("getTimeZoneId returns correct time zone id") {
// Given
val expected =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ZoneId.systemDefault().id
} else {
TimeZone.getDefault().id
}

// When
val actual = TimeUtils.getTimeZoneId()

// Then
actual shouldBe expected
actual.shouldNotBeEmpty()
}

test("getTimeZoneId returns valid timezone format") {
// When
val timeZoneId = TimeUtils.getTimeZoneId()

// Then
timeZoneId.shouldNotBeEmpty()
timeZoneId shouldNotBe ""

// Valid timezone IDs follow IANA format patterns:
// - Continental zones: "America/New_York", "Europe/London"
// - UTC variants: "UTC", "GMT"
// - Offset formats: "GMT+05:30", "UTC-08:00"
// Should not contain spaces or invalid characters
timeZoneId.shouldNotContain(" ")
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.onesignal.session.internal.session.impl

import com.onesignal.common.TimeUtils
import com.onesignal.mocks.MockHelper
import com.onesignal.user.internal.operations.TrackSessionStartOperation
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkObject
import io.mockk.verify

class SessionListenerTests : FunSpec({

test("onSessionStarted updates timezone and enqueues TrackSessionStartOperation") {
// Given
val mockTimeZone = "Europe/London"
mockkObject(TimeUtils)
every { TimeUtils.getTimeZoneId() } returns mockTimeZone

val mockOperationRepo = mockk<com.onesignal.core.internal.operations.IOperationRepo>(relaxed = true)
val mockSessionService = mockk<com.onesignal.session.internal.session.ISessionService>(relaxed = true)
val mockConfigModelStore = MockHelper.configModelStore()
val mockIdentityModelStore = MockHelper.identityModelStore()
val mockOutcomeEventsController = mockk<com.onesignal.session.internal.outcomes.IOutcomeEventsController>(relaxed = true)

val mockPropertiesModelStore = MockHelper.propertiesModelStore()

val sessionListener =
SessionListener(
mockOperationRepo,
mockSessionService,
mockConfigModelStore,
mockIdentityModelStore,
mockPropertiesModelStore,
mockOutcomeEventsController,
)

try {
// When
sessionListener.onSessionStarted()

// Then - Verify that update() was called (timezone should be set to our mocked value)
val propertiesModel = mockPropertiesModelStore.model
propertiesModel.timezone shouldBe mockTimeZone

// Also verify the operation was enqueued
verify(exactly = 1) {
mockOperationRepo.enqueue(any<TrackSessionStartOperation>(), true)
}
} finally {
// Clean up the mock
unmockkObject(TimeUtils)
}
}
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.onesignal.user.internal.operations

import com.onesignal.common.TimeUtils
import com.onesignal.common.exceptions.BackendException
import com.onesignal.common.modeling.ModelChangeTags
import com.onesignal.core.internal.operations.ExecutionResult
Expand Down Expand Up @@ -27,7 +28,9 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.runs
import io.mockk.unmockkObject

class RefreshUserOperationExecutorTests : FunSpec({
val appId = "appId"
Expand Down Expand Up @@ -327,4 +330,71 @@ class RefreshUserOperationExecutorTests : FunSpec({
mockUserBackendService.getUser(appId, IdentityConstants.ONESIGNAL_ID, remoteOneSignalId)
}
}

test("refresh user sets local timezone via propertiesModel update") {
// Given
val mockTimeZone = "America/New_York"
mockkObject(TimeUtils)
every { TimeUtils.getTimeZoneId() } returns mockTimeZone

val mockUserBackendService = mockk<IUserBackendService>()
coEvery { mockUserBackendService.getUser(appId, IdentityConstants.ONESIGNAL_ID, remoteOneSignalId) } returns
CreateUserResponse(
mapOf(IdentityConstants.ONESIGNAL_ID to remoteOneSignalId),
PropertiesObject(country = "US"),
listOf(),
)

val mockIdentityModelStore = MockHelper.identityModelStore()
val mockIdentityModel = IdentityModel()
mockIdentityModel.onesignalId = remoteOneSignalId
every { mockIdentityModelStore.model } returns mockIdentityModel
every { mockIdentityModelStore.replace(any(), any()) } just runs

val mockPropertiesModelStore = MockHelper.propertiesModelStore()
val mockPropertiesModel = PropertiesModel()
mockPropertiesModel.onesignalId = remoteOneSignalId
every { mockPropertiesModelStore.model } returns mockPropertiesModel
every { mockPropertiesModelStore.replace(any(), any()) } just runs

val mockSubscriptionsModelStore = mockk<SubscriptionModelStore>()
every { mockSubscriptionsModelStore.replaceAll(any(), any()) } just runs

val mockConfigModelStore = MockHelper.configModelStore()
val mockBuildUserService = mockk<IRebuildUserService>()

val refreshUserOperationExecutor =
RefreshUserOperationExecutor(
mockUserBackendService,
mockIdentityModelStore,
mockPropertiesModelStore,
mockSubscriptionsModelStore,
mockConfigModelStore,
mockBuildUserService,
getNewRecordState(),
)

val operations = listOf<Operation>(RefreshUserOperation(appId, remoteOneSignalId))

try {
// When
val response = refreshUserOperationExecutor.execute(operations)

// Then - Verify success and that timezone is set to our mocked value (via update() call)
response.result shouldBe ExecutionResult.SUCCESS
coVerify(exactly = 1) {
mockPropertiesModelStore.replace(
withArg {
it.country shouldBe "US"
// Verify timezone is set to our mocked timezone (what update() does)
it.timezone shouldBe mockTimeZone
},
ModelChangeTags.HYDRATE,
)
}
} finally {
// Clean up the mock
unmockkObject(TimeUtils)
}
}
})
Loading