API-style comments and ktlint formatting
Every comment is reduced to its first sentence plus @param, @return and @throws tags. Comment lines: 20040 to 8526. ktlint 1.8 applied with its default (official Kotlin) style across all modules; 262 files reformatted. The 264 remaining ktlint findings are naming and filename rules, which are code changes, and are left alone. Comments and formatting only. 1208 tests pass, counted from the XML. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
8a7f40fb13
commit
2fa430f21c
@@ -1,5 +1,6 @@
|
||||
package io.flamenet.e2e
|
||||
|
||||
import org.json.JSONObject
|
||||
import javax.crypto.KeyGenerator
|
||||
import javax.crypto.SecretKey
|
||||
import kotlin.test.Test
|
||||
@@ -7,32 +8,34 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* A timer on one message rather than the whole conversation (§6.17).
|
||||
*
|
||||
* The rule that keeps this from making the product worse is that the
|
||||
* shorter timer always wins. A per-message timer may only take life away,
|
||||
* never add it. Without that, a sender could defeat the recipient's
|
||||
* conversation timer by putting a long one on every message, turning a setting
|
||||
* the recipient chose, on their own device, into something the other party gets
|
||||
* to overrule.
|
||||
*/
|
||||
class PerMessageTimerTest {
|
||||
|
||||
private class MapStorage : Vault.Storage {
|
||||
val records = LinkedHashMap<String, ByteArray>()
|
||||
|
||||
override fun read(name: String) = records[name]
|
||||
override fun write(name: String, bytes: ByteArray) { records[name] = bytes }
|
||||
override fun delete(name: String) { records.remove(name) }
|
||||
|
||||
override fun write(
|
||||
name: String,
|
||||
bytes: ByteArray,
|
||||
) {
|
||||
records[name] = bytes
|
||||
}
|
||||
|
||||
override fun delete(name: String) {
|
||||
records.remove(name)
|
||||
}
|
||||
|
||||
override fun names() = records.keys.toList()
|
||||
}
|
||||
|
||||
private fun key(): SecretKey =
|
||||
KeyGenerator.getInstance("AES").apply { init(256) }.generateKey()
|
||||
private fun key(): SecretKey = KeyGenerator.getInstance("AES").apply { init(256) }.generateKey()
|
||||
|
||||
// ---------------------------------------------------------------- whose timer wins
|
||||
// ---------------------------------------------------------------- whose
|
||||
// timer wins.
|
||||
|
||||
@Test
|
||||
fun `a message timer applies when the conversation has none`() {
|
||||
@@ -45,13 +48,15 @@ class PerMessageTimerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* The one that matters. A message asking to live a week, in a
|
||||
* conversation set to an hour, lives an hour.
|
||||
* The one that matters.
|
||||
*/
|
||||
@Test
|
||||
fun `a longer message timer cannot extend a message's life`() {
|
||||
assertEquals(3600, Vault.effectiveLifetime(message = 604_800, conversation = 3600),
|
||||
"a sender overruled the recipient's own conversation timer")
|
||||
assertEquals(
|
||||
3600,
|
||||
Vault.effectiveLifetime(message = 604_800, conversation = 3600),
|
||||
"a sender overruled the recipient's own conversation timer",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -65,8 +70,7 @@ class PerMessageTimerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero means "no timer", not "expire immediately". Reading it the other
|
||||
* way would delete a message the instant it arrived.
|
||||
* Zero means "no timer", not "expire immediately".
|
||||
*/
|
||||
@Test
|
||||
fun `a negative timer is ignored rather than expiring immediately`() {
|
||||
@@ -74,19 +78,27 @@ class PerMessageTimerTest {
|
||||
assertEquals(3600, Vault.effectiveLifetime(message = -1, conversation = 3600))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- pruning
|
||||
// ---------------------------------------------------------------- pruning.
|
||||
|
||||
private fun store(vault: Vault, threadId: String, at: Long, secs: Int, text: String) {
|
||||
vault.append(threadId, JSONObject()
|
||||
.put("text", text).put("mine", false).put("at", at)
|
||||
.also { if (secs > 0) it.put("secs", secs) })
|
||||
private fun store(
|
||||
vault: Vault,
|
||||
threadId: String,
|
||||
at: Long,
|
||||
secs: Int,
|
||||
text: String,
|
||||
) {
|
||||
vault.append(
|
||||
threadId,
|
||||
JSONObject()
|
||||
.put("text", text)
|
||||
.put("mine", false)
|
||||
.put("at", at)
|
||||
.also { if (secs > 0) it.put("secs", secs) },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The bug the old code had. Pruning returned early when the
|
||||
* CONVERSATION had no timer, so a timed message in an untimed thread was
|
||||
* kept for ever, the feature would have looked like it worked on the
|
||||
* sending phone and done nothing on the receiving one.
|
||||
* The bug the old code had.
|
||||
*/
|
||||
@Test
|
||||
fun `a timed message expires even when the conversation has no timer`() {
|
||||
@@ -125,31 +137,36 @@ class PerMessageTimerTest {
|
||||
vault.saveDisappearSeconds(thread, 60)
|
||||
store(vault, thread, at = now - 120_000, secs = 604_800, text = "asked to live a week")
|
||||
|
||||
assertEquals(0, vault.pruneExpired(thread, now).length(),
|
||||
"a sender overruled the recipient's conversation timer")
|
||||
assertEquals(
|
||||
0,
|
||||
vault.pruneExpired(thread, now).length(),
|
||||
"a sender overruled the recipient's conversation timer",
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A message with no timestamp predates the field. Kept rather than
|
||||
* destroyed: an unknown age is not evidence of an old one, and deleting
|
||||
* somebody's history on a guess is unrecoverable.
|
||||
* A message with no timestamp predates the field.
|
||||
*/
|
||||
@Test
|
||||
fun `a message with no timestamp is kept`() {
|
||||
val vault = Vault(MapStorage(), key())
|
||||
val thread = Vault.threadForUser(7)
|
||||
vault.append(thread, JSONObject().put("text", "ancient").put("mine", false)
|
||||
.put("secs", 60))
|
||||
vault.append(
|
||||
thread,
|
||||
JSONObject()
|
||||
.put("text", "ancient")
|
||||
.put("mine", false)
|
||||
.put("secs", 60),
|
||||
)
|
||||
|
||||
assertEquals(1, vault.pruneExpired(thread, 1_000_000_000L).length())
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- the wire
|
||||
// ---------------------------------------------------------------- the
|
||||
// wire.
|
||||
|
||||
/**
|
||||
* A duration, never a deadline. An absolute expiry would be the
|
||||
* sender's clock: they could backdate a message so it vanished on arrival,
|
||||
* or forward-date one so it never expired.
|
||||
* A duration, never a deadline.
|
||||
*/
|
||||
@Test
|
||||
fun `the envelope carries a duration not a deadline`() {
|
||||
@@ -160,9 +177,9 @@ class PerMessageTimerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* `Content.text` returns raw bytes when it has nothing to carry, so a
|
||||
* timer that was not counted in that decision would be dropped and the
|
||||
* message would live for ever.
|
||||
* `Content.text` returns raw bytes when it has nothing to carry, so a timer
|
||||
* that was not counted in that decision would be dropped and the message
|
||||
* would live for ever.
|
||||
*/
|
||||
@Test
|
||||
fun `a timed message always gets an envelope`() {
|
||||
@@ -176,20 +193,23 @@ class PerMessageTimerTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* A conversation-timer announcement carries `secs` too and means
|
||||
* something entirely different. Reading both off one field is why this
|
||||
* checks the kind.
|
||||
* A conversation-timer announcement carries `secs` too and means something
|
||||
* entirely different.
|
||||
*/
|
||||
@Test
|
||||
fun `a conversation timer announcement is not a timed message`() {
|
||||
val announcement = Content.parse(Content.disappearing(3600))
|
||||
assertNull(announcement.lifetimeSeconds,
|
||||
"a conversation timer was read as one message's own")
|
||||
assertNull(
|
||||
announcement.lifetimeSeconds,
|
||||
"a conversation timer was read as one message's own",
|
||||
)
|
||||
assertEquals(3600, Content.disappearSeconds(announcement))
|
||||
|
||||
val timed = Content.parse(Content.text("meet at 6", lifetimeSeconds = 60))
|
||||
assertNull(Content.disappearSeconds(timed),
|
||||
"a timed message was read as setting the whole conversation")
|
||||
assertNull(
|
||||
Content.disappearSeconds(timed),
|
||||
"a timed message was read as setting the whole conversation",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user