in components/tabs/src/sync/bridge.rs [142:281]
fn test_sync_via_bridge() {
env_logger::try_init().ok();
let store = Arc::new(TabsStore::new_with_mem_path("test-bridge_incoming"));
// Set some local tabs for our device.
let my_tabs = vec![
RemoteTab {
title: "my first tab".to_string(),
url_history: vec!["http://1.com".to_string()],
last_used: 2,
..Default::default()
},
RemoteTab {
title: "my second tab".to_string(),
url_history: vec!["http://2.com".to_string()],
last_used: 1,
..Default::default()
},
];
store.set_local_tabs(my_tabs.clone());
let bridge = store.bridged_engine();
let client_data = ClientData {
local_client_id: "my-device".to_string(),
recent_clients: HashMap::from([
(
"my-device".to_string(),
RemoteClient {
fxa_device_id: None,
device_name: "my device".to_string(),
device_type: sync15::DeviceType::Unknown,
},
),
(
"device-no-tabs".to_string(),
RemoteClient {
fxa_device_id: None,
device_name: "device with no tabs".to_string(),
device_type: DeviceType::Unknown,
},
),
(
"device-with-a-tab".to_string(),
RemoteClient {
fxa_device_id: None,
device_name: "device with a tab".to_string(),
device_type: DeviceType::Unknown,
},
),
]),
};
bridge
.prepare_for_sync(&serde_json::to_string(&client_data).unwrap())
.expect("should work");
let records = vec![
// my-device should be ignored by sync - here it is acting as what our engine last
// wrote, but the actual tabs in our store we set above are what should be used.
json!({
"id": "my-device",
"clientName": "my device",
"tabs": [{
"title": "the title",
"urlHistory": [
"https://mozilla.org/"
],
"icon": "https://mozilla.org/icon",
"lastUsed": 1643764207
}]
}),
json!({
"id": "device-no-tabs",
"clientName": "device with no tabs",
"tabs": [],
}),
json!({
"id": "device-with-a-tab",
"clientName": "device with a tab",
"tabs": [{
"title": "the title",
"urlHistory": [
"https://mozilla.org/"
],
"icon": "https://mozilla.org/icon",
"lastUsed": 1643764207
}]
}),
// This has the main payload as OK but the tabs part invalid.
json!({
"id": "device-with-invalid-tab",
"clientName": "device with a tab",
"tabs": [{
"foo": "bar",
}]
}),
// We want this to be a valid payload but an invalid tab - so it needs an ID.
json!({
"id": "invalid-tab",
"foo": "bar"
}),
];
let mut incoming = Vec::new();
for record in records {
// Annoyingly we can't use `IncomingEnvelope` directly as it intentionally doesn't
// support Serialize - so need to use explicit json.
let envelope = json!({
"id": record.get("id"),
"modified": 0,
"payload": serde_json::to_string(&record).unwrap(),
});
incoming.push(serde_json::to_string(&envelope).unwrap());
}
bridge.store_incoming(incoming).expect("should store");
let out = bridge.apply().expect("should apply");
assert_eq!(out.len(), 1);
let ours = serde_json::from_str::<serde_json::Value>(&out[0]).unwrap();
// As above, can't use `OutgoingEnvelope` as it doesn't Deserialize.
// First, convert my_tabs from the local `RemoteTab` to the Sync specific `TabsRecord`
let expected_tabs: Vec<TabsRecordTab> =
my_tabs.into_iter().map(|t| t.to_record_tab()).collect();
let expected = json!({
"id": "my-device".to_string(),
"payload": json!({
"id": "my-device".to_string(),
"clientName": "my device",
"tabs": serde_json::to_value(expected_tabs).unwrap(),
}).to_string(),
"ttl": TABS_CLIENT_TTL,
});
assert_eq!(ours, expected);
bridge.set_uploaded(1234, vec![]).unwrap();
assert_eq!(bridge.last_sync().unwrap(), 1234);
}