Kgothatso Ngako 5fa0d08dfd test: cover nip17 room derivation and its preconditions
A NIP-17 room has no MLS group, no key packages and no invites -- membership
*is* the p-tag set on each message. Two things follow, and both are load-bearing.

The room id is deriveChatRoomId over the member set, the same aggregate the
inbound path derives from an arriving gift wrap. That is what makes creation
idempotent, and idempotence here is not a nicety: two people starting the same
conversation have to land on one room, or the thread exists twice with each side
writing into its own copy and neither seeing the other. Covered from three
angles -- the order members are named in does not change the id, creating the
same conversation twice reuses the room as it stands rather than rewriting it,
and a different member set derives a different room.

The order-independence test is guarding `deriveChatRoomId`'s own `.sorted()`,
not the DAO's `.toSet()`, and its comment now says so. That was established by
mutation rather than assumed: rebuilding the member set as an order-preserving
LinkedHashSet in the DAO changes nothing, because the derivation sorts anyway,
while removing the sort fails the test. The distinction matters for anyone
reading the DAO and concluding the set is what does the work.

And `mlsGroupState = null` is what marks the room NIP-17. sendChatMessage reads
exactly that field to choose between a kind:445 group event and per-recipient
gift wraps, so a room that acquired MLS state would have its messages routed
down a path no recipient is running.

Covered alongside: the creator is a participant of their own conversation even
when not listed among the participants -- sealGiftWrapPayload walks that list to
decide who to wrap for, so omitting the creator would send messages every other
member could read and the sender could not -- and naming the creator among the
participants does not produce a second row for them.

One test records a precondition and an asymmetry. Participant.participantPublicKey
is a foreign key onto Profile, so createNip17ChatRoom raises a SQLite constraint
failure for a member this device has no profile for, while getOrCreateChatRoom,
one method down, answers the same "never seen this user" situation by returning
null. A caller treating the two alike gets an unhandled exception out of the
first. That was found by writing the tests -- seven of them failed with SQLite
787 before every member was seeded -- and is pinned rather than seeded around
silently.

The remaining getOrCreate coverage: it returns the room already stored rather
than overwriting it with the defaults passed in, stands one up for a user it has
a profile for, and writes nothing at all when it does not.

Real secp256k1 keys throughout, because deriveChatRoomId does point arithmetic
and treats an off-curve value differently from a valid one -- hex filler would
exercise a path users never reach.

11 tests. composeApp jvmTest is 309 tests, 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 03:25:19 +02:00
2026-07-05 23:21:02 +02:00
2026-07-28 09:10:20 +02:00
2026-03-24 04:47:19 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00

This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).

  • /composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:

    • commonMain is for code thats common for all targets.
    • Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apples CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
  • /iosApp contains iOS applications. Even if youre sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.

Build and Run Android Application

To build and run the development version of the Android app, use the run configuration from the run widget in your IDEs toolbar or build it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:assembleDebug
    
  • on Windows
    .\gradlew.bat :composeApp:assembleDebug
    

Build and Run Desktop (JVM) Application

To build and run the development version of the desktop app, use the run configuration from the run widget in your IDEs toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:run
    
  • on Windows
    .\gradlew.bat :composeApp:run
    

Build and Run iOS Application

To build and run the development version of the iOS app, use the run configuration from the run widget in your IDEs toolbar or open the /iosApp directory in Xcode and run it from there.


Learn more about Kotlin Multiplatform

Description
Mantra as a kotlin multiplatform project
https://mantra.press
Readme 8.2 MiB
Languages
Kotlin 100%