1use super::managers::configuration_update_manager::ConfigurationUpdateManager;
4use super::managers::db_manager::DbManager;
5use super::managers::filter_group_manager::FilterGroupManager;
6use super::managers::filter_manager::FilterManager;
7use super::managers::filter_metadata_grabber::FilterMetadataGrabber;
8use super::managers::filter_tag_manager::FilterTagManager;
9use super::managers::filter_update_manager::FilterUpdateManager;
10use super::managers::rules_list_manager::RulesListManager;
11use super::managers::streaming_rules_manager::StreamingRulesManager;
12use super::models::{
13 configuration::Configuration, FilterId, FilterListMetadata, FilterListMetadataWithBody,
14 FullFilterList, PullMetadataResult, UpdateResult,
15};
16use crate::manager::models::configuration::request_proxy_mode::RequestProxyMode;
17use crate::manager::models::configuration::Locale;
18use crate::manager::models::disabled_rules_raw::DisabledRulesRaw;
19use crate::manager::models::filter_group::FilterGroup;
20use crate::manager::models::filter_list_rules::FilterListRules;
21use crate::manager::models::filter_list_rules_raw::FilterListRulesRaw;
22use crate::manager::models::filter_tag::FilterTag;
23use crate::manager::models::rules_count_by_filter::RulesCountByFilter;
24use crate::storage::sql_generators::operator::SQLOperator;
25use crate::storage::DbConnectionManager;
26use crate::{
27 manager::FilterListManager, ActiveRulesInfo, ActiveRulesInfoRaw, FLMError, FLMResult,
28 StoredFilterMetadata,
29};
30use std::path::Path;
31
32pub struct FilterListManagerImpl {
34 configuration: Configuration,
35 pub(crate) connection_manager: DbConnectionManager,
36}
37
38impl FilterListManager for FilterListManagerImpl {
39 fn new(mut configuration: Configuration) -> FLMResult<Box<Self>> {
40 if configuration.app_name.is_empty() {
41 return Err(FLMError::InvalidConfiguration("app_name is empty"));
42 }
43 if configuration.version.is_empty() {
44 return Err(FLMError::InvalidConfiguration("version is empty"));
45 }
46
47 configuration.normalized();
48
49 let connection_manager = DbConnectionManager::from_configuration(&configuration)?;
50 if configuration.auto_lift_up_database {
51 unsafe { connection_manager.lift_up_database()? }
52 }
53
54 Ok(Box::new(Self {
55 configuration,
56 connection_manager,
57 }))
58 }
59
60 fn install_custom_filter_list(
61 &self,
62 download_url: String,
63 is_trusted: bool,
64 title: Option<String>,
65 description: Option<String>,
66 ) -> FLMResult<FullFilterList> {
67 FilterManager::new().install_custom_filter_list_from_url(
68 &self.connection_manager,
69 &self.configuration,
70 download_url,
71 is_trusted,
72 title,
73 description,
74 )
75 }
76
77 fn fetch_filter_list_metadata(&self, url: String) -> FLMResult<FilterListMetadata> {
78 FilterMetadataGrabber::new().fetch_filter_list_metadata(&self.configuration, url)
79 }
80
81 fn fetch_filter_list_metadata_with_body(
82 &self,
83 url: String,
84 ) -> FLMResult<FilterListMetadataWithBody> {
85 FilterMetadataGrabber::new().fetch_filter_list_metadata_with_body(&self.configuration, url)
86 }
87
88 fn enable_filter_lists(&self, ids: Vec<FilterId>, is_enabled: bool) -> FLMResult<usize> {
89 FilterManager::new().enable_filter_lists(&self.connection_manager, ids, is_enabled)
90 }
91
92 fn install_filter_lists(&self, ids: Vec<FilterId>, is_installed: bool) -> FLMResult<usize> {
93 FilterManager::new().install_filter_lists(&self.connection_manager, ids, is_installed)
94 }
95
96 fn delete_custom_filter_lists(&self, ids: Vec<FilterId>) -> FLMResult<usize> {
97 FilterManager::new().delete_custom_filter_lists(&self.connection_manager, ids)
98 }
99
100 fn get_all_tags(&self) -> FLMResult<Vec<FilterTag>> {
101 FilterTagManager::new().get_all_tags(&self.connection_manager)
102 }
103
104 fn get_all_groups(&self) -> FLMResult<Vec<FilterGroup>> {
105 FilterGroupManager::new().get_all_groups(&self.connection_manager, &self.configuration)
106 }
107
108 fn get_full_filter_list_by_id(&self, filter_id: FilterId) -> FLMResult<Option<FullFilterList>> {
109 FilterManager::new().get_full_filter_list_by_id(
110 &self.connection_manager,
111 &self.configuration,
112 Some(SQLOperator::FieldEqualValue("filter_id", filter_id.into())),
113 )
114 }
115
116 fn get_stored_filters_metadata(&self) -> FLMResult<Vec<StoredFilterMetadata>> {
117 FilterManager::new().get_stored_filters_metadata(
118 &self.connection_manager,
119 &self.configuration,
120 None,
121 )
122 }
123
124 fn get_stored_filter_metadata_by_id(
125 &self,
126 filter_id: FilterId,
127 ) -> FLMResult<Option<StoredFilterMetadata>> {
128 FilterManager::new().get_stored_filter_metadata_by_id(
129 &self.connection_manager,
130 &self.configuration,
131 Some(SQLOperator::FieldEqualValue("filter_id", filter_id.into())),
132 )
133 }
134
135 fn save_custom_filter_rules(&self, rules: FilterListRules) -> FLMResult<()> {
136 RulesListManager::new().save_custom_filter_rules(
137 &self.connection_manager,
138 &self.configuration,
139 rules,
140 )
141 }
142
143 fn save_disabled_rules(
144 &self,
145 filter_id: FilterId,
146 disabled_rules: Vec<String>,
147 ) -> FLMResult<()> {
148 RulesListManager::new().save_disabled_rules(
149 &self.connection_manager,
150 filter_id,
151 disabled_rules,
152 )
153 }
154
155 fn update_filters(
156 &self,
157 ignore_filters_expiration: bool,
158 loose_timeout: i32,
159 ignore_filters_status: bool,
160 ) -> FLMResult<Option<UpdateResult>> {
161 FilterUpdateManager::new().update_filters(
162 &self.connection_manager,
163 &self.configuration,
164 ignore_filters_expiration,
165 loose_timeout,
166 ignore_filters_status,
167 )
168 }
169
170 fn update_filters_by_ids(
171 &self,
172 ids: Vec<FilterId>,
173 ignore_filters_expiration: bool,
174 loose_timeout: i32,
175 ignore_filters_status: bool,
176 ) -> FLMResult<Option<UpdateResult>> {
177 FilterUpdateManager::new().update_filters_by_ids(
178 &self.connection_manager,
179 &self.configuration,
180 ids,
181 ignore_filters_expiration,
182 loose_timeout,
183 ignore_filters_status,
184 )
185 }
186
187 fn force_update_filters_by_ids(
188 &self,
189 ids: Vec<FilterId>,
190 loose_timeout: i32,
191 ) -> FLMResult<Option<UpdateResult>> {
192 FilterUpdateManager::new().force_update_filters_by_ids(
193 &self.connection_manager,
194 &self.configuration,
195 ids,
196 loose_timeout,
197 )
198 }
199
200 fn change_locale(&mut self, suggested_locale: Locale) -> FLMResult<bool> {
201 ConfigurationUpdateManager::new().change_locale(
202 &self.connection_manager,
203 &mut self.configuration,
204 suggested_locale,
205 )
206 }
207
208 fn pull_metadata(&self) -> FLMResult<PullMetadataResult> {
209 FilterUpdateManager::new().pull_metadata(&self.connection_manager, &self.configuration)
210 }
211
212 fn update_custom_filter_metadata(
213 &self,
214 filter_id: FilterId,
215 title: String,
216 is_trusted: bool,
217 ) -> FLMResult<bool> {
218 FilterManager::new().update_custom_filter_metadata(
219 &self.connection_manager,
220 filter_id,
221 title,
222 is_trusted,
223 )
224 }
225
226 fn get_database_path(&self) -> FLMResult<String> {
227 DbManager::new().get_database_path(&self.connection_manager)
228 }
229
230 fn lift_up_database(&self) -> FLMResult<()> {
231 unsafe { self.connection_manager.lift_up_database() }
234 }
235
236 fn get_database_version(&self) -> FLMResult<Option<i32>> {
237 DbManager::new().get_database_version(&self.connection_manager)
238 }
239
240 fn install_custom_filter_from_string(
241 &self,
242 download_url: String,
243 last_download_time: i64,
244 is_enabled: bool,
245 is_trusted: bool,
246 filter_body: String,
247 custom_title: Option<String>,
248 custom_description: Option<String>,
249 ) -> FLMResult<FullFilterList> {
250 FilterManager::new().install_custom_filter_from_string(
251 &self.connection_manager,
252 &self.configuration,
253 download_url,
254 last_download_time,
255 is_enabled,
256 is_trusted,
257 filter_body,
258 custom_title,
259 custom_description,
260 )
261 }
262
263 fn get_active_rules(&self) -> FLMResult<Vec<ActiveRulesInfo>> {
264 RulesListManager::new().get_active_rules(&self.connection_manager, &self.configuration)
265 }
266
267 fn get_active_rules_raw(&self, filter_by: Vec<FilterId>) -> FLMResult<Vec<ActiveRulesInfoRaw>> {
268 RulesListManager::new().get_active_rules_raw(
269 &self.connection_manager,
270 &self.configuration,
271 filter_by,
272 )
273 }
274
275 fn get_filter_rules_as_strings(
276 &self,
277 ids: Vec<FilterId>,
278 ) -> FLMResult<Vec<FilterListRulesRaw>> {
279 RulesListManager::new().get_filter_rules_as_strings(
280 &self.connection_manager,
281 &self.configuration,
282 ids,
283 )
284 }
285
286 fn save_rules_to_file_blob<P: AsRef<Path>>(
287 &self,
288 filter_id: FilterId,
289 file_path: P,
290 ) -> FLMResult<()> {
291 StreamingRulesManager::new().save_rules_to_file_blob(
292 &self.connection_manager,
293 filter_id,
294 file_path,
295 )
296 }
297
298 fn get_disabled_rules(&self, ids: Vec<FilterId>) -> FLMResult<Vec<DisabledRulesRaw>> {
299 RulesListManager::new().get_disabled_rules(&self.connection_manager, ids)
300 }
301
302 fn set_proxy_mode(&mut self, mode: RequestProxyMode) {
303 ConfigurationUpdateManager::new().set_proxy_mode(&mut self.configuration, mode)
304 }
305
306 fn get_rules_count(&self, ids: Vec<FilterId>) -> FLMResult<Vec<RulesCountByFilter>> {
307 RulesListManager::new().get_rules_count(&self.connection_manager, ids)
308 }
309}
310
311#[cfg(test)]
312impl FilterListManagerImpl {
313 pub(crate) fn get_configuration(&self) -> &Configuration {
314 &self.configuration
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use crate::manager::managers::filter_manager::FilterManager;
321 use crate::storage::entities::rules_list::rules_list_entity::RulesListEntity;
322 use crate::storage::repositories::filter_repository::FilterRepository;
323 use crate::storage::repositories::rules_list_repository::RulesListRepository;
324 use crate::storage::repositories::Repository;
325 use crate::storage::sql_generators::operator::SQLOperator;
326 use crate::storage::with_transaction;
327 use crate::storage::DbConnectionManager;
328 use crate::test_utils::spawn_test_db_with_metadata;
329 use crate::{
330 string, Configuration, FilterId, FilterListManager, FilterListManagerImpl, FilterListRules,
331 USER_RULES_FILTER_LIST_ID,
332 };
333 use chrono::{Duration, Utc};
334 use rand::prelude::SliceRandom;
335 use rand::thread_rng;
336 use rusqlite::Connection;
337 use std::fs::File;
338 use std::ops::Sub;
339 use std::time::{SystemTime, UNIX_EPOCH};
340 use std::{env, fs};
341 use url::Url;
342
343 #[test]
344 fn test_insert_custom_filter() {
345 let source = DbConnectionManager::factory_test().unwrap();
346 let _ = spawn_test_db_with_metadata(&source);
347
348 let mut conf = Configuration::default();
349 conf.app_name = "FlmApp".to_string();
350 conf.version = "1.2.3".to_string();
351 let flm = FilterListManagerImpl::new(conf).unwrap();
352
353 let path = fs::canonicalize("./tests/fixtures/1.txt").unwrap();
354
355 let first_filter_url = Url::from_file_path(path).unwrap();
356
357 let title = String::from("first title");
358 let description =
359 String::from("Filter that enables ad blocking on websites in Russian language.");
360
361 let current_time = Utc::now().timestamp();
362
363 let full_filter_list = flm
364 .install_custom_filter_list(
365 first_filter_url.to_string(),
366 true,
367 Some(title.clone()),
368 None,
369 )
370 .unwrap();
371
372 assert!(full_filter_list.is_custom);
373 assert!(full_filter_list.is_trusted);
374
375 assert_eq!(full_filter_list.title, title);
376 assert_eq!(full_filter_list.description, description);
377
378 assert!(full_filter_list.last_download_time >= current_time);
379
380 assert!(full_filter_list.is_enabled);
381 }
382
383 #[test]
384 fn delete_filter_lists() {
385 let source = DbConnectionManager::factory_test().unwrap();
386 let (_, inserted_filters) = spawn_test_db_with_metadata(&source);
387
388 let mut conf = Configuration::default();
389 conf.app_name = "FlmApp".to_string();
390 conf.version = "1.2.3".to_string();
391 let flm = FilterListManagerImpl::new(conf).unwrap();
392
393 let deleted = flm
394 .delete_custom_filter_lists(vec![inserted_filters.first().unwrap().filter_id.unwrap()])
395 .unwrap();
396
397 assert_eq!(deleted, 0);
399
400 let path = fs::canonicalize("./tests/fixtures/1.txt").unwrap();
401 let first_filter_url = Url::from_file_path(path).unwrap();
402
403 let title = String::from("first title");
404
405 let full_filter_list = flm
406 .install_custom_filter_list(
407 first_filter_url.to_string(),
408 true,
409 Some(title.clone()),
410 None,
411 )
412 .unwrap();
413
414 let custom_was_deleted = flm
415 .delete_custom_filter_lists(vec![full_filter_list.id])
416 .unwrap();
417
418 assert_eq!(custom_was_deleted, 1)
419 }
420
421 #[test]
422 fn test_install_local_custom_filter() {
423 let source = DbConnectionManager::factory_test().unwrap();
424 let _ = spawn_test_db_with_metadata(&source);
425
426 let mut conf = Configuration::default();
427 conf.app_name = "FlmApp".to_string();
428 conf.version = "1.2.3".to_string();
429 let flm = FilterListManagerImpl::new(conf).unwrap();
430
431 let title = String::from("titleeee");
432 let description = String::from("dessscrriptiiiioooonnn");
433
434 let full_filter_list = flm
435 .install_custom_filter_list(
436 String::new(),
437 true,
438 Some(title.clone()),
439 Some(description.clone()),
440 )
441 .unwrap();
442
443 assert!(full_filter_list.id.is_negative());
444 assert_eq!(full_filter_list.title, title);
445 assert_eq!(full_filter_list.description, description);
446 assert_eq!(full_filter_list.is_trusted, true);
447 }
448
449 #[test]
450 fn test_save_disabled_rules() {
451 let mut conf = Configuration::default();
452 conf.app_name = "FlmApp".to_string();
453 conf.version = "1.2.3".to_string();
454 let flm = FilterListManagerImpl::new(conf).unwrap();
455 let source = flm.connection_manager.as_ref();
456
457 let _ = spawn_test_db_with_metadata(source);
458
459 let title = String::from("titleeee");
460 let description = String::from("dessscrriptiiiioooonnn");
461
462 let full_filter_list = flm
463 .install_custom_filter_list(
464 String::new(),
465 true,
466 Some(title.clone()),
467 Some(description.clone()),
468 )
469 .unwrap();
470
471 let disabled_rules_vec: Vec<String> = vec!["first", "second", "third"]
472 .into_iter()
473 .map(|str| str.to_string())
474 .collect();
475 let disabled_rules_string = String::from("first\nsecond\nthird");
476
477 flm.save_disabled_rules(full_filter_list.id, disabled_rules_vec)
478 .unwrap();
479
480 let binding = source
481 .execute_db(|conn: Connection| {
482 let binding = RulesListRepository::new()
483 .select(
484 &conn,
485 Some(SQLOperator::FieldEqualValue(
486 "filter_id",
487 full_filter_list.id.into(),
488 )),
489 )
490 .unwrap()
491 .unwrap();
492
493 Ok(binding)
494 })
495 .unwrap();
496
497 let rules_entity = binding.first().unwrap();
498
499 assert_eq!(rules_entity.disabled_text, disabled_rules_string);
500 }
501
502 #[test]
503 fn test_install_custom_filter_from_string() {
504 let source = DbConnectionManager::factory_test().unwrap();
505 let _ = spawn_test_db_with_metadata(&source);
506
507 let mut conf = Configuration::default();
508 conf.app_name = "FlmApp".to_string();
509 conf.version = "1.2.3".to_string();
510 let flm = FilterListManagerImpl::new(conf).unwrap();
511
512 let download_url = String::from("http://install.custom.filter.list.from.string");
513 let last_download_time = Utc::now().sub(Duration::days(5));
514 let filter_body = include_str!("../../tests/fixtures/small_pseudo_custom_filter.txt");
515
516 let filter_list = flm
517 .install_custom_filter_from_string(
518 download_url.clone(),
519 last_download_time.timestamp(),
520 true,
521 false,
522 String::from(filter_body),
523 None,
524 None,
525 )
526 .unwrap();
527
528 assert_eq!(filter_list.is_enabled, true);
529 assert_eq!(filter_list.is_trusted, false);
530 assert_eq!(filter_list.title.as_str(), "Pseudo Custom Filter Title");
531 assert_eq!(
532 filter_list.description.as_str(),
533 "Pseudo Custom Filter Description"
534 );
535 assert_eq!(filter_list.version.as_str(), "2.0.91.12");
536 assert_eq!(filter_list.expires, 5 * 86400);
537 assert_eq!(filter_list.is_custom, true);
538 assert_eq!(
539 filter_list.homepage.as_str(),
540 "https://github.com/AdguardTeam/AdGuardFilters"
541 );
542 assert_eq!(
543 filter_list.last_download_time,
544 last_download_time.timestamp()
545 );
546 assert_eq!(filter_list.time_updated, 1716903061);
547 assert_eq!(filter_list.checksum.as_str(), "GQRYLu/9jKZYam7zBiCudg");
548 assert_eq!(
549 filter_list.license.as_str(),
550 "https://github.com/AdguardTeam/AdguardFilters/blob/master/LICENSE"
551 );
552 assert!(filter_list.rules.unwrap().rules.len() > 0);
553 }
554
555 #[test]
556 fn test_we_can_understand_aliases_fields() {
557 let source = DbConnectionManager::factory_test().unwrap();
558 let _ = spawn_test_db_with_metadata(&source);
559
560 let mut conf = Configuration::default();
561 conf.app_name = "FlmApp".to_string();
562 conf.version = "1.2.3".to_string();
563 let flm = FilterListManagerImpl::new(conf).unwrap();
564
565 let download_url = String::from("http://install.custom.filter.list.from.string");
566 let last_download_time = Utc::now().sub(Duration::days(5));
567 let filter_body =
568 include_str!("../../tests/fixtures/small_pseudo_custom_filter_with_aliases.txt");
569
570 let filter_list = flm
571 .install_custom_filter_from_string(
572 download_url.clone(),
573 last_download_time.timestamp(),
574 true,
575 false,
576 String::from(filter_body),
577 None,
578 None,
579 )
580 .unwrap();
581
582 assert_eq!(filter_list.time_updated, 1719230481);
583 assert_eq!(
584 filter_list.last_download_time,
585 last_download_time.timestamp()
586 );
587 }
588
589 #[test]
590 fn test_we_can_select_localised_filters() {
591 {
592 let mut conf = Configuration::default();
593 conf.locale = String::from("el");
594 conf.app_name = "FlmApp".to_string();
595 conf.version = "1.2.3".to_string();
596
597 let flm = FilterListManagerImpl::new(conf).unwrap();
598 let source = flm.connection_manager.as_ref();
599 let _ = spawn_test_db_with_metadata(&source);
600
601 let filter = flm.get_full_filter_list_by_id(1).unwrap().unwrap();
602
603 assert_eq!(filter.title.as_str(), "AdGuard Ρωσικό φίλτρο");
604 assert_eq!(
605 filter.description.as_str(),
606 "Φίλτρο που επιτρέπει τον αποκλεισμό διαφημίσεων σε ιστότοπους στη ρωσική γλώσσα."
607 );
608 }
609
610 {
611 let mut conf = Configuration::default();
612 conf.locale = String::from("31");
614 conf.app_name = "FlmApp".to_string();
615 conf.version = "1.2.3".to_string();
616
617 let flm = FilterListManagerImpl::new(conf).unwrap();
618 let source = flm.connection_manager.as_ref();
619 let _ = spawn_test_db_with_metadata(&source);
620
621 let filter = flm.get_full_filter_list_by_id(1).unwrap().unwrap();
622
623 assert_eq!(filter.title.as_str(), "AdGuard Russian filter");
624 assert_eq!(
625 filter.description.as_str(),
626 "Filter that enables ad blocking on websites in Russian language."
627 );
628 }
629 }
630
631 #[test]
632 fn test_select_index_filter() {
633 let mut conf = Configuration::default();
634 conf.app_name = "FlmApp".to_string();
635 conf.version = "1.2.3".to_string();
636 let flm = FilterListManagerImpl::new(conf).unwrap();
637 let source = flm.connection_manager.as_ref();
638
639 let _ = spawn_test_db_with_metadata(&source);
640
641 let filter = flm.get_full_filter_list_by_id(257).unwrap().unwrap();
642
643 assert_eq!(
644 filter.subscription_url.as_str(),
645 "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/badware.txt"
646 );
647 assert_eq!(
648 filter.download_url.as_str(),
649 "https://example.org/extension/safari/filters/257_optimized.txt"
650 );
651 assert!(filter.subscription_url.len() > 0);
652 assert!(filter.download_url.len() > 0);
653 }
654
655 #[test]
656 fn test_save_custom_filter_rules_must_update_time() {
657 let source = DbConnectionManager::factory_test().unwrap();
658 let _ = spawn_test_db_with_metadata(&source);
659
660 let mut conf = Configuration::default();
661 conf.app_name = "FlmApp".to_string();
662 conf.version = "1.2.3".to_string();
663 let flm = FilterListManagerImpl::new(conf).unwrap();
664
665 let rules = FilterListRules {
666 filter_id: USER_RULES_FILTER_LIST_ID,
667 rules: vec![String::from("example.com")],
668 disabled_rules: vec![],
669 rules_count: 0,
670 };
671
672 flm.save_custom_filter_rules(rules.clone()).unwrap();
674
675 let original_time_updated = flm
676 .get_full_filter_list_by_id(USER_RULES_FILTER_LIST_ID)
677 .unwrap()
678 .unwrap()
679 .time_updated;
680
681 std::thread::sleep(core::time::Duration::from_secs(1));
683
684 flm.save_custom_filter_rules(rules).unwrap();
686
687 let user_rules = flm
688 .get_full_filter_list_by_id(USER_RULES_FILTER_LIST_ID)
689 .unwrap()
690 .unwrap();
691
692 assert_ne!(user_rules.time_updated, original_time_updated);
693 }
694
695 #[test]
696 fn test_guard_rewrite_user_rules_filter_by_another_filter() {
697 let source = DbConnectionManager::factory_test().unwrap();
698 let _ = spawn_test_db_with_metadata(&source);
699
700 let mut conf = Configuration::default();
701 conf.app_name = "FlmApp".to_string();
702 conf.version = "1.2.3".to_string();
703 let flm = FilterListManagerImpl::new(conf).unwrap();
704
705 let _ = flm
706 .install_custom_filter_from_string(
707 String::new(),
708 SystemTime::now()
709 .duration_since(UNIX_EPOCH)
710 .unwrap()
711 .as_secs() as i64,
712 true,
713 true,
714 String::from("JJ"),
715 Some("FILTER".to_string()),
716 Some("DESC".to_string()),
717 )
718 .unwrap();
719
720 let list = source
721 .execute_db(|connection: Connection| {
722 let list = FilterRepository::new()
723 .select(
724 &connection,
725 Some(SQLOperator::FieldEqualValue(
726 "filter_id",
727 USER_RULES_FILTER_LIST_ID.into(),
728 )),
729 )
730 .unwrap()
731 .unwrap();
732 Ok(list)
733 })
734 .unwrap();
735
736 assert!(!list.is_empty());
737 }
738
739 #[test]
740 fn test_database_is_automatically_lifted_in_constructor() {
741 let mut conf = Configuration::default();
742 conf.app_name = "FlmApp".to_string();
743 conf.version = "1.2.3".to_string();
744 let flm = FilterListManagerImpl::new(conf).unwrap();
745
746 let lists = FilterManager::new()
747 .get_full_filter_lists(&flm.connection_manager, &flm.configuration, None)
748 .unwrap();
749
750 assert!(lists.len() > 0);
751 }
752
753 #[test]
754 fn test_get_filter_rules_as_strings() {
755 const TEST_FILTERS_AMOUNT: usize = 3;
756 const NONEXISTENT_ID: FilterId = 450_123_456;
757
758 let mut conf = Configuration::default();
759 conf.app_name = "FlmApp".to_string();
760 conf.version = "1.2.3".to_string();
761 let flm = FilterListManagerImpl::new(conf).unwrap();
762 let source = &flm.connection_manager;
763 let (_, index_filters) = spawn_test_db_with_metadata(source);
764
765 let filter_repo = FilterRepository::new();
766 let rules_repo = RulesListRepository::new();
767
768 let guard_id = source
769 .execute_db(|connection: Connection| {
770 let guard_id = filter_repo
771 .count(
772 &connection,
773 Some(SQLOperator::FieldIn(
774 "filter_id",
775 vec![NONEXISTENT_ID.into()],
776 )),
777 )
778 .unwrap();
779
780 Ok(guard_id)
781 })
782 .unwrap();
783
784 assert_eq!(guard_id, 0);
785
786 let mut rng = thread_rng();
787 let mut ids = index_filters
788 .choose_multiple(&mut rng, TEST_FILTERS_AMOUNT)
789 .filter_map(|filter| filter.filter_id)
790 .collect::<Vec<FilterId>>();
791
792 source
793 .execute_db(|mut connection: Connection| {
794 with_transaction(&mut connection, |transaction| {
796 let entities = ids
797 .clone()
798 .into_iter()
799 .map(|id| {
800 RulesListEntity::with_disabled_text(
801 id,
802 string!("example.com\nexample.org"),
803 string!("example.com"),
804 0,
805 )
806 })
807 .collect::<Vec<RulesListEntity>>();
808
809 rules_repo.insert(&transaction, &entities).unwrap();
810
811 Ok(())
812 })
813 })
814 .unwrap();
815
816 ids.push(NONEXISTENT_ID);
817
818 let rules = flm.get_filter_rules_as_strings(ids).unwrap();
819
820 assert_eq!(rules.len(), TEST_FILTERS_AMOUNT);
821 assert!(rules
822 .iter()
823 .find(|rules| rules.filter_id == NONEXISTENT_ID)
824 .is_none())
825 }
826
827 #[test]
828 fn test_save_rules_to_file_blob() {
829 let mut path = env::current_dir().unwrap();
830 path.push("fixtures");
831 path.push(format!(
832 "test_filter_rules_{}.txt",
833 Utc::now().timestamp_micros()
834 ));
835
836 let mut conf = Configuration::default();
837 conf.app_name = "FlmApp".to_string();
838 conf.version = "1.2.3".to_string();
839 let flm = FilterListManagerImpl::new(conf).unwrap();
840
841 {
842 File::create(&path).unwrap();
843 }
844
845 let rules = FilterListRules {
846 filter_id: USER_RULES_FILTER_LIST_ID,
847 rules: vec![
848 String::from("first"),
849 String::from("second"),
850 String::from("third"),
851 String::from("fourth"),
852 String::from("fifth"),
853 ],
854 disabled_rules: vec![
855 String::from("second"),
856 String::from("fourth"),
857 String::from("second"),
858 ],
859 rules_count: 0,
860 };
861
862 flm.save_custom_filter_rules(rules).unwrap();
863
864 flm.save_rules_to_file_blob(USER_RULES_FILTER_LIST_ID, &path)
865 .unwrap();
866
867 let test_string = fs::read_to_string(&path).unwrap();
868 fs::remove_file(&path).unwrap();
869
870 assert_eq!(test_string.as_str(), "first\nthird\nfifth");
871 }
872
873 #[test]
874 fn test_get_disabled_rules() {
875 let mut conf = Configuration::default();
876 conf.app_name = "FlmApp".to_string();
877 conf.version = "1.2.3".to_string();
878 let flm = FilterListManagerImpl::new(conf).unwrap();
879
880 let source = &flm.connection_manager;
881 let (_, index_filters) = spawn_test_db_with_metadata(source);
882
883 let last_filter_id = index_filters.last().unwrap().filter_id.unwrap();
884 let first_filter_id = index_filters.first().unwrap().filter_id.unwrap();
885
886 source
887 .execute_db(|mut connection: Connection| {
888 let rules1 = RulesListEntity::with_disabled_text(
889 last_filter_id,
890 string!("Text\nDisabled Text\n123"),
891 string!("Disabled Text\n123"),
892 0,
893 );
894
895 let rules2 = RulesListEntity::with_disabled_text(
896 first_filter_id,
897 string!("Text2\nDisabled Text2"),
898 string!("Disabled Text2"),
899 0,
900 );
901
902 let tx = connection.transaction().unwrap();
903 let repo = RulesListRepository::new();
904
905 repo.insert(&tx, vec![rules1, rules2].as_slice()).unwrap();
906
907 tx.commit().unwrap();
908
909 Ok(())
910 })
911 .unwrap();
912
913 let actual = flm
914 .get_disabled_rules(vec![first_filter_id, last_filter_id])
915 .unwrap();
916
917 assert_eq!(actual[0].text.as_str(), "Disabled Text2");
918 assert_eq!(actual[1].text.as_str(), "Disabled Text\n123");
919 }
920
921 #[test]
922 fn test_change_locale() {
923 let mut conf = Configuration::default();
924 conf.app_name = "FlmApp".to_string();
925 conf.version = "1.2.3".to_string();
926 let mut flm = FilterListManagerImpl::new(conf).unwrap();
927
928 let source = &flm.connection_manager;
929 spawn_test_db_with_metadata(source);
930
931 let mut res = flm.change_locale("ru".to_string()).unwrap();
932 assert!(res);
933
934 res = flm.change_locale("ru-RU".to_string()).unwrap();
935 assert!(res);
936
937 res = flm.change_locale("ru_RU".to_string()).unwrap();
938 assert!(res);
939
940 res = flm.change_locale("ruRU".to_string()).unwrap();
941 assert!(!res);
942 }
943
944 #[test]
945 fn test_get_rules_count() {
946 let mut conf = Configuration::default();
947 conf.app_name = "FlmApp".to_string();
948 conf.version = "1.2.3".to_string();
949 let flm = FilterListManagerImpl::new(conf).unwrap();
950
951 let source = &flm.connection_manager;
952 spawn_test_db_with_metadata(source);
953
954 let user_rules_count_result = 5;
955
956 source
957 .execute_db(|mut connection: Connection| {
958 let rules = RulesListEntity::make(
959 USER_RULES_FILTER_LIST_ID,
960 string!(),
961 user_rules_count_result,
962 );
963
964 let tx = connection.transaction().unwrap();
965 let repo = RulesListRepository::new();
966
967 repo.insert(&tx, vec![rules].as_slice()).unwrap();
968
969 tx.commit().unwrap();
970
971 Ok(())
972 })
973 .unwrap();
974
975 let rules_count_by_filter = flm
976 .get_rules_count(vec![USER_RULES_FILTER_LIST_ID])
977 .unwrap();
978
979 assert_eq!(
980 rules_count_by_filter[0].filter_id,
981 USER_RULES_FILTER_LIST_ID
982 );
983 assert_eq!(
984 rules_count_by_filter[0].rules_count,
985 user_rules_count_result
986 );
987 }
988
989 #[test]
990 fn test_save_custom_filter_rules_must_update_rules_count() {
991 let source = DbConnectionManager::factory_test().unwrap();
992 spawn_test_db_with_metadata(&source);
993
994 let mut conf = Configuration::default();
995 conf.app_name = "FlmApp".to_string();
996 conf.version = "1.2.3".to_string();
997 let flm = FilterListManagerImpl::new(conf).unwrap();
998
999 let rules = FilterListRules {
1000 filter_id: USER_RULES_FILTER_LIST_ID,
1001 rules: "Text\n!Text\n# Text\n\n\nText"
1002 .split('\n')
1003 .map(str::to_string)
1004 .collect(),
1005 disabled_rules: "Disabled Text".split('\n').map(str::to_string).collect(),
1006 rules_count: 0,
1007 };
1008
1009 let user_rules_count_result = 2;
1010
1011 flm.save_custom_filter_rules(rules).unwrap();
1012
1013 let rules_count_by_filter = flm
1014 .get_rules_count(vec![USER_RULES_FILTER_LIST_ID])
1015 .unwrap();
1016
1017 assert_eq!(
1018 rules_count_by_filter[0].filter_id,
1019 USER_RULES_FILTER_LIST_ID
1020 );
1021 assert_eq!(
1022 rules_count_by_filter[0].rules_count,
1023 user_rules_count_result
1024 );
1025 }
1026
1027 #[test]
1028 fn test_install_custom_filter_sets_is_user_title_and_description_flags() {
1029 let mut conf = Configuration::default();
1030 conf.app_name = "FlmApp".to_string();
1031 conf.version = "1.2.3".to_string();
1032
1033 let flm = FilterListManagerImpl::new(conf).unwrap();
1034
1035 let source = &flm.connection_manager;
1036 spawn_test_db_with_metadata(source);
1037
1038 let installed_filter_list = flm
1040 .install_custom_filter_list(
1041 "https://filters.adtidy.org/extension/safari/filters/101_optimized.txt".to_string(),
1042 true,
1043 Some("title".to_string()),
1044 None,
1045 )
1046 .unwrap();
1047
1048 source
1049 .execute_db(|conn: Connection| {
1050 let filters = FilterRepository::new()
1051 .select(
1052 &conn,
1053 Some(SQLOperator::FieldEqualValue(
1054 "filter_id",
1055 installed_filter_list.id.into(),
1056 )),
1057 )
1058 .unwrap()
1059 .unwrap();
1060
1061 assert!(filters[0].is_user_title());
1062 assert!(!filters[0].is_user_description());
1063
1064 Ok(())
1065 })
1066 .unwrap();
1067
1068 let installed_filter_list = flm
1070 .install_custom_filter_list(
1071 "https://filters.adtidy.org/extension/safari/filters/101_optimized.txt".to_string(),
1072 true,
1073 None,
1074 Some("description".to_string()),
1075 )
1076 .unwrap();
1077
1078 source
1079 .execute_db(|conn: Connection| {
1080 let filters = FilterRepository::new()
1081 .select(
1082 &conn,
1083 Some(SQLOperator::FieldEqualValue(
1084 "filter_id",
1085 installed_filter_list.id.into(),
1086 )),
1087 )
1088 .unwrap()
1089 .unwrap();
1090
1091 assert!(!filters[0].is_user_title());
1092 assert!(filters[0].is_user_description());
1093
1094 Ok(())
1095 })
1096 .unwrap();
1097 }
1098
1099 #[test]
1100 fn test_update_custom_filter_metadata_sets_is_user_title_flag() {
1101 let mut conf = Configuration::default();
1102 conf.app_name = "FlmApp".to_string();
1103 conf.version = "1.2.3".to_string();
1104
1105 let flm = FilterListManagerImpl::new(conf).unwrap();
1106
1107 let source = &flm.connection_manager;
1108 spawn_test_db_with_metadata(source);
1109
1110 let installed_filter_list = flm
1112 .install_custom_filter_list(
1113 "https://filters.adtidy.org/extension/safari/filters/101_optimized.txt".to_string(),
1114 true,
1115 None,
1116 None,
1117 )
1118 .unwrap();
1119
1120 flm.update_custom_filter_metadata(installed_filter_list.id, "title".to_string(), true)
1121 .unwrap();
1122
1123 source
1124 .execute_db(|conn: Connection| {
1125 let filters = FilterRepository::new()
1126 .select(
1127 &conn,
1128 Some(SQLOperator::FieldEqualValue(
1129 "filter_id",
1130 installed_filter_list.id.into(),
1131 )),
1132 )
1133 .unwrap()
1134 .unwrap();
1135
1136 assert!(filters[0].is_user_title());
1137
1138 Ok(())
1139 })
1140 .unwrap();
1141 }
1142
1143 #[test]
1144 fn test_update_filters_must_not_update_title_and_description() {
1145 let mut conf = Configuration::default();
1146 conf.metadata_url = "https://filters.adtidy.org/extension/safari/filters.json".to_string();
1147 conf.metadata_locales_url =
1148 "https://filters.adtidy.org/windows/filters_i18n.json".to_string();
1149 conf.app_name = "FlmApp".to_string();
1150 conf.version = "1.2.3".to_string();
1151
1152 let flm = FilterListManagerImpl::new(conf).unwrap();
1153
1154 let source = &flm.connection_manager;
1155 spawn_test_db_with_metadata(source);
1156
1157 let installed_filter_list = flm
1159 .install_custom_filter_list(
1160 "https://filters.adtidy.org/extension/safari/filters/101_optimized.txt".to_string(),
1161 true,
1162 Some("title".to_string()),
1163 None,
1164 )
1165 .unwrap();
1166
1167 flm.update_filters(false, 0, false).unwrap();
1168
1169 source
1170 .execute_db(|conn: Connection| {
1171 let filters = FilterRepository::new()
1172 .select(
1173 &conn,
1174 Some(SQLOperator::FieldEqualValue(
1175 "filter_id",
1176 installed_filter_list.id.into(),
1177 )),
1178 )
1179 .unwrap()
1180 .unwrap();
1181
1182 assert_eq!(filters[0].title, "title");
1183 assert_ne!(filters[0].description, "description");
1184
1185 Ok(filters)
1186 })
1187 .unwrap();
1188
1189 let installed_filter_list = flm
1191 .install_custom_filter_list(
1192 "https://filters.adtidy.org/extension/safari/filters/101_optimized.txt".to_string(),
1193 true,
1194 None,
1195 Some("description".to_string()),
1196 )
1197 .unwrap();
1198
1199 flm.update_filters(false, 0, false).unwrap();
1200
1201 source
1202 .execute_db(|conn: Connection| {
1203 let filters = FilterRepository::new()
1204 .select(
1205 &conn,
1206 Some(SQLOperator::FieldEqualValue(
1207 "filter_id",
1208 installed_filter_list.id.into(),
1209 )),
1210 )
1211 .unwrap()
1212 .unwrap();
1213
1214 assert_ne!(filters[0].title, "title");
1215 assert_eq!(filters[0].description, "description");
1216
1217 Ok(filters)
1218 })
1219 .unwrap();
1220 }
1221}