SubmissionReservationTests.kt (5702B)
1 package tera.smoke 2 3 import kotlin.test.Test 4 import kotlin.test.assertEquals 5 import kotlin.test.assertFailsWith 6 import kotlin.test.assertFalse 7 import kotlin.test.assertNotEquals 8 import kotlin.test.assertTrue 9 import kotlinx.coroutines.async 10 import kotlinx.coroutines.runBlocking 11 import uniffi.tera_core.FfiAddCommandType 12 import uniffi.tera_core.FfiComposerFormRecord 13 import uniffi.tera_core.FfiComposerSaveRequest 14 import uniffi.tera_core.FfiComposerScopeRecord 15 import uniffi.tera_core.FfiConverterTypeFfiSubmissionReservationReceipt 16 import uniffi.tera_core.FfiRecoveryDisposition 17 import uniffi.tera_core.FfiSubmissionReservationRequest 18 import uniffi.tera_core.TeraAppException 19 import uniffi.tera_core.classifyErrorRecovery 20 import uniffi.tera_core.composerReserveId 21 import uniffi.tera_core.submissionReserveId 22 23 class SubmissionReservationTests { 24 private val scope = FfiComposerScopeRecord(1u, PUBLIC_KEY, "nearby") 25 private fun savedRequest() = FfiComposerSaveRequest( 26 schemaVersion = 1u, scope = scope, id = composerReserveId().id, 27 expectedRevision = null, editSequence = ULong.MAX_VALUE - 1uL, 28 form = FfiComposerFormRecord( 29 schemaVersion = 1u, commandType = FfiAddCommandType.CREATE_EVENT, 30 content = "PRIVATE incomplete\n\u0000é", identifier = null, title = null, summary = null, 31 location = null, eventTiming = null, eventStartDate = "2026-09-", eventEndDate = null, 32 eventStartUnixS = null, eventEndUnixS = null, eventTimezone = null, priceAmount = "-", 33 currency = null, unit = null, quantity = null, foodPublishedAtUnixS = null, 34 foodStatus = null, media = emptyList(), 35 ), 36 ) 37 38 @Test 39 fun reservationRecoversOneHistoricalSourceAcrossConcurrencyAndRestart(): Unit = runBlocking { 40 SmokeFixture().use { fixture -> 41 val input = savedRequest() 42 val request = FfiSubmissionReservationRequest(1u, submissionReserveId().id, scope, input.id, 1uL) 43 val runtime = fixture.runtime() 44 val first = try { 45 val saved = runtime.composerSave(input) 46 val left = async { runtime.submissionReserve(request) } 47 val right = async { runtime.submissionReserve(request) } 48 val a = left.await() 49 val b = right.await() 50 assertEquals(a.reservationId, b.reservationId) 51 assertNotEquals(a.replayed, b.replayed) 52 assertEquals(saved.draft, a.captured) 53 assertEquals(a, FfiConverterTypeFfiSubmissionReservationReceipt.lift( 54 FfiConverterTypeFfiSubmissionReservationReceipt.lower(a))) 55 runtime.composerSave(input.copy(expectedRevision = 1uL, editSequence = ULong.MAX_VALUE, 56 form = input.form.copy(content = "later edit"))) 57 a 58 } finally { 59 runtime.shutdown() 60 runtime.close() 61 } 62 val reopened = fixture.runtime() 63 try { 64 val replay = reopened.submissionReserve(request) 65 assertTrue(replay.replayed) 66 assertEquals(first.reservationId, replay.reservationId) 67 assertEquals(first.reservedAtUnixMs, replay.reservedAtUnixMs) 68 assertEquals(first.captured, replay.captured) 69 assertEquals(2uL, reopened.composerLoad(scope, input.id).revision) 70 val changed = request.copy(expectedRevision = 2uL) 71 val failure = assertFailsWith<TeraAppException.Failure> { reopened.submissionReserve(changed) } 72 assertEquals("idempotency_conflict", failure.report.code) 73 assertEquals(FfiRecoveryDisposition.IDEMPOTENCY_CONFLICT, 74 classifyErrorRecovery(1u, failure.report.code).disposition) 75 val second = reopened.submissionReserve(changed.copy(commandId = submissionReserveId().id)) 76 val third = reopened.submissionReserve(changed.copy(commandId = submissionReserveId().id)) 77 assertEquals(second.captured, third.captured) 78 assertNotEquals(second.reservationId, third.reservationId) 79 assertTrue(reopened.phase1DraftHeads(100u).isEmpty()) 80 } finally { 81 reopened.shutdown() 82 reopened.close() 83 } 84 } 85 } 86 87 @Test 88 fun invalidReservationVersionsAndUnsignedWidthsFailClosed(): Unit = runBlocking { 89 SmokeFixture().use { fixture -> 90 val runtime = fixture.runtime() 91 try { 92 val input = savedRequest() 93 runtime.composerSave(input) 94 val request = FfiSubmissionReservationRequest(1u, submissionReserveId().id, scope, input.id, 1uL) 95 for ((invalid, code) in listOf( 96 request.copy(schemaVersion = 2u) to "submission_schema_unsupported", 97 request.copy(scope = scope.copy(schemaVersion = 2u)) to "composer_schema_unsupported", 98 request.copy(commandId = "00".repeat(16)) to "submission_command_id_invalid", 99 request.copy(expectedRevision = ULong.MAX_VALUE) to "composer_revision_invalid", 100 )) { 101 val failure = assertFailsWith<TeraAppException.Failure> { runtime.submissionReserve(invalid) } 102 assertEquals(code, failure.report.code) 103 assertFalse(failure.report.retryable) 104 } 105 assertFalse(runtime.submissionReserve(request).replayed) 106 } finally { 107 runtime.shutdown() 108 runtime.close() 109 } 110 } 111 } 112 }