mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 00:16:59 +00:00
fix(commons): tighter Base64Image contract + pin serializer wire format
Two follow-up cleanups from the audit. Base64Image.parse: when the regex matched but the data capture group was missing, the migrated version returned an empty ByteArray. The original threw NPE (java.util.Base64.getDecoder().decode(null)). Both behaviors are accidents — restore the intended contract: throw the existing "Unable to convert base64 to image" Exception explicitly. FeedDefinitionSerializerTest gains a serializesToExpectedWireFormat test that pins the byte-exact JSON output for a representative multi-field feed. The legacy-Jackson migration claimed byte-identity but only round-trip and reverse-compat were covered. Any future change to field ordering / null handling / number formatting now fails this test loudly, protecting users who have saved feeds on disk and any downstream consumer expecting the stable order.
This commit is contained in:
+5
-7
@@ -30,12 +30,10 @@ object Base64Image {
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
fun parse(content: String): ByteArray {
|
||||
val matcher = pattern.find(content)
|
||||
if (matcher != null) {
|
||||
val base64String = matcher.groups[2]?.value ?: ""
|
||||
return Base64.decode(base64String)
|
||||
}
|
||||
|
||||
throw Exception("Unable to convert base64 to image $content")
|
||||
val match = pattern.find(content) ?: throw Exception("Unable to convert base64 to image $content")
|
||||
val base64String =
|
||||
match.groups[2]?.value
|
||||
?: throw Exception("Unable to convert base64 to image $content")
|
||||
return Base64.decode(base64String)
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -212,6 +212,44 @@ class FeedDefinitionSerializerTest {
|
||||
assertEquals(emptyList(), FeedDefinitionSerializer.deserializeList(" "))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun serializesToExpectedWireFormat() {
|
||||
// Pins the byte-exact JSON output so any future change to the
|
||||
// serializer (field order, null handling, number formatting) is
|
||||
// caught here. Users have feeds saved on disk in this exact format
|
||||
// and may share JSON between app versions; downstream consumers
|
||||
// depend on the field order being stable.
|
||||
val feed =
|
||||
FeedDefinition(
|
||||
id = "test-1",
|
||||
name = "Bitcoin",
|
||||
emoji = "₿",
|
||||
pinned = true,
|
||||
pinOrder = 0,
|
||||
source =
|
||||
FeedSource.Filter(
|
||||
hashtags = persistentListOf("bitcoin", "btc"),
|
||||
authors = persistentListOf("abc123"),
|
||||
relays = persistentListOf("wss://relay.damus.io"),
|
||||
excludeAuthors = persistentListOf("spammer"),
|
||||
excludeKeywords = persistentListOf("scam"),
|
||||
kinds = persistentListOf(1, 6),
|
||||
),
|
||||
refreshMode = RefreshMode.LIVE_STREAM,
|
||||
createdAt = 1000L,
|
||||
)
|
||||
|
||||
val expected =
|
||||
"""[{"id":"test-1","name":"Bitcoin","emoji":"₿","pinned":true,""" +
|
||||
""""pinOrder":0,"refreshMode":"LIVE_STREAM","createdAt":1000,""" +
|
||||
""""source":{"type":"filter","hashtags":["bitcoin","btc"],""" +
|
||||
""""authors":["abc123"],"relays":["wss://relay.damus.io"],""" +
|
||||
""""excludeAuthors":["spammer"],"excludeKeywords":["scam"],""" +
|
||||
""""kinds":[1,6]}}]"""
|
||||
|
||||
assertEquals(expected, FeedDefinitionSerializer.serializeList(listOf(feed)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parsesLegacyJacksonOutput() {
|
||||
// Wire format previously emitted by ObjectMapper. Users have feed
|
||||
|
||||
Reference in New Issue
Block a user