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>
75 lines
2.7 KiB
Kotlin
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)
|
|
}
|
|
}
|