libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

routines.kt (4588B)


      1 /*
      2  * This file is part of LibEuFin.
      3  * Copyright (C) 2024 Taler Systems S.A.
      4 
      5  * LibEuFin is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU Affero General Public License as
      7  * published by the Free Software Foundation; either version 3, or
      8  * (at your option) any later version.
      9 
     10  * LibEuFin is distributed in the hope that it will be useful, but
     11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
     13  * Public License for more details.
     14 
     15  * You should have received a copy of the GNU Affero General Public
     16  * License along with LibEuFin; see the file COPYING.  If not, see
     17  * <http://www.gnu.org/licenses/>
     18  */
     19 
     20 package tech.libeufin.common.test
     21 
     22 import io.ktor.client.statement.*
     23 import kotlinx.coroutines.coroutineScope
     24 import kotlinx.coroutines.delay
     25 import kotlinx.coroutines.launch
     26 import tech.libeufin.common.assertNoContent
     27 import tech.libeufin.common.assertOkJson
     28 
     29 suspend inline fun <reified B> abstractHistoryRoutine(
     30     crossinline ids: (B) -> List<Long>,
     31     registered: List<suspend () -> Unit>,
     32     ignored: List<suspend () -> Unit> = listOf(),
     33     polling: Boolean = true,
     34     crossinline history: suspend (String) -> HttpResponse,
     35 ) {
     36     // Check history is following specs
     37     val assertHistory: suspend HttpResponse.(Int) -> Unit = { size: Int ->
     38         assertHistoryIds<B>(size, ids)
     39     }
     40     // Get latest registered id
     41     val latestId: suspend () -> Long = {
     42         history("limit=-1").assertOkJson<B>().run { ids(this)[0] }
     43     }
     44 
     45     // Check error when no transactions
     46     history("limit=7").assertNoContent()
     47 
     48     // Run interleaved registered and ignore transactions
     49     val registeredIter = registered.iterator()
     50     val ignoredIter = ignored.iterator()
     51     while (registeredIter.hasNext() || ignoredIter.hasNext()) {
     52         if (registeredIter.hasNext()) registeredIter.next()()
     53         if (ignoredIter.hasNext()) ignoredIter.next()()
     54     }
     55 
     56     val nbRegistered = registered.size
     57     val nbIgnored = ignored.size
     58     val nbTotal = nbRegistered + nbIgnored
     59 
     60     // Check ignored
     61     history("limit=$nbTotal").assertHistory(nbRegistered)
     62     // Check skip ignored
     63     history("limit=$nbRegistered").assertHistory(nbRegistered)
     64 
     65     if (polling) {
     66         // Check no polling when we cannot have more transactions
     67         assertTime(0, 100) {
     68             history("limit=-${nbRegistered+1}&timeout_ms=1000")
     69                 .assertHistory(nbRegistered)
     70         }
     71         // Check no polling when already find transactions even if less than delta
     72         assertTime(0, 100) {
     73             history("limit=${nbRegistered+1}&timeout_ms=1000")
     74                 .assertHistory(nbRegistered)
     75         }
     76 
     77         // Check polling
     78         coroutineScope {
     79             val id = latestId()
     80             launch {  // Check polling succeed
     81                 assertTime(100, 200) {
     82                     history("limit=2&offset=$id&timeout_ms=1000")
     83                         .assertHistory(1)
     84                 }
     85             }
     86             launch {  // Check polling timeout
     87                 assertTime(200, 300) {
     88                     history("limit=1&offset=${id+nbTotal*3}&timeout_ms=200")
     89                         .assertNoContent()
     90                 }
     91             }
     92             delay(100)
     93             registered[0]()
     94         }
     95 
     96         // Test triggers
     97         for (register in registered) {
     98             coroutineScope {
     99                 val id = latestId()
    100                 launch {
    101                     assertTime(100, 200) {
    102                         history("limit=7&offset=$id&timeout_ms=1000") 
    103                             .assertHistory(1)
    104                     }
    105                 }
    106                 delay(100)
    107                 register()
    108             }
    109         }
    110 
    111         // Test doesn't trigger
    112         coroutineScope {
    113             val id = latestId()
    114             launch {
    115                 assertTime(200, 300) {
    116                     history("limit=7&offset=$id&timeout_ms=200") 
    117                         .assertNoContent()
    118                 }
    119             }
    120             delay(100)
    121             for (ignore in ignored) {
    122                 ignore()
    123             }
    124         }
    125     }
    126 
    127     // Testing ranges.
    128     repeat(20) {
    129         registered[0]()
    130     }
    131     val id = latestId()
    132     // Default
    133     history("").assertHistory(20)
    134     // forward range:
    135     history("limit=10").assertHistory(10)
    136     history("limit=10&offset=4").assertHistory(10)
    137     // backward range:
    138     history("limit=-10").assertHistory(10)
    139     history("limit=-10&offset=${id-4}").assertHistory(10)
    140 }