mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-26 10:45:21 +00:00
Nostr geohash (#276)
* first nostr build * add test file * internet access * fix relay manager * fix serialization * demo service - remove later * fix nostr * event dedupe * dedupe * ui wip * can send messages * subscription works * works * favs * works * delete chat on change * fix mentions * remove autojoin channels * styling * adjust colors * ui changes * live updates working * use local timestamp * message history in background * robust * fixes * nicknames refresh optimization * nostr service * refactor nostr * style * geohash works * centralize colors * refactoring * disable DMs for now: click on peer nickname doesnt open chat list in geohash mode * use local time * less logging * robustness * scroll nickname * adjust some text
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
package com.bitchat.android.nostr
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
/**
|
||||
* Test manager for Nostr functionality
|
||||
* Use this to verify the Nostr client works correctly
|
||||
*/
|
||||
class NostrTestManager(private val context: Context) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "NostrTestManager"
|
||||
}
|
||||
|
||||
private val testScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||
private lateinit var nostrClient: NostrClient
|
||||
|
||||
/**
|
||||
* Run comprehensive Nostr tests
|
||||
*/
|
||||
fun runTests() {
|
||||
Log.i(TAG, "🧪 Starting Nostr functionality tests...")
|
||||
|
||||
testScope.launch {
|
||||
try {
|
||||
// Test 1: Initialize client
|
||||
testClientInitialization()
|
||||
|
||||
// Test 2: Test identity generation and storage
|
||||
testIdentityManagement()
|
||||
|
||||
// Test 3: Test relay connections
|
||||
testRelayConnections()
|
||||
|
||||
// Test 4: Test cryptography
|
||||
testCryptography()
|
||||
|
||||
// Test 5: Test Bech32 encoding
|
||||
testBech32()
|
||||
|
||||
// Test 6: Test message subscription (without sending)
|
||||
testMessageSubscription()
|
||||
|
||||
Log.i(TAG, "✅ All Nostr tests completed successfully!")
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ Nostr tests failed: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun testClientInitialization() {
|
||||
Log.d(TAG, "Testing client initialization...")
|
||||
|
||||
nostrClient = NostrClient.getInstance(context)
|
||||
nostrClient.initialize()
|
||||
|
||||
// Wait for initialization
|
||||
delay(2000)
|
||||
|
||||
val isInitialized = nostrClient.isInitialized.value ?: false
|
||||
require(isInitialized) { "Client failed to initialize" }
|
||||
|
||||
Log.d(TAG, "✅ Client initialization successful")
|
||||
}
|
||||
|
||||
private suspend fun testIdentityManagement() {
|
||||
Log.d(TAG, "Testing identity management...")
|
||||
|
||||
// Test current identity
|
||||
val identity = nostrClient.getCurrentIdentity()
|
||||
requireNotNull(identity) { "No current identity" }
|
||||
|
||||
Log.d(TAG, "Current identity npub: ${identity.getShortNpub()}")
|
||||
require(identity.npub.startsWith("npub1")) { "Invalid npub format" }
|
||||
require(identity.publicKeyHex.length == 64) { "Invalid public key length" }
|
||||
require(identity.privateKeyHex.length == 64) { "Invalid private key length" }
|
||||
|
||||
// Test geohash identity derivation
|
||||
val geohashIdentity = NostrIdentityBridge.deriveIdentity("u4pruydq", context)
|
||||
require(geohashIdentity.npub.startsWith("npub1")) { "Invalid geohash identity npub" }
|
||||
require(geohashIdentity.publicKeyHex != identity.publicKeyHex) { "Geohash identity should be different" }
|
||||
|
||||
Log.d(TAG, "Geohash identity npub: ${geohashIdentity.getShortNpub()}")
|
||||
Log.d(TAG, "✅ Identity management test successful")
|
||||
}
|
||||
|
||||
private suspend fun testRelayConnections() {
|
||||
Log.d(TAG, "Testing relay connections...")
|
||||
|
||||
// Wait for potential relay connections
|
||||
delay(3000)
|
||||
|
||||
val relayInfo = nostrClient.relayInfo.value ?: emptyList()
|
||||
require(relayInfo.isNotEmpty()) { "No relays configured" }
|
||||
|
||||
Log.d(TAG, "Configured relays: ${relayInfo.size}")
|
||||
relayInfo.forEach { relay ->
|
||||
Log.d(TAG, "Relay: ${relay.url} - Connected: ${relay.isConnected}")
|
||||
}
|
||||
|
||||
Log.d(TAG, "✅ Relay configuration test successful")
|
||||
}
|
||||
|
||||
private suspend fun testCryptography() {
|
||||
Log.d(TAG, "Testing cryptography functions...")
|
||||
|
||||
// Test key generation
|
||||
val (privateKey, publicKey) = NostrCrypto.generateKeyPair()
|
||||
require(privateKey.length == 64) { "Invalid private key length" }
|
||||
require(publicKey.length == 64) { "Invalid public key length" }
|
||||
require(NostrCrypto.isValidPrivateKey(privateKey)) { "Generated private key is invalid" }
|
||||
require(NostrCrypto.isValidPublicKey(publicKey)) { "Generated public key is invalid" }
|
||||
|
||||
// Test key derivation
|
||||
val derivedPublic = NostrCrypto.derivePublicKey(privateKey)
|
||||
require(derivedPublic == publicKey) { "Key derivation mismatch" }
|
||||
|
||||
// Test encryption/decryption
|
||||
val (recipientPrivate, recipientPublic) = NostrCrypto.generateKeyPair()
|
||||
val plaintext = "Hello, Nostr world! This is a test message."
|
||||
|
||||
val encrypted = NostrCrypto.encryptNIP44(plaintext, recipientPublic, privateKey)
|
||||
require(encrypted.isNotEmpty()) { "Encryption failed" }
|
||||
|
||||
val decrypted = NostrCrypto.decryptNIP44(encrypted, publicKey, recipientPrivate)
|
||||
require(decrypted == plaintext) { "Decryption failed: expected '$plaintext', got '$decrypted'" }
|
||||
|
||||
Log.d(TAG, "✅ Cryptography test successful")
|
||||
}
|
||||
|
||||
private suspend fun testBech32() {
|
||||
Log.d(TAG, "Testing Bech32 encoding...")
|
||||
|
||||
val testData = "hello world test data for bech32".toByteArray()
|
||||
val encoded = Bech32.encode("test", testData)
|
||||
require(encoded.startsWith("test1")) { "Invalid bech32 encoding" }
|
||||
|
||||
val (hrp, decoded) = Bech32.decode(encoded)
|
||||
require(hrp == "test") { "HRP mismatch" }
|
||||
require(decoded.contentEquals(testData)) { "Data mismatch after decode" }
|
||||
|
||||
// Test with actual public key
|
||||
val (_, publicKey) = NostrCrypto.generateKeyPair()
|
||||
val npub = Bech32.encode("npub", publicKey.hexToByteArray())
|
||||
require(npub.startsWith("npub1")) { "Invalid npub encoding" }
|
||||
|
||||
val (npubHrp, npubData) = Bech32.decode(npub)
|
||||
require(npubHrp == "npub") { "npub HRP mismatch" }
|
||||
require(npubData.toHexString() == publicKey) { "npub data mismatch" }
|
||||
|
||||
Log.d(TAG, "✅ Bech32 test successful")
|
||||
}
|
||||
|
||||
private suspend fun testMessageSubscription() {
|
||||
Log.d(TAG, "Testing message subscription...")
|
||||
|
||||
var messageReceived = false
|
||||
|
||||
// Subscribe to private messages (won't receive any in test, but tests the subscription mechanism)
|
||||
nostrClient.subscribeToPrivateMessages { content, senderNpub, timestamp ->
|
||||
Log.d(TAG, "📥 Received test private message from $senderNpub: $content")
|
||||
messageReceived = true
|
||||
}
|
||||
|
||||
// Subscribe to a test geohash
|
||||
nostrClient.subscribeToGeohash("u4pru") { content, senderPubkey, nickname, timestamp ->
|
||||
Log.d(TAG, "📥 Received test geohash message from ${senderPubkey.take(16)}...: $content")
|
||||
messageReceived = true
|
||||
}
|
||||
|
||||
// Wait a bit to see if any messages come through
|
||||
delay(2000)
|
||||
|
||||
Log.d(TAG, "✅ Message subscription test successful (no messages expected in test)")
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sending a message to yourself (loopback test)
|
||||
*/
|
||||
fun testLoopbackMessage() {
|
||||
testScope.launch {
|
||||
try {
|
||||
val identity = nostrClient.getCurrentIdentity()
|
||||
requireNotNull(identity) { "No identity available for loopback test" }
|
||||
|
||||
Log.i(TAG, "🔄 Testing loopback private message...")
|
||||
|
||||
// Send message to ourselves
|
||||
nostrClient.sendPrivateMessage(
|
||||
content = "Test loopback message at ${System.currentTimeMillis()}",
|
||||
recipientNpub = identity.npub,
|
||||
onSuccess = {
|
||||
Log.i(TAG, "✅ Loopback message sent successfully")
|
||||
},
|
||||
onError = { error ->
|
||||
Log.e(TAG, "❌ Loopback message failed: $error")
|
||||
}
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ Loopback test failed: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sending a geohash message
|
||||
*/
|
||||
fun testGeohashMessage() {
|
||||
testScope.launch {
|
||||
try {
|
||||
Log.i(TAG, "🌍 Testing geohash message...")
|
||||
|
||||
nostrClient.sendGeohashMessage(
|
||||
content = "Test geohash message from Android at ${System.currentTimeMillis()}",
|
||||
geohash = "u4pru",
|
||||
nickname = "android-test",
|
||||
onSuccess = {
|
||||
Log.i(TAG, "✅ Geohash message sent successfully")
|
||||
},
|
||||
onError = { error ->
|
||||
Log.e(TAG, "❌ Geohash message failed: $error")
|
||||
}
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "❌ Geohash test failed: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get debug information about the Nostr client
|
||||
*/
|
||||
fun getDebugInfo(): String {
|
||||
return buildString {
|
||||
appendLine("=== Nostr Client Debug Info ===")
|
||||
|
||||
val identity = nostrClient.getCurrentIdentity()
|
||||
if (identity != null) {
|
||||
appendLine("Identity: ${identity.getShortNpub()}")
|
||||
appendLine("Public Key: ${identity.publicKeyHex.take(16)}...")
|
||||
appendLine("Created: ${java.util.Date(identity.createdAt)}")
|
||||
} else {
|
||||
appendLine("No identity loaded")
|
||||
}
|
||||
|
||||
val isInitialized = nostrClient.isInitialized.value ?: false
|
||||
appendLine("Initialized: $isInitialized")
|
||||
|
||||
val isConnected = nostrClient.relayConnectionStatus.value ?: false
|
||||
appendLine("Relay Connected: $isConnected")
|
||||
|
||||
val relays = nostrClient.relayInfo.value ?: emptyList()
|
||||
appendLine("Relays (${relays.size}):")
|
||||
relays.forEach { relay ->
|
||||
appendLine(" ${relay.url}: ${if (relay.isConnected) "✅" else "❌"} (sent: ${relay.messagesSent}, received: ${relay.messagesReceived})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown test manager
|
||||
*/
|
||||
fun shutdown() {
|
||||
testScope.cancel()
|
||||
nostrClient.shutdown()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user