Every comment is reduced to its first sentence plus @param, @return and @throws tags. Comment lines: 20040 to 8526. ktlint 1.8 applied with its default (official Kotlin) style across all modules; 262 files reformatted. The 264 remaining ktlint findings are naming and filename rules, which are code changes, and are left alone. Comments and formatting only. 1208 tests pass, counted from the XML. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
100 lines
3.5 KiB
Kotlin
100 lines
3.5 KiB
Kotlin
package io.flamenet.e2e
|
|
|
|
import kotlin.test.Test
|
|
import kotlin.test.assertEquals
|
|
import kotlin.test.assertNull
|
|
import kotlin.test.assertTrue
|
|
|
|
/**
|
|
* Parsing an address, and refusing everything that is nearly one.
|
|
*/
|
|
class ContactCardTest {
|
|
@Test
|
|
fun `a bare address with no scheme is accepted`() {
|
|
val card = ContactCard.decode("fn1abc@relay1.flamenet.io")
|
|
assertEquals("fn1abc", card?.fnid)
|
|
assertEquals("relay1.flamenet.io", card?.relay)
|
|
assertNull(card?.deliveryKey, "no key means it cannot be sealed, and that is knowable")
|
|
}
|
|
|
|
@Test
|
|
fun `the scheme is stripped and the host lowercased`() {
|
|
val card = ContactCard.decode(" FLAMENET:fn1abc@Relay1.Flamenet.IO ")
|
|
assertEquals("fn1abc", card?.fnid)
|
|
assertEquals("relay1.flamenet.io", card?.relay)
|
|
assertEquals("fn1abc@relay1.flamenet.io", card?.address)
|
|
}
|
|
|
|
@Test
|
|
fun `what it encodes parses back to the same address`() {
|
|
val card = ContactCard("fn1abc", "relay1.flamenet.io")
|
|
assertEquals("flamenet:fn1abc@relay1.flamenet.io", card.encoded)
|
|
assertEquals(card.address, ContactCard.decode(card.encoded)?.address)
|
|
}
|
|
|
|
/**
|
|
* Kept only so an older card still parses.
|
|
*/
|
|
@Test
|
|
fun `an older card carrying a delivery key still parses`() {
|
|
val card = ContactCard.decode("flamenet:fn1abc@relay1.flamenet.io?k=" + "A".repeat(44))
|
|
assertEquals("fn1abc", card?.fnid)
|
|
assertEquals("relay1.flamenet.io", card?.relay)
|
|
assertEquals("A".repeat(44), card?.deliveryKey)
|
|
// And it is NOT re-emitted, because publishing a secret-shaped thing
|
|
// for nothing doubles the length of every address.
|
|
assertTrue("?" !in card!!.encoded, card.encoded)
|
|
}
|
|
|
|
/**
|
|
* A relay is a hostname.
|
|
*/
|
|
@Test
|
|
fun `a relay that is a URL is refused`() {
|
|
assertNull(ContactCard.decode("fn1@https://evil.example.com/path"))
|
|
assertNull(ContactCard.decode("fn1@evil.example.com/path"))
|
|
assertNull(ContactCard.decode("fn1@evil.example.com:8443"))
|
|
}
|
|
|
|
@Test
|
|
fun `the nearly right is refused`() {
|
|
assertNull(ContactCard.decode(""))
|
|
assertNull(ContactCard.decode("fn1abc"), "no relay at all")
|
|
assertNull(ContactCard.decode("@relay1.flamenet.io"), "no address")
|
|
assertNull(ContactCard.decode("fn1abc@"), "no relay")
|
|
assertNull(ContactCard.decode("fn1abc@localhost"), "a hostname needs a dot")
|
|
}
|
|
|
|
/**
|
|
* A second `@` means this is not one address.
|
|
*/
|
|
@Test
|
|
fun `two at signs is not one address`() {
|
|
assertNull(ContactCard.decode("fn1abc@a@b.example.com"))
|
|
}
|
|
|
|
/**
|
|
* Two spellings of one address would open two threads for one person, so
|
|
* everything that files a message goes through the same normaliser.
|
|
*/
|
|
@Test
|
|
fun `canonical gives one spelling for one address`() {
|
|
val spellings =
|
|
listOf(
|
|
"fn1abc@relay1.flamenet.io",
|
|
"flamenet:fn1abc@relay1.flamenet.io",
|
|
" fn1abc@RELAY1.Flamenet.io ",
|
|
"flamenet:fn1abc@relay1.flamenet.io?k=" + "A".repeat(44),
|
|
)
|
|
val canonical = spellings.map { ContactCard.canonical(it) }.toSet()
|
|
assertEquals(setOf("fn1abc@relay1.flamenet.io"), canonical, "got $canonical")
|
|
}
|
|
|
|
@Test
|
|
fun `canonical refuses what decode refuses`() {
|
|
for (bad in listOf("", "fn1abc", "fn1abc@localhost", "fn1@evil.example.com/x")) {
|
|
assertNull(ContactCard.canonical(bad), "accepted $bad")
|
|
}
|
|
}
|
|
}
|