Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -52,6 +52,9 @@ internal class OneSignalImp(
@Volatile
private var initState: InitState = InitState.NOT_STARTED

@Volatile
private var initFailureMessage: String? = null

override val sdkVersion: String = OneSignalUtils.sdkVersion

override val isInitialized: Boolean
Expand Down Expand Up @@ -279,7 +282,9 @@ internal class OneSignalImp(
val startupService = bootstrapServices()
val result = resolveAppId(appId, configModel, preferencesService)
if (result.failed) {
Logging.warn("suspendInitInternal: no appId provided or found in legacy config.")
val message = "suspendInitInternal: no appId provided or found in local storage. Please pass a valid appId to initWithContext()."
initFailureMessage = message
Logging.warn(message)
initState = InitState.FAILED
notifyInitComplete()
return false
Expand Down Expand Up @@ -360,6 +365,8 @@ internal class OneSignalImp(
* @param operationName Optional operation name to include in error messages (e.g., "login", "logout")
*/
private suspend fun waitUntilInitInternal(operationName: String? = null) {
val failureMessage = initFailureMessage ?: "Initialization failed. Cannot proceed."

when (initState) {
InitState.NOT_STARTED -> {
val message = if (operationName != null) {
Expand Down Expand Up @@ -395,12 +402,12 @@ internal class OneSignalImp(

// Re-check state after waiting - init might have failed during the wait
if (initState == InitState.FAILED) {
throw IllegalStateException("Initialization failed. Cannot proceed.")
throw IllegalStateException(failureMessage)
}
// initState is guaranteed to be SUCCESS here - consistent state
}
InitState.FAILED -> {
throw IllegalStateException("Initialization failed. Cannot proceed.")
throw IllegalStateException(failureMessage)
}
else -> {
// SUCCESS - already initialized, no need to wait
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,6 @@ class SDKInitTests : FunSpec({

beforeAny {
Logging.logLevel = LogLevel.NONE

// Aggressive pre-test cleanup to avoid state leakage across tests
val context = getApplicationContext<Context>()
val prefs = context.getSharedPreferences("OneSignal", Context.MODE_PRIVATE)
prefs.edit()
.clear()
.commit()

val otherPrefs = context.getSharedPreferences("com.onesignal", Context.MODE_PRIVATE)
otherPrefs.edit()
.clear()
.commit()

Thread.sleep(100)
}

afterAny {
Expand All @@ -65,19 +51,27 @@ class SDKInitTests : FunSpec({
otherPrefs.edit()
.clear()
.commit()
}

// Wait longer to ensure cleanup is complete
Thread.sleep(100)
test("accessor instances after initWithContext without appID shows the failure reason") {
// Given
val context = getApplicationContext<Context>()
val os = OneSignalImp()

// Clear any in-memory state by initializing and logging out a fresh instance
try {
val os = OneSignalImp()
os.initWithContext(context, "appId")
os.logout()
Thread.sleep(100)
} catch (ignored: Exception) {
// ignore cleanup exceptions
// When
os.initWithContext(context)

// Then
val ex = shouldThrow<IllegalStateException> {
os.user.onesignalId // Should trigger waitUntilInitInternal → throw failure message
}
ex.message shouldBe "suspendInitInternal: no appId provided or found in local storage. Please pass a valid appId to initWithContext()."

// Calling initWithContext with an appID after the failure should not throw anymore
val result = os.initWithContext(context, "appID")
waitForInitialization(os)
result shouldBe true
os.isInitialized shouldBe true
}

test("OneSignal accessors throw before calling initWithContext") {
Expand Down
Loading