abstract_version_control/
queries.rs

1use abstract_sdk::std::{
2    objects::{
3        module::{Module, ModuleInfo, ModuleVersion},
4        module_reference::ModuleReference,
5        namespace::Namespace,
6        AccountId,
7    },
8    version_control::{
9        state::{ACCOUNT_ADDRESSES, REGISTERED_MODULES, YANKED_MODULES},
10        AccountBaseResponse, ModuleFilter, ModuleResponse, ModulesListResponse, ModulesResponse,
11        NamespaceListResponse,
12    },
13};
14use abstract_std::{
15    objects::module::ModuleStatus,
16    version_control::{
17        state::{NAMESPACES_INFO, PENDING_MODULES},
18        ModuleConfiguration, NamespaceInfo, NamespaceResponse,
19    },
20};
21use cosmwasm_std::{Deps, Order, StdError, StdResult};
22use cw_storage_plus::{Bound, Map};
23
24use crate::{contract::VCResult, error::VCError};
25
26const DEFAULT_LIMIT: u8 = 10;
27const MAX_LIMIT: u8 = 20;
28
29pub fn handle_account_address_query(
30    deps: Deps,
31    account_id: AccountId,
32) -> StdResult<AccountBaseResponse> {
33    let account_address = ACCOUNT_ADDRESSES.load(deps.storage, &account_id);
34    match account_address {
35        Err(_) => Err(StdError::generic_err(
36            VCError::UnknownAccountId { id: account_id }.to_string(),
37        )),
38        Ok(base) => Ok(AccountBaseResponse { account_base: base }),
39    }
40}
41
42pub fn handle_modules_query(deps: Deps, modules: Vec<ModuleInfo>) -> StdResult<ModulesResponse> {
43    let mut modules_response = ModulesResponse { modules: vec![] };
44    for mut module in modules {
45        let maybe_module_ref = if let ModuleVersion::Version(_) = module.version {
46            REGISTERED_MODULES.load(deps.storage, &module)
47        } else {
48            // get latest
49            let versions: StdResult<Vec<(String, ModuleReference)>> = REGISTERED_MODULES
50                .prefix((module.namespace.clone(), module.name.clone()))
51                .range(deps.storage, None, None, Order::Descending)
52                .take(1)
53                .collect();
54            let (latest_version, id) = versions?
55                .first()
56                .ok_or_else(|| StdError::GenericErr {
57                    msg: VCError::ModuleNotFound(module.clone()).to_string(),
58                })?
59                .clone();
60            module.version = ModuleVersion::Version(latest_version);
61            Ok(id)
62        };
63
64        match maybe_module_ref {
65            Err(_) => Err(StdError::generic_err(
66                VCError::ModuleNotFound(module).to_string(),
67            )),
68            Ok(mod_ref) => {
69                modules_response.modules.push(ModuleResponse {
70                    module: Module {
71                        info: module.clone(),
72                        reference: mod_ref,
73                    },
74                    config: ModuleConfiguration::from_storage(deps.storage, &module)?,
75                });
76                Ok(())
77            }
78        }?;
79    }
80
81    Ok(modules_response)
82}
83
84pub fn handle_module_list_query(
85    deps: Deps,
86    start_after: Option<ModuleInfo>,
87    limit: Option<u8>,
88    filter: Option<ModuleFilter>,
89) -> VCResult<ModulesListResponse> {
90    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
91
92    let ModuleFilter {
93        namespace: ref namespace_filter,
94        name: ref name_filter,
95        version: version_filter,
96        status,
97    } = filter.unwrap_or_default();
98
99    let mod_lib = match status {
100        Some(ModuleStatus::Registered) => &REGISTERED_MODULES,
101        Some(ModuleStatus::Pending) => &PENDING_MODULES,
102        Some(ModuleStatus::Yanked) => &YANKED_MODULES,
103        None => &REGISTERED_MODULES,
104    };
105    let mut modules: Vec<(ModuleInfo, ModuleReference)> = vec![];
106
107    if let Some(namespace_filter) = namespace_filter {
108        let namespace_filter = Namespace::new(namespace_filter)?;
109        modules.extend(filter_modules_by_namespace(
110            deps,
111            start_after,
112            limit,
113            namespace_filter,
114            name_filter,
115            mod_lib,
116        )?);
117    } else {
118        let start_bound: Option<Bound<&ModuleInfo>> = start_after.as_ref().map(Bound::exclusive);
119
120        // Load all modules
121        modules.extend(
122            mod_lib
123                .range(deps.storage, start_bound, None, Order::Ascending)
124                .take(limit)
125                .collect::<StdResult<Vec<_>>>()?,
126        );
127    };
128
129    // handle name and version filter after loading all modules
130    if namespace_filter.is_none() && name_filter.is_some() {
131        let name_filter = name_filter.as_ref().unwrap();
132        modules.retain(|(module_info, _)| &module_info.name == name_filter);
133    }
134    if let Some(version) = version_filter.map(ModuleVersion::Version) {
135        modules.retain(|(info, _)| info.version == version);
136    }
137
138    let modules = modules
139        .into_iter()
140        .map(|(module_info, mod_ref)| {
141            Ok(ModuleResponse {
142                module: Module {
143                    info: module_info.clone(),
144                    reference: mod_ref,
145                },
146                config: ModuleConfiguration::from_storage(deps.storage, &module_info)?,
147            })
148        })
149        .collect::<Result<Vec<_>, StdError>>()?;
150
151    Ok(ModulesListResponse { modules })
152}
153
154pub fn handle_namespaces_query(
155    deps: Deps,
156    accounts: Vec<AccountId>,
157) -> StdResult<NamespaceListResponse> {
158    let mut namespaces_response = NamespaceListResponse { namespaces: vec![] };
159    for account_id in accounts {
160        namespaces_response.namespaces.extend(
161            NAMESPACES_INFO
162                .idx
163                .account_id
164                .prefix(account_id)
165                .range(deps.storage, None, None, Order::Ascending)
166                .collect::<StdResult<Vec<_>>>()?,
167        );
168    }
169
170    Ok(namespaces_response)
171}
172
173pub fn handle_namespace_query(deps: Deps, namespace: Namespace) -> StdResult<NamespaceResponse> {
174    let account_id = NAMESPACES_INFO.may_load(deps.storage, &namespace)?;
175    let Some(account_id) = account_id else {
176        return Ok(NamespaceResponse::Unclaimed {});
177    };
178
179    let account_base = ACCOUNT_ADDRESSES.load(deps.storage, &account_id)?;
180    Ok(NamespaceResponse::Claimed(NamespaceInfo {
181        account_id,
182        account_base,
183    }))
184}
185
186pub fn handle_namespace_list_query(
187    deps: Deps,
188    start_after: Option<Namespace>,
189    limit: Option<u8>,
190) -> StdResult<NamespaceListResponse> {
191    let start_bound = start_after.as_ref().map(Bound::exclusive);
192    let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize;
193
194    let namespaces = NAMESPACES_INFO
195        .range(deps.storage, start_bound, None, Order::Ascending)
196        .take(limit)
197        .collect::<StdResult<Vec<_>>>()?;
198
199    Ok(NamespaceListResponse { namespaces })
200}
201
202/// Filter the modules with their primary key prefix (namespace)
203fn filter_modules_by_namespace(
204    deps: Deps,
205    start_after: Option<ModuleInfo>,
206    limit: usize,
207    namespace: Namespace,
208    name: &Option<String>,
209    mod_lib: &Map<&ModuleInfo, ModuleReference>,
210) -> StdResult<Vec<(ModuleInfo, ModuleReference)>> {
211    let mut modules: Vec<(ModuleInfo, ModuleReference)> = vec![];
212
213    // Filter by name using full prefix
214    if let Some(name) = name {
215        let start_bound: Option<Bound<String>> =
216            start_after.map(|info| Bound::exclusive(info.version.to_string()));
217
218        modules.extend(
219            mod_lib
220                .prefix((namespace.clone(), name.clone()))
221                .range(deps.storage, start_bound, None, Order::Ascending)
222                .take(limit)
223                .collect::<StdResult<Vec<_>>>()?
224                .into_iter()
225                .map(|(version, reference)| {
226                    (
227                        ModuleInfo {
228                            namespace: namespace.clone(),
229                            name: name.clone(),
230                            version: ModuleVersion::Version(version),
231                        },
232                        reference,
233                    )
234                }),
235        )
236    } else {
237        // Filter by just namespace using sub prefix
238        let start_bound: Option<Bound<(String, String)>> =
239            start_after.map(|token| Bound::exclusive((token.name, token.version.to_string())));
240
241        modules.extend(
242            mod_lib
243                .sub_prefix(namespace.clone())
244                .range(deps.storage, start_bound, None, Order::Ascending)
245                .take(limit)
246                .collect::<StdResult<Vec<_>>>()?
247                .into_iter()
248                .map(|((name, version), reference)| {
249                    (
250                        ModuleInfo {
251                            namespace: namespace.clone(),
252                            name,
253                            version: ModuleVersion::Version(version),
254                        },
255                        reference,
256                    )
257                }),
258        );
259    }
260    Ok(modules)
261}
262
263#[cfg(test)]
264mod test {
265    use super::*;
266
267    use crate::contract;
268    use abstract_std::{manager, objects::account::AccountTrace, version_control::*};
269    use abstract_testing::{prelude::*, MockQuerierOwnership};
270    use cosmwasm_std::{
271        testing::{mock_dependencies, mock_env, mock_info},
272        Addr, Binary, DepsMut, StdError,
273    };
274    use speculoos::prelude::*;
275
276    type VersionControlTestResult = Result<(), VCError>;
277
278    const TEST_OTHER: &str = "testother";
279    const TEST_OTHER_ACCOUNT_ID: AccountId = AccountId::const_new(2, AccountTrace::Local);
280    const TEST_OTHER_PROXY_ADDR: &str = "proxy1";
281    const TEST_OTHER_MANAGER_ADDR: &str = "manager1";
282
283    pub fn mock_manager_querier() -> MockQuerierBuilder {
284        MockQuerierBuilder::default()
285            .with_smart_handler(TEST_MANAGER, |msg| {
286                match from_json(msg).unwrap() {
287                    manager::QueryMsg::Config {} => {
288                        let resp = manager::ConfigResponse {
289                            version_control_address: Addr::unchecked(TEST_VERSION_CONTROL),
290                            module_factory_address: Addr::unchecked(TEST_MODULE_FACTORY),
291                            account_id: TEST_ACCOUNT_ID, // mock value, not used
292                            is_suspended: false,
293                        };
294                        Ok(to_json_binary(&resp).unwrap())
295                    }
296                    _ => panic!("unexpected message"),
297                }
298            })
299            .with_smart_handler(TEST_OTHER_MANAGER_ADDR, |msg| {
300                match from_json(msg).unwrap() {
301                    manager::QueryMsg::Config {} => {
302                        let resp = manager::ConfigResponse {
303                            version_control_address: Addr::unchecked(TEST_VERSION_CONTROL),
304                            module_factory_address: Addr::unchecked(TEST_MODULE_FACTORY),
305                            account_id: TEST_OTHER_ACCOUNT_ID, // mock value, not used
306                            is_suspended: false,
307                        };
308                        Ok(to_json_binary(&resp).unwrap())
309                    }
310                    _ => panic!("unexpected message"),
311                }
312            })
313            .with_owner(TEST_MANAGER, Some(OWNER))
314            .with_owner(TEST_OTHER_MANAGER_ADDR, Some(TEST_OTHER))
315    }
316
317    fn mock_init(mut deps: DepsMut) -> VersionControlTestResult {
318        let info = mock_info(OWNER, &[]);
319        let admin = info.sender.to_string();
320
321        contract::instantiate(
322            deps.branch(),
323            mock_env(),
324            info,
325            InstantiateMsg {
326                admin,
327                security_disabled: Some(true),
328                namespace_registration_fee: None,
329            },
330        )?;
331        execute_as_admin(
332            deps.branch(),
333            ExecuteMsg::UpdateConfig {
334                account_factory_address: Some(TEST_ACCOUNT_FACTORY.to_string()),
335                security_disabled: None,
336                namespace_registration_fee: None,
337            },
338        )?;
339
340        Ok(())
341    }
342
343    /// Initialize the version_control with admin as creator and test account
344    fn mock_init_with_account(mut deps: DepsMut) -> VCResult {
345        mock_init(deps.branch())?;
346        execute_as(
347            deps.branch(),
348            TEST_ACCOUNT_FACTORY,
349            ExecuteMsg::AddAccount {
350                account_id: TEST_ACCOUNT_ID,
351                account_base: test_account_base(),
352                namespace: None,
353            },
354        )?;
355        execute_as(
356            deps.branch(),
357            TEST_ACCOUNT_FACTORY,
358            ExecuteMsg::AddAccount {
359                account_id: TEST_OTHER_ACCOUNT_ID,
360                account_base: AccountBase {
361                    manager: Addr::unchecked(TEST_OTHER_MANAGER_ADDR),
362                    proxy: Addr::unchecked(TEST_OTHER_PROXY_ADDR),
363                },
364                namespace: None,
365            },
366        )
367    }
368
369    fn execute_as(deps: DepsMut, sender: &str, msg: ExecuteMsg) -> VCResult {
370        contract::execute(deps, mock_env(), mock_info(sender, &[]), msg)
371    }
372
373    fn execute_as_admin(deps: DepsMut, msg: ExecuteMsg) -> VCResult {
374        contract::execute(deps, mock_env(), mock_info(OWNER, &[]), msg)
375    }
376
377    fn query_helper(deps: Deps, msg: QueryMsg) -> VCResult<Binary> {
378        contract::query(deps, mock_env(), msg)
379    }
380
381    mod module {
382        use super::*;
383
384        use abstract_std::objects::module::ModuleVersion::Latest;
385
386        fn add_namespace(deps: DepsMut, namespace: &str) {
387            let msg = ExecuteMsg::ClaimNamespace {
388                account_id: TEST_ACCOUNT_ID,
389                namespace: namespace.to_string(),
390            };
391
392            let res = execute_as_admin(deps, msg);
393            assert_that!(&res).is_ok();
394        }
395
396        fn add_module(deps: DepsMut, new_module_info: ModuleInfo) {
397            let add_msg = ExecuteMsg::ProposeModules {
398                modules: vec![(new_module_info, ModuleReference::App(0))],
399            };
400
401            let res = execute_as_admin(deps, add_msg);
402            assert_that!(&res).is_ok();
403        }
404
405        #[test]
406        fn get_module() -> VersionControlTestResult {
407            let mut deps = mock_dependencies();
408            deps.querier = mock_manager_querier().build();
409            mock_init_with_account(deps.as_mut())?;
410            let new_module_info =
411                ModuleInfo::from_id("test:module", ModuleVersion::Version("0.1.2".into())).unwrap();
412
413            let ModuleInfo {
414                name, namespace, ..
415            } = new_module_info.clone();
416
417            add_namespace(deps.as_mut(), "test");
418            add_module(deps.as_mut(), new_module_info.clone());
419
420            let query_msg = QueryMsg::Modules {
421                infos: vec![ModuleInfo {
422                    namespace,
423                    name,
424                    version: Latest {},
425                }],
426            };
427
428            let ModulesResponse { mut modules } =
429                from_json(query_helper(deps.as_ref(), query_msg)?)?;
430            assert_that!(modules.swap_remove(0).module.info).is_equal_to(&new_module_info);
431            Ok(())
432        }
433
434        #[test]
435        fn none_when_no_matching_version() -> VersionControlTestResult {
436            let mut deps = mock_dependencies();
437            deps.querier = mock_manager_querier().build();
438            mock_init_with_account(deps.as_mut())?;
439            let new_module_info =
440                ModuleInfo::from_id("test:module", ModuleVersion::Version("0.1.2".into())).unwrap();
441
442            let ModuleInfo {
443                name, namespace, ..
444            } = new_module_info.clone();
445
446            add_namespace(deps.as_mut(), "test");
447            add_module(deps.as_mut(), new_module_info);
448
449            let query_msg = QueryMsg::Modules {
450                infos: vec![ModuleInfo {
451                    namespace,
452                    name,
453                    version: ModuleVersion::Version("024209.902.902".to_string()),
454                }],
455            };
456
457            let res = query_helper(deps.as_ref(), query_msg);
458            assert_that!(res)
459                .is_err()
460                .matches(|e| matches!(e, VCError::Std(StdError::GenericErr { .. })));
461            Ok(())
462        }
463
464        #[test]
465        fn get_latest_when_multiple_registered() -> VersionControlTestResult {
466            let mut deps = mock_dependencies();
467            deps.querier = mock_manager_querier().build();
468            mock_init_with_account(deps.as_mut())?;
469
470            add_namespace(deps.as_mut(), "test");
471
472            let module_id = "test:module";
473            let oldest_version =
474                ModuleInfo::from_id(module_id, ModuleVersion::Version("0.1.2".into())).unwrap();
475
476            add_module(deps.as_mut(), oldest_version);
477            let newest_version =
478                ModuleInfo::from_id(module_id, ModuleVersion::Version("100.1.2".into())).unwrap();
479            add_module(deps.as_mut(), newest_version.clone());
480
481            let another_version =
482                ModuleInfo::from_id(module_id, ModuleVersion::Version("1.1.2".into())).unwrap();
483            add_module(deps.as_mut(), another_version);
484
485            let query_msg = QueryMsg::Modules {
486                infos: vec![ModuleInfo {
487                    namespace: Namespace::new("test")?,
488                    name: "module".to_string(),
489                    version: Latest {},
490                }],
491            };
492
493            let ModulesResponse { mut modules } =
494                from_json(query_helper(deps.as_ref(), query_msg)?)?;
495            assert_that!(modules.swap_remove(0).module.info).is_equal_to(&newest_version);
496            Ok(())
497        }
498    }
499
500    use cosmwasm_std::from_json;
501
502    /// Add namespaces
503    fn add_namespaces(mut deps: DepsMut, acc_and_namespace: Vec<(AccountId, &str)>, sender: &str) {
504        for (account_id, namespace) in acc_and_namespace {
505            let msg = ExecuteMsg::ClaimNamespace {
506                account_id,
507                namespace: namespace.to_string(),
508            };
509
510            let res = execute_as(deps.branch(), sender, msg);
511            assert_that!(&res).is_ok();
512        }
513    }
514
515    /// Add the provided modules to the version control
516    fn propose_modules(deps: DepsMut, new_module_infos: Vec<ModuleInfo>, sender: &str) {
517        let modules = new_module_infos
518            .into_iter()
519            .map(|info| (info, ModuleReference::App(0)))
520            .collect();
521        let add_msg = ExecuteMsg::ProposeModules { modules };
522        let res = execute_as(deps, sender, add_msg);
523        assert_that!(&res).is_ok();
524    }
525
526    /// Yank the provided module in the version control
527    fn yank_module(deps: DepsMut, module_info: ModuleInfo) {
528        let yank_msg = ExecuteMsg::YankModule {
529            module: module_info,
530        };
531        let res = execute_as_admin(deps, yank_msg);
532        assert_that!(&res).is_ok();
533    }
534
535    /// Init verison control with some test modules.
536    fn init_with_mods(mut deps: DepsMut) {
537        mock_init_with_account(deps.branch()).unwrap();
538
539        add_namespaces(deps.branch(), vec![(TEST_ACCOUNT_ID, "cw-plus")], OWNER);
540        add_namespaces(
541            deps.branch(),
542            vec![(TEST_OTHER_ACCOUNT_ID, "4t2")],
543            TEST_OTHER,
544        );
545
546        let cw_mods = vec![
547            ModuleInfo::from_id("cw-plus:module1", ModuleVersion::Version("0.1.2".into())).unwrap(),
548            ModuleInfo::from_id("cw-plus:module2", ModuleVersion::Version("0.1.2".into())).unwrap(),
549            ModuleInfo::from_id("cw-plus:module3", ModuleVersion::Version("0.1.2".into())).unwrap(),
550        ];
551        propose_modules(deps.branch(), cw_mods, OWNER);
552
553        let fortytwo_mods = vec![
554            ModuleInfo::from_id("4t2:module1", ModuleVersion::Version("0.1.2".into())).unwrap(),
555            ModuleInfo::from_id("4t2:module2", ModuleVersion::Version("0.1.2".into())).unwrap(),
556            ModuleInfo::from_id("4t2:module3", ModuleVersion::Version("0.1.2".into())).unwrap(),
557        ];
558        propose_modules(deps, fortytwo_mods, TEST_OTHER);
559    }
560
561    mod modules {
562        use super::*;
563
564        #[test]
565        fn get_cw_plus_modules() -> VersionControlTestResult {
566            let mut deps = mock_dependencies();
567            deps.querier = mock_manager_querier().build();
568            init_with_mods(deps.as_mut());
569
570            let namespace = Namespace::new("cw-plus")?;
571
572            let query_msg = QueryMsg::Modules {
573                infos: vec![
574                    ModuleInfo {
575                        namespace: namespace.clone(),
576                        name: "module1".to_string(),
577                        version: ModuleVersion::Latest {},
578                    },
579                    ModuleInfo {
580                        namespace: namespace.clone(),
581                        name: "module2".to_string(),
582                        version: ModuleVersion::Latest {},
583                    },
584                    ModuleInfo {
585                        namespace: namespace.clone(),
586                        name: "module3".to_string(),
587                        version: ModuleVersion::Latest {},
588                    },
589                ],
590            };
591
592            let ModulesResponse { modules } = from_json(query_helper(deps.as_ref(), query_msg)?)?;
593            assert_that!(modules).has_length(3);
594            for module in modules {
595                assert_that!(module.module.info.namespace).is_equal_to(namespace.clone());
596                assert_that!(module.module.info.version)
597                    .is_equal_to(&ModuleVersion::Version("0.1.2".into()));
598            }
599            Ok(())
600        }
601
602        #[test]
603        fn get_modules_not_found() -> VersionControlTestResult {
604            let mut deps = mock_dependencies();
605            deps.querier = mock_manager_querier().build();
606            init_with_mods(deps.as_mut());
607
608            let query_msg = QueryMsg::Modules {
609                infos: vec![ModuleInfo {
610                    namespace: Namespace::new("not")?,
611                    name: "found".to_string(),
612                    version: ModuleVersion::Latest {},
613                }],
614            };
615
616            let res = query_helper(deps.as_ref(), query_msg);
617            assert_that!(res)
618                .is_err()
619                .matches(|e| matches!(e, VCError::Std(StdError::GenericErr { .. })));
620            Ok(())
621        }
622    }
623
624    mod list_modules {
625        use super::*;
626
627        fn filtered_list_msg(filter: ModuleFilter) -> QueryMsg {
628            QueryMsg::ModuleList {
629                filter: Some(filter),
630                start_after: None,
631                limit: None,
632            }
633        }
634
635        #[test]
636        fn filter_by_namespace_existing() {
637            let mut deps = mock_dependencies();
638            deps.querier = mock_manager_querier().build();
639            init_with_mods(deps.as_mut());
640            let filtered_namespace = "cw-plus".to_string();
641
642            let filter = ModuleFilter {
643                namespace: Some(filtered_namespace.clone()),
644                ..Default::default()
645            };
646            let list_msg = filtered_list_msg(filter);
647
648            let res = query_helper(deps.as_ref(), list_msg);
649
650            assert_that!(res).is_ok().map(|res| {
651                let ModulesListResponse { modules } = from_json(res).unwrap();
652                assert_that!(modules).has_length(3);
653
654                for entry in modules {
655                    assert_that!(entry.module.info.namespace)
656                        .is_equal_to(Namespace::unchecked(filtered_namespace.clone()));
657                }
658
659                res
660            });
661        }
662
663        #[test]
664        fn filter_default_returns_only_non_yanked() {
665            let mut deps = mock_dependencies();
666            deps.querier = mock_manager_querier().build();
667            init_with_mods(deps.as_mut());
668
669            let cw_mods = vec![
670                ModuleInfo::from_id("cw-plus:module4", ModuleVersion::Version("0.1.2".into()))
671                    .unwrap(),
672                ModuleInfo::from_id("cw-plus:module5", ModuleVersion::Version("0.1.2".into()))
673                    .unwrap(),
674            ];
675            propose_modules(deps.as_mut(), cw_mods, OWNER);
676            yank_module(
677                deps.as_mut(),
678                ModuleInfo::from_id("cw-plus:module4", ModuleVersion::Version("0.1.2".into()))
679                    .unwrap(),
680            );
681            yank_module(
682                deps.as_mut(),
683                ModuleInfo::from_id("cw-plus:module5", ModuleVersion::Version("0.1.2".into()))
684                    .unwrap(),
685            );
686
687            let list_msg = QueryMsg::ModuleList {
688                filter: None,
689                start_after: None,
690                limit: None,
691            };
692
693            let res = query_helper(deps.as_ref(), list_msg);
694
695            assert_that!(res).is_ok().map(|res| {
696                let ModulesListResponse { modules } = from_json(res).unwrap();
697                assert_that!(modules).has_length(6);
698
699                let yanked_module_names = ["module4".to_string(), "module5".to_string()];
700                for entry in modules {
701                    if entry.module.info.namespace == Namespace::unchecked("cw-plus") {
702                        assert!(!yanked_module_names
703                            .iter()
704                            .any(|e| e == &entry.module.info.name));
705                    }
706                }
707
708                res
709            });
710        }
711
712        #[test]
713        fn filter_yanked_by_namespace_existing() {
714            let mut deps = mock_dependencies();
715            deps.querier = mock_manager_querier().build();
716            init_with_mods(deps.as_mut());
717
718            let cw_mods = vec![
719                ModuleInfo::from_id("cw-plus:module4", ModuleVersion::Version("0.1.2".into()))
720                    .unwrap(),
721                ModuleInfo::from_id("cw-plus:module5", ModuleVersion::Version("0.1.2".into()))
722                    .unwrap(),
723            ];
724            propose_modules(deps.as_mut(), cw_mods, OWNER);
725            yank_module(
726                deps.as_mut(),
727                ModuleInfo::from_id("cw-plus:module4", ModuleVersion::Version("0.1.2".into()))
728                    .unwrap(),
729            );
730            yank_module(
731                deps.as_mut(),
732                ModuleInfo::from_id("cw-plus:module5", ModuleVersion::Version("0.1.2".into()))
733                    .unwrap(),
734            );
735
736            let filtered_namespace = "cw-plus".to_string();
737
738            let filter = ModuleFilter {
739                status: Some(ModuleStatus::Yanked),
740                namespace: Some(filtered_namespace.clone()),
741                ..Default::default()
742            };
743            let list_msg = QueryMsg::ModuleList {
744                filter: Some(filter),
745                start_after: None,
746                limit: None,
747            };
748
749            let res = query_helper(deps.as_ref(), list_msg);
750
751            assert_that!(res).is_ok().map(|res| {
752                let ModulesListResponse { modules } = from_json(res).unwrap();
753                assert_that!(modules).has_length(2);
754
755                for entry in modules {
756                    assert_that!(entry.module.info.namespace)
757                        .is_equal_to(Namespace::unchecked(filtered_namespace.clone()));
758                }
759
760                res
761            });
762        }
763
764        #[test]
765        fn filter_by_namespace_non_existing() {
766            let mut deps = mock_dependencies();
767            deps.querier = mock_manager_querier().build();
768            mock_init_with_account(deps.as_mut()).unwrap();
769            add_namespaces(deps.as_mut(), vec![(TEST_ACCOUNT_ID, "cw-plus")], OWNER);
770            let cw_mods = vec![ModuleInfo::from_id(
771                "cw-plus:module1",
772                ModuleVersion::Version("0.1.2".into()),
773            )
774            .unwrap()];
775            propose_modules(deps.as_mut(), cw_mods, OWNER);
776
777            let filtered_namespace = "cw-plus".to_string();
778
779            let filter = ModuleFilter {
780                namespace: Some(filtered_namespace),
781                ..Default::default()
782            };
783
784            let list_msg = filtered_list_msg(filter);
785
786            let res = query_helper(deps.as_ref(), list_msg);
787
788            assert_that!(res).is_ok().map(|res| {
789                let ModulesListResponse { modules } = from_json(res).unwrap();
790                assert_that!(modules).has_length(1);
791
792                res
793            });
794        }
795
796        #[test]
797        fn filter_by_namespace_and_name() {
798            let mut deps = mock_dependencies();
799            deps.querier = mock_manager_querier().build();
800            init_with_mods(deps.as_mut());
801
802            let filtered_namespace = "cw-plus".to_string();
803            let filtered_name = "module2".to_string();
804
805            let filter = ModuleFilter {
806                namespace: Some(filtered_namespace.clone()),
807                name: Some(filtered_name.clone()),
808                ..Default::default()
809            };
810
811            let list_msg = filtered_list_msg(filter);
812
813            let res = query_helper(deps.as_ref(), list_msg);
814
815            assert_that!(res).is_ok().map(|res| {
816                let ModulesListResponse { modules } = from_json(res).unwrap();
817                assert_that!(modules).has_length(1);
818
819                let module = modules[0].clone();
820                assert_that!(module.module.info.namespace)
821                    .is_equal_to(Namespace::unchecked(filtered_namespace.clone()));
822                assert_that!(module.module.info.name).is_equal_to(filtered_name.clone());
823                res
824            });
825        }
826
827        #[test]
828        fn filter_by_namespace_and_name_with_multiple_versions() {
829            let mut deps = mock_dependencies();
830            deps.querier = mock_manager_querier().build();
831            init_with_mods(deps.as_mut());
832
833            let filtered_namespace = "cw-plus".to_string();
834            let filtered_name = "module2".to_string();
835
836            propose_modules(
837                deps.as_mut(),
838                vec![ModuleInfo::from_id(
839                    format!("{filtered_namespace}:{filtered_name}").as_str(),
840                    ModuleVersion::Version("0.1.3".into()),
841                )
842                .unwrap()],
843                OWNER,
844            );
845
846            let filter = ModuleFilter {
847                namespace: Some(filtered_namespace.clone()),
848                name: Some(filtered_name.clone()),
849                ..Default::default()
850            };
851
852            let list_msg = filtered_list_msg(filter);
853
854            let res = query_helper(deps.as_ref(), list_msg);
855
856            assert_that!(res).is_ok().map(|res| {
857                let ModulesListResponse { modules } = from_json(res).unwrap();
858                assert_that!(modules).has_length(2);
859
860                for module in modules {
861                    assert_that!(module.module.info.namespace)
862                        .is_equal_to(Namespace::unchecked(filtered_namespace.clone()));
863                    assert_that!(module.module.info.name).is_equal_to(filtered_name.clone());
864                }
865                res
866            });
867        }
868
869        #[test]
870        fn filter_by_only_version_many() {
871            let mut deps = mock_dependencies();
872            deps.querier = mock_manager_querier().build();
873            init_with_mods(deps.as_mut());
874
875            let filtered_version = "0.1.2".to_string();
876
877            let filter = ModuleFilter {
878                version: Some(filtered_version.clone()),
879                ..Default::default()
880            };
881
882            let list_msg = filtered_list_msg(filter);
883
884            let res = query_helper(deps.as_ref(), list_msg);
885
886            assert_that!(res).is_ok().map(|res| {
887                let ModulesListResponse { modules } = from_json(res).unwrap();
888                assert_that!(modules).has_length(6);
889
890                for module in modules {
891                    assert_that!(module.module.info.version.to_string())
892                        .is_equal_to(filtered_version.clone());
893                }
894                res
895            });
896        }
897
898        #[test]
899        fn filter_by_only_version_none() {
900            let mut deps = mock_dependencies();
901            deps.querier = mock_manager_querier().build();
902            init_with_mods(deps.as_mut());
903
904            let filtered_version = "5555".to_string();
905
906            let filter = ModuleFilter {
907                version: Some(filtered_version),
908                ..Default::default()
909            };
910
911            let list_msg = filtered_list_msg(filter);
912
913            let res = query_helper(deps.as_ref(), list_msg);
914
915            assert_that!(res).is_ok().map(|res| {
916                let ModulesListResponse { modules } = from_json(res).unwrap();
917                assert_that!(modules).is_empty();
918
919                res
920            });
921        }
922
923        #[test]
924        fn filter_by_name_and_version() {
925            let mut deps = mock_dependencies();
926            deps.querier = mock_manager_querier().build();
927            init_with_mods(deps.as_mut());
928
929            let filtered_name = "module2".to_string();
930            let filtered_version = "0.1.2".to_string();
931
932            let filter = ModuleFilter {
933                name: Some(filtered_name.clone()),
934                version: Some(filtered_version.clone()),
935                ..Default::default()
936            };
937
938            let list_msg = filtered_list_msg(filter);
939
940            let res = query_helper(deps.as_ref(), list_msg);
941
942            assert_that!(res).is_ok().map(|res| {
943                let ModulesListResponse { modules } = from_json(res).unwrap();
944                // We expect two because both cw-plus and snth have a module2 with version 0.1.2
945                assert_that!(modules).has_length(2);
946
947                for module in modules {
948                    assert_that!(module.module.info.name).is_equal_to(filtered_name.clone());
949                    assert_that!(module.module.info.version.to_string())
950                        .is_equal_to(filtered_version.clone());
951                }
952                res
953            });
954        }
955
956        #[test]
957        fn filter_by_namespace_and_version() {
958            let mut deps = mock_dependencies();
959            deps.querier = mock_manager_querier().build();
960            init_with_mods(deps.as_mut());
961
962            let filtered_namespace = "cw-plus".to_string();
963            let filtered_version = "0.1.2".to_string();
964
965            let filter = ModuleFilter {
966                namespace: Some(filtered_namespace.clone()),
967                version: Some(filtered_version.clone()),
968                ..Default::default()
969            };
970
971            let list_msg = filtered_list_msg(filter);
972
973            let res = query_helper(deps.as_ref(), list_msg);
974
975            assert_that!(res).is_ok().map(|res| {
976                let ModulesListResponse { modules } = from_json(res).unwrap();
977                assert_that!(modules).has_length(3);
978
979                for module in modules {
980                    assert_that!(module.module.info.namespace)
981                        .is_equal_to(Namespace::unchecked(filtered_namespace.clone()));
982                    assert_that!(module.module.info.version.to_string())
983                        .is_equal_to(filtered_version.clone());
984                }
985
986                res
987            });
988        }
989    }
990
991    mod query_namespaces {
992        use super::*;
993
994        #[test]
995        fn namespaces() {
996            let mut deps = mock_dependencies();
997            deps.querier = mock_manager_querier().build();
998            init_with_mods(deps.as_mut());
999
1000            // get for test other account
1001            let res = query_helper(
1002                deps.as_ref(),
1003                QueryMsg::Namespaces {
1004                    accounts: vec![TEST_OTHER_ACCOUNT_ID],
1005                },
1006            );
1007            assert_that!(res).is_ok().map(|res| {
1008                let NamespacesResponse { namespaces } = from_json(res).unwrap();
1009                assert_that!(namespaces[0].0.to_string()).is_equal_to("4t2".to_string());
1010                res
1011            });
1012        }
1013    }
1014
1015    mod handle_account_address_query {
1016        use super::*;
1017
1018        #[test]
1019        fn not_registered_should_be_unknown() -> VersionControlTestResult {
1020            let mut deps = mock_dependencies();
1021            mock_init(deps.as_mut())?;
1022
1023            let not_registered = AccountId::new(15, AccountTrace::Local)?;
1024            let res = query_helper(
1025                deps.as_ref(),
1026                QueryMsg::AccountBase {
1027                    account_id: not_registered.clone(),
1028                },
1029            );
1030
1031            // let res2 = from_json(res.unwrap())?;
1032
1033            assert_that!(res)
1034                .is_err()
1035                .is_equal_to(VCError::Std(StdError::generic_err(
1036                    VCError::UnknownAccountId { id: not_registered }.to_string(),
1037                )));
1038
1039            Ok(())
1040        }
1041
1042        #[test]
1043        fn registered_should_return_account_base() -> VersionControlTestResult {
1044            let mut deps = mock_dependencies();
1045            mock_init_with_account(deps.as_mut())?;
1046
1047            let res = query_helper(
1048                deps.as_ref(),
1049                QueryMsg::AccountBase {
1050                    account_id: TEST_ACCOUNT_ID,
1051                },
1052            );
1053
1054            assert_that!(res).is_ok().map(|res| {
1055                let AccountBaseResponse { account_base } = from_json(res).unwrap();
1056                assert_that!(account_base).is_equal_to(test_account_base());
1057                res
1058            });
1059
1060            Ok(())
1061        }
1062    }
1063}