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. */ @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() 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) } }