Skip to content

Commit

Permalink
[CLI][E2E] Add test for aptos move publish (#8590)
Browse files Browse the repository at this point in the history
  • Loading branch information
banool authored Jun 15, 2023
1 parent ad2cbfd commit b7ddfcf
Show file tree
Hide file tree
Showing 12 changed files with 503 additions and 20 deletions.
4 changes: 4 additions & 0 deletions aptos-move/move-examples/cli-e2e-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# CLI E2E tests
These packages, one per production network, are used by the CLI E2E tests to test the correctness of the `aptos move` subcommand group. As such there is no particular rhyme or reason to what goes into these, it is meant to be an expressive selection of different, new features we might want to assert.

As it is now the 3 packages share the same source code. Down the line we might want to use these tests to confirm that the CLI works with a new feature as it lands in devnet, then testnet, then mainnet. For that we'd need to separate the source.
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
module addr::cli_e2e_tests {
use std::error;
use std::option::{Self, Option};
use std::signer;
use std::string::{Self, String};

use aptos_framework::object::{Self, ConstructorRef, Object};

use aptos_token_objects::collection;
use aptos_token_objects::token;
use aptos_std::string_utils;

const ENOT_A_HERO: u64 = 1;
const ENOT_A_WEAPON: u64 = 2;
const ENOT_A_GEM: u64 = 3;
const ENOT_CREATOR: u64 = 4;
const EINVALID_WEAPON_UNEQUIP: u64 = 5;
const EINVALID_GEM_UNEQUIP: u64 = 6;
const EINVALID_TYPE: u64 = 7;

struct OnChainConfig has key {
collection: String,
}

#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct Hero has key {
armor: Option<Object<Armor>>,
gender: String,
race: String,
shield: Option<Object<Shield>>,
weapon: Option<Object<Weapon>>,
mutator_ref: token::MutatorRef,
}

#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct Armor has key {
defense: u64,
gem: Option<Object<Gem>>,
weight: u64,
}

#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct Gem has key {
attack_modifier: u64,
defense_modifier: u64,
magic_attribute: String,
}

#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct Shield has key {
defense: u64,
gem: Option<Object<Gem>>,
weight: u64,
}

#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct Weapon has key {
attack: u64,
gem: Option<Object<Gem>>,
weapon_type: String,
weight: u64,
}

fun init_module(account: &signer) {
let collection = string::utf8(b"Hero Quest!");
collection::create_unlimited_collection(
account,
string::utf8(b"collection description"),
collection,
option::none(),
string::utf8(b"collection uri"),
);

let on_chain_config = OnChainConfig {
collection: string::utf8(b"Hero Quest!"),
};
move_to(account, on_chain_config);
}

fun create(
creator: &signer,
description: String,
name: String,
uri: String,
): ConstructorRef acquires OnChainConfig {
let on_chain_config = borrow_global<OnChainConfig>(signer::address_of(creator));
token::create_named_token(
creator,
on_chain_config.collection,
description,
name,
option::none(),
uri,
)
}

// Creation methods

public fun create_hero(
creator: &signer,
description: String,
gender: String,
name: String,
race: String,
uri: String,
): Object<Hero> acquires OnChainConfig {
let constructor_ref = create(creator, description, name, uri);
let token_signer = object::generate_signer(&constructor_ref);

let hero = Hero {
armor: option::none(),
gender,
race,
shield: option::none(),
weapon: option::none(),
mutator_ref: token::generate_mutator_ref(&constructor_ref),
};
move_to(&token_signer, hero);

object::address_to_object(signer::address_of(&token_signer))
}

public fun create_weapon(
creator: &signer,
attack: u64,
description: String,
name: String,
uri: String,
weapon_type: String,
weight: u64,
): Object<Weapon> acquires OnChainConfig {
let constructor_ref = create(creator, description, name, uri);
let token_signer = object::generate_signer(&constructor_ref);

let weapon = Weapon {
attack,
gem: option::none(),
weapon_type,
weight,
};
move_to(&token_signer, weapon);

object::address_to_object(signer::address_of(&token_signer))
}

