1use crate::sync::engine::TabsEngine;
6use crate::TabsStore;
7use std::sync::Arc;
8
9impl TabsStore {
10 pub fn bridged_engine(self: Arc<Self>) -> Arc<TabsBridgedEngine> {
12 let engine = TabsEngine::new(self);
13 Arc::new(TabsBridgedEngine::new(Box::new(engine)))
14 }
15}
16
17sync15::uniffi_bridged_engine!(TabsBridgedEngine);
22
23#[cfg(test)]
24mod tests {
25 use super::*;
26 use crate::storage::{RemoteTab, TABS_CLIENT_TTL};
27 use crate::sync::record::TabsRecordTab;
28 use serde_json::json;
29 use std::collections::HashMap;
30 use sync15::{ClientData, DeviceType, RemoteClient};
31
32 #[test]
34 fn test_sync_via_bridge() {
35 error_support::init_for_tests();
36
37 let store = Arc::new(TabsStore::new_with_mem_path("test-bridge_incoming"));
38
39 let my_tabs = vec![
41 RemoteTab {
42 title: "my first tab".to_string(),
43 url_history: vec!["http://1.com".to_string()],
44 last_used: 2,
45 ..Default::default()
46 },
47 RemoteTab {
48 title: "my second tab".to_string(),
49 url_history: vec!["http://2.com".to_string()],
50 last_used: 1,
51 ..Default::default()
52 },
53 ];
54 store.set_local_tabs(my_tabs.clone());
55
56 let bridge = store.bridged_engine();
57
58 let client_data = ClientData {
59 local_client_id: "my-device".to_string(),
60 recent_clients: HashMap::from([
61 (
62 "my-device".to_string(),
63 RemoteClient {
64 fxa_device_id: None,
65 device_name: "my device".to_string(),
66 device_type: sync15::DeviceType::Unknown,
67 },
68 ),
69 (
70 "device-no-tabs".to_string(),
71 RemoteClient {
72 fxa_device_id: None,
73 device_name: "device with no tabs".to_string(),
74 device_type: DeviceType::Unknown,
75 },
76 ),
77 (
78 "device-with-a-tab".to_string(),
79 RemoteClient {
80 fxa_device_id: None,
81 device_name: "device with a tab".to_string(),
82 device_type: DeviceType::Unknown,
83 },
84 ),
85 ]),
86 };
87 bridge
88 .set_clients(&serde_json::to_string(&client_data).unwrap())
89 .expect("should work");
90
91 let records = vec![
92 json!({
95 "id": "my-device",
96 "clientName": "my device",
97 "tabs": [{
98 "title": "the title",
99 "urlHistory": [
100 "https://mozilla.org/"
101 ],
102 "icon": "https://mozilla.org/icon",
103 "lastUsed": 1643764207
104 }]
105 }),
106 json!({
107 "id": "device-no-tabs",
108 "clientName": "device with no tabs",
109 "tabs": [],
110 }),
111 json!({
112 "id": "device-with-a-tab",
113 "clientName": "device with a tab",
114 "tabs": [{
115 "title": "the title",
116 "urlHistory": [
117 "https://mozilla.org/"
118 ],
119 "icon": "https://mozilla.org/icon",
120 "lastUsed": 1643764207
121 }]
122 }),
123 json!({
125 "id": "device-with-invalid-tab",
126 "clientName": "device with a tab",
127 "tabs": [{
128 "foo": "bar",
129 }]
130 }),
131 json!({
133 "id": "invalid-tab",
134 "foo": "bar"
135 }),
136 ];
137
138 let mut incoming = Vec::new();
139 for record in records {
140 let envelope = json!({
143 "id": record.get("id"),
144 "modified": 0,
145 "payload": serde_json::to_string(&record).unwrap(),
146 });
147 incoming.push(serde_json::to_string(&envelope).unwrap());
148 }
149
150 bridge.store_incoming(incoming).expect("should store");
151
152 let out = bridge.apply(0).expect("should apply");
154
155 assert_eq!(out.len(), 1);
156 let ours = serde_json::from_str::<serde_json::Value>(&out[0]).unwrap();
157 let expected_tabs: Vec<TabsRecordTab> = my_tabs.into_iter().map(Into::into).collect();
160 let expected = json!({
161 "id": "my-device".to_string(),
162 "payload": json!({
163 "id": "my-device".to_string(),
164 "clientName": "my device",
165 "tabs": serde_json::to_value(expected_tabs).unwrap(),
166 }).to_string(),
167 "ttl": TABS_CLIENT_TTL,
168 });
169
170 assert_eq!(ours, expected);
171 bridge.set_uploaded(1234, vec![]).unwrap();
172 assert_eq!(bridge.last_sync().unwrap(), 1234);
173 }
174
175 #[test]
176 fn test_sync_meta() {
177 error_support::init_for_tests();
178
179 let store = Arc::new(TabsStore::new_with_mem_path("test-meta"));
180 let bridge = store.bridged_engine();
181
182 assert_eq!(bridge.last_sync().unwrap(), 0);
184 bridge.set_uploaded(3, vec![]).unwrap();
185 assert_eq!(bridge.last_sync().unwrap(), 3);
186
187 assert!(bridge.sync_id().unwrap().is_none());
188
189 bridge.ensure_current_sync_id("some_guid").unwrap();
190 assert_eq!(bridge.sync_id().unwrap(), Some("some_guid".to_string()));
191 assert_eq!(bridge.last_sync().unwrap(), 0);
193 bridge.set_uploaded(3, vec![]).unwrap();
195
196 bridge.reset_sync_id().unwrap();
197 assert_ne!(bridge.sync_id().unwrap(), Some("some_guid".to_string()));
199 assert_eq!(bridge.last_sync().unwrap(), 0);
201 bridge.set_uploaded(3, vec![]).unwrap();
203
204 bridge.reset().unwrap();
206 assert_eq!(bridge.last_sync().unwrap(), 0);
207 assert!(bridge.sync_id().unwrap().is_none());
208 }
209}