Skip to main content

axone_objectarium/
contract.rs

1use crate::error::BucketError;
2#[cfg(not(feature = "library"))]
3use cosmwasm_std::entry_point;
4use cosmwasm_std::{to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
5use cw2::set_contract_version;
6use cw_utils::nonpayable;
7
8use crate::crypto;
9use crate::error::ContractError;
10use crate::msg::{ExecuteMsg, InstantiateMsg, ObjectId, QueryMsg};
11use crate::state;
12use crate::state::{objects, pins, Bucket, Object, Pin, BUCKET, DATA};
13
14// version info for migration info
15const CONTRACT_NAME: &str = concat!("crates.io:", env!("CARGO_PKG_NAME"));
16const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
17
18#[cfg_attr(not(feature = "library"), entry_point)]
19pub fn instantiate(
20    deps: DepsMut<'_>,
21    _env: Env,
22    info: MessageInfo,
23    msg: InstantiateMsg,
24) -> Result<Response, ContractError> {
25    nonpayable(&info)?;
26    let bucket = Bucket::try_new(
27        info.sender,
28        msg.bucket,
29        msg.config.try_into()?,
30        msg.limits.try_into()?,
31        msg.pagination.try_into()?,
32    )?;
33
34    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
35    BUCKET.save(deps.storage, &bucket)?;
36
37    Ok(Response::default())
38}
39
40#[cfg_attr(not(feature = "library"), entry_point)]
41pub fn execute(
42    deps: DepsMut<'_>,
43    _env: Env,
44    info: MessageInfo,
45    msg: ExecuteMsg,
46) -> Result<Response, ContractError> {
47    nonpayable(&info)?;
48
49    match msg {
50        ExecuteMsg::StoreObject {
51            data,
52            pin,
53            compression_algorithm,
54        } => execute::store_object(deps, info, data, pin, compression_algorithm),
55        ExecuteMsg::PinObject { id } => execute::pin_object(deps, info, id),
56        ExecuteMsg::UnpinObject { id } => execute::unpin_object(deps, info, id),
57        ExecuteMsg::ForgetObject { id } => execute::forget_object(deps, info, id),
58    }
59}
60
61pub mod execute {
62    use super::*;
63    use crate::compress::CompressionAlgorithm;
64    use crate::crypto::Hash;
65    use crate::msg;
66    use crate::state::BucketLimits;
67    use crate::ContractError::ObjectPinned;
68    use cosmwasm_std::{Addr, Order, Storage, Uint128};
69
70    pub fn store_object(
71        deps: DepsMut<'_>,
72        info: MessageInfo,
73        data: Binary,
74        pin: bool,
75        compression_algorithm: Option<msg::CompressionAlgorithm>,
76    ) -> Result<Response, ContractError> {
77        let size = (data.len() as u128).into();
78        let bucket = BUCKET.load(deps.storage)?;
79        let compressions = &bucket.config.accepted_compression_algorithms;
80        let compression: CompressionAlgorithm = compression_algorithm
81            .map(Into::into)
82            .or_else(|| compressions.first().cloned())
83            .unwrap_or(CompressionAlgorithm::Passthrough);
84
85        // pre-conditions
86        if let Some(limit) = bucket.limits.max_object_size {
87            if size > limit {
88                return Err(BucketError::MaxObjectSizeLimitExceeded(size, limit).into());
89            }
90        }
91        if let Some(limit) = bucket.limits.max_objects {
92            let value = bucket.stat.object_count + Uint128::one();
93            if value > limit {
94                return Err(BucketError::MaxObjectsLimitExceeded(value, limit).into());
95            }
96        }
97        if let Some(limit) = bucket.limits.max_object_pins {
98            if pin && limit.is_zero() {
99                return Err(BucketError::MaxObjectPinsLimitExceeded(Uint128::one(), limit).into());
100            }
101        }
102        if let Some(limit) = bucket.limits.max_total_size {
103            let value = bucket.stat.size + size;
104            if value > limit {
105                return Err(BucketError::MaxTotalSizeLimitExceeded(value, limit).into());
106            }
107        }
108        if !compressions.contains(&compression) {
109            return Err(BucketError::CompressionAlgorithmNotAccepted(
110                compression.into(),
111                bucket
112                    .config
113                    .accepted_compression_algorithms
114                    .into_iter()
115                    .map(Into::into)
116                    .collect(),
117            )
118            .into());
119        }
120
121        // store object data
122        let id = crypto::hash(&bucket.config.hash_algorithm.into(), &data.to_vec());
123        let mut res = Response::new()
124            .add_attribute("action", "store_object")
125            .add_attribute("id", id.to_string());
126
127        let data_path = DATA.key(id.clone());
128
129        let (old_obj, mut new_obj) = if !data_path.has(deps.storage) {
130            let compressed_data = compression.compress(&data)?;
131            data_path.save(deps.storage, &compressed_data)?;
132
133            let compressed_size = (compressed_data.len() as u128).into();
134
135            // save bucket stats
136            BUCKET.update(deps.storage, |mut bucket| -> Result<_, ContractError> {
137                let stat = &mut bucket.stat;
138                stat.size += size;
139                stat.object_count += Uint128::one();
140                stat.compressed_size += compressed_size;
141                Ok(bucket)
142            })?;
143
144            res = res
145                .add_attribute("size", size)
146                .add_attribute("compressed_size", compressed_size);
147
148            (
149                None,
150                Object {
151                    id: id.clone(),
152                    owner: info.sender.clone(),
153                    size,
154                    pin_count: Uint128::zero(),
155                    compression,
156                    compressed_size,
157                },
158            )
159        } else {
160            let old = objects().load(deps.storage, id.clone())?;
161            (Some(old.clone()), old)
162        };
163
164        let mut pinned = false;
165        if pin {
166            pinned = may_pin_object(deps.storage, info.sender, &mut new_obj)?;
167        }
168
169        objects().replace(deps.storage, id, Some(&new_obj), old_obj.as_ref())?;
170
171        Ok(res.add_attribute("pinned", pinned.to_string()))
172    }
173
174    pub fn pin_object(
175        deps: DepsMut<'_>,
176        info: MessageInfo,
177        object_id: ObjectId,
178    ) -> Result<Response, ContractError> {
179        let res = Response::new()
180            .add_attribute("action", "pin_object")
181            .add_attribute("id", object_id.clone());
182
183        let id: Hash = object_id.try_into()?;
184        let object = objects().load(deps.storage, id.clone())?;
185        let mut updated_object = object.clone();
186
187        if may_pin_object(deps.storage, info.sender, &mut updated_object)? {
188            objects().replace(deps.storage, id, Some(&updated_object), Some(&object))?;
189        }
190
191        Ok(res)
192    }
193
194    pub fn unpin_object(
195        deps: DepsMut<'_>,
196        info: MessageInfo,
197        object_id: ObjectId,
198    ) -> Result<Response, ContractError> {
199        let id: Hash = object_id.clone().try_into()?;
200        let object_path = objects().key(id.clone());
201        let mut object = object_path.load(deps.storage)?;
202
203        let res = Response::new()
204            .add_attribute("action", "unpin_object")
205            .add_attribute("id", object_id);
206
207        if !pins().has(deps.storage, (id.clone(), info.sender.clone())) {
208            return Ok(res);
209        }
210
211        object.pin_count -= Uint128::one();
212        object_path.save(deps.storage, &object)?;
213
214        pins().remove(deps.storage, (id, info.sender))?;
215
216        Ok(res)
217    }
218
219    pub fn forget_object(
220        deps: DepsMut<'_>,
221        info: MessageInfo,
222        object_id: ObjectId,
223    ) -> Result<Response, ContractError> {
224        let id: Hash = object_id.clone().try_into()?;
225        if pins().has(deps.storage, (id.clone(), info.sender.clone())) {
226            pins().remove(deps.storage, (id.clone(), info.sender))?;
227        }
228
229        if pins()
230            .idx
231            .object
232            .prefix(id.clone())
233            .keys_raw(deps.storage, None, None, Order::Ascending)
234            .next()
235            .is_some()
236        {
237            return Err(ObjectPinned {});
238        }
239        let object = query::object(deps.as_ref(), object_id.clone())?;
240        BUCKET.update(deps.storage, |mut b| -> Result<_, ContractError> {
241            b.stat.object_count -= Uint128::one();
242            b.stat.size -= object.size;
243            b.stat.compressed_size -= object.compressed_size;
244            Ok(b)
245        })?;
246
247        objects().remove(deps.storage, id.clone())?;
248        DATA.remove(deps.storage, id);
249
250        Ok(Response::new()
251            .add_attribute("action", "forget_object")
252            .add_attribute("id", object_id))
253    }
254
255    fn may_pin_object(
256        storage: &mut dyn Storage,
257        pinner: Addr,
258        target: &mut Object,
259    ) -> Result<bool, ContractError> {
260        if pins().has(storage, (target.id.clone(), pinner.clone())) {
261            return Ok(false);
262        }
263
264        target.pin_count += Uint128::one();
265
266        let bucket = BUCKET.load(storage)?;
267
268        match bucket.limits {
269            BucketLimits {
270                max_object_pins: Some(max),
271                ..
272            } if max < target.pin_count => {
273                Err(BucketError::MaxObjectPinsLimitExceeded(target.pin_count, max).into())
274            }
275            _ => {
276                pins().save(
277                    storage,
278                    (target.id.clone(), pinner.clone()),
279                    &Pin {
280                        id: target.id.clone(),
281                        address: pinner,
282                    },
283                )?;
284                Ok(true)
285            }
286        }
287    }
288}
289
290#[cfg_attr(not(feature = "library"), entry_point)]
291pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
292    match msg {
293        QueryMsg::Bucket {} => to_json_binary(&query::bucket(deps)?),
294        QueryMsg::Object { id } => to_json_binary(&query::object(deps, id)?),
295        QueryMsg::ObjectData { id } => to_json_binary(&query::data(deps, id)?),
296        QueryMsg::Objects {
297            address,
298            after,
299            first,
300        } => to_json_binary(&query::fetch_objects(deps, address, after, first)?),
301        QueryMsg::ObjectPins { id, after, first } => {
302            to_json_binary(&query::object_pins(deps, id, after, first)?)
303        }
304    }
305}
306
307pub mod query {
308    use super::*;
309    use crate::crypto::Hash;
310    use crate::cursor;
311    use crate::msg::{
312        BucketResponse, Cursor, ObjectPinsResponse, ObjectResponse, ObjectsResponse, PageInfo,
313    };
314    use crate::pagination::{PaginationHandler, QueryPage};
315    use cosmwasm_std::{Addr, Order, StdError};
316
317    pub fn bucket(deps: Deps<'_>) -> StdResult<BucketResponse> {
318        let bucket = BUCKET.load(deps.storage)?;
319
320        Ok(BucketResponse {
321            name: bucket.name,
322            config: bucket.config.into(),
323            limits: bucket.limits.into(),
324            pagination: bucket.pagination.into(),
325            stat: bucket.stat.into(),
326        })
327    }
328
329    pub fn object(deps: Deps<'_>, object_id: ObjectId) -> StdResult<ObjectResponse> {
330        let id: Hash = object_id.try_into()?;
331        let object = objects().load(deps.storage, id)?;
332        Ok((&object).into())
333    }
334
335    pub fn data(deps: Deps<'_>, object_id: ObjectId) -> StdResult<Binary> {
336        let id: Hash = object_id.try_into()?;
337        let compression = objects().load(deps.storage, id.clone())?.compression;
338        let data = DATA.load(deps.storage, id)?;
339
340        compression
341            .decompress(&data)
342            .map_err(|e| StdError::serialize_err(format!("{:?}", compression), e))
343            .map(Binary::from)
344    }
345
346    pub fn fetch_objects(
347        deps: Deps<'_>,
348        address: Option<String>,
349        after: Option<Cursor>,
350        first: Option<u32>,
351    ) -> StdResult<ObjectsResponse> {
352        let address = match address {
353            Some(raw) => Some(deps.api.addr_validate(&raw)?),
354            _ => None,
355        };
356
357        let handler: PaginationHandler<'_, Object, Hash> =
358            PaginationHandler::from(BUCKET.load(deps.storage)?.pagination);
359
360        let page: (Vec<Object>, PageInfo) = handler.query_page(
361            |min_bound| match address {
362                Some(addr) => objects().idx.owner.prefix(addr).range(
363                    deps.storage,
364                    min_bound,
365                    None,
366                    Order::Ascending,
367                ),
368                _ => objects().range(deps.storage, min_bound, None, Order::Ascending),
369            },
370            after,
371            first,
372        )?;
373
374        Ok(ObjectsResponse {
375            data: page.0.iter().map(Into::into).collect(),
376            page_info: page.1,
377        })
378    }
379
380    pub fn object_pins(
381        deps: Deps<'_>,
382        object_id: ObjectId,
383        after: Option<Cursor>,
384        first: Option<u32>,
385    ) -> StdResult<ObjectPinsResponse> {
386        let id: Hash = object_id.try_into()?;
387        objects().load(deps.storage, id.clone())?;
388
389        let handler: PaginationHandler<'_, Pin, (Hash, Addr)> =
390            PaginationHandler::from(BUCKET.load(deps.storage)?.pagination);
391
392        let page: (Vec<Pin>, PageInfo) = handler.query_page_cursor_fn(
393            |min_bound| {
394                pins().idx.object.prefix(id.clone()).range(
395                    deps.storage,
396                    min_bound,
397                    None,
398                    Order::Ascending,
399                )
400            },
401            |c| {
402                cursor::decode(c)
403                    .and_then(|raw| deps.api.addr_validate(raw.as_str()))
404                    .map(|addr| (id.clone(), addr))
405            },
406            |pin: &Pin| cursor::encode(pin.clone().address.into_string()),
407            after,
408            first,
409        )?;
410
411        Ok(ObjectPinsResponse {
412            data: page
413                .0
414                .iter()
415                .map(|pin: &Pin| pin.address.as_str().to_string())
416                .collect(),
417            page_info: page.1,
418        })
419    }
420}
421
422impl From<state::HashAlgorithm> for crypto::HashAlgorithm {
423    fn from(algorithm: state::HashAlgorithm) -> Self {
424        match algorithm {
425            state::HashAlgorithm::MD5 => crypto::HashAlgorithm::MD5,
426            state::HashAlgorithm::Sha224 => crypto::HashAlgorithm::Sha224,
427            state::HashAlgorithm::Sha256 => crypto::HashAlgorithm::Sha256,
428            state::HashAlgorithm::Sha384 => crypto::HashAlgorithm::Sha384,
429            state::HashAlgorithm::Sha512 => crypto::HashAlgorithm::Sha512,
430        }
431    }
432}
433
434#[cfg(test)]
435mod tests {
436    use super::*;
437    use crate::compress;
438    use crate::crypto::Hash;
439    use crate::error::BucketError;
440    use crate::msg::{
441        BucketConfig, BucketConfigBuilder, BucketLimitsBuilder, BucketResponse, BucketStat,
442        BucketStatBuilder, CompressionAlgorithm, HashAlgorithm, ObjectPinsResponse, ObjectResponse,
443        ObjectsResponse, PageInfo, PaginationConfigBuilder,
444    };
445    use base64::{engine::general_purpose, Engine as _};
446    use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env};
447    use cosmwasm_std::StdError::NotFound;
448    use cosmwasm_std::{coins, from_json, Addr, Attribute, Order, StdError, Uint128};
449    use cw_utils::PaymentError;
450
451    use crate::msg::CompressionAlgorithm::{Passthrough, Snappy};
452    use std::any::type_name;
453    use testing::addr::{addr, CREATOR, SENDER};
454    use testing::mock::mock_env_addr;
455
456    fn decode_hex(hex: &str) -> Vec<u8> {
457        base16ct::lower::decode_vec(hex).unwrap()
458    }
459
460    fn with_namespace(key: &[u8]) -> Vec<u8> {
461        let namespace = decode_hex("00064f424a454354");
462        let mut v = Vec::with_capacity(namespace.len() + key.len());
463        v.extend(namespace);
464        v.extend_from_slice(key);
465        v
466    }
467
468    fn not_found_object_info<T>(hex: &str) -> String {
469        let type_name = type_name::<T>();
470        let key = with_namespace(&decode_hex(hex));
471        format!("type: {type_name}; key: {:02X?}", key)
472    }
473
474    #[test]
475    fn proper_initialization() {
476        let mut deps = mock_dependencies();
477
478        let msg = InstantiateMsg {
479            bucket: "foo".to_string(),
480            config: Default::default(),
481            limits: Default::default(),
482            pagination: Default::default(),
483        };
484        let info = message_info(&addr(CREATOR), &[]);
485
486        let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
487        assert_eq!(0, res.messages.len());
488
489        let res = query(deps.as_ref(), mock_env(), QueryMsg::Bucket {}).unwrap();
490        let value: BucketResponse = from_json(&res).unwrap();
491        assert_eq!("foo", value.name);
492        assert_eq!(value.config, Default::default());
493        assert_eq!(value.limits, Default::default());
494        assert_eq!(value.pagination.max_page_size, 30);
495        assert_eq!(value.pagination.default_page_size, 10);
496
497        // check internal state too
498        let bucket = BUCKET.load(&deps.storage).unwrap();
499        assert_eq!(addr(CREATOR), bucket.owner);
500        assert_eq!(Uint128::zero(), bucket.stat.size);
501        assert_eq!(Uint128::zero(), bucket.stat.object_count);
502    }
503
504    #[test]
505    fn proper_config_initialization() {
506        let mut deps = mock_dependencies();
507
508        // Define the test cases
509        let test_cases = vec![
510            (HashAlgorithm::MD5, HashAlgorithm::MD5),
511            (HashAlgorithm::Sha224, HashAlgorithm::Sha224),
512            (HashAlgorithm::Sha256, HashAlgorithm::Sha256),
513            (HashAlgorithm::Sha384, HashAlgorithm::Sha384),
514            (HashAlgorithm::Sha512, HashAlgorithm::Sha512),
515        ];
516
517        for (hash_algorithm, expected_hash_algorithm) in test_cases {
518            let msg = InstantiateMsg {
519                bucket: "bar".to_string(),
520                config: BucketConfig {
521                    hash_algorithm,
522                    ..Default::default()
523                },
524                limits: Default::default(),
525                pagination: Default::default(),
526            };
527            let info = message_info(&addr(CREATOR), &[]);
528
529            let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
530
531            let res = query(deps.as_ref(), mock_env(), QueryMsg::Bucket {}).unwrap();
532            let value: BucketResponse = from_json(&res).unwrap();
533
534            assert_eq!("bar", value.name);
535            assert_eq!(value.config.hash_algorithm, expected_hash_algorithm);
536        }
537    }
538
539    #[test]
540    fn proper_limits_initialization() {
541        let mut deps = mock_dependencies();
542
543        let msg = InstantiateMsg {
544            bucket: "bar".to_string(),
545            config: Default::default(),
546            limits: BucketLimitsBuilder::default()
547                .max_total_size(Uint128::new(20000))
548                .max_objects(Uint128::new(10))
549                .max_object_size(Uint128::new(2000))
550                .max_object_pins(Uint128::new(1))
551                .build()
552                .unwrap(),
553            pagination: PaginationConfigBuilder::default()
554                .max_page_size(50)
555                .default_page_size(30)
556                .build()
557                .unwrap(),
558        };
559        let info = message_info(&addr(CREATOR), &[]);
560        let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
561
562        let res = query(deps.as_ref(), mock_env(), QueryMsg::Bucket {}).unwrap();
563        let response: BucketResponse = from_json(&res).unwrap();
564        assert_eq!(response.name, "bar");
565        assert_eq!(
566            response.limits,
567            BucketLimitsBuilder::default()
568                .max_total_size(Uint128::new(20000))
569                .max_objects(Uint128::new(10))
570                .max_object_size(Uint128::new(2000))
571                .max_object_pins(Uint128::new(1))
572                .build()
573                .unwrap(),
574        );
575        assert_eq!(
576            response.pagination,
577            PaginationConfigBuilder::default()
578                .max_page_size(50)
579                .default_page_size(30)
580                .build()
581                .unwrap(),
582        );
583        assert_eq!(response.stat, BucketStat::default());
584    }
585
586    #[test]
587    fn proper_pagination_initialization() {
588        let mut deps = mock_dependencies();
589        let msg = InstantiateMsg {
590            bucket: "bar".to_string(),
591            config: Default::default(),
592            limits: Default::default(),
593            pagination: PaginationConfigBuilder::default()
594                .max_page_size(50)
595                .default_page_size(30)
596                .build()
597                .unwrap(),
598        };
599        instantiate(
600            deps.as_mut(),
601            mock_env(),
602            message_info(&addr(CREATOR), &[]),
603            msg,
604        )
605        .unwrap();
606
607        let res = query(deps.as_ref(), mock_env(), QueryMsg::Bucket {}).unwrap();
608        let value: BucketResponse = from_json(&res).unwrap();
609        assert_eq!(value.pagination.max_page_size, 50);
610        assert_eq!(value.pagination.default_page_size, 30);
611    }
612
613    #[test]
614    fn invalid_initialization() {
615        let cases = vec![
616            (
617                Default::default(),
618                Default::default(),
619                PaginationConfigBuilder::default()
620                    .max_page_size(u32::MAX)
621                    .build()
622                    .unwrap(),
623                Some(StdError::generic_err(
624                    "'max_page_size' cannot exceed 'u32::MAX - 1'",
625                )),
626            ),
627            (
628                Default::default(),
629                Default::default(),
630                PaginationConfigBuilder::default()
631                    .default_page_size(31)
632                    .build()
633                    .unwrap(),
634                Some(StdError::generic_err(
635                    "'default_page_size' cannot exceed 'max_page_size'",
636                )),
637            ),
638            (
639                Default::default(),
640                Default::default(),
641                PaginationConfigBuilder::default()
642                    .default_page_size(701)
643                    .max_page_size(700)
644                    .build()
645                    .unwrap(),
646                Some(StdError::generic_err(
647                    "'default_page_size' cannot exceed 'max_page_size'",
648                )),
649            ),
650            (
651                Default::default(),
652                Default::default(),
653                PaginationConfigBuilder::default()
654                    .default_page_size(20)
655                    .max_page_size(20)
656                    .build()
657                    .unwrap(),
658                None,
659            ),
660            (
661                Default::default(),
662                Default::default(),
663                PaginationConfigBuilder::default()
664                    .default_page_size(0)
665                    .build()
666                    .unwrap(),
667                Some(StdError::generic_err("'default_page_size' cannot be zero")),
668            ),
669            (
670                Default::default(),
671                BucketLimitsBuilder::default()
672                    .max_objects(0u128)
673                    .build()
674                    .unwrap(),
675                Default::default(),
676                Some(StdError::generic_err("'max_objects' cannot be zero")),
677            ),
678            (
679                Default::default(),
680                BucketLimitsBuilder::default()
681                    .max_object_size(0u128)
682                    .build()
683                    .unwrap(),
684                Default::default(),
685                Some(StdError::generic_err("'max_object_size' cannot be zero")),
686            ),
687            (
688                Default::default(),
689                BucketLimitsBuilder::default()
690                    .max_total_size(0u128)
691                    .build()
692                    .unwrap(),
693                Default::default(),
694                Some(StdError::generic_err("'max_total_size' cannot be zero")),
695            ),
696            (
697                Default::default(),
698                BucketLimitsBuilder::default()
699                    .max_total_size(10u128)
700                    .max_object_size(20u128)
701                    .build()
702                    .unwrap(),
703                Default::default(),
704                Some(StdError::generic_err(
705                    "'max_total_size' cannot be less than 'max_object_size'",
706                )),
707            ),
708            (
709                Default::default(),
710                BucketLimitsBuilder::default()
711                    .max_total_size(20u128)
712                    .max_object_size(20u128)
713                    .build()
714                    .unwrap(),
715                Default::default(),
716                None,
717            ),
718            (
719                BucketConfigBuilder::default()
720                    .accepted_compression_algorithms(vec![CompressionAlgorithm::Passthrough])
721                    .build()
722                    .unwrap(),
723                Default::default(),
724                Default::default(),
725                None,
726            ),
727            (
728                BucketConfigBuilder::default()
729                    .accepted_compression_algorithms(vec![])
730                    .build()
731                    .unwrap(),
732                Default::default(),
733                Default::default(),
734                Some(StdError::generic_err(
735                    "'accepted_compression_algorithms' cannot be empty",
736                )),
737            ),
738            (
739                Default::default(),
740                Default::default(),
741                Default::default(),
742                None,
743            ),
744        ];
745        for case in cases {
746            let mut deps = mock_dependencies();
747            let msg = InstantiateMsg {
748                bucket: "bar".to_string(),
749                config: case.0,
750                limits: case.1,
751                pagination: case.2,
752            };
753            match instantiate(
754                deps.as_mut(),
755                mock_env(),
756                message_info(&addr(CREATOR), &[]),
757                msg,
758            ) {
759                Err(err) => {
760                    assert!(case.3.is_some());
761                    assert_eq!(err, ContractError::Std(case.3.unwrap()))
762                }
763                _ => assert!(case.3.is_none()),
764            }
765        }
766    }
767
768    #[test]
769    fn empty_name_initialization() {
770        let mut deps = mock_dependencies();
771
772        let msg = InstantiateMsg {
773            bucket: "".to_string(),
774            config: Default::default(),
775            limits: Default::default(),
776            pagination: Default::default(),
777        };
778        let info = message_info(&addr(CREATOR), &[]);
779
780        let err = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err();
781
782        assert_eq!(err, ContractError::Bucket(BucketError::EmptyName));
783    }
784
785    #[test]
786    fn whitespace_initialization() {
787        let mut deps = mock_dependencies();
788
789        let msg = InstantiateMsg {
790            bucket: "foo bar".to_string(),
791            config: Default::default(),
792            limits: Default::default(),
793            pagination: Default::default(),
794        };
795        let info = message_info(&addr(CREATOR), &[]);
796
797        let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
798        assert_eq!(0, res.messages.len());
799
800        let res = query(deps.as_ref(), mock_env(), QueryMsg::Bucket {}).unwrap();
801        let value: BucketResponse = from_json(&res).unwrap();
802        assert_eq!("foobar", value.name);
803    }
804
805    #[test]
806    fn funds_initialization() {
807        let mut deps = mock_dependencies();
808        let msg = InstantiateMsg {
809            bucket: "foo".to_string(),
810            config: Default::default(),
811            limits: Default::default(),
812            pagination: Default::default(),
813        };
814        let info = message_info(&addr(CREATOR), &coins(10, "uaxone"));
815
816        let res = instantiate(deps.as_mut(), mock_env(), info, msg);
817        assert!(res.is_err());
818        assert_eq!(
819            res.unwrap_err(),
820            ContractError::Payment(PaymentError::NonPayable {})
821        );
822    }
823
824    #[test]
825    fn execute_fail_with_funds() {
826        let mut deps = mock_dependencies();
827        let env = mock_env();
828        let info = message_info(&addr(SENDER), &coins(10, "uaxone"));
829
830        let messages = vec![
831            ExecuteMsg::StoreObject {
832                data: Binary::from("data".as_bytes()),
833                pin: false,
834                compression_algorithm: None,
835            },
836            ExecuteMsg::PinObject {
837                id: "object_id".to_string(),
838            },
839            ExecuteMsg::UnpinObject {
840                id: "object_id".to_string(),
841            },
842            ExecuteMsg::ForgetObject {
843                id: "object_id".to_string(),
844            },
845        ];
846
847        for msg in messages {
848            let result = execute(deps.as_mut(), env.clone(), info.clone(), msg);
849            assert!(result.is_err());
850            assert_eq!(
851                result.unwrap_err(),
852                ContractError::Payment(PaymentError::NonPayable {})
853            );
854        }
855    }
856
857    #[test]
858    fn store_object_without_limits() {
859        let obj1_content = &general_purpose::STANDARD.encode("hello");
860        let obj2_content = &general_purpose::STANDARD.encode("okp4");
861
862        let test_cases = vec![
863            (
864                HashAlgorithm::MD5,
865                vec![
866                    (
867                        obj1_content,
868                        true,
869                        "5d41402abc4b2a76b9719d911017c592"
870                            .to_string(),
871                        5,
872                        vec![
873                            Attribute::new("action", "store_object"),
874                            Attribute::new("id", "5d41402abc4b2a76b9719d911017c592"
875                                .to_string()),
876                            Attribute::new("size", "5"),
877                            Attribute::new("compressed_size", "5"),
878                            Attribute::new("pinned", "true"),
879                        ]
880                    ),
881                    (
882                        obj2_content,
883                        false,
884                        "33f41d49353ad1a876e36918f64eac4d"
885                            .to_string(),
886                        4,
887                        vec![
888                            Attribute::new("action", "store_object"),
889                            Attribute::new("id", "33f41d49353ad1a876e36918f64eac4d"
890                                .to_string()),
891                            Attribute::new("size", "4"),
892                            Attribute::new("compressed_size", "4"),
893                            Attribute::new("pinned", "false"),
894                        ]
895                    ),
896                ],
897            ),
898            (
899                HashAlgorithm::Sha224,
900                vec![
901                    (
902                        obj1_content,
903                        true,
904                        "ea09ae9cc6768c50fcee903ed054556e5bfc8347907f12598aa24193"
905                            .to_string(),
906                        5,
907                        vec![
908                            Attribute::new("action", "store_object"),
909                            Attribute::new("id", "ea09ae9cc6768c50fcee903ed054556e5bfc8347907f12598aa24193"
910                                .to_string()),
911                            Attribute::new("size", "5"),
912                            Attribute::new("compressed_size", "5"),
913                            Attribute::new("pinned", "true"),
914                        ]
915                    ),
916                    (
917                        obj2_content,
918                        false,
919                        "fe798aa30e560c57d69c46982b2bb1320dc86813730bb7c6406ce84b"
920                            .to_string(),
921                        4,
922                        vec![
923                            Attribute::new("action", "store_object"),
924                            Attribute::new("id", "fe798aa30e560c57d69c46982b2bb1320dc86813730bb7c6406ce84b"
925                                .to_string()),
926                            Attribute::new("size", "4"),
927                            Attribute::new("compressed_size", "4"),
928                            Attribute::new("pinned", "false"),
929                        ]
930                    ),
931                ],
932            ),
933            (
934                HashAlgorithm::Sha256,
935                vec![
936                    (
937                        obj1_content,
938                        true,
939                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
940                            .to_string(),
941                        5,
942                        vec![
943                            Attribute::new("action", "store_object"),
944                            Attribute::new("id", "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
945                                .to_string()),
946                            Attribute::new("size", "5"),
947                            Attribute::new("compressed_size", "5"),
948                            Attribute::new("pinned", "true"),
949                        ]
950                    ),
951                    (
952                        obj2_content,
953                        false,
954                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6"
955                            .to_string(),
956                        4,
957                        vec![
958                            Attribute::new("action", "store_object"),
959                            Attribute::new("id", "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6"
960                                .to_string()),
961                            Attribute::new("size", "4"),
962                            Attribute::new("compressed_size", "4"),
963                            Attribute::new("pinned", "false"),
964                        ]
965                    ),
966                ],
967            ),
968            (
969                HashAlgorithm::Sha384,
970                vec![
971                    (
972                        obj1_content,
973                        true,
974                        "59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c3553bcdb9c666fa90125a3c79f90397bdf5f6a13de828684f"
975                            .to_string(),
976                        5,
977                        vec![
978                            Attribute::new("action", "store_object"),
979                            Attribute::new("id", "59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c3553bcdb9c666fa90125a3c79f90397bdf5f6a13de828684f"
980                                .to_string()),
981                            Attribute::new("size", "5"),
982                            Attribute::new("compressed_size", "5"),
983                            Attribute::new("pinned", "true"),
984                        ]
985                    ),
986                    (
987                        obj2_content,
988                        false,
989                        "e700b122a81f64ce34ab67c6a815987536a05b0590bbeb32cf5e88963edd8c6e69c9e43b0f957f242d984f09f91bcaf2"
990                            .to_string(),
991                        4,
992                        vec![
993                            Attribute::new("action", "store_object"),
994                            Attribute::new("id", "e700b122a81f64ce34ab67c6a815987536a05b0590bbeb32cf5e88963edd8c6e69c9e43b0f957f242d984f09f91bcaf2"
995                                .to_string()),
996                            Attribute::new("size", "4"),
997                            Attribute::new("compressed_size", "4"),
998                            Attribute::new("pinned", "false"),
999                        ]
1000                    ),
1001                ],
1002            ),
1003            (
1004                HashAlgorithm::Sha512,
1005                vec![
1006                    (
1007                        obj1_content,
1008                        true,
1009                        "9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043"
1010                            .to_string(),
1011                        5,
1012                        vec![
1013                            Attribute::new("action", "store_object"),
1014                            Attribute::new("id", "9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043"
1015                                .to_string()),
1016                            Attribute::new("size", "5"),
1017                            Attribute::new("compressed_size", "5"),
1018                            Attribute::new("pinned", "true"),
1019                        ]
1020                    ),
1021                    (
1022                        obj2_content,
1023                        false,
1024                        "e4f4025e1e28abb473c89bcae03ded972e91b4427e8970be87f645cc34b9b203d633c12760e32c97011439640cba159f60992e10aac8023fa2577cadc1be3b55"
1025                            .to_string(),
1026                        4,
1027                        vec![
1028                            Attribute::new("action", "store_object"),
1029                            Attribute::new("id", "e4f4025e1e28abb473c89bcae03ded972e91b4427e8970be87f645cc34b9b203d633c12760e32c97011439640cba159f60992e10aac8023fa2577cadc1be3b55"
1030                                .to_string()),
1031                            Attribute::new("size", "4"),
1032                            Attribute::new("compressed_size", "4"),
1033                            Attribute::new("pinned", "false"),
1034                        ]
1035                    ),
1036                ],
1037            ),
1038        ];
1039
1040        for (hash_algorithm, objs) in test_cases {
1041            let mut deps = mock_dependencies();
1042            let info = message_info(&addr(CREATOR), &[]);
1043
1044            instantiate(
1045                deps.as_mut(),
1046                mock_env(),
1047                info.clone(),
1048                InstantiateMsg {
1049                    bucket: "test".to_string(),
1050                    config: BucketConfig {
1051                        hash_algorithm,
1052                        ..Default::default()
1053                    },
1054                    limits: Default::default(),
1055                    pagination: Default::default(),
1056                },
1057            )
1058            .unwrap();
1059
1060            for (content, pin, expected_hash, expected_size, expected_attr) in &objs {
1061                let msg = ExecuteMsg::StoreObject {
1062                    data: Binary::from_base64(content).unwrap(),
1063                    pin: *pin,
1064                    compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1065                };
1066                let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1067                assert_eq!(res.attributes, *expected_attr);
1068
1069                assert_eq!(
1070                    Binary::from_base64(content).unwrap(),
1071                    Binary::from(
1072                        DATA.load(&deps.storage, decode_hex(&expected_hash).into())
1073                            .unwrap()
1074                    ),
1075                );
1076
1077                let created = objects()
1078                    .load(&deps.storage, decode_hex(&expected_hash).into())
1079                    .unwrap();
1080                assert_eq!(created.id, decode_hex(&expected_hash).into());
1081                assert_eq!(created.owner, info.sender.clone());
1082                assert_eq!(created.size.u128(), *expected_size);
1083                assert_eq!(
1084                    created.pin_count,
1085                    if *pin {
1086                        Uint128::one()
1087                    } else {
1088                        Uint128::zero()
1089                    }
1090                );
1091
1092                assert_eq!(
1093                    pins().has(
1094                        &deps.storage,
1095                        (decode_hex(&expected_hash).into(), info.clone().sender),
1096                    ),
1097                    *pin,
1098                );
1099            }
1100
1101            let bucket = BUCKET.load(&deps.storage).unwrap();
1102            assert_eq!(
1103                bucket.stat.size.u128(),
1104                objs.iter().map(|x| x.3).sum::<u128>()
1105            );
1106            assert_eq!(
1107                bucket.stat.object_count.u128(),
1108                u128::try_from(objs.len()).unwrap()
1109            );
1110            assert_eq!(
1111                objects()
1112                    .keys_raw(&deps.storage, None, None, Order::Ascending)
1113                    .count(),
1114                2
1115            );
1116            assert_eq!(
1117                pins()
1118                    .keys_raw(&deps.storage, None, None, Order::Ascending)
1119                    .count(),
1120                1
1121            );
1122        }
1123    }
1124
1125    #[test]
1126    fn store_object_already_stored() {
1127        let mut deps = mock_dependencies();
1128        let info = message_info(&addr(CREATOR), &[]);
1129        let msg = InstantiateMsg {
1130            bucket: String::from("test"),
1131            config: Default::default(),
1132            limits: Default::default(),
1133            pagination: Default::default(),
1134        };
1135        instantiate(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1136
1137        let object = general_purpose::STANDARD.encode("already existing object");
1138        execute(
1139            deps.as_mut(),
1140            mock_env(),
1141            info.clone(),
1142            ExecuteMsg::StoreObject {
1143                data: Binary::from_base64(object.as_str()).unwrap(),
1144                pin: false,
1145                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1146            },
1147        )
1148        .unwrap();
1149
1150        let res = execute(
1151            deps.as_mut(),
1152            mock_env(),
1153            info.clone(),
1154            ExecuteMsg::StoreObject {
1155                data: Binary::from_base64(object.as_str()).unwrap(),
1156                pin: true,
1157                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1158            },
1159        );
1160
1161        assert!(res.is_ok());
1162        assert!(pins().has(
1163            &deps.storage,
1164            (
1165                decode_hex("46c4b2f687df251a98cc83cc35437e9893c16861899c2f9d183e1de57d3a2c0e")
1166                    .into(),
1167                info.sender
1168            ),
1169        ));
1170        assert_eq!(
1171            objects()
1172                .load(
1173                    &deps.storage,
1174                    decode_hex("46c4b2f687df251a98cc83cc35437e9893c16861899c2f9d183e1de57d3a2c0e")
1175                        .into()
1176                )
1177                .unwrap()
1178                .pin_count,
1179            Uint128::one()
1180        );
1181    }
1182
1183    #[test]
1184    fn store_object_limits() {
1185        let cases = vec![
1186            (
1187                BucketLimitsBuilder::default()
1188                    .max_objects(2u128)
1189                    .build()
1190                    .unwrap(),
1191                None,
1192            ),
1193            (
1194                BucketLimitsBuilder::default()
1195                    .max_object_size(5u128)
1196                    .build()
1197                    .unwrap(),
1198                None,
1199            ),
1200            (
1201                BucketLimitsBuilder::default()
1202                    .max_total_size(9u128)
1203                    .build()
1204                    .unwrap(),
1205                None,
1206            ),
1207            (
1208                BucketLimitsBuilder::default()
1209                    .max_object_pins(1u128)
1210                    .build()
1211                    .unwrap(),
1212                None,
1213            ),
1214            (
1215                BucketLimitsBuilder::default()
1216                    .max_objects(1u128)
1217                    .build()
1218                    .unwrap(),
1219                Some(ContractError::Bucket(BucketError::MaxObjectsLimitExceeded(
1220                    2u128.into(),
1221                    1u128.into(),
1222                ))),
1223            ),
1224            (
1225                BucketLimitsBuilder::default()
1226                    .max_object_size(4u128)
1227                    .build()
1228                    .unwrap(),
1229                Some(ContractError::Bucket(
1230                    BucketError::MaxObjectSizeLimitExceeded(5u128.into(), 4u128.into()),
1231                )),
1232            ),
1233            (
1234                BucketLimitsBuilder::default()
1235                    .max_total_size(8u128)
1236                    .build()
1237                    .unwrap(),
1238                Some(ContractError::Bucket(
1239                    BucketError::MaxTotalSizeLimitExceeded(9u128.into(), 8u128.into()),
1240                )),
1241            ),
1242            (
1243                BucketLimitsBuilder::default()
1244                    .max_object_pins(0u128)
1245                    .build()
1246                    .unwrap(),
1247                Some(ContractError::Bucket(
1248                    BucketError::MaxObjectPinsLimitExceeded(1u128.into(), 0u128.into()),
1249                )),
1250            ),
1251        ];
1252
1253        let obj1 = general_purpose::STANDARD.encode("okp4");
1254        let obj2 = general_purpose::STANDARD.encode("hello");
1255
1256        for case in cases {
1257            let mut deps = mock_dependencies();
1258            let info = message_info(&addr(CREATOR), &[]);
1259            let msg = InstantiateMsg {
1260                bucket: String::from("test"),
1261                config: Default::default(),
1262                limits: case.0,
1263                pagination: Default::default(),
1264            };
1265            instantiate(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1266
1267            let msg = ExecuteMsg::StoreObject {
1268                data: Binary::from_base64(obj1.as_str()).unwrap(),
1269                pin: false,
1270                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1271            };
1272            execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1273            let msg = ExecuteMsg::StoreObject {
1274                data: Binary::from_base64(obj2.as_str()).unwrap(),
1275                pin: true,
1276                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1277            };
1278            let res = execute(deps.as_mut(), mock_env(), info.clone(), msg);
1279
1280            assert_eq!(res.err(), case.1);
1281        }
1282    }
1283
1284    #[test]
1285    fn store_object_compressed() {
1286        use either::Either;
1287
1288        struct ExpectedCompressionResult {
1289            compression_algorithm: CompressionAlgorithm,
1290            compressed_size: u128,
1291        }
1292        struct TC {
1293            accepted_compression_algorithms: Vec<CompressionAlgorithm>,
1294            compression_algorithm: Option<CompressionAlgorithm>,
1295            expected_result: Either<ContractError, ExpectedCompressionResult>,
1296        }
1297
1298        let cases: Vec<TC> = vec![
1299            TC {
1300                accepted_compression_algorithms: vec![
1301                    CompressionAlgorithm::Passthrough,
1302                    CompressionAlgorithm::Snappy,
1303                    CompressionAlgorithm::Lzma,
1304                ],
1305                compression_algorithm: None,
1306                expected_result: Either::Right(ExpectedCompressionResult {
1307                    compression_algorithm: CompressionAlgorithm::Passthrough,
1308                    compressed_size: 466,
1309                }),
1310            },
1311            TC {
1312                accepted_compression_algorithms: vec![
1313                    CompressionAlgorithm::Passthrough,
1314                    CompressionAlgorithm::Snappy,
1315                    CompressionAlgorithm::Lzma,
1316                ],
1317                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1318                expected_result: Either::Right(ExpectedCompressionResult {
1319                    compression_algorithm: CompressionAlgorithm::Passthrough,
1320                    compressed_size: 466,
1321                }),
1322            },
1323            TC {
1324                accepted_compression_algorithms: vec![
1325                    CompressionAlgorithm::Passthrough,
1326                    CompressionAlgorithm::Snappy,
1327                    CompressionAlgorithm::Lzma,
1328                ],
1329                compression_algorithm: Some(CompressionAlgorithm::Snappy),
1330                expected_result: Either::Right(ExpectedCompressionResult {
1331                    compression_algorithm: CompressionAlgorithm::Snappy,
1332                    compressed_size: 414,
1333                }),
1334            },
1335            TC {
1336                accepted_compression_algorithms: vec![
1337                    CompressionAlgorithm::Passthrough,
1338                    CompressionAlgorithm::Snappy,
1339                    CompressionAlgorithm::Lzma,
1340                ],
1341                compression_algorithm: Some(CompressionAlgorithm::Lzma),
1342                expected_result: Either::Right(ExpectedCompressionResult {
1343                    compression_algorithm: CompressionAlgorithm::Lzma,
1344                    compressed_size: 344,
1345                }),
1346            },
1347            TC {
1348                accepted_compression_algorithms: vec![CompressionAlgorithm::Passthrough],
1349                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1350                expected_result: Either::Right(ExpectedCompressionResult {
1351                    compression_algorithm: CompressionAlgorithm::Passthrough,
1352                    compressed_size: 466,
1353                }),
1354            },
1355            TC {
1356                accepted_compression_algorithms: vec![CompressionAlgorithm::Snappy],
1357                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1358                expected_result: Either::Left(ContractError::Bucket(
1359                    BucketError::CompressionAlgorithmNotAccepted(
1360                        CompressionAlgorithm::Passthrough,
1361                        vec![CompressionAlgorithm::Snappy],
1362                    ),
1363                )),
1364            },
1365        ];
1366        let data ="In a magical land,  there were many realms, one of which was known as OKP4. Within \
1367            this realm, druid programmers possessed the power to create smart contracts. As the kingdom \
1368            grew, the druids used their skills to power decentralized systems, bringing prosperity and \
1369            wonder to all who sought their expertise. And so, the legend of the druid programmers and \
1370            their magical smart contracts lived on, inspiring future generations to unlock the power of \
1371            the digital realm.";
1372        let obj = general_purpose::STANDARD.encode(data);
1373        let obj_id = "25056da0c504e6beb9d8666f9e5919a4a02689f4bceeb4698a21c651f07d8e04";
1374
1375        for case in cases {
1376            // Arrange
1377            let mut deps = mock_dependencies();
1378            let info = message_info(&addr(CREATOR), &[]);
1379            let msg = InstantiateMsg {
1380                bucket: String::from("test"),
1381                config: BucketConfig {
1382                    accepted_compression_algorithms: case.accepted_compression_algorithms,
1383                    ..Default::default()
1384                },
1385                limits: Default::default(),
1386                pagination: Default::default(),
1387            };
1388            instantiate(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1389
1390            // Act
1391            let res = execute(
1392                deps.as_mut(),
1393                mock_env(),
1394                info.clone(),
1395                ExecuteMsg::StoreObject {
1396                    data: Binary::from_base64(obj.as_str()).unwrap(),
1397                    pin: false,
1398                    compression_algorithm: case.compression_algorithm,
1399                },
1400            );
1401
1402            // Assert
1403            match case.expected_result {
1404                Either::Left(err) => assert_eq!(res.err(), Some(err)),
1405                Either::Right(expected) => {
1406                    let _to_assert_if_we_want = res.unwrap();
1407                    let res_object_info = query::object(deps.as_ref(), obj_id.to_string()).unwrap();
1408                    let res_object_data = query::data(deps.as_ref(), obj_id.to_string()).unwrap();
1409
1410                    assert_eq!(
1411                        res_object_info,
1412                        ObjectResponse {
1413                            id: obj_id.to_string(),
1414                            owner: addr(CREATOR).to_string(),
1415                            is_pinned: false,
1416                            size: Uint128::from(data.len() as u128),
1417                            compressed_size: expected.compressed_size.into(),
1418                            compression_algorithm: expected.compression_algorithm,
1419                        }
1420                    );
1421                    assert_eq!(res_object_data, data.as_bytes().to_vec());
1422                }
1423            }
1424        }
1425    }
1426
1427    #[test]
1428    fn store_object_check_attributes() {
1429        let obj_content = &general_purpose::STANDARD.encode("hello");
1430        let obj_exist_content = &general_purpose::STANDARD.encode("axone");
1431        let obj_exist_pinned_content = &general_purpose::STANDARD.encode("protocol");
1432        let obj_large_content = &general_purpose::STANDARD.encode("In a world of interconnected systems, there were countless realms, one of which was known as AXONE. Within this realm, AI researchers harnessed the power to create collaborative AI models. As the realm expanded, the researchers used their expertise to power collaborative systems, bringing innovation and advancement to all who sought their knowledge. And so, the legend of the AI researchers and their collaborative AI models lived on, inspiring future generations to unlock the potential of the digital realm with AI.");
1433
1434        let test_cases = vec![
1435            (
1436                obj_content,
1437                true,
1438                Passthrough,
1439                vec![
1440                    Attribute::new("action", "store_object"),
1441                    Attribute::new(
1442                        "id",
1443                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
1444                            .to_string(),
1445                    ),
1446                    Attribute::new("size", "5"),
1447                    Attribute::new("compressed_size", "5"),
1448                    Attribute::new("pinned", "true"),
1449                ],
1450            ),
1451            (
1452                obj_content,
1453                false,
1454                Passthrough,
1455                vec![
1456                    Attribute::new("action", "store_object"),
1457                    Attribute::new(
1458                        "id",
1459                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
1460                            .to_string(),
1461                    ),
1462                    Attribute::new("size", "5"),
1463                    Attribute::new("compressed_size", "5"),
1464                    Attribute::new("pinned", "false"),
1465                ],
1466            ),
1467            (
1468                obj_large_content,
1469                true,
1470                Snappy,
1471                vec![
1472                    Attribute::new("action", "store_object"),
1473                    Attribute::new(
1474                        "id",
1475                        "afb9c7804a3515714a3ec2313c990df31d54000b890ae677dcaaa1060b437660"
1476                            .to_string(),
1477                    ),
1478                    Attribute::new("size", "519"),
1479                    Attribute::new("compressed_size", "453"),
1480                    Attribute::new("pinned", "true"),
1481                ],
1482            ),
1483            (
1484                obj_exist_content,
1485                true,
1486                Passthrough,
1487                vec![
1488                    Attribute::new("action", "store_object"),
1489                    Attribute::new(
1490                        "id",
1491                        "45a8243ff863a08531c666569ce9997b63df94c2e2aeedaed3d32656ee1ae622"
1492                            .to_string(),
1493                    ),
1494                    Attribute::new("pinned", "true"),
1495                ],
1496            ),
1497            (
1498                obj_exist_content,
1499                false,
1500                Passthrough,
1501                vec![
1502                    Attribute::new("action", "store_object"),
1503                    Attribute::new(
1504                        "id",
1505                        "45a8243ff863a08531c666569ce9997b63df94c2e2aeedaed3d32656ee1ae622"
1506                            .to_string(),
1507                    ),
1508                    Attribute::new("pinned", "false"),
1509                ],
1510            ),
1511            (
1512                obj_exist_pinned_content,
1513                false,
1514                Passthrough,
1515                vec![
1516                    Attribute::new("action", "store_object"),
1517                    Attribute::new(
1518                        "id",
1519                        "2ea88c7a30351b12a4dcfc06cdce2af6eab18416176466c2500cb6ef74f745bf"
1520                            .to_string(),
1521                    ),
1522                    Attribute::new("pinned", "false"),
1523                ],
1524            ),
1525            (
1526                obj_exist_pinned_content,
1527                true,
1528                Passthrough,
1529                vec![
1530                    Attribute::new("action", "store_object"),
1531                    Attribute::new(
1532                        "id",
1533                        "2ea88c7a30351b12a4dcfc06cdce2af6eab18416176466c2500cb6ef74f745bf"
1534                            .to_string(),
1535                    ),
1536                    Attribute::new("pinned", "false"),
1537                ],
1538            ),
1539        ];
1540
1541        for (content, pin, compression_algorithm, expected_attr) in &test_cases {
1542            let mut deps = mock_dependencies();
1543            let info = message_info(&addr(CREATOR), &[]);
1544            let msg = InstantiateMsg {
1545                bucket: String::from("test"),
1546                config: Default::default(),
1547                limits: Default::default(),
1548                pagination: Default::default(),
1549            };
1550            instantiate(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1551
1552            _ = execute(
1553                deps.as_mut(),
1554                mock_env(),
1555                info.clone(),
1556                ExecuteMsg::StoreObject {
1557                    data: Binary::from_base64(obj_exist_content).unwrap(),
1558                    pin: false,
1559                    compression_algorithm: Some(Passthrough),
1560                },
1561            );
1562
1563            _ = execute(
1564                deps.as_mut(),
1565                mock_env(),
1566                info.clone(),
1567                ExecuteMsg::StoreObject {
1568                    data: Binary::from_base64(obj_exist_pinned_content).unwrap(),
1569                    pin: true,
1570                    compression_algorithm: Some(Passthrough),
1571                },
1572            );
1573
1574            let msg = ExecuteMsg::StoreObject {
1575                data: Binary::from_base64(content).unwrap(),
1576                pin: *pin,
1577                compression_algorithm: Some(*compression_algorithm),
1578            };
1579
1580            let res = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1581            assert_eq!(res.attributes, *expected_attr);
1582        }
1583    }
1584
1585    #[test]
1586    fn object() {
1587        let mut deps = mock_dependencies();
1588        let info = message_info(&addr(CREATOR), &[]);
1589
1590        let msg = InstantiateMsg {
1591            bucket: String::from("test"),
1592            config: Default::default(),
1593            limits: Default::default(),
1594            pagination: Default::default(),
1595        };
1596        instantiate(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1597
1598        match query(
1599            deps.as_ref(),
1600            mock_env(),
1601            QueryMsg::Object {
1602                id: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824".to_string(),
1603            },
1604        )
1605        .err()
1606        .unwrap()
1607        {
1608            NotFound { .. } => (),
1609            _ => panic!("assertion failed"),
1610        }
1611
1612        let data = general_purpose::STANDARD.encode("hello");
1613        let msg = ExecuteMsg::StoreObject {
1614            data: Binary::from_base64(data.as_str()).unwrap(),
1615            pin: true,
1616            compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1617        };
1618        execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1619
1620        let msg = QueryMsg::Object {
1621            id: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824".to_string(),
1622        };
1623        let result = query(deps.as_ref(), mock_env(), msg).unwrap();
1624        let response: ObjectResponse = from_json(&result).unwrap();
1625        assert_eq!(
1626            response.id,
1627            ObjectId::from("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824")
1628        );
1629        assert_eq!(response.owner, info.sender.as_str());
1630        assert!(response.is_pinned);
1631        assert_eq!(response.size.u128(), 5u128);
1632
1633        let data = general_purpose::STANDARD.encode("okp4");
1634        let msg = ExecuteMsg::StoreObject {
1635            data: Binary::from_base64(data.as_str()).unwrap(),
1636            pin: false,
1637            compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1638        };
1639        execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1640
1641        let msg = QueryMsg::Object {
1642            id: "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6".to_string(),
1643        };
1644        let result = query(deps.as_ref(), mock_env(), msg).unwrap();
1645        let response: ObjectResponse = from_json(&result).unwrap();
1646        assert_eq!(
1647            response.id,
1648            ObjectId::from("315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6")
1649        );
1650        assert_eq!(response.owner, info.sender.as_str());
1651        assert!(!response.is_pinned);
1652        assert_eq!(response.size.u128(), 4u128);
1653    }
1654
1655    #[test]
1656    fn object_data() {
1657        struct TC {
1658            compression_algorithm: Option<CompressionAlgorithm>,
1659        }
1660
1661        let cases = vec![
1662            TC {
1663                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
1664            },
1665            TC {
1666                compression_algorithm: Some(CompressionAlgorithm::Snappy),
1667            },
1668            TC {
1669                compression_algorithm: Some(CompressionAlgorithm::Lzma),
1670            },
1671        ];
1672
1673        for case in cases {
1674            let mut deps = mock_dependencies();
1675            let info = message_info(&addr(CREATOR), &[]);
1676            let data =
1677                Binary::from_base64(general_purpose::STANDARD.encode("okp4").as_str()).unwrap();
1678
1679            let msg = InstantiateMsg {
1680                bucket: String::from("test"),
1681                config: Default::default(),
1682                limits: Default::default(),
1683                pagination: Default::default(),
1684            };
1685            instantiate(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1686
1687            match query(
1688                deps.as_ref(),
1689                mock_env(),
1690                QueryMsg::ObjectData {
1691                    id: "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6"
1692                        .to_string(),
1693                },
1694            )
1695            .err()
1696            .unwrap()
1697            {
1698                NotFound { .. } => (),
1699                _ => panic!("assertion failed"),
1700            }
1701
1702            let msg = ExecuteMsg::StoreObject {
1703                data: data.clone(),
1704                pin: false,
1705                compression_algorithm: case.compression_algorithm,
1706            };
1707            execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
1708
1709            let msg = QueryMsg::ObjectData {
1710                id: "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6".to_string(),
1711            };
1712            let result = query(deps.as_ref(), mock_env(), msg).unwrap();
1713            assert_eq!(result, to_json_binary(&data).unwrap());
1714        }
1715    }
1716
1717    #[test]
1718    fn object_data_error() {
1719        let mut deps = mock_dependencies();
1720        let id: Hash = vec![1, 2, 3].into();
1721        let data = &vec![255, 255, 0];
1722
1723        let object = &Object {
1724            id: id.clone(),
1725            owner: Addr::unchecked("john"),
1726            size: 42u8.into(),
1727            pin_count: Uint128::one(),
1728            compression: compress::CompressionAlgorithm::Lzma,
1729            compressed_size: Uint128::from(data.len() as u128),
1730        };
1731
1732        objects()
1733            .save(deps.as_mut().storage, object.id.clone(), object)
1734            .expect("no error when storing object");
1735        let data_path = DATA.key(id.clone());
1736        data_path
1737            .save(deps.as_mut().storage, &data)
1738            .expect("no error when storing data");
1739
1740        let msg = QueryMsg::ObjectData { id: id.to_string() };
1741
1742        let result = query(deps.as_ref(), mock_env(), msg);
1743        assert_eq!(
1744            result,
1745            Err(StdError::serialize_err(
1746                "Lzma",
1747                "lzma error: LZMA header invalid properties: 255 must be < 225"
1748            ))
1749        );
1750    }
1751
1752    #[test]
1753    fn pin_object() {
1754        struct TC {
1755            objects: Vec<ObjectId>,
1756            senders: Vec<MessageInfo>,
1757            expected_count: usize,
1758            expected_error: Option<ContractError>,
1759            expected_object_pin_count: Vec<(ObjectId, Uint128)>,
1760        }
1761
1762        let cases = vec![
1763            TC {
1764                // One object, 1 one pinner => 1 pin
1765                objects: vec![ObjectId::from(
1766                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1767                )],
1768                senders: vec![message_info(&addr("bob"), &[])],
1769                expected_count: 1,
1770                expected_error: None,
1771                expected_object_pin_count: vec![(
1772                    ObjectId::from(
1773                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1774                    ),
1775                    Uint128::one(),
1776                )],
1777            },
1778            TC {
1779                // Same object, two pinners => 2 pin
1780                objects: vec![
1781                    ObjectId::from(
1782                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1783                    ),
1784                    ObjectId::from(
1785                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1786                    ),
1787                ],
1788                senders: vec![
1789                    message_info(&addr("bob"), &[]),
1790                    message_info(&addr("alice"), &[]),
1791                ],
1792                expected_count: 2,
1793                expected_error: None,
1794                expected_object_pin_count: vec![(
1795                    ObjectId::from(
1796                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1797                    ),
1798                    Uint128::new(2),
1799                )],
1800            },
1801            TC {
1802                // Same object, one pinner twice => 1 pin
1803                objects: vec![
1804                    ObjectId::from(
1805                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1806                    ),
1807                    ObjectId::from(
1808                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1809                    ),
1810                ],
1811                senders: vec![
1812                    message_info(&addr("bob"), &[]),
1813                    message_info(&addr("bob"), &[]),
1814                ],
1815                expected_count: 1,
1816                expected_error: None,
1817                expected_object_pin_count: vec![(
1818                    ObjectId::from(
1819                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1820                    ),
1821                    Uint128::one(),
1822                )],
1823            },
1824            TC {
1825                // two objects, same pinner => 2 pin
1826                objects: vec![
1827                    ObjectId::from(
1828                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1829                    ),
1830                    ObjectId::from(
1831                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1832                    ),
1833                ],
1834                senders: vec![
1835                    message_info(&addr("bob"), &[]),
1836                    message_info(&addr("bob"), &[]),
1837                ],
1838                expected_count: 2,
1839                expected_error: None,
1840                expected_object_pin_count: vec![
1841                    (
1842                        ObjectId::from(
1843                            "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1844                        ),
1845                        Uint128::one(),
1846                    ),
1847                    (
1848                        ObjectId::from(
1849                            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1850                        ),
1851                        Uint128::one(),
1852                    ),
1853                ],
1854            },
1855            TC {
1856                // two objects, two pinner => 2 pin
1857                objects: vec![
1858                    ObjectId::from(
1859                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1860                    ),
1861                    ObjectId::from(
1862                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1863                    ),
1864                ],
1865                senders: vec![
1866                    message_info(&addr("bob"), &[]),
1867                    message_info(&addr("alice"), &[]),
1868                ],
1869                expected_count: 2,
1870                expected_error: None,
1871                expected_object_pin_count: vec![
1872                    (
1873                        ObjectId::from(
1874                            "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1875                        ),
1876                        Uint128::one(),
1877                    ),
1878                    (
1879                        ObjectId::from(
1880                            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1881                        ),
1882                        Uint128::one(),
1883                    ),
1884                ],
1885            },
1886            TC {
1887                // two objects, two pinner, twice 1 pinner => 2 pin
1888                objects: vec![
1889                    ObjectId::from(
1890                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1891                    ),
1892                    ObjectId::from(
1893                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1894                    ),
1895                    ObjectId::from(
1896                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1897                    ),
1898                ],
1899                senders: vec![
1900                    message_info(&addr("bob"), &[]),
1901                    message_info(&addr("alice"), &[]),
1902                    message_info(&addr("alice"), &[]),
1903                ],
1904                expected_count: 2,
1905                expected_error: None,
1906                expected_object_pin_count: vec![
1907                    (
1908                        ObjectId::from(
1909                            "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1910                        ),
1911                        Uint128::one(),
1912                    ),
1913                    (
1914                        ObjectId::from(
1915                            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1916                        ),
1917                        Uint128::one(),
1918                    ),
1919                ],
1920            },
1921            TC {
1922                // exceed limits
1923                objects: vec![
1924                    ObjectId::from(
1925                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1926                    ),
1927                    ObjectId::from(
1928                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1929                    ),
1930                    ObjectId::from(
1931                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1932                    ),
1933                    ObjectId::from(
1934                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1935                    ),
1936                ],
1937                senders: vec![
1938                    message_info(&addr("bob"), &[]),
1939                    message_info(&addr("alice"), &[]),
1940                    message_info(&addr("martin"), &[]),
1941                    message_info(&addr("pierre"), &[]),
1942                ],
1943                expected_count: 3,
1944                expected_error: Some(ContractError::Bucket(
1945                    BucketError::MaxObjectPinsLimitExceeded(Uint128::new(3), Uint128::new(2)),
1946                )),
1947                expected_object_pin_count: vec![
1948                    (
1949                        ObjectId::from(
1950                            "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1951                        ),
1952                        Uint128::one(),
1953                    ),
1954                    (
1955                        ObjectId::from(
1956                            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
1957                        ),
1958                        Uint128::new(2),
1959                    ),
1960                ],
1961            },
1962            TC {
1963                // Object not exists
1964                objects: vec![ObjectId::from(
1965                    "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56",
1966                )],
1967                senders: vec![message_info(&addr("bob"), &[])],
1968                expected_count: 0,
1969                expected_error: Some(ContractError::Std(StdError::not_found(
1970                    not_found_object_info::<Object>(
1971                        "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56",
1972                    ),
1973                ))),
1974                expected_object_pin_count: vec![(
1975                    ObjectId::from(
1976                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1977                    ),
1978                    Uint128::zero(),
1979                )],
1980            },
1981            TC {
1982                // Invalid object id
1983                objects: vec![ObjectId::from("invalid id")],
1984                senders: vec![message_info(&addr("bob"), &[])],
1985                expected_count: 0,
1986                expected_error: Some(ContractError::Std(StdError::parse_err(
1987                    type_name::<Vec<u8>>(),
1988                    "invalid Base16 encoding".to_string(),
1989                ))),
1990                expected_object_pin_count: vec![(
1991                    ObjectId::from(
1992                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
1993                    ),
1994                    Uint128::zero(),
1995                )],
1996            },
1997        ];
1998
1999        for case in cases {
2000            let mut deps = mock_dependencies();
2001            let info = message_info(&addr(CREATOR), &[]);
2002
2003            instantiate(
2004                deps.as_mut(),
2005                mock_env(),
2006                info.clone(),
2007                InstantiateMsg {
2008                    bucket: "test".to_string(),
2009                    config: Default::default(),
2010                    limits: BucketLimitsBuilder::default()
2011                        .max_object_pins(Uint128::new(2))
2012                        .build()
2013                        .unwrap(),
2014                    pagination: Default::default(),
2015                },
2016            )
2017            .unwrap();
2018
2019            let data = general_purpose::STANDARD.encode("okp4");
2020            let msg = ExecuteMsg::StoreObject {
2021                data: Binary::from_base64(data.as_str()).unwrap(),
2022                pin: false,
2023                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2024            };
2025            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2026
2027            let data = general_purpose::STANDARD.encode("data");
2028            let msg = ExecuteMsg::StoreObject {
2029                data: Binary::from_base64(data.as_str()).unwrap(),
2030                pin: false,
2031                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2032            };
2033            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2034
2035            let data = general_purpose::STANDARD.encode("hello");
2036            let msg = ExecuteMsg::StoreObject {
2037                data: Binary::from_base64(data.as_str()).unwrap(),
2038                pin: false,
2039                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2040            };
2041            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2042
2043            let mut last_result: Option<Result<Response, ContractError>> = None;
2044            case.objects
2045                .iter()
2046                .zip(case.senders)
2047                .for_each(|(object_id, info)| {
2048                    last_result = Some(execute(
2049                        deps.as_mut(),
2050                        mock_env(),
2051                        info,
2052                        ExecuteMsg::PinObject {
2053                            id: object_id.clone(),
2054                        },
2055                    ));
2056                });
2057
2058            match case.expected_error {
2059                Some(err) => assert_eq!(last_result.unwrap().unwrap_err(), err),
2060                _ => {
2061                    assert_eq!(
2062                        pins()
2063                            .keys_raw(&deps.storage, None, None, Order::Ascending)
2064                            .count(),
2065                        case.expected_count
2066                    );
2067                    for (object_id, count) in case.expected_object_pin_count {
2068                        assert_eq!(
2069                            objects()
2070                                .load(&deps.storage, decode_hex(&object_id).into())
2071                                .unwrap()
2072                                .pin_count,
2073                            count
2074                        );
2075                    }
2076                }
2077            }
2078        }
2079    }
2080
2081    #[test]
2082    fn unpin_object() {
2083        struct TC {
2084            pin: Vec<ObjectId>,
2085            pin_senders: Vec<MessageInfo>,
2086            unpin: Vec<ObjectId>,
2087            unpin_senders: Vec<MessageInfo>,
2088            expected_count: usize,
2089            expected_error: Option<ContractError>,
2090            expected_object_pin_count: Vec<(ObjectId, Uint128)>,
2091        }
2092
2093        let cases = vec![
2094            TC {
2095                pin: vec![ObjectId::from(
2096                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2097                )],
2098                pin_senders: vec![message_info(&addr("bob"), &[])],
2099                unpin: vec![ObjectId::from(
2100                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2101                )],
2102                unpin_senders: vec![message_info(&addr("bob"), &[])],
2103                expected_count: 0,
2104                expected_error: None,
2105                expected_object_pin_count: vec![(
2106                    ObjectId::from(
2107                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2108                    ),
2109                    Uint128::zero(),
2110                )],
2111            },
2112            TC {
2113                pin: vec![ObjectId::from(
2114                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2115                )],
2116                pin_senders: vec![message_info(&addr("bob"), &[])],
2117                unpin: vec![ObjectId::from(
2118                    "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2119                )],
2120                unpin_senders: vec![message_info(&addr("bob"), &[])],
2121                expected_count: 1,
2122                expected_error: None,
2123                expected_object_pin_count: vec![
2124                    (
2125                        ObjectId::from(
2126                            "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2127                        ),
2128                        Uint128::one(),
2129                    ),
2130                    (
2131                        ObjectId::from(
2132                            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2133                        ),
2134                        Uint128::zero(),
2135                    ),
2136                ],
2137            },
2138            TC {
2139                pin: vec![
2140                    ObjectId::from(
2141                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2142                    ),
2143                    ObjectId::from(
2144                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2145                    ),
2146                ],
2147                pin_senders: vec![
2148                    message_info(&addr("bob"), &[]),
2149                    message_info(&addr("bob"), &[]),
2150                ],
2151                unpin: vec![ObjectId::from(
2152                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2153                )],
2154                unpin_senders: vec![message_info(&addr("bob"), &[])],
2155                expected_count: 1,
2156                expected_error: None,
2157                expected_object_pin_count: vec![
2158                    (
2159                        ObjectId::from(
2160                            "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2161                        ),
2162                        Uint128::zero(),
2163                    ),
2164                    (
2165                        ObjectId::from(
2166                            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2167                        ),
2168                        Uint128::one(),
2169                    ),
2170                ],
2171            },
2172            TC {
2173                pin: vec![],
2174                pin_senders: vec![],
2175                unpin: vec![ObjectId::from(
2176                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2177                )],
2178                unpin_senders: vec![message_info(&addr("bob"), &[])],
2179                expected_count: 0,
2180                expected_error: None,
2181                expected_object_pin_count: vec![(
2182                    ObjectId::from(
2183                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2184                    ),
2185                    Uint128::zero(),
2186                )],
2187            },
2188            TC {
2189                pin: vec![
2190                    ObjectId::from(
2191                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2192                    ),
2193                    ObjectId::from(
2194                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2195                    ),
2196                    ObjectId::from(
2197                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2198                    ),
2199                    ObjectId::from(
2200                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2201                    ),
2202                ],
2203                pin_senders: vec![
2204                    message_info(&addr("bob"), &[]),
2205                    message_info(&addr("alice"), &[]),
2206                    message_info(&addr("martin"), &[]),
2207                    message_info(&addr("pierre"), &[]),
2208                ],
2209                unpin: vec![ObjectId::from(
2210                    "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2211                )],
2212                unpin_senders: vec![message_info(&addr("martin"), &[])],
2213                expected_count: 3,
2214                expected_error: None,
2215                expected_object_pin_count: vec![
2216                    (
2217                        ObjectId::from(
2218                            "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2219                        ),
2220                        Uint128::one(),
2221                    ),
2222                    (
2223                        ObjectId::from(
2224                            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2225                        ),
2226                        Uint128::new(2),
2227                    ),
2228                ],
2229            },
2230            TC {
2231                // Object not exists
2232                pin: vec![ObjectId::from(
2233                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2234                )],
2235                pin_senders: vec![message_info(&addr("bob"), &[])],
2236                unpin: vec![ObjectId::from(
2237                    "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56",
2238                )],
2239                unpin_senders: vec![message_info(&addr("martin"), &[])],
2240                expected_count: 1,
2241                expected_error: Some(ContractError::Std(StdError::not_found(
2242                    not_found_object_info::<Object>(
2243                        "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56",
2244                    ),
2245                ))),
2246                expected_object_pin_count: vec![(
2247                    ObjectId::from(
2248                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2249                    ),
2250                    Uint128::one(),
2251                )],
2252            },
2253            TC {
2254                // Invalid object id
2255                pin: vec![ObjectId::from(
2256                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2257                )],
2258                pin_senders: vec![message_info(&addr("bob"), &[])],
2259                unpin: vec![ObjectId::from("invalid id")],
2260                unpin_senders: vec![message_info(&addr("martin"), &[])],
2261                expected_count: 1,
2262                expected_error: Some(ContractError::Std(StdError::parse_err(
2263                    type_name::<Vec<u8>>(),
2264                    "invalid Base16 encoding".to_string(),
2265                ))),
2266                expected_object_pin_count: vec![(
2267                    ObjectId::from(
2268                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2269                    ),
2270                    Uint128::one(),
2271                )],
2272            },
2273        ];
2274
2275        for case in cases {
2276            let mut deps = mock_dependencies();
2277            let info = message_info(&addr(CREATOR), &[]);
2278
2279            instantiate(
2280                deps.as_mut(),
2281                mock_env(),
2282                info.clone(),
2283                InstantiateMsg {
2284                    bucket: "test".to_string(),
2285                    config: Default::default(),
2286                    limits: Default::default(),
2287                    pagination: Default::default(),
2288                },
2289            )
2290            .unwrap();
2291
2292            let data = general_purpose::STANDARD.encode("okp4");
2293            let msg = ExecuteMsg::StoreObject {
2294                data: Binary::from_base64(data.as_str()).unwrap(),
2295                pin: false,
2296                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2297            };
2298            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2299
2300            let data = general_purpose::STANDARD.encode("data");
2301            let msg = ExecuteMsg::StoreObject {
2302                data: Binary::from_base64(data.as_str()).unwrap(),
2303                pin: false,
2304                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2305            };
2306            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2307
2308            let data = general_purpose::STANDARD.encode("hello");
2309            let msg = ExecuteMsg::StoreObject {
2310                data: Binary::from_base64(data.as_str()).unwrap(),
2311                pin: false,
2312                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2313            };
2314            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2315
2316            let mut last_result: Option<Result<Response, ContractError>> = None;
2317            case.pin
2318                .iter()
2319                .zip(case.pin_senders)
2320                .for_each(|(object_id, info)| {
2321                    last_result = Some(execute(
2322                        deps.as_mut(),
2323                        mock_env(),
2324                        info,
2325                        ExecuteMsg::PinObject {
2326                            id: object_id.clone(),
2327                        },
2328                    ));
2329                });
2330            case.unpin
2331                .iter()
2332                .zip(case.unpin_senders)
2333                .for_each(|(object_id, info)| {
2334                    last_result = Some(execute(
2335                        deps.as_mut(),
2336                        mock_env(),
2337                        info,
2338                        ExecuteMsg::UnpinObject {
2339                            id: object_id.clone(),
2340                        },
2341                    ));
2342                });
2343
2344            match case.expected_error {
2345                Some(err) => assert_eq!(last_result.unwrap().unwrap_err(), err),
2346                _ => {
2347                    assert_eq!(
2348                        pins()
2349                            .keys_raw(&deps.storage, None, None, Order::Ascending)
2350                            .count(),
2351                        case.expected_count
2352                    );
2353                    for (object_id, count) in case.expected_object_pin_count {
2354                        assert_eq!(
2355                            objects()
2356                                .load(&deps.storage, decode_hex(&object_id).into())
2357                                .unwrap()
2358                                .pin_count,
2359                            count
2360                        );
2361                    }
2362                }
2363            }
2364        }
2365    }
2366
2367    #[test]
2368    fn fetch_objects() {
2369        let mut deps = mock_dependencies();
2370        let info1 = message_info(&addr("creator1"), &[]);
2371        let info2 = message_info(&addr("creator2"), &[]);
2372
2373        let msg = InstantiateMsg {
2374            bucket: String::from("test"),
2375            config: Default::default(),
2376            limits: Default::default(),
2377            pagination: Default::default(),
2378        };
2379        instantiate(deps.as_mut(), mock_env(), info1.clone(), msg).unwrap();
2380
2381        let msg = QueryMsg::Objects {
2382            address: None,
2383            first: None,
2384            after: None,
2385        };
2386        let result = query(deps.as_ref(), mock_env(), msg).unwrap();
2387        let response: ObjectsResponse = from_json(&result).unwrap();
2388        assert_eq!(response.data.len(), 0);
2389        assert_eq!(
2390            response.page_info,
2391            PageInfo {
2392                has_next_page: false,
2393                cursor: "".to_string(),
2394            }
2395        );
2396
2397        let data = general_purpose::STANDARD.encode("object1");
2398        let msg = ExecuteMsg::StoreObject {
2399            data: Binary::from_base64(data.as_str()).unwrap(),
2400            pin: false,
2401            compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2402        };
2403        execute(deps.as_mut(), mock_env(), info1.clone(), msg).unwrap();
2404        let data = general_purpose::STANDARD.encode("object2");
2405        let msg = ExecuteMsg::StoreObject {
2406            data: Binary::from_base64(data.as_str()).unwrap(),
2407            pin: false,
2408            compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2409        };
2410        execute(deps.as_mut(), mock_env(), info1, msg).unwrap();
2411        let data = general_purpose::STANDARD.encode("object3");
2412        let msg = ExecuteMsg::StoreObject {
2413            data: Binary::from_base64(data.as_str()).unwrap(),
2414            pin: false,
2415            compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2416        };
2417        execute(deps.as_mut(), mock_env(), info2, msg).unwrap();
2418
2419        let cases = vec![
2420            (
2421                QueryMsg::Objects {
2422                    address: None,
2423                    first: None,
2424                    after: None,
2425                },
2426                3,
2427                PageInfo {
2428                    has_next_page: false,
2429                    cursor: "CZC4Avd5xNeJaBkK6MYrA1ZSQPNr76GU1k2JJSjmaDyF".to_string(),
2430                },
2431            ),
2432            (
2433                QueryMsg::Objects {
2434                    address: Some(addr("unknown").to_string()),
2435                    first: None,
2436                    after: None,
2437                },
2438                0,
2439                PageInfo {
2440                    has_next_page: false,
2441                    cursor: "".to_string(),
2442                },
2443            ),
2444            (
2445                QueryMsg::Objects {
2446                    address: Some(addr("creator1").to_string()),
2447                    first: None,
2448                    after: None,
2449                },
2450                2,
2451                PageInfo {
2452                    has_next_page: false,
2453                    cursor: "CZC4Avd5xNeJaBkK6MYrA1ZSQPNr76GU1k2JJSjmaDyF".to_string(),
2454                },
2455            ),
2456            (
2457                QueryMsg::Objects {
2458                    address: Some(addr("creator1").to_string()),
2459                    first: Some(1),
2460                    after: None,
2461                },
2462                1,
2463                PageInfo {
2464                    has_next_page: true,
2465                    cursor: "5bfWM6UF5MowkQVp16q5pnXvwc9SVkS4xZkFeVLdswjU".to_string(),
2466                },
2467            ),
2468            (
2469                QueryMsg::Objects {
2470                    address: Some(addr("creator1").to_string()),
2471                    first: Some(1),
2472                    after: Some("5bfWM6UF5MowkQVp16q5pnXvwc9SVkS4xZkFeVLdswjU".to_string()),
2473                },
2474                1,
2475                PageInfo {
2476                    has_next_page: false,
2477                    cursor: "CZC4Avd5xNeJaBkK6MYrA1ZSQPNr76GU1k2JJSjmaDyF".to_string(),
2478                },
2479            ),
2480        ];
2481
2482        for case in cases {
2483            let msg = case.0;
2484            let result = query(deps.as_ref(), mock_env(), msg).unwrap();
2485            let response: ObjectsResponse = from_json(&result).unwrap();
2486            assert_eq!(response.data.len(), case.1);
2487            assert_eq!(response.page_info, case.2);
2488        }
2489
2490        let msg = QueryMsg::Objects {
2491            address: Some(addr("creator2").to_string()),
2492            first: None,
2493            after: None,
2494        };
2495        let result = query(deps.as_ref(), mock_env(), msg).unwrap();
2496        let response: ObjectsResponse = from_json(&result).unwrap();
2497        assert_eq!(
2498            response.data.first().unwrap(),
2499            &ObjectResponse {
2500                id: "0a6d95579ba3dd2f79c870906fd894007ce449020d111d358894cfbbcd9a03a4".to_string(),
2501                owner: addr("creator2").to_string(),
2502                is_pinned: false,
2503                size: 7u128.into(),
2504                compressed_size: 7u128.into(),
2505                compression_algorithm: CompressionAlgorithm::Passthrough,
2506            }
2507        );
2508    }
2509
2510    #[test]
2511    fn object_pins() {
2512        let mut deps = mock_dependencies();
2513        let info1 = message_info(&addr("creator1"), &[]);
2514        let info2 = message_info(&addr("creator2"), &[]);
2515
2516        let msg = InstantiateMsg {
2517            bucket: String::from("test"),
2518            config: Default::default(),
2519            limits: Default::default(),
2520            pagination: Default::default(),
2521        };
2522        instantiate(deps.as_mut(), mock_env(), info1.clone(), msg).unwrap();
2523
2524        let data = general_purpose::STANDARD.encode("object1");
2525        let msg = ExecuteMsg::StoreObject {
2526            data: Binary::from_base64(data.as_str()).unwrap(),
2527            pin: false,
2528            compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2529        };
2530        execute(deps.as_mut(), mock_env(), info1.clone(), msg).unwrap();
2531        // 1: 445008b7f2932922bdb184771d9978516a4f89d77000c2d6eab18b0894aac3a7
2532        let data = general_purpose::STANDARD.encode("object2");
2533        let msg = ExecuteMsg::StoreObject {
2534            data: Binary::from_base64(data.as_str()).unwrap(),
2535            pin: true,
2536            compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2537        };
2538        execute(deps.as_mut(), mock_env(), info2, msg).unwrap();
2539        // 2: abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56
2540        let msg = ExecuteMsg::PinObject {
2541            id: "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56".to_string(),
2542        };
2543        execute(deps.as_mut(), mock_env(), info1, msg).unwrap();
2544
2545        let cases = vec![
2546            (
2547                QueryMsg::ObjectPins {
2548                    id: "445008b7f2932922bdb184771d9978516a4f89d77000c2d6eab18b0894aac3a7"
2549                        .to_string(),
2550                    first: None,
2551                    after: None,
2552                },
2553                Vec::<Addr>::new(),
2554                PageInfo {
2555                    has_next_page: false,
2556                    cursor: "".to_string(),
2557                },
2558            ),
2559            (
2560                QueryMsg::ObjectPins {
2561                    id: "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56"
2562                        .to_string(),
2563                    first: None,
2564                    after: None,
2565                },
2566                vec![addr("creator2"), addr("creator1")],
2567                PageInfo {
2568                    has_next_page: false,
2569                    cursor: "3wwsaS9LwRtjTvYihWjb64ph5BdBrkTrt8f2sG6vBiE774tMxX31Rs9Mqn91NGQ6MzUow4Gi5iBR6STw68tuG2esLAdK".to_string(),
2570                },
2571            ),
2572            (
2573                QueryMsg::ObjectPins {
2574                    id: "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56"
2575                        .to_string(),
2576                    first: Some(1),
2577                    after: None,
2578                },
2579                vec![addr("creator2")],
2580                PageInfo {
2581                    has_next_page: true,
2582                    cursor: "3wwsaS9LwRtjTrrGpg2qSGEo3gFkR53R2hUNX2PapfytooQuGcPiyF4zRneMECgJiZXS336Cd7pZU4CJ96nt3mcoQR8g".to_string(),
2583                },
2584            ),
2585            (
2586                QueryMsg::ObjectPins {
2587                    id: "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56"
2588                        .to_string(),
2589                    first: Some(1),
2590                    after: Some("3wwsaS9LwRtjTrrGpg2qSGEo3gFkR53R2hUNX2PapfytooQuGcPiyF4zRneMECgJiZXS336Cd7pZU4CJ96nt3mcoQR8g".to_string()),
2591                },
2592                vec![addr("creator1")],
2593                PageInfo {
2594                    has_next_page: false,
2595                    cursor: "3wwsaS9LwRtjTvYihWjb64ph5BdBrkTrt8f2sG6vBiE774tMxX31Rs9Mqn91NGQ6MzUow4Gi5iBR6STw68tuG2esLAdK".to_string(),
2596                },
2597            ),
2598        ];
2599
2600        for case in cases {
2601            let result = query(deps.as_ref(), mock_env_addr(), case.0).unwrap();
2602            let response: ObjectPinsResponse = from_json(&result).unwrap();
2603            assert_eq!(
2604                response
2605                    .data
2606                    .iter()
2607                    .map(|a| Addr::unchecked(a))
2608                    .collect::<Vec<Addr>>(),
2609                case.1
2610            );
2611            assert_eq!(response.page_info, case.2);
2612        }
2613    }
2614
2615    #[test]
2616    fn object_pins_errors() {
2617        let mut deps = mock_dependencies();
2618
2619        let msg = InstantiateMsg {
2620            bucket: String::from("test"),
2621            config: Default::default(),
2622            limits: Default::default(),
2623            pagination: Default::default(),
2624        };
2625        instantiate(
2626            deps.as_mut(),
2627            mock_env(),
2628            message_info(&addr("creator1"), &[]),
2629            msg,
2630        )
2631        .unwrap();
2632
2633        let cases = vec![
2634            (
2635                QueryMsg::ObjectPins {
2636                    id: "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56"
2637                        .to_string(),
2638                    after: None,
2639                    first: None,
2640                },
2641                StdError::not_found(not_found_object_info::<Object>(
2642                    "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56",
2643                )),
2644            ),
2645            (
2646                QueryMsg::ObjectPins {
2647                    id: "invalid id".to_string(),
2648                    after: None,
2649                    first: None,
2650                },
2651                StdError::parse_err(
2652                    type_name::<Vec<u8>>(),
2653                    "invalid Base16 encoding".to_string(),
2654                ),
2655            ),
2656        ];
2657
2658        for case in cases {
2659            let res = query(deps.as_ref(), mock_env(), case.0).err().unwrap();
2660            assert_eq!(res, case.1)
2661        }
2662    }
2663
2664    #[test]
2665    fn forget_object() {
2666        struct TC {
2667            pins: Vec<ObjectId>,
2668            pins_senders: Vec<MessageInfo>,
2669            forget_objects: Vec<ObjectId>,
2670            forget_senders: Vec<MessageInfo>,
2671            expected_count: usize,
2672            expected_total_size: Uint128,
2673            expected_compressed_size: Uint128,
2674            expected_error: Option<ContractError>,
2675        }
2676
2677        let cases = vec![
2678            TC {
2679                pins: vec![],
2680                pins_senders: vec![],
2681                forget_objects: vec![ObjectId::from(
2682                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2683                )],
2684                forget_senders: vec![message_info(&addr("bob"), &[])],
2685                expected_count: 3,
2686                expected_total_size: Uint128::new(474),
2687                expected_compressed_size: Uint128::new(418),
2688                expected_error: None,
2689            },
2690            TC {
2691                pins: vec![],
2692                pins_senders: vec![],
2693                forget_objects: vec![
2694                    ObjectId::from(
2695                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2696                    ),
2697                    ObjectId::from(
2698                        "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
2699                    ),
2700                ],
2701                forget_senders: vec![
2702                    message_info(&addr("bob"), &[]),
2703                    message_info(&addr("bob"), &[]),
2704                ],
2705                expected_count: 2,
2706                expected_total_size: Uint128::new(469),
2707                expected_compressed_size: Uint128::new(413),
2708                expected_error: None,
2709            },
2710            TC {
2711                pins: vec![],
2712                pins_senders: vec![],
2713                forget_objects: vec![ObjectId::from(
2714                    "d1abcabb14dd23d2cf60472dffb4823be10ac20148e8ef7b9644cc14fcf8a073",
2715                )],
2716                forget_senders: vec![
2717                    message_info(&addr("bob"), &[]),
2718                    message_info(&addr("bob"), &[]),
2719                ],
2720                expected_count: 3,
2721                expected_total_size: Uint128::new(13),
2722                expected_compressed_size: Uint128::new(13),
2723                expected_error: None,
2724            },
2725            TC {
2726                pins: vec![ObjectId::from(
2727                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2728                )],
2729                pins_senders: vec![message_info(&addr("bob"), &[])],
2730                forget_objects: vec![ObjectId::from(
2731                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2732                )],
2733                forget_senders: vec![message_info(&addr("alice"), &[])], // the sender is different from the pinner, so error
2734                expected_count: 4,
2735                expected_total_size: Uint128::new(478),
2736                expected_compressed_size: Uint128::new(422),
2737                expected_error: Some(ContractError::ObjectPinned {}),
2738            },
2739            TC {
2740                pins: vec![ObjectId::from(
2741                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2742                )],
2743                pins_senders: vec![message_info(&addr("bob"), &[])],
2744                forget_objects: vec![ObjectId::from(
2745                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2746                )],
2747                forget_senders: vec![message_info(&addr("bob"), &[])], // the sender is the same as the pinner, so forget should work
2748                expected_count: 3,
2749                expected_total_size: Uint128::new(474),
2750                expected_compressed_size: Uint128::new(418),
2751                expected_error: None,
2752            },
2753            TC {
2754                pins: vec![
2755                    ObjectId::from(
2756                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2757                    ),
2758                    ObjectId::from(
2759                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2760                    ),
2761                ],
2762                pins_senders: vec![
2763                    message_info(&addr("bob"), &[]),
2764                    message_info(&addr("alice"), &[]),
2765                ],
2766                forget_objects: vec![ObjectId::from(
2767                    "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2768                )],
2769                forget_senders: vec![message_info(&addr("bob"), &[])], // the sender is the same as the pinner, but another pinner is on it so error
2770                expected_count: 4,
2771                expected_total_size: Uint128::new(478),
2772                expected_compressed_size: Uint128::new(422),
2773                expected_error: Some(ContractError::ObjectPinned {}),
2774            },
2775            TC {
2776                pins: vec![
2777                    ObjectId::from(
2778                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2779                    ),
2780                    ObjectId::from(
2781                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2782                    ),
2783                ],
2784                pins_senders: vec![
2785                    message_info(&addr("bob"), &[]),
2786                    message_info(&addr("alice"), &[]),
2787                ],
2788                forget_objects: vec![ObjectId::from(
2789                    "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56",
2790                )],
2791                forget_senders: vec![message_info(&addr("bob"), &[])], // the sender is the same as the pinner, but another pinner is on it so error
2792                expected_count: 4,
2793                expected_total_size: Uint128::new(478),
2794                expected_compressed_size: Uint128::new(422),
2795                expected_error: Some(ContractError::Std(StdError::not_found(
2796                    not_found_object_info::<Object>(
2797                        "abafa4428bdc8c34dae28bbc17303a62175f274edf59757b3e9898215a428a56",
2798                    ),
2799                ))),
2800            },
2801            TC {
2802                pins: vec![
2803                    ObjectId::from(
2804                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2805                    ),
2806                    ObjectId::from(
2807                        "315d0d9ab12c5f8884100055f79de50b72db4bd2c9bfd3df049d89640fed1fa6",
2808                    ),
2809                ],
2810                pins_senders: vec![
2811                    message_info(&addr("bob"), &[]),
2812                    message_info(&addr("alice"), &[]),
2813                ],
2814                forget_objects: vec![ObjectId::from("invalid id")],
2815                forget_senders: vec![message_info(&addr("bob"), &[])], // the sender is the same as the pinner, but another pinner is on it so error
2816                expected_count: 4,
2817                expected_total_size: Uint128::new(478),
2818                expected_compressed_size: Uint128::new(422),
2819                expected_error: Some(ContractError::Std(StdError::parse_err(
2820                    type_name::<Vec<u8>>(),
2821                    "invalid Base16 encoding".to_string(),
2822                ))),
2823            },
2824        ];
2825
2826        for case in cases {
2827            let mut deps = mock_dependencies();
2828            let info = message_info(&addr(CREATOR), &[]);
2829
2830            instantiate(
2831                deps.as_mut(),
2832                mock_env(),
2833                info.clone(),
2834                InstantiateMsg {
2835                    bucket: "test".to_string(),
2836                    config: Default::default(),
2837                    limits: Default::default(),
2838                    pagination: Default::default(),
2839                },
2840            )
2841            .unwrap();
2842
2843            let data = general_purpose::STANDARD.encode("okp4");
2844            let msg = ExecuteMsg::StoreObject {
2845                data: Binary::from_base64(data.as_str()).unwrap(),
2846                pin: false,
2847                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2848            };
2849            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2850
2851            let data = general_purpose::STANDARD.encode("data");
2852            let msg = ExecuteMsg::StoreObject {
2853                data: Binary::from_base64(data.as_str()).unwrap(),
2854                pin: false,
2855                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2856            };
2857            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2858
2859            let data = general_purpose::STANDARD.encode("hello");
2860            let msg = ExecuteMsg::StoreObject {
2861                data: Binary::from_base64(data.as_str()).unwrap(),
2862                pin: false,
2863                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2864            };
2865            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2866
2867            let data = general_purpose::STANDARD.encode(
2868                "In a magical land, there \
2869            were many realms, one of which was known as OKP4. Within this realm, druid programmers \
2870            possessed the power to create smart contracts. As the kingdom grew, the druids used \
2871            their skills to power decentralized systems, bringing prosperity and wonder to all who \
2872            sought their expertise. And so, the legend of the druid programmers and their magical \
2873            smart contracts lived on, inspiring future generations to unlock the power of the \
2874            digital realm.",
2875            );
2876            let msg = ExecuteMsg::StoreObject {
2877                data: Binary::from_base64(data.as_str()).unwrap(),
2878                pin: false,
2879                compression_algorithm: Some(CompressionAlgorithm::Snappy),
2880            };
2881            let _ = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
2882
2883            case.pins
2884                .iter()
2885                .zip(case.pins_senders)
2886                .for_each(|(object_id, info)| {
2887                    _ = execute(
2888                        deps.as_mut(),
2889                        mock_env(),
2890                        info,
2891                        ExecuteMsg::PinObject {
2892                            id: object_id.clone(),
2893                        },
2894                    );
2895                });
2896
2897            let mut last_result: Option<Result<Response, ContractError>> = None;
2898
2899            case.forget_objects
2900                .iter()
2901                .zip(case.forget_senders)
2902                .for_each(|(object_id, info)| {
2903                    last_result = Some(execute(
2904                        deps.as_mut(),
2905                        mock_env(),
2906                        info,
2907                        ExecuteMsg::ForgetObject {
2908                            id: object_id.clone(),
2909                        },
2910                    ));
2911                });
2912
2913            match case.expected_error {
2914                Some(err) => assert_eq!(last_result.unwrap().unwrap_err(), err),
2915                _ => {
2916                    for object_id in case.forget_objects {
2917                        assert_eq!(
2918                            objects()
2919                                .load(&deps.storage, decode_hex(object_id.as_str()).into())
2920                                .unwrap_err(),
2921                            StdError::not_found(not_found_object_info::<Object>(&object_id))
2922                        );
2923                    }
2924                }
2925            }
2926            assert_eq!(
2927                objects()
2928                    .keys_raw(&deps.storage, None, None, Order::Ascending)
2929                    .count(),
2930                case.expected_count
2931            );
2932
2933            let bucket = query::bucket(deps.as_ref()).unwrap();
2934            assert_eq!(
2935                bucket.stat,
2936                BucketStatBuilder::default()
2937                    .object_count(Uint128::from(case.expected_count as u128))
2938                    .size(case.expected_total_size)
2939                    .compressed_size(case.expected_compressed_size)
2940                    .build()
2941                    .unwrap()
2942            );
2943            assert_eq!(
2944                bucket.stat.object_count,
2945                Uint128::from(case.expected_count as u128)
2946            );
2947            assert_eq!(bucket.stat.size, case.expected_total_size);
2948            assert_eq!(bucket.stat.compressed_size, case.expected_compressed_size);
2949        }
2950    }
2951
2952    #[test]
2953    fn store_forgotten_object() {
2954        let mut deps = mock_dependencies();
2955        let info = message_info(&addr(CREATOR), &[]);
2956
2957        instantiate(
2958            deps.as_mut(),
2959            mock_env(),
2960            info.clone(),
2961            InstantiateMsg {
2962                bucket: "test".to_string(),
2963                config: Default::default(),
2964                limits: Default::default(),
2965                pagination: Default::default(),
2966            },
2967        )
2968        .unwrap();
2969
2970        let data = general_purpose::STANDARD.encode("data");
2971        let _ = execute(
2972            deps.as_mut(),
2973            mock_env(),
2974            info.clone(),
2975            ExecuteMsg::StoreObject {
2976                data: Binary::from_base64(data.as_str()).unwrap(),
2977                pin: false,
2978                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
2979            },
2980        )
2981        .unwrap();
2982
2983        let _ = execute(
2984            deps.as_mut(),
2985            mock_env(),
2986            info.clone(),
2987            ExecuteMsg::ForgetObject {
2988                id: "3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7".to_string(),
2989            },
2990        )
2991        .unwrap();
2992
2993        let result = execute(
2994            deps.as_mut(),
2995            mock_env(),
2996            info,
2997            ExecuteMsg::StoreObject {
2998                data: Binary::from_base64(data.as_str()).unwrap(),
2999                pin: false,
3000                compression_algorithm: Some(CompressionAlgorithm::Passthrough),
3001            },
3002        );
3003
3004        assert_eq!(
3005            result.err(),
3006            None,
3007            "Object should successfully restored after a forgot"
3008        );
3009    }
3010}