mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-13 05:49:57 +02:00
refactor(bindings): use production saved device APIs
This commit is contained in:
@@ -28,7 +28,7 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testExperimentalKeychainIdentitySurvivesRestart() throws {
|
||||
func testProtectedKeychainIdentitySurvivesStandardConstructorRestart() throws {
|
||||
let directory = try FileManager.default.url(
|
||||
for: .itemReplacementDirectory,
|
||||
in: .userDomainMask,
|
||||
@@ -39,7 +39,7 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
||||
defer { try? FileManager.default.removeItem(at: directory) }
|
||||
|
||||
let sink = RecordingSink()
|
||||
let first = try VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
let first = try VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir: directory.path,
|
||||
eventSink: sink,
|
||||
limits: defaultCoreLimits(),
|
||||
@@ -53,7 +53,7 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
||||
)
|
||||
first.shutdown()
|
||||
|
||||
let restarted = try VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
let restarted = try VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir: directory.path,
|
||||
eventSink: RecordingSink(),
|
||||
limits: defaultCoreLimits(),
|
||||
@@ -74,7 +74,7 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
||||
defer { try? FileManager.default.removeItem(at: directory) }
|
||||
|
||||
let sink = RecordingSink()
|
||||
let core = try VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
let core = try VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir: directory.path,
|
||||
eventSink: sink,
|
||||
limits: defaultCoreLimits(),
|
||||
@@ -133,7 +133,9 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
||||
// regenerated surface is what the harness compiles against.
|
||||
let _: (
|
||||
(String, CoreEventSink, CoreLimits, CoreNetworkConfig) throws -> VnidropCore
|
||||
) = VnidropCore.initializeWithExperimentalSavedDevices
|
||||
) = VnidropCore.initializeWithLimitsAndNetworkConfig
|
||||
let capabilities: SavedDeviceCapabilities = savedDeviceCapabilities()
|
||||
XCTAssertGreaterThanOrEqual(capabilities.domainContractVersion, 1)
|
||||
XCTAssertNotNil(defaultCoreLimits().maxSavedDevices)
|
||||
return
|
||||
}
|
||||
@@ -159,7 +161,9 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
||||
"generated bindings must not expose \(needle)"
|
||||
)
|
||||
}
|
||||
XCTAssertTrue(source.contains("initializeWithExperimentalSavedDevices"))
|
||||
XCTAssertTrue(source.contains("initializeWithLimitsAndNetworkConfig"))
|
||||
XCTAssertTrue(source.contains("public struct SavedDeviceCapabilities"))
|
||||
XCTAssertTrue(source.contains("public func savedDeviceCapabilities()"))
|
||||
XCTAssertTrue(source.contains("setSavedDeviceLabel"))
|
||||
XCTAssertTrue(source.contains("listSavedDevices"))
|
||||
XCTAssertTrue(source.contains("revision"))
|
||||
|
||||
@@ -53,7 +53,7 @@ struct NativeCoreBindingFactory: CoreBindingFactory {
|
||||
case .localOnly:
|
||||
nativeConfiguration = CoreNetworkConfig(mode: .localOnly, relayUrls: [])
|
||||
}
|
||||
return try VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
return try VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir: appDataDir,
|
||||
eventSink: eventSink,
|
||||
limits: defaultCoreLimits(),
|
||||
@@ -523,4 +523,3 @@ private extension ReceiverRequest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -930,8 +930,15 @@ fn android_public_surface_omits_raw_secrets_and_generic_mutation() {
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
kotlin.contains("initializeWithExperimentalSavedDevices"),
|
||||
"experimental Android init must remain on the public binding surface"
|
||||
kotlin.contains("initializeWithLimitsAndNetworkConfig"),
|
||||
"production protected Android init must remain on the public binding surface"
|
||||
);
|
||||
assert!(
|
||||
kotlin.contains("public data class SavedDeviceCapabilities (")
|
||||
&& kotlin.contains(
|
||||
"public expect fun `savedDeviceCapabilities`(): SavedDeviceCapabilities",
|
||||
),
|
||||
"production saved-device capabilities must remain on the public binding surface"
|
||||
);
|
||||
assert!(
|
||||
kotlin.contains("SavedDevice"),
|
||||
|
||||
@@ -841,8 +841,13 @@ fn apple_public_bindings_omit_raw_secrets_and_generic_mutation() {
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
source.contains("initializeWithExperimentalSavedDevices"),
|
||||
"Swift bindings must expose experimental saved-device init"
|
||||
source.contains("initializeWithLimitsAndNetworkConfig"),
|
||||
"Swift bindings must expose standard protected initialization"
|
||||
);
|
||||
assert!(
|
||||
source.contains("public struct SavedDeviceCapabilities")
|
||||
&& source.contains("public func savedDeviceCapabilities()"),
|
||||
"Swift bindings must expose production saved-device capabilities"
|
||||
);
|
||||
assert!(
|
||||
source.contains("setSavedDeviceLabel"),
|
||||
|
||||
@@ -34,8 +34,12 @@ class SavedDeviceCoreContractBindingHygieneTest {
|
||||
}
|
||||
|
||||
assertTrue(
|
||||
source.contains("initializeWithExperimentalSavedDevices"),
|
||||
"experimental saved-device initializer must remain public",
|
||||
source.contains("initializeWithLimitsAndNetworkConfig"),
|
||||
"production protected initializer must remain public",
|
||||
)
|
||||
assertTrue(source.contains("public data class SavedDeviceCapabilities ("))
|
||||
assertTrue(
|
||||
source.contains("public expect fun `savedDeviceCapabilities`(): SavedDeviceCapabilities"),
|
||||
)
|
||||
assertTrue(
|
||||
source.contains("SavedDevice"),
|
||||
|
||||
@@ -455,7 +455,7 @@ private object ProtectedCoreFactory : CoreFactory {
|
||||
appDataDir: String,
|
||||
eventSink: CoreEventSink,
|
||||
relaySettings: RelaySettings,
|
||||
): VnidropCore = VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
): VnidropCore = VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir,
|
||||
eventSink,
|
||||
defaultCoreLimits(),
|
||||
|
||||
@@ -6,17 +6,26 @@ import kotlin.test.assertTrue
|
||||
import uniffi.vnidrop.CoreEvent
|
||||
import uniffi.vnidrop.CoreEventSink
|
||||
import uniffi.vnidrop.VnidropCore
|
||||
import uniffi.vnidrop.VnidropException
|
||||
|
||||
class CoreNativeLoadTest {
|
||||
@Test
|
||||
fun generatedBindingsCanInitializeRustCore() {
|
||||
val coreDir = Files.createTempDirectory("vnidrop-jvm-test")
|
||||
val core = VnidropCore.initialize(
|
||||
appDataDir = coreDir.toString(),
|
||||
eventSink = object : CoreEventSink {
|
||||
override fun onEvent(event: CoreEvent) = Unit
|
||||
},
|
||||
)
|
||||
val core = try {
|
||||
VnidropCore.initialize(
|
||||
appDataDir = coreDir.toString(),
|
||||
eventSink = object : CoreEventSink {
|
||||
override fun onEvent(event: CoreEvent) = Unit
|
||||
},
|
||||
)
|
||||
} catch (_: VnidropException.SecureStorageUnavailable) {
|
||||
coreDir.toFile().deleteRecursively()
|
||||
return
|
||||
} catch (_: VnidropException.SecureStorageLocked) {
|
||||
coreDir.toFile().deleteRecursively()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
assertTrue(core.status().endpointId.isNotBlank())
|
||||
|
||||
@@ -9,9 +9,10 @@ import uniffi.vnidrop.CoreEventSink
|
||||
import uniffi.vnidrop.VnidropCore
|
||||
import uniffi.vnidrop.defaultCoreLimits
|
||||
import uniffi.vnidrop.defaultCoreNetworkConfig
|
||||
import uniffi.vnidrop.savedDeviceCapabilities
|
||||
|
||||
/**
|
||||
* Optional Windows host harness for the experimental saved-device core.
|
||||
* Optional Windows host harness for the saved-device core.
|
||||
*
|
||||
* Linux/macOS CI skips so shared jvmTest stays green; Windows desktop runs the
|
||||
* DPAPI-backed initialize/restart identity check against public bindings.
|
||||
@@ -22,6 +23,7 @@ class WindowsSavedDeviceCoreContractTest {
|
||||
if (!isWindowsHost()) {
|
||||
return
|
||||
}
|
||||
assertTrue(savedDeviceCapabilities().domainContractVersion >= 1u)
|
||||
|
||||
val coreDir = Files.createTempDirectory("vnidrop-windows-contract")
|
||||
val sink = object : CoreEventSink {
|
||||
@@ -30,7 +32,7 @@ class WindowsSavedDeviceCoreContractTest {
|
||||
}
|
||||
}
|
||||
|
||||
val first = VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
val first = VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir = coreDir.toString(),
|
||||
eventSink = sink,
|
||||
limits = defaultCoreLimits(),
|
||||
@@ -45,7 +47,7 @@ class WindowsSavedDeviceCoreContractTest {
|
||||
first.shutdown()
|
||||
}
|
||||
|
||||
val restarted = VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
val restarted = VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir = coreDir.toString(),
|
||||
eventSink = sink,
|
||||
limits = defaultCoreLimits(),
|
||||
@@ -77,8 +79,7 @@ class WindowsSavedDeviceCoreContractTest {
|
||||
fail("public bindings must not expose $forbidden")
|
||||
}
|
||||
}
|
||||
// Typed saved-device operations (names may be mangled by UniFFI); require
|
||||
// at least the experimental constructor surface used above.
|
||||
// Typed saved-device operations (names may be mangled by UniFFI).
|
||||
assertTrue(
|
||||
methods.any { it.contains("listSaved", ignoreCase = true) }
|
||||
|| methods.any { it.contains("SavedDevice", ignoreCase = true) }
|
||||
|
||||
@@ -9,6 +9,7 @@ import kotlin.test.assertTrue
|
||||
import uniffi.vnidrop.CoreNetworkConfig
|
||||
import uniffi.vnidrop.CoreRelayMode
|
||||
import uniffi.vnidrop.VnidropCore
|
||||
import uniffi.vnidrop.VnidropException
|
||||
|
||||
class CoreRepositoryStorageTest {
|
||||
@Test
|
||||
@@ -25,12 +26,18 @@ class CoreRepositoryStorageTest {
|
||||
},
|
||||
)
|
||||
try {
|
||||
assertTrue(
|
||||
repository.initialize(
|
||||
appData.toString(),
|
||||
RelaySettings(mode = RelayMode.LocalOnly),
|
||||
).isSuccess,
|
||||
val initialized = repository.initialize(
|
||||
appData.toString(),
|
||||
RelaySettings(mode = RelayMode.LocalOnly),
|
||||
)
|
||||
val initializationError = initialized.exceptionOrNull()
|
||||
if (
|
||||
initializationError is VnidropException.SecureStorageUnavailable ||
|
||||
initializationError is VnidropException.SecureStorageLocked
|
||||
) {
|
||||
return@runTest
|
||||
}
|
||||
initialized.getOrThrow()
|
||||
val endpointId = repository.state.value.status?.endpointId
|
||||
val share = repository.sharePath(
|
||||
path = source.toString(),
|
||||
|
||||
@@ -34,8 +34,12 @@ class SavedDeviceCoreContractBindingHygieneTest {
|
||||
}
|
||||
|
||||
assertTrue(
|
||||
source.contains("initializeWithExperimentalSavedDevices"),
|
||||
"experimental saved-device initializer must remain public",
|
||||
source.contains("initializeWithLimitsAndNetworkConfig"),
|
||||
"production protected initializer must remain public",
|
||||
)
|
||||
assertTrue(source.contains("public data class SavedDeviceCapabilities ("))
|
||||
assertTrue(
|
||||
source.contains("public expect fun `savedDeviceCapabilities`(): SavedDeviceCapabilities"),
|
||||
)
|
||||
assertTrue(
|
||||
source.contains("SavedDevice"),
|
||||
|
||||
@@ -8,16 +8,18 @@ import uniffi.vnidrop.CoreEvent
|
||||
import uniffi.vnidrop.CoreEventSink
|
||||
import uniffi.vnidrop.CoreNetworkConfig
|
||||
import uniffi.vnidrop.CoreRelayMode
|
||||
import uniffi.vnidrop.SavedDeviceCapabilities
|
||||
import uniffi.vnidrop.VnidropCore
|
||||
import uniffi.vnidrop.VnidropException
|
||||
import uniffi.vnidrop.defaultCoreLimits
|
||||
import uniffi.vnidrop.savedDeviceCapabilities
|
||||
|
||||
/**
|
||||
* JVM-adjacent Android contract smoke.
|
||||
*
|
||||
* Full Keystore-backed identity/restart lives in the Rust
|
||||
* `platform_contract_android` harness. Here we prove the regenerated UniFFI
|
||||
* surface exposes revision-bearing events and the experimental initializer,
|
||||
* surface exposes revision-bearing events and the production initializer,
|
||||
* exercising protected init when the host credential store is available.
|
||||
*/
|
||||
class SavedDeviceCoreContractJvmSmokeTest {
|
||||
@@ -33,11 +35,19 @@ class SavedDeviceCoreContractJvmSmokeTest {
|
||||
}
|
||||
val network = CoreNetworkConfig(CoreRelayMode.LOCAL_ONLY, emptyList())
|
||||
|
||||
val first = VnidropCore.initializeWithNetworkConfig(
|
||||
coreDir.toString(),
|
||||
sink,
|
||||
network,
|
||||
)
|
||||
val first = try {
|
||||
VnidropCore.initializeWithNetworkConfig(
|
||||
coreDir.toString(),
|
||||
sink,
|
||||
network,
|
||||
)
|
||||
} catch (_: VnidropException.SecureStorageUnavailable) {
|
||||
coreDir.toFile().deleteRecursively()
|
||||
return
|
||||
} catch (_: VnidropException.SecureStorageLocked) {
|
||||
coreDir.toFile().deleteRecursively()
|
||||
return
|
||||
}
|
||||
val endpointId = try {
|
||||
val id = first.status().endpointId
|
||||
assertTrue(id.isNotBlank())
|
||||
@@ -66,14 +76,19 @@ class SavedDeviceCoreContractJvmSmokeTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun experimentalProtectedInitWorksWhenHostSecretStoreIsAvailable() {
|
||||
val coreDir = Files.createTempDirectory("vnidrop-android-contract-experimental")
|
||||
fun productionProtectedInitWorksWhenHostSecretStoreIsAvailable() {
|
||||
val capabilities: SavedDeviceCapabilities = savedDeviceCapabilities()
|
||||
assertTrue(capabilities.domainContractVersion >= 1u)
|
||||
assertTrue(capabilities.relationshipProtocolVersion >= 1u)
|
||||
assertTrue(capabilities.targetedTransferProtocolVersion >= 1u)
|
||||
|
||||
val coreDir = Files.createTempDirectory("vnidrop-android-contract-production")
|
||||
val sink = object : CoreEventSink {
|
||||
override fun onEvent(event: CoreEvent) = Unit
|
||||
}
|
||||
val network = CoreNetworkConfig(CoreRelayMode.LOCAL_ONLY, emptyList())
|
||||
val core = try {
|
||||
VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
coreDir.toString(),
|
||||
sink,
|
||||
defaultCoreLimits(),
|
||||
|
||||
@@ -10,16 +10,20 @@ import uniffi.vnidrop.CoreEventSink
|
||||
import uniffi.vnidrop.VnidropCore
|
||||
import uniffi.vnidrop.defaultCoreLimits
|
||||
import uniffi.vnidrop.defaultCoreNetworkConfig
|
||||
import uniffi.vnidrop.savedDeviceCapabilities
|
||||
|
||||
/**
|
||||
* Linux host harness for the experimental saved-device core contract (ticket 17).
|
||||
* Linux host harness for the saved-device core contract.
|
||||
*
|
||||
* Identity restart against Secret Service runs only on Linux (CI host). Binding
|
||||
* hygiene assertions run on every JVM so macOS/Windows desktop checks stay useful.
|
||||
*/
|
||||
class SavedDeviceCoreContractLinuxTest {
|
||||
@Test
|
||||
fun experimentalInitRestartsSameIdentityOnLinux() {
|
||||
fun productionInitRestartsSameIdentityOnLinux() {
|
||||
val capabilities = savedDeviceCapabilities()
|
||||
assertTrue(capabilities.domainContractVersion >= 1u)
|
||||
|
||||
val isLinux = System.getProperty("os.name").orEmpty().lowercase().contains("linux")
|
||||
if (!isLinux) {
|
||||
return
|
||||
@@ -29,7 +33,7 @@ class SavedDeviceCoreContractLinuxTest {
|
||||
val sink = object : CoreEventSink {
|
||||
override fun onEvent(event: CoreEvent) = Unit
|
||||
}
|
||||
val first = VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
val first = VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir = coreDir.toString(),
|
||||
eventSink = sink,
|
||||
limits = defaultCoreLimits(),
|
||||
@@ -44,7 +48,7 @@ class SavedDeviceCoreContractLinuxTest {
|
||||
first.shutdown()
|
||||
}
|
||||
|
||||
val second = VnidropCore.initializeWithExperimentalSavedDevices(
|
||||
val second = VnidropCore.initializeWithLimitsAndNetworkConfig(
|
||||
appDataDir = coreDir.toString(),
|
||||
eventSink = sink,
|
||||
limits = defaultCoreLimits(),
|
||||
@@ -89,8 +93,8 @@ class SavedDeviceCoreContractLinuxTest {
|
||||
)
|
||||
}
|
||||
assertTrue(
|
||||
companionMethods.contains("initializeWithExperimentalSavedDevices"),
|
||||
"experimental saved-device init must be on the public binding",
|
||||
companionMethods.contains("initializeWithLimitsAndNetworkConfig"),
|
||||
"production protected init must be on the public binding",
|
||||
)
|
||||
assertTrue(
|
||||
instanceMethods.contains("setSavedDeviceLabel"),
|
||||
|
||||
Reference in New Issue
Block a user