public fun create_gem(
creator: &signer,
attack_modifier: u64,
defense_modifier: u64,
description: String,
magic_attribute: String,
name: String,
uri: String,
): Object<Gem> acquires OnChainConfig {
let constructor_ref = create(creator, description, name, uri);
let token_signer = object::generate_signer(&constructor_ref);

let gem = Gem {
attack_modifier,
defense_modifier,
magic_attribute,
};
move_to(&token_signer, gem);

object::address_to_object(signer::address_of(&token_signer))
}

// Transfer wrappers

public fun hero_equip_weapon(owner: &signer, hero: Object<Hero>, weapon: Object<Weapon>) acquires Hero {
let hero_obj = borrow_global_mut<Hero>(object::object_address(&hero));
option::fill(&mut hero_obj.weapon, weapon);
object::transfer_to_object(owner, weapon, hero);
}

public fun hero_unequip_weapon(owner: &signer, hero: Object<Hero>, weapon: Object<Weapon>) acquires Hero {
let hero_obj = borrow_global_mut<Hero>(object::object_address(&hero));
let stored_weapon = option::extract(&mut hero_obj.weapon);
assert!(stored_weapon == weapon, error::not_found(EINVALID_WEAPON_UNEQUIP));
object::transfer(owner, weapon, signer::address_of(owner));
}

public fun weapon_equip_gem(owner: &signer, weapon: Object<Weapon>, gem: Object<Gem>) acquires Weapon {
let weapon_obj = borrow_global_mut<Weapon>(object::object_address(&weapon));
option::fill(&mut weapon_obj.gem, gem);
object::transfer_to_object(owner, gem, weapon);
}

public fun weapon_unequip_gem(owner: &signer, weapon: Object<Weapon>, gem: Object<Gem>) acquires Weapon {
let weapon_obj = borrow_global_mut<Weapon>(object::object_address(&weapon));
let stored_gem = option::extract(&mut weapon_obj.gem);
assert!(stored_gem == gem, error::not_found(EINVALID_GEM_UNEQUIP));
object::transfer(owner, gem, signer::address_of(owner));
}

// Entry functions

entry fun mint_hero(
account: &signer,
description: String,
gender: String,
name: String,
race: String,
uri: String,
) acquires OnChainConfig {
create_hero(account, description, gender, name, race, uri);
}

entry fun set_hero_description(
creator: &signer,
collection: String,
name: String,
description: String,
) acquires Hero {
let (hero_obj, hero) = get_hero(
&signer::address_of(creator),
&collection,
&name,
);
let creator_addr = token::creator(hero_obj);
assert!(creator_addr == signer::address_of(creator), error::permission_denied(ENOT_CREATOR));
token::set_description(&hero.mutator_ref, description);
}

// View functions
#[view]
fun view_hero(creator: address, collection: String, name: String): Hero acquires Hero {
let token_address = token::create_token_address(
&creator,
&collection,
&name,
);
move_from<Hero>(token_address)
}

#[view]
fun view_hero_by_object(hero_obj: Object<Hero>): Hero acquires Hero {
let token_address = object::object_address(&hero_obj);
move_from<Hero>(token_address)
}

#[view]
fun view_object<T: key>(obj: Object<T>): String acquires Armor, Gem, Hero, Shield, Weapon {
let token_address = object::object_address(&obj);
if (exists<Armor>(token_address)) {
string_utils::to_string(borrow_global<Armor>(token_address))
} else if (exists<Gem>(token_address)) {
string_utils::to_string(borrow_global<Gem>(token_address))
} else if (exists<Hero>(token_address)) {
string_utils::to_string(borrow_global<Hero>(token_address))
} else if (exists<Shield>(token_address)) {
string_utils::to_string(borrow_global<Shield>(token_address))
} else if (exists<Weapon>(token_address)) {
string_utils::to_string(borrow_global<Weapon>(token_address))
} else {
abort EINVALID_TYPE
}
}

