taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

ConfigSessionTest.kt (3954B)


      1 /*
      2  * This file is part of GNU Taler
      3  * (C) 2026 Taler Systems S.A.
      4  * SPDX-License-Identifier: GPL-3.0-or-later
      5  */
      6 package net.taler.merchantpos.config
      7 
      8 import android.app.Application
      9 
     10 import androidx.test.core.app.ApplicationProvider
     11 
     12 import net.taler.common.EventObserver
     13 import net.taler.common.createDefaultHttpClient
     14 import net.taler.merchantlib.MerchantConfig
     15 
     16 import org.junit.After
     17 import org.junit.Assert.assertEquals
     18 import org.junit.Assert.assertNotNull
     19 import org.junit.Assert.assertNull
     20 import org.junit.Test
     21 import org.junit.runner.RunWith
     22 import org.robolectric.RobolectricTestRunner
     23 import org.robolectric.annotation.Config
     24 
     25 import kotlinx.coroutines.CoroutineScope
     26 import kotlinx.coroutines.Dispatchers
     27 import kotlinx.coroutines.SupervisorJob
     28 import kotlinx.coroutines.cancel
     29 
     30 @RunWith(RobolectricTestRunner::class)
     31 @Config(application = Application::class, sdk = [28])
     32 class ConfigSessionTest
     33 {
     34     private val app = ApplicationProvider.getApplicationContext<Application>()
     35     private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
     36     private val client = createDefaultHttpClient(logging = false)
     37     private val manager = ConfigManager(app, scope, client)
     38     private val base = "https://example.invalid/instances/test"
     39 
     40     private fun configure()
     41     {
     42         manager.debugApplyFixture(
     43             PosConfig(categories = emptyList(), products = emptyList()),
     44             MerchantConfig(base, "secret-token:current"),
     45             "CHF",
     46         )
     47     }
     48 
     49     @After
     50     fun cleanup()
     51     {
     52         scope.cancel()
     53         client.close()
     54         app
     55             .getSharedPreferences("taler-merchant-terminal", 0)
     56             .edit()
     57             .clear()
     58             .commit()
     59     }
     60 
     61     @Test
     62     fun `token prefix probe and login failures leave the active merchant configured`()
     63     {
     64         configure()
     65         manager.handleUnauthorized("$base/private/pos", "Bearer current")
     66         manager.handleUnauthorized("$base/private/token", "Bearer secret-token:current")
     67         manager.handleUnauthorized("$base/challenge/challenge/confirm", null)
     68         assertNotNull(manager.merchantConfig)
     69         assertNull(manager.sessionExpired.value)
     70     }
     71 
     72     @Test
     73     fun `late responses from another merchant or old credentials cannot expire current session`()
     74     {
     75         configure()
     76         manager.handleUnauthorized(
     77             "https://elsewhere.invalid/instances/test/private/orders",
     78             "Bearer secret-token:current"
     79         )
     80         manager.handleUnauthorized("${base}2/private/orders", "Bearer secret-token:current")
     81         manager.handleUnauthorized("$base/private/orders", "Bearer secret-token:old")
     82         manager.handleUnauthorized("$base/private/orders", null)
     83         assertNotNull(manager.merchantConfig)
     84         assertNull(manager.sessionExpired.value)
     85     }
     86 
     87     @Test
     88     fun `real session rejection clears credentials and is consumed once across observer recreation`()
     89     {
     90         configure()
     91         var notifications = 0
     92         val first = EventObserver<Unit> { notifications++ }
     93         manager.sessionExpired.observeForever(first)
     94         manager.handleUnauthorized("$base/private/orders?limit=20", "Bearer secret-token:current")
     95         assertNull(manager.merchantConfig)
     96         assertEquals("", (manager.config as net.taler.merchantpos.config.Config.New).accessToken)
     97         assertEquals(1, notifications)
     98         manager.sessionExpired.removeObserver(first)
     99         val recreated = EventObserver<Unit> { notifications++ }
    100         manager.sessionExpired.observeForever(recreated)
    101         assertEquals(1, notifications)
    102         manager.handleUnauthorized("$base/private/orders", "Bearer secret-token:current")
    103         assertEquals(1, notifications)
    104         configure()
    105         manager.handleUnauthorized("$base/private/orders", "Bearer secret-token:current")
    106         assertEquals(2, notifications)
    107         manager.sessionExpired.removeObserver(recreated)
    108     }
    109 }