Put the app back to the last state that worked
Tonight's merges left sign-in, messages, calls and stories broken. Restores
app, engine and client to fa92581 — the tree that shipped as v8 and works.
Why they broke: those features are not independent. A message, a call
signal, a story and a story reply are the same content envelope with a
different kind, so they run through the same few functions in Content.kt.
Twenty-three MRs, each written against an older main and green on its own
branch, had never existed together until they were merged in one evening.
The conflicts that produced were resolved mechanically — union a parameter
list, merge a condition — and that produces code which compiles and passes
while being wrong: a field on the wrong struct, an argument in the wrong
order, a term in the wrong condition. Three were caught by the compiler.
The ones the compiler cannot see are why calls stopped connecting.
And the gate could not see any of it. 489 unit tests stayed green through
every break, because they test units: "assemble produces this JSON" is a
unit test, "Android shows what iOS sent" is not.
versionCode 10 so it installs over the broken v9.
Nothing is lost — every MR is still a merged commit with its branch on the
remote, and re-lands in small batches once there is a test that actually
sends a message between two devices.
This commit is contained in:
@@ -1,129 +0,0 @@
|
||||
package io.flamenet.e2e
|
||||
|
||||
import org.json.JSONObject
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/** Multi-item stories (spec §6.23, ios#53). */
|
||||
class StorySetTest {
|
||||
|
||||
private fun ref(id: Int, expires: Double = 100.0) = Attachment.Ref(
|
||||
kind = Attachment.STORY, id = "b$id", key = ByteArray(32),
|
||||
digest = ByteArray(32), bytes = 1, mime = "image/jpeg",
|
||||
width = 1, height = 1, duration = 0.0, expires = expires,
|
||||
)
|
||||
|
||||
private fun item(id: Int, set: String?, i: Int = 0, n: Int = 1, expires: Double = 100.0) =
|
||||
Vault.Story(
|
||||
id = "s$id", authorId = 7, mine = false, ref = ref(id, expires),
|
||||
caption = "", postedAt = id.toLong(),
|
||||
set = set?.let { Content.StorySet(it, i, n) },
|
||||
)
|
||||
|
||||
// -- The wire -------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun `an item carries its set position and size`() {
|
||||
val wire = Content.attachment(ref(1), storySet = Content.StorySet("set-1", 2, 5))
|
||||
val content = Content.parse(wire)
|
||||
assertEquals("set-1", content.storySet?.id)
|
||||
assertEquals(2, content.storySet?.i)
|
||||
assertEquals(5, content.storySet?.n)
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔴 One envelope per item. An array of attachments would fail the
|
||||
* single-`att` guard on any build predating §6.23 and render the whole set
|
||||
* as a message in the conversation.
|
||||
*/
|
||||
@Test
|
||||
fun `an item is an ordinary story envelope`() {
|
||||
val json = JSONObject(Content.attachment(ref(1), storySet = Content.StorySet("s", 0, 2)))
|
||||
assertEquals(Attachment.STORY, json.getString("kind"))
|
||||
assertTrue(json.has("att"))
|
||||
assertFalse(json.has("atts"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an ordinary story has no set`() {
|
||||
assertNull(Content.parse(Content.attachment(ref(1))).storySet)
|
||||
}
|
||||
|
||||
// -- Grouping -------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun `items of one set are grouped in position order`() {
|
||||
val groups = Vault.groupedStories(listOf(
|
||||
item(3, "a", i = 2, n = 3), item(1, "a", i = 0, n = 3), item(2, "a", i = 1, n = 3)))
|
||||
assertEquals(1, groups.size)
|
||||
assertEquals(listOf("s1", "s2", "s3"), groups[0].map { it.id })
|
||||
}
|
||||
|
||||
/** ⚠️ `n` says three; two came. Render what arrived. */
|
||||
@Test
|
||||
fun `a partly arrived set is still rendered`() {
|
||||
val groups = Vault.groupedStories(listOf(
|
||||
item(1, "a", i = 0, n = 3), item(3, "a", i = 2, n = 3)))
|
||||
assertEquals(1, groups.size)
|
||||
assertEquals(2, groups[0].size)
|
||||
}
|
||||
|
||||
/** ⚠️ Two singles must not collide just because neither has a set id. */
|
||||
@Test
|
||||
fun `two sets and a single stay apart`() {
|
||||
val groups = Vault.groupedStories(listOf(
|
||||
item(1, "a", i = 0, n = 2), item(2, "b"), item(3, null), item(4, "a", i = 1, n = 2)))
|
||||
assertEquals(3, groups.size)
|
||||
assertEquals(listOf("s1", "s4"), groups[0].map { it.id })
|
||||
}
|
||||
|
||||
// -- Expiry ---------------------------------------------------------------
|
||||
|
||||
/** 🔴 The earliest wins, so a sender cannot outlive part of its own set. */
|
||||
@Test
|
||||
fun `a set expires at its earliest stamp`() {
|
||||
assertEquals(100.0, Vault.setExpiry(listOf(
|
||||
item(1, "a", expires = 300.0),
|
||||
item(2, "a", expires = 100.0),
|
||||
item(3, "a", expires = 200.0))))
|
||||
}
|
||||
|
||||
/** ⚠️ A zero means "never" and must not be read as "expires at the epoch". */
|
||||
@Test
|
||||
fun `a zero stamp does not expire the whole set`() {
|
||||
assertEquals(500.0, Vault.setExpiry(listOf(
|
||||
item(1, "a", expires = 0.0), item(2, "a", expires = 500.0))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a set of never-expiring items never expires`() {
|
||||
assertEquals(0.0, Vault.setExpiry(listOf(
|
||||
item(1, "a", expires = 0.0), item(2, "a", expires = 0.0))))
|
||||
}
|
||||
|
||||
// -- Storage --------------------------------------------------------------
|
||||
|
||||
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 names() = records.keys.toList()
|
||||
}
|
||||
|
||||
/** ⚠️ The set survives a reload, or a relaunch scatters every sequence. */
|
||||
@Test
|
||||
fun `the set survives the vault round trip`() {
|
||||
val vault = Vault(MapStorage(),
|
||||
javax.crypto.KeyGenerator.getInstance("AES").apply { init(256) }.generateKey())
|
||||
val now = System.currentTimeMillis() / 1000.0
|
||||
vault.saveStories(listOf(item(1, "a", i = 1, n = 4, expires = now + 600)), now)
|
||||
val back = vault.loadStories().single()
|
||||
assertEquals("a", back.set?.id)
|
||||
assertEquals(1, back.set?.i)
|
||||
assertEquals(4, back.set?.n)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user