mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-14 14:19:57 +02:00
fix(saved-devices): preserve targeted transfer direction
This commit is contained in:
@@ -139,7 +139,14 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
|||||||
(String, CoreEventSink, CoreLimits, CoreNetworkConfig) throws -> VnidropCore
|
(String, CoreEventSink, CoreLimits, CoreNetworkConfig) throws -> VnidropCore
|
||||||
) = VnidropCore.resetUnrecoverableIdentityWithLimitsAndNetworkConfig
|
) = VnidropCore.resetUnrecoverableIdentityWithLimitsAndNetworkConfig
|
||||||
let capabilities: SavedDeviceCapabilities = savedDeviceCapabilities()
|
let capabilities: SavedDeviceCapabilities = savedDeviceCapabilities()
|
||||||
|
let role: TargetedTransferRole = .sender
|
||||||
XCTAssertGreaterThanOrEqual(capabilities.domainContractVersion, 1)
|
XCTAssertGreaterThanOrEqual(capabilities.domainContractVersion, 1)
|
||||||
|
switch role {
|
||||||
|
case .sender:
|
||||||
|
break
|
||||||
|
case .receiver:
|
||||||
|
XCTFail("targeted-transfer role binding decoded the wrong case")
|
||||||
|
}
|
||||||
XCTAssertNotNil(defaultCoreLimits().maxSavedDevices)
|
XCTAssertNotNil(defaultCoreLimits().maxSavedDevices)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -172,6 +179,8 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
|||||||
XCTAssertTrue(source.contains("resetUnrecoverableIdentityWithLimitsAndNetworkConfig"))
|
XCTAssertTrue(source.contains("resetUnrecoverableIdentityWithLimitsAndNetworkConfig"))
|
||||||
XCTAssertTrue(source.contains("public struct SavedDeviceCapabilities"))
|
XCTAssertTrue(source.contains("public struct SavedDeviceCapabilities"))
|
||||||
XCTAssertTrue(source.contains("public func savedDeviceCapabilities()"))
|
XCTAssertTrue(source.contains("public func savedDeviceCapabilities()"))
|
||||||
|
XCTAssertTrue(source.contains("public var role: TargetedTransferRole"))
|
||||||
|
XCTAssertTrue(source.contains("public enum TargetedTransferRole"))
|
||||||
XCTAssertTrue(source.contains("setSavedDeviceLabel"))
|
XCTAssertTrue(source.contains("setSavedDeviceLabel"))
|
||||||
XCTAssertTrue(source.contains("listSavedDevices"))
|
XCTAssertTrue(source.contains("listSavedDevices"))
|
||||||
XCTAssertTrue(source.contains("revision"))
|
XCTAssertTrue(source.contains("revision"))
|
||||||
|
|||||||
@@ -88,10 +88,18 @@ pub enum TargetedTransferState {
|
|||||||
Deleted,
|
Deleted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This installation's immutable role in a Targeted transfer.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
|
||||||
|
pub enum TargetedTransferRole {
|
||||||
|
Sender,
|
||||||
|
Receiver,
|
||||||
|
}
|
||||||
|
|
||||||
/// Immutable recipient-bound transfer snapshot, separate from an ordinary share.
|
/// Immutable recipient-bound transfer snapshot, separate from an ordinary share.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)]
|
||||||
pub struct TargetedTransfer {
|
pub struct TargetedTransfer {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
pub role: TargetedTransferRole,
|
||||||
pub sender_endpoint_id: String,
|
pub sender_endpoint_id: String,
|
||||||
pub receiver_endpoint_id: String,
|
pub receiver_endpoint_id: String,
|
||||||
pub manifest_id: String,
|
pub manifest_id: String,
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ pub use api::{
|
|||||||
PairingEligibilitySummary, PendingTargetedOffer, PublishedOutput, ReceiveOutputSink,
|
PairingEligibilitySummary, PendingTargetedOffer, PublishedOutput, ReceiveOutputSink,
|
||||||
ReceiveOutputSinkV2, ReceivedArtifact, ReceivedLocatorKind, ReceiverRequest, RuntimeStatus,
|
ReceiveOutputSinkV2, ReceivedArtifact, ReceivedLocatorKind, ReceiverRequest, RuntimeStatus,
|
||||||
SavedDevice, SavedDeviceCapabilities, ShareMetadataInput, ShareResult, ShareSource, SourceKind,
|
SavedDevice, SavedDeviceCapabilities, ShareMetadataInput, ShareResult, ShareSource, SourceKind,
|
||||||
StoredTransfer, TargetedOfferResponse, TargetedTransfer, TargetedTransferState,
|
StoredTransfer, TargetedOfferResponse, TargetedTransfer, TargetedTransferRole,
|
||||||
TicketInspection, TransferAccessMode, TransferMetadata,
|
TargetedTransferState, TicketInspection, TransferAccessMode, TransferMetadata,
|
||||||
};
|
};
|
||||||
pub use error::VnidropError;
|
pub use error::VnidropError;
|
||||||
pub use runtime::VnidropCore;
|
pub use runtime::VnidropCore;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ mod state;
|
|||||||
mod store;
|
mod store;
|
||||||
mod store_outbox;
|
mod store_outbox;
|
||||||
|
|
||||||
|
pub(crate) use crate::api::TargetedTransferRole;
|
||||||
pub(crate) use auth::{
|
pub(crate) use auth::{
|
||||||
auth_secret_material, reconstruct_authorization, TargetedAuthorization,
|
auth_secret_material, reconstruct_authorization, TargetedAuthorization,
|
||||||
TargetedAuthorizationDraft,
|
TargetedAuthorizationDraft,
|
||||||
@@ -18,6 +19,4 @@ pub(crate) use auth::{
|
|||||||
pub(crate) use inbox::{RespondError, TargetedOfferInbox};
|
pub(crate) use inbox::{RespondError, TargetedOfferInbox};
|
||||||
pub(crate) use protocol::TargetedTransferProtocol;
|
pub(crate) use protocol::TargetedTransferProtocol;
|
||||||
pub(crate) use schema::ensure_schema;
|
pub(crate) use schema::ensure_schema;
|
||||||
pub(crate) use store::{
|
pub(crate) use store::{state_as_str, TargetedTransferRow, TargetedTransferStore};
|
||||||
state_as_str, TargetedTransferRole, TargetedTransferRow, TargetedTransferStore,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
use sqlx::{Row, SqlitePool};
|
use sqlx::{Row, SqlitePool};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{TargetedTransfer, TargetedTransferState},
|
api::{TargetedTransfer, TargetedTransferRole, TargetedTransferState},
|
||||||
error::VnidropError,
|
error::VnidropError,
|
||||||
util::now_ms,
|
util::now_ms,
|
||||||
};
|
};
|
||||||
@@ -430,7 +430,7 @@ impl TargetedTransferStore {
|
|||||||
pub(crate) async fn get(&self, id: &str) -> Result<Option<TargetedTransfer>, VnidropError> {
|
pub(crate) async fn get(&self, id: &str) -> Result<Option<TargetedTransfer>, VnidropError> {
|
||||||
let row = sqlx::query(
|
let row = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, sender_endpoint_id, receiver_endpoint_id, manifest_id, transfer_name,
|
SELECT id, role, sender_endpoint_id, receiver_endpoint_id, manifest_id, transfer_name,
|
||||||
file_count, total_size, verified_bytes, state, created_at, updated_at
|
file_count, total_size, verified_bytes, state, created_at, updated_at
|
||||||
FROM targeted_transfers WHERE id = ?1
|
FROM targeted_transfers WHERE id = ?1
|
||||||
"#,
|
"#,
|
||||||
@@ -465,7 +465,7 @@ impl TargetedTransferStore {
|
|||||||
pub(crate) async fn list(&self) -> Result<Vec<TargetedTransfer>, VnidropError> {
|
pub(crate) async fn list(&self) -> Result<Vec<TargetedTransfer>, VnidropError> {
|
||||||
let rows = sqlx::query(
|
let rows = sqlx::query(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, sender_endpoint_id, receiver_endpoint_id, manifest_id, transfer_name,
|
SELECT id, role, sender_endpoint_id, receiver_endpoint_id, manifest_id, transfer_name,
|
||||||
file_count, total_size, verified_bytes, state, created_at, updated_at
|
file_count, total_size, verified_bytes, state, created_at, updated_at
|
||||||
FROM targeted_transfers
|
FROM targeted_transfers
|
||||||
ORDER BY updated_at DESC
|
ORDER BY updated_at DESC
|
||||||
@@ -692,12 +692,6 @@ impl TargetedTransferStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub(crate) enum TargetedTransferRole {
|
|
||||||
Sender,
|
|
||||||
Receiver,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct TargetedTransferRow {
|
pub(crate) struct TargetedTransferRow {
|
||||||
pub(crate) id: String,
|
pub(crate) id: String,
|
||||||
@@ -721,6 +715,7 @@ pub(crate) struct TargetedTransferRow {
|
|||||||
fn row_to_transfer(row: sqlx::sqlite::SqliteRow) -> Result<TargetedTransfer, VnidropError> {
|
fn row_to_transfer(row: sqlx::sqlite::SqliteRow) -> Result<TargetedTransfer, VnidropError> {
|
||||||
Ok(TargetedTransfer {
|
Ok(TargetedTransfer {
|
||||||
id: row.get("id"),
|
id: row.get("id"),
|
||||||
|
role: parse_role(&row.get::<String, _>("role"))?,
|
||||||
sender_endpoint_id: row.get("sender_endpoint_id"),
|
sender_endpoint_id: row.get("sender_endpoint_id"),
|
||||||
receiver_endpoint_id: row.get("receiver_endpoint_id"),
|
receiver_endpoint_id: row.get("receiver_endpoint_id"),
|
||||||
manifest_id: row.get("manifest_id"),
|
manifest_id: row.get("manifest_id"),
|
||||||
|
|||||||
@@ -856,6 +856,11 @@ fn apple_public_bindings_omit_raw_secrets_and_generic_mutation() {
|
|||||||
&& source.contains("public func savedDeviceCapabilities()"),
|
&& source.contains("public func savedDeviceCapabilities()"),
|
||||||
"Swift bindings must expose production saved-device capabilities"
|
"Swift bindings must expose production saved-device capabilities"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
source.contains("public var role: TargetedTransferRole")
|
||||||
|
&& source.contains("public enum TargetedTransferRole"),
|
||||||
|
"Swift targeted-transfer snapshots must expose their persisted role"
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
source.contains("setSavedDeviceLabel"),
|
source.contains("setSavedDeviceLabel"),
|
||||||
"Swift bindings must expose saved-device rename"
|
"Swift bindings must expose saved-device rename"
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ use crate::{
|
|||||||
secure_secret::{FaultInjectingSecretStore, ReferenceStoreFailure},
|
secure_secret::{FaultInjectingSecretStore, ReferenceStoreFailure},
|
||||||
CoreEvent, CoreEventSink, CoreNetworkConfig, CoreRelayMode, DeviceRelationshipState,
|
CoreEvent, CoreEventSink, CoreNetworkConfig, CoreRelayMode, DeviceRelationshipState,
|
||||||
PendingTargetedOffer, PublishedOutput, ReceiveOutputSink, ReceiveOutputSinkV2,
|
PendingTargetedOffer, PublishedOutput, ReceiveOutputSink, ReceiveOutputSinkV2,
|
||||||
ReceivedLocatorKind, ShareMetadataInput, ShareSource, SourceKind, TargetedTransferState,
|
ReceivedLocatorKind, ShareMetadataInput, ShareSource, SourceKind, TargetedTransferRole,
|
||||||
TransferAccessMode, VnidropCore, VnidropError,
|
TargetedTransferState, TransferAccessMode, VnidropCore, VnidropError,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecordingSink {
|
struct RecordingSink {
|
||||||
@@ -381,6 +381,7 @@ fn identity_reset_cancels_targeted_authorization_bound_to_the_lost_endpoint() {
|
|||||||
.get_targeted_transfer(transfer.id.clone())
|
.get_targeted_transfer(transfer.id.clone())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(snapshot.role, TargetedTransferRole::Sender);
|
||||||
assert_eq!(snapshot.state, TargetedTransferState::Cancelled);
|
assert_eq!(snapshot.state, TargetedTransferState::Cancelled);
|
||||||
assert!(recovered
|
assert!(recovered
|
||||||
.targeted_blob_ticket_for_test(transfer.id)
|
.targeted_blob_ticket_for_test(transfer.id)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use support::TestNode;
|
|||||||
use vnidrop::{
|
use vnidrop::{
|
||||||
saved_device_capabilities, DeviceRelationship, DeviceRelationshipState, SavedDevice,
|
saved_device_capabilities, DeviceRelationship, DeviceRelationshipState, SavedDevice,
|
||||||
SavedDeviceCapabilities, ShareMetadataInput, ShareSource, SourceKind, TargetedTransfer,
|
SavedDeviceCapabilities, ShareMetadataInput, ShareSource, SourceKind, TargetedTransfer,
|
||||||
TargetedTransferState, TransferAccessMode, VnidropError,
|
TargetedTransferRole, TargetedTransferState, TransferAccessMode, VnidropError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -38,6 +38,7 @@ fn saved_devices_relationships_and_targeted_transfers_are_distinct_contracts() {
|
|||||||
};
|
};
|
||||||
let transfer = TargetedTransfer {
|
let transfer = TargetedTransfer {
|
||||||
id: "targeted-transfer-id".to_string(),
|
id: "targeted-transfer-id".to_string(),
|
||||||
|
role: TargetedTransferRole::Sender,
|
||||||
sender_endpoint_id: "sender-endpoint".to_string(),
|
sender_endpoint_id: "sender-endpoint".to_string(),
|
||||||
receiver_endpoint_id: device.endpoint_id.clone(),
|
receiver_endpoint_id: device.endpoint_id.clone(),
|
||||||
manifest_id: "immutable-manifest-id".to_string(),
|
manifest_id: "immutable-manifest-id".to_string(),
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ class SavedDeviceCoreContractBindingHygieneTest {
|
|||||||
assertTrue(
|
assertTrue(
|
||||||
source.contains("public expect fun `savedDeviceCapabilities`(): SavedDeviceCapabilities"),
|
source.contains("public expect fun `savedDeviceCapabilities`(): SavedDeviceCapabilities"),
|
||||||
)
|
)
|
||||||
|
assertTrue(source.contains("var `role`: TargetedTransferRole"))
|
||||||
|
assertTrue(source.contains("public enum class TargetedTransferRole"))
|
||||||
assertTrue(
|
assertTrue(
|
||||||
source.contains("SavedDevice"),
|
source.contains("SavedDevice"),
|
||||||
"SavedDevice model must remain on the public surface",
|
"SavedDevice model must remain on the public surface",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import uniffi.vnidrop.SourceKind
|
|||||||
import uniffi.vnidrop.StoredTransfer
|
import uniffi.vnidrop.StoredTransfer
|
||||||
import uniffi.vnidrop.TargetedOfferResponse
|
import uniffi.vnidrop.TargetedOfferResponse
|
||||||
import uniffi.vnidrop.TargetedTransfer
|
import uniffi.vnidrop.TargetedTransfer
|
||||||
|
import uniffi.vnidrop.TargetedTransferRole
|
||||||
import uniffi.vnidrop.TargetedTransferState
|
import uniffi.vnidrop.TargetedTransferState
|
||||||
import uniffi.vnidrop.TicketInspection
|
import uniffi.vnidrop.TicketInspection
|
||||||
import uniffi.vnidrop.TransferMetadata
|
import uniffi.vnidrop.TransferMetadata
|
||||||
@@ -643,6 +644,7 @@ private fun PendingTargetedOffer.toModel(): PendingTargetedOfferModel = PendingT
|
|||||||
|
|
||||||
private fun TargetedTransfer.toModel(): TargetedTransferModel = TargetedTransferModel(
|
private fun TargetedTransfer.toModel(): TargetedTransferModel = TargetedTransferModel(
|
||||||
id = id,
|
id = id,
|
||||||
|
role = role.toModel(),
|
||||||
senderEndpointId = senderEndpointId,
|
senderEndpointId = senderEndpointId,
|
||||||
receiverEndpointId = receiverEndpointId,
|
receiverEndpointId = receiverEndpointId,
|
||||||
manifestId = manifestId,
|
manifestId = manifestId,
|
||||||
@@ -655,6 +657,11 @@ private fun TargetedTransfer.toModel(): TargetedTransferModel = TargetedTransfer
|
|||||||
updatedAt = updatedAt,
|
updatedAt = updatedAt,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun TargetedTransferRole.toModel(): TargetedTransferRoleModel = when (this) {
|
||||||
|
TargetedTransferRole.SENDER -> TargetedTransferRoleModel.Sender
|
||||||
|
TargetedTransferRole.RECEIVER -> TargetedTransferRoleModel.Receiver
|
||||||
|
}
|
||||||
|
|
||||||
private fun TargetedTransferState.toModel(): TargetedTransferStateModel = when (this) {
|
private fun TargetedTransferState.toModel(): TargetedTransferStateModel = when (this) {
|
||||||
TargetedTransferState.PREPARING -> TargetedTransferStateModel.Preparing
|
TargetedTransferState.PREPARING -> TargetedTransferStateModel.Preparing
|
||||||
TargetedTransferState.OFFERING -> TargetedTransferStateModel.Offering
|
TargetedTransferState.OFFERING -> TargetedTransferStateModel.Offering
|
||||||
|
|||||||
@@ -68,8 +68,14 @@ enum class TargetedTransferStateModel {
|
|||||||
Deleted,
|
Deleted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class TargetedTransferRoleModel {
|
||||||
|
Sender,
|
||||||
|
Receiver,
|
||||||
|
}
|
||||||
|
|
||||||
data class TargetedTransferModel(
|
data class TargetedTransferModel(
|
||||||
val id: String,
|
val id: String,
|
||||||
|
val role: TargetedTransferRoleModel,
|
||||||
val senderEndpointId: String,
|
val senderEndpointId: String,
|
||||||
val receiverEndpointId: String,
|
val receiverEndpointId: String,
|
||||||
val manifestId: String,
|
val manifestId: String,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.vnidrop.app.core.ReceiveFolder
|
|||||||
import com.vnidrop.app.core.SavedDeviceModel
|
import com.vnidrop.app.core.SavedDeviceModel
|
||||||
import com.vnidrop.app.core.TargetedOfferResponseModel
|
import com.vnidrop.app.core.TargetedOfferResponseModel
|
||||||
import com.vnidrop.app.core.TargetedTransferModel
|
import com.vnidrop.app.core.TargetedTransferModel
|
||||||
|
import com.vnidrop.app.core.TargetedTransferRoleModel
|
||||||
import com.vnidrop.app.core.TargetedTransferStateModel
|
import com.vnidrop.app.core.TargetedTransferStateModel
|
||||||
import com.vnidrop.app.preferences.PreferencesRepository
|
import com.vnidrop.app.preferences.PreferencesRepository
|
||||||
import com.vnidrop.app.ui.feedback.UiMessage
|
import com.vnidrop.app.ui.feedback.UiMessage
|
||||||
@@ -365,8 +366,7 @@ class SavedDevicesViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun TargetedTransferModel.toExperienceItem(savedNames: Map<String, String>): SavedDeviceTransferItem {
|
private fun TargetedTransferModel.toExperienceItem(savedNames: Map<String, String>): SavedDeviceTransferItem {
|
||||||
val localEndpointId = repository.state.value.status?.endpointId
|
val outgoing = role == TargetedTransferRoleModel.Sender
|
||||||
val outgoing = senderEndpointId == localEndpointId
|
|
||||||
val peerEndpointId = if (outgoing) receiverEndpointId else senderEndpointId
|
val peerEndpointId = if (outgoing) receiverEndpointId else senderEndpointId
|
||||||
return SavedDeviceTransferItem(
|
return SavedDeviceTransferItem(
|
||||||
id = id,
|
id = id,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.vnidrop.app.core.ReceiveFolderKind
|
|||||||
import com.vnidrop.app.core.SavedDeviceModel
|
import com.vnidrop.app.core.SavedDeviceModel
|
||||||
import com.vnidrop.app.core.TargetedOfferResponseModel
|
import com.vnidrop.app.core.TargetedOfferResponseModel
|
||||||
import com.vnidrop.app.core.TargetedTransferModel
|
import com.vnidrop.app.core.TargetedTransferModel
|
||||||
|
import com.vnidrop.app.core.TargetedTransferRoleModel
|
||||||
import com.vnidrop.app.core.TargetedTransferStateModel
|
import com.vnidrop.app.core.TargetedTransferStateModel
|
||||||
import com.vnidrop.app.preferences.AppPreferences
|
import com.vnidrop.app.preferences.AppPreferences
|
||||||
import com.vnidrop.app.support.FakeCoreGateway
|
import com.vnidrop.app.support.FakeCoreGateway
|
||||||
@@ -49,7 +50,15 @@ class SavedDevicesViewModelTest {
|
|||||||
deviceRelationships = listOf(incoming("incoming"))
|
deviceRelationships = listOf(incoming("incoming"))
|
||||||
savedDevices = listOf(device("peer", "Office PC", "Authenticated PC"))
|
savedDevices = listOf(device("peer", "Office PC", "Authenticated PC"))
|
||||||
pendingTargetedOffers = listOf(offer("offer", "peer"))
|
pendingTargetedOffers = listOf(offer("offer", "peer"))
|
||||||
targetedTransfers = listOf(transfer("history", "local", "peer", TargetedTransferStateModel.AwaitingApproval))
|
targetedTransfers = listOf(
|
||||||
|
transfer(
|
||||||
|
"history",
|
||||||
|
TargetedTransferRoleModel.Sender,
|
||||||
|
"local",
|
||||||
|
"peer",
|
||||||
|
TargetedTransferStateModel.AwaitingApproval,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val viewModel = createViewModel(core)
|
val viewModel = createViewModel(core)
|
||||||
runCurrent()
|
runCurrent()
|
||||||
@@ -74,6 +83,29 @@ class SavedDevicesViewModelTest {
|
|||||||
assertEquals(false, state.loadFailed)
|
assertEquals(false, state.loadFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun persistedOutgoingTransferKeepsDirectionAfterIdentityReset() = runTest {
|
||||||
|
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
||||||
|
val core = initializedCore().apply {
|
||||||
|
targetedTransfers = listOf(
|
||||||
|
transfer(
|
||||||
|
id = "past-send",
|
||||||
|
role = TargetedTransferRoleModel.Sender,
|
||||||
|
sender = "retired-local-identity",
|
||||||
|
receiver = "peer",
|
||||||
|
state = TargetedTransferStateModel.Completed,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val viewModel = createViewModel(core)
|
||||||
|
runCurrent()
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
val transfer = viewModel.state.value.targetedTransfers.single()
|
||||||
|
assertEquals(SavedDeviceTransferDirection.Outgoing, transfer.direction)
|
||||||
|
assertEquals("peer", transfer.peerEndpointId)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun pairingPromptCommandsAndDismissalUseTheSameDurableLists() = runTest {
|
fun pairingPromptCommandsAndDismissalUseTheSameDurableLists() = runTest {
|
||||||
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
||||||
@@ -158,7 +190,15 @@ class SavedDevicesViewModelTest {
|
|||||||
fun interruptedTransferResumeUsesThePlatformSinkWhenAvailable() = runTest {
|
fun interruptedTransferResumeUsesThePlatformSinkWhenAvailable() = runTest {
|
||||||
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
||||||
val core = initializedCore().apply {
|
val core = initializedCore().apply {
|
||||||
targetedTransfers = listOf(transfer("resume-sink", "peer", "local", TargetedTransferStateModel.Interrupted))
|
targetedTransfers = listOf(
|
||||||
|
transfer(
|
||||||
|
"resume-sink",
|
||||||
|
TargetedTransferRoleModel.Receiver,
|
||||||
|
"peer",
|
||||||
|
"local",
|
||||||
|
TargetedTransferStateModel.Interrupted,
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val viewModel = createViewModel(
|
val viewModel = createViewModel(
|
||||||
core,
|
core,
|
||||||
@@ -180,9 +220,9 @@ class SavedDevicesViewModelTest {
|
|||||||
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
Dispatchers.setMain(StandardTestDispatcher(testScheduler))
|
||||||
val core = initializedCore().apply {
|
val core = initializedCore().apply {
|
||||||
targetedTransfers = listOf(
|
targetedTransfers = listOf(
|
||||||
transfer("resume", "peer", "local", TargetedTransferStateModel.Interrupted),
|
transfer("resume", TargetedTransferRoleModel.Receiver, "peer", "local", TargetedTransferStateModel.Interrupted),
|
||||||
transfer("cancel", "local", "peer", TargetedTransferStateModel.AwaitingApproval),
|
transfer("cancel", TargetedTransferRoleModel.Sender, "local", "peer", TargetedTransferStateModel.AwaitingApproval),
|
||||||
transfer("delete", "peer", "local", TargetedTransferStateModel.Completed),
|
transfer("delete", TargetedTransferRoleModel.Receiver, "peer", "local", TargetedTransferStateModel.Completed),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val viewModel = createViewModel(core)
|
val viewModel = createViewModel(core)
|
||||||
@@ -336,11 +376,13 @@ class SavedDevicesViewModelTest {
|
|||||||
|
|
||||||
private fun transfer(
|
private fun transfer(
|
||||||
id: String,
|
id: String,
|
||||||
|
role: TargetedTransferRoleModel,
|
||||||
sender: String,
|
sender: String,
|
||||||
receiver: String,
|
receiver: String,
|
||||||
state: TargetedTransferStateModel,
|
state: TargetedTransferStateModel,
|
||||||
) = TargetedTransferModel(
|
) = TargetedTransferModel(
|
||||||
id = id,
|
id = id,
|
||||||
|
role = role,
|
||||||
senderEndpointId = sender,
|
senderEndpointId = sender,
|
||||||
receiverEndpointId = receiver,
|
receiverEndpointId = receiver,
|
||||||
manifestId = "manifest-$id",
|
manifestId = "manifest-$id",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.vnidrop.app.core.SavedDeviceModel
|
|||||||
import com.vnidrop.app.core.Share
|
import com.vnidrop.app.core.Share
|
||||||
import com.vnidrop.app.core.ShareAccessPolicy
|
import com.vnidrop.app.core.ShareAccessPolicy
|
||||||
import com.vnidrop.app.core.TargetedTransferModel
|
import com.vnidrop.app.core.TargetedTransferModel
|
||||||
|
import com.vnidrop.app.core.TargetedTransferRoleModel
|
||||||
import com.vnidrop.app.core.TargetedTransferStateModel
|
import com.vnidrop.app.core.TargetedTransferStateModel
|
||||||
import com.vnidrop.app.support.FakeCoreGateway
|
import com.vnidrop.app.support.FakeCoreGateway
|
||||||
import com.vnidrop.app.support.FakeFilePreviewRepository
|
import com.vnidrop.app.support.FakeFilePreviewRepository
|
||||||
@@ -235,6 +236,7 @@ class TransferDraftViewModelTest {
|
|||||||
|
|
||||||
private fun targeted(id: String, peerId: String) = TargetedTransferModel(
|
private fun targeted(id: String, peerId: String) = TargetedTransferModel(
|
||||||
id = id,
|
id = id,
|
||||||
|
role = TargetedTransferRoleModel.Sender,
|
||||||
senderEndpointId = "me",
|
senderEndpointId = "me",
|
||||||
receiverEndpointId = peerId,
|
receiverEndpointId = peerId,
|
||||||
manifestId = "manifest",
|
manifestId = "manifest",
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ class SavedDeviceCoreContractBindingHygieneTest {
|
|||||||
assertTrue(
|
assertTrue(
|
||||||
source.contains("public expect fun `savedDeviceCapabilities`(): SavedDeviceCapabilities"),
|
source.contains("public expect fun `savedDeviceCapabilities`(): SavedDeviceCapabilities"),
|
||||||
)
|
)
|
||||||
|
assertTrue(source.contains("var `role`: TargetedTransferRole"))
|
||||||
|
assertTrue(source.contains("public enum class TargetedTransferRole"))
|
||||||
assertTrue(
|
assertTrue(
|
||||||
source.contains("SavedDevice"),
|
source.contains("SavedDevice"),
|
||||||
"SavedDevice model must remain on the public surface",
|
"SavedDevice model must remain on the public surface",
|
||||||
|
|||||||
Reference in New Issue
Block a user