From 16a1274cfb6e2971a8c388e187b545003ef933c0 Mon Sep 17 00:00:00 2001 From: Manolis Liolios Date: Mon, 2 Dec 2024 13:34:41 +0200 Subject: [PATCH] Proper testing setup --- packages/discounts/sources/free_claims.move | 72 +-- packages/discounts/sources/house.move | 12 +- .../discounts/tests/free_claims_test.move | 563 ++++++++++-------- 3 files changed, 354 insertions(+), 293 deletions(-) diff --git a/packages/discounts/sources/free_claims.move b/packages/discounts/sources/free_claims.move index 4acda6b5..bf943c1e 100644 --- a/packages/discounts/sources/free_claims.move +++ b/packages/discounts/sources/free_claims.move @@ -83,6 +83,42 @@ public fun free_claim_with_day_one( self.internal_apply_full_discount(suins, intent, object::id(day_one), ctx); } +/// An admin action to authorize a type T for free claiming of names by +/// presenting +/// an object of type `T`. +public fun authorize_type( + self: &mut DiscountHouse, + _: &AdminCap, + domain_length_range: Range, + ctx: &mut TxContext, +) { + self.assert_version_is_valid(); + assert!(!self.uid_mut().exists_(FreeClaimsKey()), EConfigExists); + + self + .uid_mut() + .add( + FreeClaimsKey(), + FreeClaimsConfig { + domain_length_range, + used_objects: linked_table::new(ctx), + }, + ); +} + +/// Force-deauthorize type T from free claims. +/// Drops the linked_table. +public fun deauthorize_type(self: &mut DiscountHouse, _: &AdminCap): LinkedTable { + self.assert_version_is_valid(); + self.assert_config_exists(); + + let FreeClaimsConfig { used_objects, domain_length_range: _ } = self + .uid_mut() + .remove(FreeClaimsKey()); + + used_objects +} + /// Internal helper that checks if there's a valid configuration for T, /// validates that the domain name is of vlaid length, and then does the /// registration. @@ -122,42 +158,6 @@ fun internal_apply_full_discount( ); } -/// An admin action to authorize a type T for free claiming of names by -/// presenting -/// an object of type `T`. -public fun authorize_type( - _: &AdminCap, - self: &mut DiscountHouse, - domain_length_range: Range, - ctx: &mut TxContext, -) { - self.assert_version_is_valid(); - assert!(!self.uid_mut().exists_(FreeClaimsKey()), EConfigExists); - - self - .uid_mut() - .add( - FreeClaimsKey(), - FreeClaimsConfig { - domain_length_range, - used_objects: linked_table::new(ctx), - }, - ); -} - -/// Force-deauthorize type T from free claims. -/// Drops the linked_table. -public fun force_deauthorize_type(_: &AdminCap, self: &mut DiscountHouse) { - self.assert_version_is_valid(); - self.assert_config_exists(); - - let FreeClaimsConfig { used_objects, domain_length_range: _ } = self - .uid_mut() - .remove(FreeClaimsKey()); - - used_objects.drop(); -} - fun config_mut(self: &mut DiscountHouse): &mut FreeClaimsConfig { self.uid_mut().borrow_mut<_, FreeClaimsConfig>(FreeClaimsKey()) } diff --git a/packages/discounts/sources/house.move b/packages/discounts/sources/house.move index 2f4bf036..451cd9ff 100644 --- a/packages/discounts/sources/house.move +++ b/packages/discounts/sources/house.move @@ -30,20 +30,16 @@ fun init(ctx: &mut TxContext) { }) } -public(package) macro fun discount_house_key(): String { - b"object_discount".to_string() -} - public fun set_version(self: &mut DiscountHouse, _: &AdminCap, version: u8) { self.version = version; } -public(package) fun assert_version_is_valid(self: &DiscountHouse) { - assert!(self.version == VERSION, EInvalidVersion); +public(package) macro fun discount_house_key(): String { + b"object_discount".to_string() } -public(package) fun uid(self: &DiscountHouse): &UID { - &self.id +public(package) fun assert_version_is_valid(self: &DiscountHouse) { + assert!(self.version == VERSION, EInvalidVersion); } /// A helper function to get a mutable reference to the UID. diff --git a/packages/discounts/tests/free_claims_test.move b/packages/discounts/tests/free_claims_test.move index d08b726c..ca2dccde 100644 --- a/packages/discounts/tests/free_claims_test.move +++ b/packages/discounts/tests/free_claims_test.move @@ -1,249 +1,314 @@ -// // Copyright (c) Mysten Labs, Inc. -// // SPDX-License-Identifier: Apache-2.0 - -// #[test_only] -// module discounts::free_claims_tests; - -// use day_one::day_one::{Self, DayOne}; -// use discounts::free_claims; -// use discounts::house::{Self, DiscountHouse, DiscountHouseApp}; -// use std::string::{utf8, String}; -// use sui::clock::{Self, Clock}; -// use sui::test_scenario::{Self as ts, Scenario, ctx}; -// use suins::registry; -// use suins::suins::{Self, SuiNS, AdminCap}; - -// // An authorized type to test. -// public struct TestAuthorized has key, store { id: UID } - -// // An unauthorized type to test. -// public struct TestUnauthorized has key { id: UID } - -// const SUINS_ADDRESS: address = @0xA001; -// const USER_ADDRESS: address = @0xA002; - -// fun test_init(): Scenario { -// let mut scenario_val = ts::begin(SUINS_ADDRESS); -// let scenario = &mut scenario_val; -// { -// let mut suins = suins::init_for_testing(scenario.ctx()); -// suins.authorize_app_for_testing(); -// suins.share_for_testing(); -// house::init_for_testing(scenario.ctx()); -// let clock = clock::create_for_testing(scenario.ctx()); -// clock.share_for_testing(); -// }; -// { -// ts::next_tx(scenario, SUINS_ADDRESS); -// let admin_cap = scenario.take_from_sender(); -// let mut suins = scenario.take_shared(); -// let mut discount_house = scenario.take_shared(); - -// // a more expensive alternative. -// free_claims::authorize_type( -// &admin_cap, -// &mut discount_house, -// vector[10, 63], -// scenario.ctx(), -// ); -// free_claims::authorize_type( -// &admin_cap, -// &mut discount_house, -// vector[10, 63], -// scenario.ctx(), -// ); -// registry::init_for_testing(&admin_cap, &mut suins, scenario.ctx()); - -// ts::return_shared(discount_house); -// ts::return_shared(suins); -// ts::return_to_sender(scenario, admin_cap); -// }; -// scenario_val -// } - -// fun test_end(mut scenario_val: Scenario) { -// let scenario = &mut scenario_val; -// { -// ts::next_tx(scenario, SUINS_ADDRESS); -// let admin_cap = scenario.take_from_sender(); -// let mut discount_house = scenario.take_shared(); -// free_claims::deauthorize_type( -// &admin_cap, -// &mut discount_house, -// ); -// free_claims::deauthorize_type(&admin_cap, &mut discount_house); -// ts::return_shared(discount_house); -// ts::return_to_sender(scenario, admin_cap); -// }; -// ts::end(scenario_val); -// } - -// fun burn_authorized(authorized: TestAuthorized) { -// let TestAuthorized { id } = authorized; -// id.delete(); -// } - -// fun free_claim_with_type( -// item: &T, -// scenario: &mut Scenario, -// domain_name: String, -// user: address, -// ) { -// ts::next_tx(scenario, user); -// let mut suins = scenario.take_shared(); -// let mut discount_house = scenario.take_shared(); -// let clock = scenario.take_shared(); - -// let name = free_claims::free_claim( -// &mut discount_house, -// &mut suins, -// item, -// domain_name, -// &clock, -// scenario.ctx(), -// ); - -// transfer::public_transfer(name, user); - -// ts::return_shared(discount_house); -// ts::return_shared(suins); -// ts::return_shared(clock); -// } - -// fun free_claim_with_day_one( -// item: &DayOne, -// scenario: &mut Scenario, -// domain_name: String, -// user: address, -// ) { -// ts::next_tx(scenario, user); -// let mut suins = ts::take_shared(scenario); -// let mut discount_house = ts::take_shared(scenario); -// let clock = ts::take_shared(scenario); - -// let name = free_claims::free_claim_with_day_one( -// &mut discount_house, -// &mut suins, -// item, -// domain_name, -// &clock, -// scenario.ctx(), -// ); - -// transfer::public_transfer(name, user); - -// ts::return_shared(discount_house); -// ts::return_shared(suins); -// ts::return_shared(clock); -// } - -// #[test] -// fun test_e2e() { -// let mut scenario_val = test_init(); -// let scenario = &mut scenario_val; - -// let test_item = TestAuthorized { -// id: object::new(scenario.ctx()), -// }; - -// free_claim_with_type( -// &test_item, -// scenario, -// utf8(b"01234567890.sui"), -// USER_ADDRESS, -// ); - -// burn_authorized(test_item); -// test_end(scenario_val); -// } - -// #[test] -// fun use_day_one() { -// let mut scenario_val = test_init(); -// let scenario = &mut scenario_val; - -// let mut day_one = day_one::mint_for_testing(scenario.ctx()); -// day_one.set_is_active_for_testing(true); - -// free_claim_with_day_one( -// &day_one, -// scenario, -// utf8(b"0123456789.sui"), -// USER_ADDRESS, -// ); - -// day_one.burn_for_testing(); -// test_end(scenario_val); -// } - -// #[test, expected_failure(abort_code = discounts::free_claims::EAlreadyClaimed)] -// fun test_tries_to_claim_again_with_same_object_failure() { -// let mut scenario_val = test_init(); -// let scenario = &mut scenario_val; - -// let test_item = TestAuthorized { -// id: object::new(scenario.ctx()), -// }; - -// free_claim_with_type( -// &test_item, -// scenario, -// utf8(b"01234567890.sui"), -// USER_ADDRESS, -// ); - -// // tries to claim again using the same test_item. -// free_claim_with_type( -// &test_item, -// scenario, -// utf8(b"01234567891.sui"), -// USER_ADDRESS, -// ); - -// burn_authorized(test_item); -// test_end(scenario_val); -// } - -// #[ -// test, -// expected_failure( -// abort_code = discounts::free_claims::EInvalidCharacterRange, -// ), -// ] -// fun test_invalid_size_failure() { -// let mut scenario_val = test_init(); -// let scenario = &mut scenario_val; - -// let test_item = TestAuthorized { -// id: object::new(scenario.ctx()), -// }; - -// free_claim_with_type( -// &test_item, -// scenario, -// utf8(b"012345678.sui"), -// USER_ADDRESS, -// ); - -// burn_authorized(test_item); -// test_end(scenario_val); -// } - -// #[test, expected_failure(abort_code = discounts::free_claims::EConfigNotExists)] -// fun register_with_unauthorized_type() { -// let mut scenario_val = test_init(); -// let scenario = &mut scenario_val; - -// let test_item = TestUnauthorized { -// id: object::new(scenario.ctx()), -// }; - -// free_claim_with_type( -// &test_item, -// scenario, -// utf8(b"test.sui"), -// USER_ADDRESS, -// ); - -// abort 1337 -// } +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[test_only] +module discounts::free_claims_test; + +use day_one::day_one::{Self, DayOne}; +use discounts::free_claims::{Self, FreeClaimsApp}; +use discounts::house::{Self, DiscountHouse}; +use sui::clock; +use sui::test_scenario::{Self as ts, Scenario, ctx}; +use sui::test_utils::{destroy, assert_eq}; +use suins::constants; +use suins::payment::{Self, PaymentIntent}; +use suins::pricing_config; +use suins::registry; +use suins::suins::{Self, SuiNS, AdminCap}; + +// an authorized type to test. +public struct TestAuthorized has key, store { + id: UID +} + +// another authorized type to test. +public struct AnotherAuthorized has key { + id: UID, +} + +// an unauthorized type to test. +public struct TestUnauthorized has key { + id: UID, +} + +const SUINS_ADDRESS: address = @0xA001; +const USER_ADDRESS: address = @0xA002; + +fun test_init(): Scenario { + let mut scenario_val = ts::begin(SUINS_ADDRESS); + let scenario = &mut scenario_val; + { + let mut suins = suins::init_for_testing(scenario.ctx()); + suins.authorize_app_for_testing(); + suins.share_for_testing(); + house::init_for_testing(scenario.ctx()); + let clock = clock::create_for_testing(scenario.ctx()); + clock.share_for_testing(); + }; + { + scenario.next_tx(SUINS_ADDRESS); + let admin_cap = scenario.take_from_sender(); + let mut suins = scenario.take_shared(); + let mut discount_house = scenario.take_shared(); + + // a more expensive alternative. + free_claims::authorize_type( + &mut discount_house, + &admin_cap, + pricing_config::new_range(vector[5,63]), // only 5+ letter names + scenario.ctx(), + ); + // a much cheaper price for another type. + free_claims::authorize_type( + &mut discount_house, + &admin_cap, + pricing_config::new_range(vector[3,4]), // only 3 and 4 letter names + scenario.ctx(), + ); + + free_claims::authorize_type( + &mut discount_house, + &admin_cap, + pricing_config::new_range(vector[3,63]), // any actual + scenario.ctx(), + ); + + registry::init_for_testing(&admin_cap, &mut suins, scenario.ctx()); + + ts::return_shared(discount_house); + ts::return_shared(suins); + scenario.return_to_sender(admin_cap); + }; + scenario_val +} + +#[test] +fun test_e2e() { + init_purchase!(USER_ADDRESS, b"fivel.sui", |discount_house, suins, intent, scenario| { + assert_eq(intent.request_data().base_amount(), 50 * constants::mist_per_sui()); + + let obj = TestAuthorized { id: object::new(scenario.ctx()) }; + + free_claims::free_claim( + discount_house, + suins, + intent, + &obj, + scenario.ctx(), + ); + + assert_eq(intent.request_data().base_amount(), 0); + assert_eq(intent.request_data().discounts_applied().size(), 1); + assert_eq(intent.request_data().discount_applied(), true); + destroy(obj); + }); +} + +#[test] +fun register_day_one() { + init_purchase!(USER_ADDRESS, b"wow.sui", |discount_house, suins, intent, scenario| { + assert_eq(intent.request_data().base_amount(), 1200 * constants::mist_per_sui()); + + let mut day_one = day_one::mint_for_testing(scenario.ctx()); + day_one.set_is_active_for_testing(true); + + free_claims::free_claim_with_day_one( + discount_house, + suins, + intent, + &day_one, + scenario.ctx(), + ); + + assert_eq(intent.request_data().base_amount(), 0); + assert_eq(intent.request_data().discounts_applied().size(), 1); + assert_eq(intent.request_data().discount_applied(), true); + + day_one.burn_for_testing(); + }); +} + +#[test] +fun test_deauthorize_discount() { + let mut scenario = test_init(); + scenario.next_tx(SUINS_ADDRESS); + let mut discount_house = scenario.take_shared(); + let admin_cap = scenario.take_from_sender(); + + let table = free_claims::deauthorize_type( + &mut discount_house, + &admin_cap, + ); + sui::transfer::public_transfer(table, scenario.ctx().sender()); + + ts::return_shared(discount_house); + scenario.return_to_sender(admin_cap); + + scenario.end(); +} + +#[test, expected_failure(abort_code = ::discounts::free_claims::EConfigNotExists)] +fun register_with_unauthorized_type() { + init_purchase!(USER_ADDRESS, b"fivel.sui", |discount_house, suins, intent, scenario| { + + let unauthorized = TestUnauthorized { id: object::new(scenario.ctx()) }; + free_claims::free_claim( + discount_house, + suins, + intent, + &unauthorized, + scenario.ctx(), + ); + destroy(unauthorized); + }); +} + +#[test, expected_failure(abort_code = ::discounts::free_claims::EAlreadyClaimed)] +#[allow(dead_code)] +fun test_already_claimed() { + init_purchase!(USER_ADDRESS, b"fivel.sui", |discount_house, suins, intent, scenario| { + assert_eq(intent.request_data().base_amount(), 50 * constants::mist_per_sui()); + + let obj = TestAuthorized { id: object::new(scenario.ctx()) }; + + free_claims::free_claim( + discount_house, + suins, + intent, + &obj, + scenario.ctx(), + ); + + free_claims::free_claim( + discount_house, + suins, + intent, + &obj, + scenario.ctx(), + ); + abort 1337 + }); +} + +#[test, expected_failure(abort_code = ::discounts::free_claims::EInvalidCharacterRange)] +#[allow(dead_code)] +fun test_domain_out_of_range() { + init_purchase!(USER_ADDRESS, b"fiv.sui", |discount_house, suins, intent, scenario| { + let obj = TestAuthorized { id: object::new(scenario.ctx()) }; + + free_claims::free_claim( + discount_house, + suins, + intent, + &obj, + scenario.ctx(), + ); + + abort 1337 + }); +} + +#[test, expected_failure(abort_code = ::discounts::free_claims::EConfigExists)] +fun test_authorize_config_twice() { + let mut scenario = test_init(); + scenario.next_tx(SUINS_ADDRESS); + let mut discount_house = scenario.take_shared(); + let admin_cap = scenario.take_from_sender(); + + free_claims::authorize_type( + &mut discount_house, + &admin_cap, + pricing_config::new_range(vector[5,63]), + scenario.ctx(), + ); + + abort 1337 +} + +#[test, expected_failure(abort_code = ::discounts::house::EInvalidVersion)] +fun test_version_togge() { + let mut scenario = test_init(); + scenario.next_tx(SUINS_ADDRESS); + let mut discount_house = scenario.take_shared(); + let admin_cap = scenario.take_from_sender(); + + discount_house.set_version(&admin_cap, 255); + + discount_house.assert_version_is_valid(); + + abort 1337 +} + + +#[test, expected_failure(abort_code = ::discounts::free_claims::EConfigNotExists)] +fun test_deauthorize_non_existing_config() { + let mut scenario = test_init(); + scenario.next_tx(SUINS_ADDRESS); + let mut discount_house = scenario.take_shared(); + let admin_cap = scenario.take_from_sender(); + + let _table = free_claims::deauthorize_type( + &mut discount_house, + &admin_cap, + ); + + abort 1337 +} + +#[test, expected_failure(abort_code = ::discounts::free_claims::ENotValidForDayOne)] +fun use_day_one_for_casual_flow_failure() { + init_purchase!(USER_ADDRESS, b"fivel.sui", |discount_house, suins, intent, scenario| { + let day_one = day_one::mint_for_testing(scenario.ctx()); + + free_claims::free_claim( + discount_house, + suins, + intent, + &day_one, + scenario.ctx(), + ); + day_one.burn_for_testing(); + }); +} + +#[test, expected_failure(abort_code = ::discounts::free_claims::ENotActiveDayOne)] +fun use_inactive_day_one_failure() { + init_purchase!(USER_ADDRESS, b"fivel.sui", |discount_house, suins, intent, scenario| { + let mut day_one = day_one::mint_for_testing(scenario.ctx()); + day_one.set_is_active_for_testing(false); + + free_claims::free_claim_with_day_one( + discount_house, + suins, + intent, + &day_one, + scenario.ctx(), + ); + day_one.burn_for_testing(); + }); +} + +macro fun init_purchase( + $addr: address, + $domain_name: vector, + $f: |&mut DiscountHouse, &mut SuiNS, &mut PaymentIntent, &mut Scenario|, +) { + let addr = $addr; + let dm = $domain_name; + + let mut scenario = test_init(); + scenario.next_tx(addr); + + // take the discount house + let mut discount_house = scenario.take_shared(); + let mut suins = scenario.take_shared(); + let mut intent = payment::init_registration(&mut suins, dm.to_string()); + + $f(&mut discount_house, &mut suins, &mut intent, &mut scenario); + + destroy(intent); + destroy(discount_house); + destroy(suins); + + scenario.end(); +}