inline fun get_hero(creator: &address, collection: &String, name: &String): (Object<Hero>, &Hero) {
let token_address = token::create_token_address(
creator,
collection,
name,
);
(object::address_to_object<Hero>(token_address), borrow_global<Hero>(token_address))
}

#[test(account = @0x3)]
fun test_hero_with_gem_weapon(account: &signer) acquires Hero, OnChainConfig, Weapon {
init_module(account);

let hero = create_hero(
account,
string::utf8(b"The best hero ever!"),
string::utf8(b"Male"),
string::utf8(b"Wukong"),
string::utf8(b"Monkey God"),
string::utf8(b""),
);

let weapon = create_weapon(
account,
32,
string::utf8(b"A magical staff!"),
string::utf8(b"Ruyi Jingu Bang"),
string::utf8(b""),
string::utf8(b"staff"),
15,
);

let gem = create_gem(
account,
32,
32,
string::utf8(b"Beautiful specimen!"),
string::utf8(b"earth"),
string::utf8(b"jade"),
string::utf8(b""),
);

let account_address = signer::address_of(account);
assert!(object::is_owner(hero, account_address), 0);
assert!(object::is_owner(weapon, account_address), 1);
assert!(object::is_owner(gem, account_address), 2);

hero_equip_weapon(account, hero, weapon);
assert!(object::is_owner(hero, account_address), 3);
assert!(object::is_owner(weapon, object::object_address(&hero)), 4);
assert!(object::is_owner(gem, account_address), 5);

weapon_equip_gem(account, weapon, gem);
assert!(object::is_owner(hero, account_address), 6);
assert!(object::is_owner(weapon, object::object_address(&hero)), 7);
assert!(object::is_owner(gem, object::object_address(&weapon)), 8);

hero_unequip_weapon(account, hero, weapon);
assert!(object::is_owner(hero, account_address), 9);
assert!(object::is_owner(weapon, account_address), 10);
assert!(object::is_owner(gem, object::object_address(&weapon)), 11);

weapon_unequip_gem(account, weapon, gem);
assert!(object::is_owner(hero, account_address), 12);
assert!(object::is_owner(weapon, account_address), 13);
assert!(object::is_owner(gem, account_address), 14);
}
}
16 changes: 16 additions & 0 deletions aptos-move/move-examples/cli-e2e-tests/devnet/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "cli_e2e_tests"
version = "0.0.1"

[addresses]
addr = "_"

[dependencies.AptosFramework]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "devnet"
subdir = "aptos-move/framework/aptos-framework"

[dependencies.AptosTokenObjects]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "devnet"
subdir = "aptos-move/framework/aptos-token-objects"
1 change: 1 addition & 0 deletions aptos-move/move-examples/cli-e2e-tests/devnet/sources
16 changes: 16 additions & 0 deletions aptos-move/move-examples/cli-e2e-tests/mainnet/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "cli_e2e_tests"
version = "0.0.1"

[addresses]
addr = "_"

[dependencies.AptosFramework]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "mainnet"
subdir = "aptos-move/framework/aptos-framework"

[dependencies.AptosTokenObjects]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "mainnet"
subdir = "aptos-move/framework/aptos-token-objects"
1 change: 1 addition & 0 deletions aptos-move/move-examples/cli-e2e-tests/mainnet/sources
16 changes: 16 additions & 0 deletions aptos-move/move-examples/cli-e2e-tests/testnet/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "cli_e2e_tests"
version = "0.0.1"

[addresses]
addr = "_"

[dependencies.AptosFramework]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "testnet"
subdir = "aptos-move/framework/aptos-framework"

[dependencies.AptosTokenObjects]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "testnet"
subdir = "aptos-move/framework/aptos-token-objects"
1 change: 1 addition & 0 deletions aptos-move/move-examples/cli-e2e-tests/testnet/sources
Loading

0 comments on commit b7ddfcf

Please sign in to comment.