Files
flamenet-messenger-android/engine/src/test/kotlin/io/flamenet/e2e/MentionsTest.kt
T
Paul HittandClaude Opus 4.8 19f4247b71 Recognise mentions in a group message (§6.18)
Codec only — the app does not compose or act on them yet — but it must
already understand an envelope the iPhone app sends, and more
importantly must not be made to do work by one.

🔴 Bounded and deduplicated on receipt. The list comes off the wire, and
a hostile sender could name ten thousand ids in one envelope; the cost
of that lands on every recipient's device rather than theirs. Capped at
256, far above any real roster.

A malformed list does not break the message: it is an ordinary message
with an odd field, not a reason to drop what somebody wrote.

Refs paulhitt/flamenet-messenger-ios#49

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 15:56:26 -04:00

75 lines
2.7 KiB
Kotlin

package io.flamenet.e2e
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
/**
* Addressing one person inside a group (§6.18).
*
* ⚠️ **The codec half.** The Android app does not compose or act on mentions
* yet, but it must already understand an envelope the iPhone app sends — and,
* more importantly, must not be made to do work by one. The bound below is the
* part that matters today.
*/
class MentionsTest {
@Test
fun `mentions ride the envelope`() {
val wire = Content.groupText("@Ada look", "g1", messageId = "m",
mentions = listOf(2L, 4L))
assertEquals(listOf(2L, 4L), Content.parse(wire).mentions)
}
/** Omitted when there are none, which is nearly every message. */
@Test
fun `a message with no mentions carries none`() {
val wire = Content.groupText("hello", "g1", messageId = "m")
assertFalse(wire.contains("ment"), "wrote an empty mention list: $wire")
assertTrue(Content.parse(wire).mentions.isEmpty())
}
/**
* 🔴 **Bounded.** The list comes off the wire, and a hostile sender could
* name ten thousand ids in one envelope — the cost of which lands on every
* recipient's device rather than theirs.
*/
@Test
fun `an absurd mention list is bounded`() {
val huge = (1..10_000).joinToString(",")
val wire = """{"fnc":1,"kind":"text","ment":[$huge],"gid":"g1","caption":"hi"}"""
assertEquals(Content.MAXIMUM_MENTIONS, Content.parse(wire).mentions.size)
}
@Test
fun `a repeated mention is counted once`() {
val wire = """{"fnc":1,"kind":"text","ment":[2,2,2,4],"gid":"g1","caption":"hi"}"""
assertEquals(listOf(2L, 4L), Content.parse(wire).mentions)
}
/** ⚠️ A malformed list must not throw — it is an ordinary message with an
* odd field, not a reason to drop what somebody wrote. */
@Test
fun `a malformed mention list does not break the message`() {
val wire = """{"fnc":1,"kind":"text","ment":"not an array","gid":"g1","caption":"hi"}"""
val content = Content.parse(wire)
assertEquals("hi", content.text)
assertTrue(content.mentions.isEmpty())
}
/** ⚠️ `fnc` first, like every other envelope. */
@Test
fun `the envelope starts with the marker`() {
assertTrue(Content.groupText("hi", "g1", mentions = listOf(2L))
.startsWith("{\"fnc\":1,"))
}
/** A mention does not stop a message being a message. */
@Test
fun `a mentioning message is still renderable`() {
assertTrue(Content.parse(
Content.groupText("@Ada hi", "g1", mentions = listOf(2L))).isRenderable)
}
}