Merge latest main into emoji feature branch
# Conflicts: # amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt # amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt # amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/RelaySubscriptionsCoordinator.kt # amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/RememberForeverStates.kt # amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountFeedContentStates.kt
This commit is contained in:
+26
-14
@@ -37,8 +37,20 @@ class KeyPackageUtilsTest {
|
||||
private val testPubKey = "a".repeat(64)
|
||||
private val testRef = "b".repeat(64)
|
||||
|
||||
/**
|
||||
* Per MIP-00 the `d` tag MUST be 64 lowercase hex characters
|
||||
* (a random 32-byte slot ID). The strict [KeyPackageUtils.isValid] enforces
|
||||
* this on the parse side, so test fixtures need realistic 64-char hex
|
||||
* slot IDs even when we only care about the selection logic.
|
||||
*/
|
||||
private fun slot(label: Int): String = "0".repeat(63) + label.toString(16)
|
||||
|
||||
private val slot0 = slot(0)
|
||||
private val slot1 = slot(1)
|
||||
private val slotLastResort = "f".repeat(64)
|
||||
|
||||
private fun makeKeyPackageEvent(
|
||||
dTag: String = "0",
|
||||
dTag: String = slot0,
|
||||
createdAt: Long = 1000,
|
||||
encoding: String = "base64",
|
||||
ciphersuite: String = "0x0001",
|
||||
@@ -105,8 +117,8 @@ class KeyPackageUtilsTest {
|
||||
|
||||
@Test
|
||||
fun testSelectBest_PrefersNewest() {
|
||||
val old = makeKeyPackageEvent(dTag = "0", createdAt = 1000)
|
||||
val newer = makeKeyPackageEvent(dTag = "1", createdAt = 2000)
|
||||
val old = makeKeyPackageEvent(dTag = slot0, createdAt = 1000)
|
||||
val newer = makeKeyPackageEvent(dTag = slot1, createdAt = 2000)
|
||||
|
||||
val best = KeyPackageUtils.selectBest(listOf(old, newer))
|
||||
assertNotNull(best)
|
||||
@@ -115,33 +127,33 @@ class KeyPackageUtilsTest {
|
||||
|
||||
@Test
|
||||
fun testSelectBest_PrefersNonLastResort() {
|
||||
val lastResort = makeKeyPackageEvent(dTag = "lr", createdAt = 3000)
|
||||
val regular = makeKeyPackageEvent(dTag = "0", createdAt = 1000)
|
||||
val lastResort = makeKeyPackageEvent(dTag = slotLastResort, createdAt = 3000)
|
||||
val regular = makeKeyPackageEvent(dTag = slot0, createdAt = 1000)
|
||||
|
||||
// Even though lastResort is newer, regular is preferred
|
||||
val best = KeyPackageUtils.selectBest(listOf(lastResort, regular), lastResortDTag = "lr")
|
||||
val best = KeyPackageUtils.selectBest(listOf(lastResort, regular), lastResortDTag = slotLastResort)
|
||||
assertNotNull(best)
|
||||
assertEquals("0", best.dTag())
|
||||
assertEquals(slot0, best.dTag())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSelectBest_FallsBackToLastResort() {
|
||||
val lastResort = makeKeyPackageEvent(dTag = "lr", createdAt = 3000)
|
||||
val lastResort = makeKeyPackageEvent(dTag = slotLastResort, createdAt = 3000)
|
||||
|
||||
// Only last-resort available
|
||||
val best = KeyPackageUtils.selectBest(listOf(lastResort), lastResortDTag = "lr")
|
||||
val best = KeyPackageUtils.selectBest(listOf(lastResort), lastResortDTag = slotLastResort)
|
||||
assertNotNull(best)
|
||||
assertEquals("lr", best.dTag())
|
||||
assertEquals(slotLastResort, best.dTag())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSelectBest_FiltersOutInvalid() {
|
||||
val invalid = makeKeyPackageEvent(dTag = "0", encoding = "raw")
|
||||
val valid = makeKeyPackageEvent(dTag = "1", createdAt = 500)
|
||||
val invalid = makeKeyPackageEvent(dTag = slot0, encoding = "raw")
|
||||
val valid = makeKeyPackageEvent(dTag = slot1, createdAt = 500)
|
||||
|
||||
val best = KeyPackageUtils.selectBest(listOf(invalid, valid))
|
||||
assertNotNull(best)
|
||||
assertEquals("1", best.dTag())
|
||||
assertEquals(slot1, best.dTag())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -159,7 +171,7 @@ class KeyPackageUtilsTest {
|
||||
val template =
|
||||
KeyPackageUtils.buildRotation(
|
||||
newKeyPackageBase64 = "bmV3IGtleXBhY2thZ2U=",
|
||||
dTagSlot = "0",
|
||||
dTagSlot = slot0,
|
||||
newKeyPackageRef = testRef,
|
||||
relays = emptyList(),
|
||||
)
|
||||
|
||||
+121
-13
@@ -115,27 +115,24 @@ class MarmotMipComplianceTest {
|
||||
|
||||
@Test
|
||||
fun marmotGroupData_rejectsZeroDisappearingSecsOnDecode() {
|
||||
// Hand-crafted TLS blob: version=3, group_id=32x0, empty opaque2 for
|
||||
// name/description/admins/relays/images, then disappearing_message_secs
|
||||
// = 8 bytes of zero (invalid).
|
||||
// Hand-crafted TLS blob (MIP-01 QUIC VarInt length prefixes):
|
||||
// uint16 version=3 | opaque group_id[32] | 8x empty VarInt(0) fields
|
||||
// (name..image_upload_key) | disappearing_message_secs = VarInt(8) + 8
|
||||
// zero bytes (invalid per MIP-01).
|
||||
val header =
|
||||
ByteArray(2 + 32) {
|
||||
// version + groupId
|
||||
when (it) {
|
||||
0 -> 0
|
||||
|
||||
1 -> 3
|
||||
|
||||
// version=3
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
// 8x opaque2 fields of length 0, each encoded as two zero bytes:
|
||||
// 8 empty VarInt-prefixed opaque fields, each a single 0x00 byte:
|
||||
// name, description, admin_pubkeys, relays, image_hash, image_key,
|
||||
// image_nonce, image_upload_key
|
||||
val zeroFields = ByteArray(8 * 2) // all zeros
|
||||
// disappearing_message_secs opaque2 with 8 zero bytes
|
||||
val disappearingField = ByteArray(2 + 8).also { it[1] = 8 }
|
||||
val zeroFields = ByteArray(8) // all 0x00
|
||||
// disappearing_message_secs: VarInt(8) = 0x08, then 8 zero bytes
|
||||
val disappearingField = ByteArray(1 + 8).also { it[0] = 0x08 }
|
||||
val blob = header + zeroFields + disappearingField
|
||||
|
||||
// decodeTls catches any exception and returns null
|
||||
@@ -150,13 +147,124 @@ class MarmotMipComplianceTest {
|
||||
it[0] = 0
|
||||
it[1] = 99
|
||||
}
|
||||
val zeroFields = ByteArray(8 * 2) // name..image_upload_key
|
||||
val disappearingField = ByteArray(2) // zero-length
|
||||
val zeroFields = ByteArray(8) // 8x VarInt(0) for name..image_upload_key
|
||||
val disappearingField = ByteArray(1) // VarInt(0) — zero-length field
|
||||
|
||||
val blob = header + zeroFields + disappearingField
|
||||
|
||||
assertNull(MarmotGroupData.decodeTls(blob))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun marmotGroupData_rejectsDuplicateAdminPubkeys() {
|
||||
// MIP-01: admin_pubkeys MUST NOT contain duplicate keys.
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
MarmotGroupData(
|
||||
nostrGroupId = groupId32,
|
||||
adminPubkeys = listOf(adminPubkey, adminPubkey),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// --- MIP-01 byte-level interop fixtures (MDK reference) ----------------
|
||||
//
|
||||
// These fixtures were produced by serializing the identical struct via the
|
||||
// Rust `tls_codec` 0.4 crate used by MDK (see commit message for the
|
||||
// generator). They pin Amethyst's v2 encoder output byte-for-byte against
|
||||
// the MDK reference, so any future regression in VarInt framing surfaces
|
||||
// immediately.
|
||||
//
|
||||
// Fixtures are v2 (no `disappearing_message_secs` field) because MDK's
|
||||
// current `CURRENT_VERSION = 2` reference has no v3 support yet.
|
||||
|
||||
private fun mdkFixtureA(): ByteArray =
|
||||
(
|
||||
// version=2 + 32 bytes of group_id (all zero)
|
||||
"0002" +
|
||||
"00".repeat(32) +
|
||||
// name=empty, description=empty (VarInt(0) = single 0x00)
|
||||
"0000" +
|
||||
// admin_pubkeys: VarInt(32) = 0x20, then one 32-byte key of 0xAA
|
||||
"20" + "aa".repeat(32) +
|
||||
// relays: outer VarInt(21) = 0x15; inner VarInt(20) = 0x14 +
|
||||
// "wss://relay.example/" (20 bytes)
|
||||
"15" + "14" + "7773733a2f2f72656c61792e6578616d706c652f" +
|
||||
// image_hash, image_key, image_nonce, image_upload_key — all empty
|
||||
"00000000"
|
||||
).hexToByteArray()
|
||||
|
||||
private fun mdkFixtureB(): ByteArray =
|
||||
(
|
||||
"0002" +
|
||||
"11".repeat(32) +
|
||||
// name: VarInt(8)=0x08 + "Amethyst"
|
||||
"08" + "416d657468797374" +
|
||||
// description: VarInt(10)=0x0a + "Test group"
|
||||
"0a" + "546573742067726f7570" +
|
||||
// admin_pubkeys: outer VarInt(64) — 64 = 0x40, two-byte VarInt
|
||||
// prefix "40 40" (high bits 01, value 0x0040) + 2×32 bytes
|
||||
"4040" + "bb".repeat(32) + "cc".repeat(32) +
|
||||
// relays outer VarInt(44) = 0x2c, then two inner relays each
|
||||
// VarInt(21) + 21-byte URL
|
||||
"2c" +
|
||||
"15" + "7773733a2f2f72656c6179312e6578616d706c652f" +
|
||||
"15" + "7773733a2f2f72656c6179322e6578616d706c652f" +
|
||||
// image_* all empty
|
||||
"00000000"
|
||||
).hexToByteArray()
|
||||
|
||||
@Test
|
||||
fun marmotGroupData_encodesFixtureAByteForByteVsMdk() {
|
||||
// Encode an Amethyst MarmotGroupData with the same inputs and assert the
|
||||
// bytes match MDK's tls_codec 0.4 output exactly.
|
||||
val data =
|
||||
MarmotGroupData(
|
||||
version = 2,
|
||||
nostrGroupId = "00".repeat(32),
|
||||
name = "",
|
||||
description = "",
|
||||
adminPubkeys = listOf("aa".repeat(32)),
|
||||
relays = listOf("wss://relay.example/"),
|
||||
)
|
||||
assertContentEquals(mdkFixtureA(), data.encodeTls())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun marmotGroupData_encodesFixtureBByteForByteVsMdk() {
|
||||
val data =
|
||||
MarmotGroupData(
|
||||
version = 2,
|
||||
nostrGroupId = "11".repeat(32),
|
||||
name = "Amethyst",
|
||||
description = "Test group",
|
||||
adminPubkeys = listOf("bb".repeat(32), "cc".repeat(32)),
|
||||
relays = listOf("wss://relay1.example/", "wss://relay2.example/"),
|
||||
)
|
||||
assertContentEquals(mdkFixtureB(), data.encodeTls())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun marmotGroupData_decodesMdkFixtureA() {
|
||||
val decoded = assertNotNull(MarmotGroupData.decodeTls(mdkFixtureA()))
|
||||
assertEquals(2, decoded.version)
|
||||
assertEquals("00".repeat(32), decoded.nostrGroupId)
|
||||
assertEquals("", decoded.name)
|
||||
assertEquals("", decoded.description)
|
||||
assertEquals(listOf("aa".repeat(32)), decoded.adminPubkeys)
|
||||
assertEquals(listOf("wss://relay.example/"), decoded.relays)
|
||||
assertNull(decoded.disappearingMessageSecs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun marmotGroupData_decodesMdkFixtureB() {
|
||||
val decoded = assertNotNull(MarmotGroupData.decodeTls(mdkFixtureB()))
|
||||
assertEquals(2, decoded.version)
|
||||
assertEquals("Amethyst", decoded.name)
|
||||
assertEquals("Test group", decoded.description)
|
||||
assertEquals(listOf("bb".repeat(32), "cc".repeat(32)), decoded.adminPubkeys)
|
||||
assertEquals(listOf("wss://relay1.example/", "wss://relay2.example/"), decoded.relays)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mip01ImageCrypto_deriveImageKeyIs32BytesAndDeterministic() {
|
||||
val seed = "11".repeat(32).hexToByteArray()
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.clip
|
||||
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class LiveActivitiesClipEventTest {
|
||||
private val host = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
private val viewerAuthor = "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
|
||||
private val dummySig = "0".repeat(128)
|
||||
|
||||
@Test
|
||||
fun parsesZapStreamShapedClip() {
|
||||
val event =
|
||||
LiveActivitiesClipEvent(
|
||||
id = "2".repeat(64),
|
||||
pubKey = viewerAuthor,
|
||||
createdAt = 1_700_000_000L,
|
||||
tags =
|
||||
arrayOf(
|
||||
arrayOf("a", "${LiveActivitiesEvent.KIND}:$host:stream-d", "wss://relay.example"),
|
||||
arrayOf("p", host),
|
||||
arrayOf("r", "https://cdn.example/clip.mp4"),
|
||||
arrayOf("title", "Nice moment"),
|
||||
arrayOf("alt", "Live stream clip"),
|
||||
),
|
||||
content = "Check this out",
|
||||
sig = dummySig,
|
||||
)
|
||||
|
||||
val activity = assertNotNull(event.activity())
|
||||
assertEquals(LiveActivitiesEvent.KIND, activity.kind)
|
||||
assertEquals(host, activity.pubKeyHex)
|
||||
assertEquals("stream-d", activity.dTag)
|
||||
|
||||
assertEquals(host, event.host())
|
||||
assertEquals("https://cdn.example/clip.mp4", event.videoUrl())
|
||||
assertEquals("Nice moment", event.title())
|
||||
assertEquals("Check this out", event.content)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ignoresClipLackingStreamReference() {
|
||||
val event =
|
||||
LiveActivitiesClipEvent(
|
||||
id = "2".repeat(64),
|
||||
pubKey = viewerAuthor,
|
||||
createdAt = 1_700_000_000L,
|
||||
tags =
|
||||
arrayOf(
|
||||
arrayOf("p", host),
|
||||
arrayOf("r", "https://cdn.example/clip.mp4"),
|
||||
arrayOf("title", "Nice moment"),
|
||||
),
|
||||
content = "",
|
||||
sig = dummySig,
|
||||
)
|
||||
|
||||
assertNull(event.activity())
|
||||
assertNull(event.activityAddress())
|
||||
assertEquals("https://cdn.example/clip.mp4", event.videoUrl())
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.raid
|
||||
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class LiveActivitiesRaidEventTest {
|
||||
private val sourceHost = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
private val targetHost = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
||||
private val author = "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
private val dummySig = "0".repeat(128)
|
||||
|
||||
@Test
|
||||
fun parsesRootAndMentionAddresses() {
|
||||
val event =
|
||||
LiveActivitiesRaidEvent(
|
||||
id = "1".repeat(64),
|
||||
pubKey = author,
|
||||
createdAt = 1_700_000_000L,
|
||||
tags =
|
||||
arrayOf(
|
||||
arrayOf("a", "${LiveActivitiesEvent.KIND}:$sourceHost:source-d", "wss://relay.example", "root"),
|
||||
arrayOf("a", "${LiveActivitiesEvent.KIND}:$targetHost:target-d", "", "mention"),
|
||||
),
|
||||
content = "Heading over to stream!",
|
||||
sig = dummySig,
|
||||
)
|
||||
|
||||
val from = assertNotNull(event.fromAddress())
|
||||
assertEquals(LiveActivitiesEvent.KIND, from.kind)
|
||||
assertEquals(sourceHost, from.pubKeyHex)
|
||||
assertEquals("source-d", from.dTag)
|
||||
|
||||
val to = assertNotNull(event.toAddress())
|
||||
assertEquals(LiveActivitiesEvent.KIND, to.kind)
|
||||
assertEquals(targetHost, to.pubKeyHex)
|
||||
assertEquals("target-d", to.dTag)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ignoresUnmarkedOrWrongKindATags() {
|
||||
val event =
|
||||
LiveActivitiesRaidEvent(
|
||||
id = "1".repeat(64),
|
||||
pubKey = author,
|
||||
createdAt = 1_700_000_000L,
|
||||
tags =
|
||||
arrayOf(
|
||||
// Wrong marker
|
||||
arrayOf("a", "${LiveActivitiesEvent.KIND}:$sourceHost:x", "", "reply"),
|
||||
// Wrong kind
|
||||
arrayOf("a", "30023:$sourceHost:article", "", "root"),
|
||||
// No marker at all
|
||||
arrayOf("a", "${LiveActivitiesEvent.KIND}:$sourceHost:y"),
|
||||
),
|
||||
content = "",
|
||||
sig = dummySig,
|
||||
)
|
||||
|
||||
assertNull(event.fromAddress())
|
||||
assertNull(event.toAddress())
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip53LiveActivities.streaming
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class LiveActivitiesEventGoalTagTest {
|
||||
private val host = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
private val goalId = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
|
||||
private val dummySig = "0".repeat(128)
|
||||
|
||||
@Test
|
||||
fun readsZapStreamGoalTag() {
|
||||
val event =
|
||||
LiveActivitiesEvent(
|
||||
id = "3".repeat(64),
|
||||
pubKey = host,
|
||||
createdAt = 1_700_000_000L,
|
||||
tags =
|
||||
arrayOf(
|
||||
arrayOf("d", "stream-d"),
|
||||
arrayOf("title", "My stream"),
|
||||
arrayOf("goal", goalId),
|
||||
),
|
||||
content = "",
|
||||
sig = dummySig,
|
||||
)
|
||||
|
||||
assertEquals(goalId, event.goalEventId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returnsNullWhenNoGoalTag() {
|
||||
val event =
|
||||
LiveActivitiesEvent(
|
||||
id = "3".repeat(64),
|
||||
pubKey = host,
|
||||
createdAt = 1_700_000_000L,
|
||||
tags = arrayOf(arrayOf("d", "stream-d")),
|
||||
content = "",
|
||||
sig = dummySig,
|
||||
)
|
||||
|
||||
assertNull(event.goalEventId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returnsNullWhenGoalTagEmpty() {
|
||||
val event =
|
||||
LiveActivitiesEvent(
|
||||
id = "3".repeat(64),
|
||||
pubKey = host,
|
||||
createdAt = 1_700_000_000L,
|
||||
tags =
|
||||
arrayOf(
|
||||
arrayOf("d", "stream-d"),
|
||||
arrayOf("goal", ""),
|
||||
),
|
||||
content = "",
|
||||
sig = dummySig,
|
||||
)
|
||||
|
||||
assertNull(event.goalEventId())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user