android: test avatar event framing, limits and member updates
This commit is contained in:
1 parent
9c70eab075
commit
15dbb6fe51
3 files changed
+120
No files matched your search
@@ -0,0 +1,80 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
class AvatarProtocolTest {
|
||||
private val uid = 0xfedcba9876543210uL.toLong()
|
||||
private fun buffer(size: Int) = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN)
|
||||
private fun group(size: Int = 0): ByteArray = buffer(60 + size).apply {
|
||||
putLong(71); putLong(uid); putLong(123)
|
||||
put(ByteArray(32) { 0xa5.toByte() }); put(1); put(0); putShort(size.toShort())
|
||||
put(ByteArray(size) { 0x7f })
|
||||
}.array()
|
||||
private fun response(): ByteArray = buffer(141).apply {
|
||||
putLong(uid); putLong(23)
|
||||
put(4); put(2); put(2); putShort(0)
|
||||
putLong(71); putLong(uid); putLong(0); putLong(512)
|
||||
put(16 + 117, 1)
|
||||
}.array()
|
||||
|
||||
@Test fun unsignedNodeAndContentAddress() {
|
||||
assertEquals(uid, AvatarProtocol.node("fedcba9876543210"))
|
||||
assertEquals("fedcba9876543210", AvatarProtocol.hex(uid))
|
||||
assertTrue(AvatarProtocol.validFile("a".repeat(64) + ".png"))
|
||||
listOf("A".repeat(64) + ".png", "../" + "a".repeat(64) + ".png", "a.png", "a".repeat(64) + ".jpg")
|
||||
.forEach { assertFalse(it, AvatarProtocol.validFile(it)) }
|
||||
assertEquals(0L, AvatarProtocol.node("invalid"))
|
||||
}
|
||||
@Test fun groupLimitAndSignedDeletion() {
|
||||
val record = AvatarProtocol.group(group(16384))
|
||||
assertEquals(71L, record.id); assertEquals(uid, record.group); assertEquals(123L, record.version)
|
||||
assertEquals("a5".repeat(32), record.hash); assertTrue(record.canEdit); assertFalse(record.failed)
|
||||
assertEquals(16384, record.image.size)
|
||||
assertEquals(0, AvatarProtocol.group(group()).image.size)
|
||||
}
|
||||
@Test fun rejectTruncatedOversizedAndMalformedGroup() {
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.group(group(1).copyOf(60)) }
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.group(group(16385)) }
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.group(group().also { it[56] = 2 }) }
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.group(group().also { it[57] = -1 }) }
|
||||
}
|
||||
@Test fun statHasSizeAndTypeWithoutAContentHash() {
|
||||
// STAT общего сервера возвращает метаданные; хеш присутствует только после COMMIT/stream-open.
|
||||
val reply = AvatarProtocol.response(response())
|
||||
assertEquals(uid, reply.group); assertEquals(23L, reply.server); assertEquals(uid, reply.owner)
|
||||
assertEquals(71L, reply.id); assertEquals(2, reply.command); assertEquals(0, reply.status)
|
||||
assertEquals(512L, reply.size); assertEquals(1, reply.fileType); assertEquals("0".repeat(64), reply.hash)
|
||||
}
|
||||
@Test fun rejectInvalidFileFraming() {
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.response(response().copyOf(140)) }
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.response(response().also { it[16] = 3 }) }
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.response(response().also { it[17] = 1 }) }
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.response(response().also { it[135] = 1 }) }
|
||||
val path = response().copyOf(144).also { it[135] = 3; "abc".toByteArray().copyInto(it, 141) }
|
||||
assertEquals(71L, AvatarProtocol.response(path).id)
|
||||
}
|
||||
@Test fun progressMustFinishBeforeResolvingARequest() {
|
||||
val progress = buffer(51).apply {
|
||||
putLong(71); putLong(uid); putLong(23); putLong(uid)
|
||||
putLong(0); putLong(512); putShort(0); put(1)
|
||||
}.array()
|
||||
assertNull(AvatarProtocol.transfer(progress))
|
||||
progress[50] = 2
|
||||
assertEquals(71L, AvatarProtocol.transfer(progress)?.id)
|
||||
progress[50] = 3
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.transfer(progress) }
|
||||
progress[50] = 2
|
||||
ByteBuffer.wrap(progress).order(ByteOrder.LITTLE_ENDIAN).putLong(32, 513)
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.transfer(progress) }
|
||||
}
|
||||
@Test fun failedPublicationRemainsAnError() {
|
||||
val result = buffer(17).apply { putLong(71); putLong(uid); put(1) }.array()
|
||||
assertEquals(1, AvatarProtocol.published(result).status)
|
||||
assertEquals(uid, AvatarProtocol.published(result).group)
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.published(result.copyOf(16)) }
|
||||
assertThrows(IllegalArgumentException::class.java) { AvatarProtocol.published(result.also { it[16] = 2 }) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.utun.chat.data
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
class MemberAvatarTest {
|
||||
@Test fun sharedSnapshotKeepsAvatarAndSignedStorageRole() {
|
||||
val bytes = ByteArray(157)
|
||||
val b = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN)
|
||||
b.putLong(0, 123); bytes[8] = 1; bytes[10] = 2
|
||||
"Alice".toByteArray().copyInto(bytes, 15)
|
||||
val server = 0xfedcba9876543210uL.toLong()
|
||||
b.putLong(79, server)
|
||||
val file = "b".repeat(64) + ".png"
|
||||
file.toByteArray().copyInto(bytes, 87); bytes[156] = 1
|
||||
val previous = ChatMember(123, "Old", isVerified = true, nodeType = 3, directUp = true, bgpNodes = 7)
|
||||
val member = requireNotNull(parseMemberWire(bytes)).toChatMember(previous)
|
||||
assertEquals("Alice", member.name); assertTrue(member.online); assertTrue(member.isAdmin)
|
||||
assertEquals(server, member.avatarServer); assertEquals(file, member.avatarFile); assertTrue(member.storageRole)
|
||||
assertTrue(member.isVerified); assertEquals(3, member.nodeType); assertTrue(member.directUp); assertEquals(7, member.bgpNodes)
|
||||
// Сеть передаёт storage priority отдельно от подписанной роли хранителя.
|
||||
assertFalse(member.isStorage)
|
||||
}
|
||||
@Test fun clearingReferenceDoesNotKeepAnOldAvatar() {
|
||||
val previous = ChatMember(123, "Alice", avatarServer = 456, avatarFile = "b".repeat(64) + ".png", storageRole = true)
|
||||
val updated = MemberWire(123, 1, true, 0, 0, 0, false, "Alice").toChatMember(previous)
|
||||
assertEquals(0L, updated.avatarServer); assertEquals("", updated.avatarFile); assertFalse(updated.storageRole)
|
||||
assertTrue(updated.isSelf)
|
||||
}
|
||||
@Test fun recordsAtAnOffsetRequireAll157Bytes() {
|
||||
val bytes = ByteArray(160)
|
||||
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).putLong(3, 987)
|
||||
assertEquals(987L, parseMemberWire(bytes, 3)?.nodeId)
|
||||
assertNull(parseMemberWire(bytes, 4)); assertNull(parseMemberWire(bytes, -1))
|
||||
assertNull(parseMemberWire(bytes.copyOf(156)))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user