@@ -390,17 +390,166 @@ fn validate_length(bytes: &[u8]) -> StdResult<()> {
390
390
/// // `env3` is one block and 5.5 seconds later
391
391
/// ```
392
392
pub fn mock_env ( ) -> Env {
393
- let contract_addr = MockApi :: default ( ) . addr_make ( "cosmos2contract" ) ;
394
- Env {
395
- block : BlockInfo {
396
- height : 12_345 ,
397
- time : Timestamp :: from_nanos ( 1_571_797_419_879_305_533 ) ,
393
+ let mut envs = Envs :: new ( BECH32_PREFIX ) ;
394
+ envs. make ( )
395
+ }
396
+
397
+ /// A factory type that stores chain information such as bech32 prefix and can make mock `Env`s from there.
398
+ ///
399
+ /// It increments height for each mock call and block time by 5 seconds but is otherwise dumb.
400
+ ///
401
+ /// In contrast to using `mock_env`, the bech32 prefix must always be specified.
402
+ ///
403
+ /// ## Examples
404
+ ///
405
+ /// Typical usage
406
+ ///
407
+ /// ```
408
+ /// # use cosmwasm_std::Timestamp;
409
+ /// use cosmwasm_std::testing::Envs;
410
+ ///
411
+ /// let mut envs = Envs::new("food");
412
+ ///
413
+ /// let env = envs.make();
414
+ /// assert_eq!(env.contract.address.as_str(), "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj");
415
+ /// assert_eq!(env.block.height, 12_345);
416
+ /// assert_eq!(env.block.time, Timestamp::from_nanos(1_571_797_419_879_305_533));
417
+ ///
418
+ /// let env = envs.make();
419
+ /// assert_eq!(env.contract.address.as_str(), "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj");
420
+ /// assert_eq!(env.block.height, 12_346);
421
+ /// assert_eq!(env.block.time, Timestamp::from_nanos(1_571_797_424_879_305_533));
422
+ ///
423
+ /// let env = envs.make();
424
+ /// assert_eq!(env.contract.address.as_str(), "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj");
425
+ /// assert_eq!(env.block.height, 12_347);
426
+ /// assert_eq!(env.block.time, Timestamp::from_nanos(1_571_797_429_879_305_533));
427
+ /// ```
428
+ ///
429
+ /// Or use with iterator
430
+ ///
431
+ /// ```
432
+ /// # use cosmwasm_std::Timestamp;
433
+ /// use cosmwasm_std::testing::Envs;
434
+ ///
435
+ /// let mut envs = Envs::new("food");
436
+ ///
437
+ /// for (index, env) in envs.take(100).enumerate() {
438
+ /// assert_eq!(env.contract.address.as_str(), "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj");
439
+ /// assert_eq!(env.block.height, 12_345 + index as u64);
440
+ /// assert_eq!(env.block.time, Timestamp::from_nanos(1_571_797_419_879_305_533).plus_seconds((index*5) as u64));
441
+ /// }
442
+ /// ```
443
+ pub struct Envs {
444
+ chain_id : String ,
445
+ contract_address : Addr ,
446
+ /// The number of nanoseconds between two consecutive blocks
447
+ block_time : u64 ,
448
+ last_height : u64 ,
449
+ last_time : Timestamp ,
450
+ }
451
+
452
+ /// Options to create an `Envs` instance.
453
+ ///
454
+ /// ## Examples
455
+ ///
456
+ /// Must be constructed with the help of `Default` since new options might be added later.
457
+ ///
458
+ /// ```
459
+ /// # use cosmwasm_std::Timestamp;
460
+ /// use cosmwasm_std::testing::{Envs, EnvsOptions};
461
+ ///
462
+ /// let mut options = EnvsOptions::default();
463
+ /// options.chain_id = "megachain".to_string();
464
+ /// options.bech32_prefix = "mega";
465
+ /// let mut envs = Envs::with_options(options);
466
+ ///
467
+ /// let env = envs.make();
468
+ /// assert_eq!(env.block.chain_id, "megachain");
469
+ /// assert_eq!(env.contract.address.as_str(), "mega1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts7vnj8h");
470
+ /// ```
471
+ #[ derive( Clone , Debug ) ]
472
+ #[ non_exhaustive]
473
+ pub struct EnvsOptions {
474
+ pub bech32_prefix : & ' static str , /* static due to MockApi's Copy requirement. No better idea for now. */
475
+ pub block_time : u64 ,
476
+ // The height before the first `make` call
477
+ pub initial_height : u64 ,
478
+ // The block time before the first `make` call
479
+ pub initial_time : Timestamp ,
480
+ pub chain_id : String ,
481
+ }
482
+
483
+ impl Default for EnvsOptions {
484
+ fn default ( ) -> Self {
485
+ EnvsOptions {
486
+ bech32_prefix : BECH32_PREFIX ,
487
+ block_time : 5_000_000_000 , // 5s
488
+ initial_height : 12_344 ,
489
+ initial_time : Timestamp :: from_nanos ( 1_571_797_419_879_305_533 ) . minus_seconds ( 5 ) ,
398
490
chain_id : "cosmos-testnet-14002" . to_string ( ) ,
399
- } ,
400
- transaction : Some ( TransactionInfo { index : 3 } ) ,
401
- contract : ContractInfo {
402
- address : contract_addr,
403
- } ,
491
+ }
492
+ }
493
+ }
494
+
495
+ impl Envs {
496
+ pub fn new ( bech32_prefix : & ' static str ) -> Self {
497
+ Self :: with_options ( EnvsOptions {
498
+ bech32_prefix,
499
+ ..Default :: default ( )
500
+ } )
501
+ }
502
+
503
+ pub fn with_options ( options : EnvsOptions ) -> Self {
504
+ let api = MockApi :: default ( ) . with_prefix ( options. bech32_prefix ) ;
505
+ Envs {
506
+ chain_id : options. chain_id ,
507
+ // Default values here for compatibility with old `mock_env` function. They could be changed to anything else if there is a good reason.
508
+ contract_address : api. addr_make ( "cosmos2contract" ) ,
509
+ block_time : options. block_time ,
510
+ last_height : options. initial_height ,
511
+ last_time : options. initial_time ,
512
+ }
513
+ }
514
+
515
+ pub fn make ( & mut self ) -> Env {
516
+ self . checked_make ( ) . unwrap ( )
517
+ }
518
+
519
+ fn checked_make ( & mut self ) -> Option < Env > {
520
+ let height = self . last_height . checked_add ( 1 ) ?;
521
+ let time = Timestamp :: from_nanos ( self . last_time . nanos ( ) . checked_add ( self . block_time ) ?) ;
522
+
523
+ self . last_height = height;
524
+ self . last_time = time;
525
+
526
+ Some ( Env {
527
+ block : BlockInfo {
528
+ height,
529
+ time,
530
+ chain_id : self . chain_id . clone ( ) ,
531
+ } ,
532
+ transaction : Some ( TransactionInfo { index : 3 } ) ,
533
+ contract : ContractInfo {
534
+ address : self . contract_address . clone ( ) ,
535
+ } ,
536
+ } )
537
+ }
538
+ }
539
+
540
+ impl Default for Envs {
541
+ fn default ( ) -> Self {
542
+ Envs :: with_options ( EnvsOptions :: default ( ) )
543
+ }
544
+ }
545
+
546
+ // The iterator implementation ends in case of overflows to avoid panics.
547
+ // Using this is recommended for very long running test suites.
548
+ impl Iterator for Envs {
549
+ type Item = Env ;
550
+
551
+ fn next ( & mut self ) -> Option < Self :: Item > {
552
+ self . checked_make ( )
404
553
}
405
554
}
406
555
@@ -1277,9 +1426,98 @@ mod tests {
1277
1426
include_bytes ! ( "../../../crypto/testdata/eth-headers/1699693797.394876721s.json" ) ;
1278
1427
1279
1428
#[ test]
1280
- fn mock_env_matches_mock_contract_addr ( ) {
1281
- let contract_address = mock_env ( ) . contract . address ;
1282
- assert_eq ! ( contract_address, Addr :: unchecked( MOCK_CONTRACT_ADDR ) ) ;
1429
+ fn mock_env_works ( ) {
1430
+ let env = mock_env ( ) ;
1431
+ assert_eq ! (
1432
+ env,
1433
+ Env {
1434
+ block: BlockInfo {
1435
+ height: 12345 ,
1436
+ time: Timestamp :: from_nanos( 1571797419879305533 ) ,
1437
+ chain_id: "cosmos-testnet-14002" . to_string( )
1438
+ } ,
1439
+ transaction: Some ( TransactionInfo { index: 3 } ) ,
1440
+ contract: ContractInfo {
1441
+ address: Addr :: unchecked( MOCK_CONTRACT_ADDR )
1442
+ }
1443
+ }
1444
+ )
1445
+ }
1446
+
1447
+ #[ test]
1448
+ fn envs_works ( ) {
1449
+ let mut envs = Envs :: new ( "food" ) ;
1450
+
1451
+ let env = envs. make ( ) ;
1452
+ assert_eq ! (
1453
+ env. contract. address. as_str( ) ,
1454
+ "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj"
1455
+ ) ;
1456
+ assert_eq ! ( env. block. height, 12_345 ) ;
1457
+ assert_eq ! (
1458
+ env. block. time,
1459
+ Timestamp :: from_nanos( 1_571_797_419_879_305_533 )
1460
+ ) ;
1461
+
1462
+ let env = envs. make ( ) ;
1463
+ assert_eq ! (
1464
+ env. contract. address. as_str( ) ,
1465
+ "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj"
1466
+ ) ;
1467
+ assert_eq ! ( env. block. height, 12_346 ) ;
1468
+ assert_eq ! (
1469
+ env. block. time,
1470
+ Timestamp :: from_nanos( 1_571_797_424_879_305_533 )
1471
+ ) ;
1472
+
1473
+ let env = envs. make ( ) ;
1474
+ assert_eq ! (
1475
+ env. contract. address. as_str( ) ,
1476
+ "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj"
1477
+ ) ;
1478
+ assert_eq ! ( env. block. height, 12_347 ) ;
1479
+ assert_eq ! (
1480
+ env. block. time,
1481
+ Timestamp :: from_nanos( 1_571_797_429_879_305_533 )
1482
+ ) ;
1483
+ }
1484
+
1485
+ #[ test]
1486
+ fn envs_implements_iterator ( ) {
1487
+ let envs = Envs :: new ( "food" ) ;
1488
+
1489
+ let result: Vec < _ > = envs. into_iter ( ) . take ( 5 ) . collect ( ) ;
1490
+
1491
+ assert_eq ! (
1492
+ result[ 0 ] . contract. address. as_str( ) ,
1493
+ "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj"
1494
+ ) ;
1495
+ assert_eq ! ( result[ 0 ] . block. height, 12_345 ) ;
1496
+ assert_eq ! (
1497
+ result[ 0 ] . block. time,
1498
+ Timestamp :: from_nanos( 1_571_797_419_879_305_533 )
1499
+ ) ;
1500
+
1501
+ assert_eq ! (
1502
+ result[ 4 ] . contract. address. as_str( ) ,
1503
+ "food1jpev2csrppg792t22rn8z8uew8h3sjcpglcd0qv9g8gj8ky922ts74yrjj"
1504
+ ) ;
1505
+ assert_eq ! ( result[ 4 ] . block. height, 12_349 ) ;
1506
+ assert_eq ! (
1507
+ result[ 4 ] . block. time,
1508
+ Timestamp :: from_nanos( 1_571_797_439_879_305_533 )
1509
+ ) ;
1510
+
1511
+ // Get a millions envs through iterator
1512
+ let mut envs = Envs :: new ( "yo" ) ;
1513
+ let first = envs. next ( ) . unwrap ( ) ;
1514
+ let last = envs. take ( 1_000_000 ) . last ( ) . unwrap ( ) ;
1515
+ assert_eq ! ( first. block. height, 12_345 ) ;
1516
+ assert_eq ! ( last. block. height, 1_012_345 ) ;
1517
+ assert_eq ! (
1518
+ last. block. time,
1519
+ first. block. time. plus_seconds( 1_000_000 * 5 )
1520
+ ) ;
1283
1521
}
1284
1522
1285
1523
#[ test]
0 commit comments