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 }