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 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) => ®ISTERED_MODULES,
101 Some(ModuleStatus::Pending) => &PENDING_MODULES,
102 Some(ModuleStatus::Yanked) => &YANKED_MODULES,
103 None => ®ISTERED_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 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 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
202fn 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 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 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, 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, 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 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 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 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 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 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 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 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 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}