Implement Android Home activity and HTTPS synchronization (#97)
This commit is contained in:
1 parent
8c9341f214
commit
5c933a99a3
29 files changed
+2008
-110
No files matched your search
+88
@@ -4,6 +4,9 @@ import androidx.test.platform.app.InstrumentationRegistry
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileAuthenticationFfiException
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileClock
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileEntryEditorInput
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileHomeErrorKind
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileHomeFfiException
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileHomeFreshness
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileOnboardingErrorKind
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobileOnboardingFfiException
|
||||
import de.rfc1437.ironstorage.bridge.generated.MobilePrivateDirectories
|
||||
@@ -21,6 +24,80 @@ import org.junit.Assume.assumeTrue
|
||||
import org.junit.Test
|
||||
|
||||
class AndroidOnboardingTest {
|
||||
@Test fun homeUsesThePrivateRuntimeAndSharesDirtyEditorReservations() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val id = "home-${UUID.randomUUID()}"
|
||||
val directories = MobilePrivateDirectories(
|
||||
File(context.filesDir, id).apply { mkdirs() }.canonicalPath,
|
||||
File(context.noBackupFilesDir, id).apply { mkdirs() }.canonicalPath,
|
||||
File(context.cacheDir, id).apply { mkdirs() }.canonicalPath
|
||||
)
|
||||
val backend = MemoryBackend()
|
||||
val clock = object : MobileClock {
|
||||
override fun monotonicMilliseconds(): ULong =
|
||||
android.os.SystemClock.elapsedRealtime().toULong()
|
||||
}
|
||||
NativeRuntime(context).privateStorage(directories).use { runtime ->
|
||||
runtime.androidOnboarding(backend).use { setup ->
|
||||
val key = requireNotNull(
|
||||
setup.generate("Home fixture", "public-test-passphrase").selectedKey
|
||||
)
|
||||
setup.setupLocal(key, "public-test-passphrase")
|
||||
}
|
||||
runtime.authentication(backend, clock).use { authentication ->
|
||||
val draft = authentication.beginCreateEntry("", "draft", "public-test-passphrase")
|
||||
runtime.androidHomeOperation(backend, clock).use { home ->
|
||||
assertFalse(home.cached().remoteConfigured)
|
||||
assertFalse(home.activityKnown())
|
||||
for (action in listOf({ home.pull() }, { home.push() })) {
|
||||
val error =
|
||||
assertThrows(MobileHomeFfiException.Failed::class.java) { action() }
|
||||
assertEquals(MobileHomeErrorKind.DIRTY_LOCAL_CHANGES, error.kind)
|
||||
}
|
||||
assertTrue(authentication.entryEditor(draft.id).dirty)
|
||||
}
|
||||
authentication.saveEntryEditor(
|
||||
draft.id,
|
||||
listOf(
|
||||
MobileEntryEditorInput(draft.page.fields[0].id, null, "fixture-password")
|
||||
)
|
||||
)
|
||||
// Seed an uncommitted encrypted fixture; application code never reads the store.
|
||||
val vault = File(runtime.layout().vault)
|
||||
File(vault, "commit-fixture.gpg").writeBytes(File(vault, "draft.gpg").readBytes())
|
||||
runtime.androidHomeOperation(backend, clock).use { home ->
|
||||
assertTrue(home.cached().summaries.any { it.id.startsWith("local:") })
|
||||
assertEquals(
|
||||
"Changes Committed",
|
||||
home.commit("Encrypted Home fixture").notice?.title
|
||||
)
|
||||
}
|
||||
authentication.manualLock()
|
||||
runtime.androidHomeOperation(backend, clock).use { home ->
|
||||
assertEquals(MobileHomeFreshness.CURRENT, home.refreshGesture().freshness)
|
||||
assertTrue(home.identity().name.isNotEmpty())
|
||||
val error = assertThrows(MobileHomeFfiException.Failed::class.java) {
|
||||
home.commit("No encrypted changes")
|
||||
}
|
||||
assertEquals(MobileHomeErrorKind.NO_CHANGES, error.kind)
|
||||
for (action in listOf({ home.fetch() }, { home.pull() }, { home.push() })) {
|
||||
val missing =
|
||||
assertThrows(MobileHomeFfiException.Failed::class.java) { action() }
|
||||
assertEquals(MobileHomeErrorKind.CONFIGURATION, missing.kind)
|
||||
}
|
||||
assertFalse(authentication.state().unlocked)
|
||||
}
|
||||
runtime.androidHomeOperation(backend, clock).use { home ->
|
||||
home.cancel()
|
||||
val error = assertThrows(MobileHomeFfiException.Failed::class.java) {
|
||||
home.commit("Cancelled commit")
|
||||
}
|
||||
assertEquals(MobileHomeErrorKind.INTERRUPTED, error.kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun privateNavigationTraversesWithoutAuthenticationAndRejectsReplacedEntries() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val id = "navigation-${UUID.randomUUID()}"
|
||||
@@ -193,6 +270,17 @@ class AndroidOnboardingTest {
|
||||
assertFalse(config.contains("public-test-token"))
|
||||
assertFalse(config.contains("public-replacement-token"))
|
||||
}
|
||||
val clock = object : MobileClock {
|
||||
override fun monotonicMilliseconds(): ULong =
|
||||
android.os.SystemClock.elapsedRealtime().toULong()
|
||||
}
|
||||
runtime.androidHomeOperation(backend, clock).use { home ->
|
||||
assertTrue(home.cached().remoteConfigured)
|
||||
assertEquals(MobileHomeFreshness.CURRENT, home.fetch().freshness)
|
||||
// The local main branch is absent from this public repository's master branch.
|
||||
assertFalse(home.activityKnown())
|
||||
assertEquals("Tracked Branch Is Unavailable", home.cached().notice?.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user