Skip to content

Commit

Permalink
refactor: minor refinement in integration tests; Adding product lifec…
Browse files Browse the repository at this point in the history
…ycle methods
  • Loading branch information
aorumbayev committed Sep 6, 2023
1 parent 67604a0 commit a635e4b
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cicd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
run: npm run lint:scripts

unit-tests:
name: Unit tests
name: Integration tests (TestNet)
runs-on: ${{ matrix.os }}
strategy:
matrix:
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@
{
"type": "chore",
"release": "patch"
},
{
"type": "refactor",
"release": "patch"
}
]
}
Expand Down
47 changes: 47 additions & 0 deletions src/clients/SubtopiaClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
DiscountType,
Duration,
SubscriptionType,
LifecycleState,
} from "../enums";

import {
Expand Down Expand Up @@ -198,6 +199,52 @@ export class SubtopiaClient {
});
}

protected async updateLifecycle({
lifecycle,
}: {
lifecycle: LifecycleState;
}): Promise<{
txID: string;
}> {
const updateLifecycleAtc = new AtomicTransactionComposer();
updateLifecycleAtc.addMethodCall({
appID: this.appID,
method: new ABIMethod({
name: "update_lifecycle",
args: [
{
type: "uint64",
name: "lifecycle",
desc: "The new lifecycle.",
},
],
returns: { type: "void" },
}),
methodArgs: [lifecycle],
sender: this.creator.addr,
signer: this.creator.signer,
suggestedParams: await getParamsWithFeeCount(this.algodClient, 1),
});

const response = await updateLifecycleAtc.execute(this.algodClient, 10);

return {
txID: response.txIDs.pop() as string,
};
}

public async enable(): Promise<{
txID: string;
}> {
return this.updateLifecycle({ lifecycle: LifecycleState.ENABLED });
}

public async disable(): Promise<{
txID: string;
}> {
return this.updateLifecycle({ lifecycle: LifecycleState.DISABLED });
}

public async getAppState(
withPriceNormalization = true
): Promise<ProductState> {
Expand Down
62 changes: 56 additions & 6 deletions src/clients/SubtopiaRegistryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export class SubtopiaRegistryClient {
{
type: "application",
name: "infrastructure",
desc: "The INFRASTRUCTURE.",
desc: "The product.",
},
{
type: "application",
Expand Down Expand Up @@ -498,12 +498,12 @@ export class SubtopiaRegistryClient {
{
type: "uint64",
name: "sub_type",
desc: "The sub type of the INFRASTRUCTURE.",
desc: "The sub type of The product.",
},
{
type: "uint64",
name: "price",
desc: "The price of the INFRASTRUCTURE.",
desc: "The price of The product.",
},
{
type: "uint64",
Expand All @@ -513,17 +513,17 @@ export class SubtopiaRegistryClient {
{
type: "asset",
name: "coin",
desc: "The coin of the INFRASTRUCTURE.",
desc: "The coin of The product.",
},
{
type: "string",
name: "unit_name",
desc: "The unit name of the INFRASTRUCTURE.",
desc: "The unit name of The product.",
},
{
type: "string",
name: "image_url",
desc: "The image URL of the INFRASTRUCTURE.",
desc: "The image URL of The product.",
},
{
type: "address",
Expand Down Expand Up @@ -625,4 +625,54 @@ export class SubtopiaRegistryClient {
infrastructureID: Number(response.methodResults[0].returnValue),
};
}

public async deleteInfrastructure({
infrastructureID,
lockerID,
}: {
infrastructureID: number;
lockerID: number;
}): Promise<{
txID: string;
}> {
const deleteInfraAtc = new AtomicTransactionComposer();
deleteInfraAtc.addMethodCall({
appID: this.appID,
method: new ABIMethod({
name: "delete_infrastructure",
args: [
{
type: "application",
name: "infrastructure",
desc: "The product.",
},
{
type: "application",
name: "locker",
desc: "The locker.",
},
],
returns: { type: "void" },
}),
methodArgs: [infrastructureID, lockerID],
boxes: [
{
appIndex: this.appID,
name: new Uint8Array([
...Buffer.from("cl-"),
...decodeAddress(this.creator.addr).publicKey,
]),
},
],
sender: this.creator.addr,
signer: this.creator.signer,
suggestedParams: await getParamsWithFeeCount(this.algodClient, 4),
});

const response = await deleteInfraAtc.execute(this.algodClient, 10);

return {
txID: response.txIDs.pop() as string,
};
}
}
5 changes: 5 additions & 0 deletions src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,8 @@ export enum LockerType {
CREATOR = 0,
USER = 1,
}

export enum LifecycleState {
ENABLED = 0,
DISABLED = 1,
}
135 changes: 86 additions & 49 deletions tests/subtopia.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,30 @@ import {
transactionSignerAccount,
transferAlgos,
} from "@algorandfoundation/algokit-utils";
import { TransactionSignerAccount } from "@algorandfoundation/algokit-utils/types/account";

const CONFIG = {
SERVER_URL: "https://testnet-api.algonode.cloud",
DISPENSER_MNEMONIC: process.env[
"TESTNET_SUBTOPIA_DISPENSER_MNEMONIC"
] as string,
CREATOR_MNEMONIC: process.env["TESTNET_SUBTOPIA_CREATOR_MNEMONIC"] as string,
BOB_MNEMONIC: process.env["TESTNET_SUBTOPIA_BOB_MNEMONIC"] as string,
};

const algodClient = getAlgoClient({
server: "https://testnet-api.algonode.cloud",
server: CONFIG.SERVER_URL,
});

const dispenserAccount = mnemonicToSecretKey(
process.env["TESTNET_SUBTOPIA_DISPENSER_MNEMONIC"] as string
);
const dispenserAccount = mnemonicToSecretKey(CONFIG.DISPENSER_MNEMONIC);
const creatorAccount = mnemonicToSecretKey(CONFIG.CREATOR_MNEMONIC);
const bobTestAccount = mnemonicToSecretKey(CONFIG.BOB_MNEMONIC);

const creatorAccount = mnemonicToSecretKey(
process.env["TESTNET_SUBTOPIA_CREATOR_MNEMONIC"] as string
);
const creatorSignerAccount = transactionSignerAccount(
makeBasicAccountTransactionSigner(creatorAccount),
creatorAccount.addr
);

const bobTestAccount = mnemonicToSecretKey(
process.env["TESTNET_SUBTOPIA_BOB_MNEMONIC"] as string
);

const refundTestnetAlgos = async (account: Account) => {
const accountInfo = await algodClient.accountInformation(account.addr).do();

Expand All @@ -65,6 +68,31 @@ const refundTestnetAlgos = async (account: Account) => {
}
};

async function setupSubtopiaRegistryClient(
signerAccount: TransactionSignerAccount
) {
const subtopiaRegistryClient = await SubtopiaRegistryClient.init(
algodClient,
signerAccount,
ChainType.TESTNET
);

let lockerID = await SubtopiaRegistryClient.getLocker({
registryID: subtopiaRegistryClient.appID,
algodClient: algodClient,
ownerAddress: creatorAccount.addr,
});
if (!lockerID) {
const response = await subtopiaRegistryClient.createLocker({
creator: signerAccount,
lockerType: LockerType.CREATOR,
});
lockerID = response.lockerID;
}

return { subtopiaRegistryClient, lockerID };
}

describe("subtopia", () => {
beforeAll(async () => {
const dispenserInfo = await algodClient
Expand Down Expand Up @@ -103,27 +131,11 @@ describe("subtopia", () => {
});

it(
"should correctly add product, create subscription, delete subscription and delete product",
"should manage product and subscription lifecycle correctly",
async () => {
// Setup
const subtopiaRegistryClient = await SubtopiaRegistryClient.init(
algodClient,
creatorSignerAccount,
ChainType.TESTNET
);

let lockerID = await SubtopiaRegistryClient.getLocker({
registryID: subtopiaRegistryClient.appID,
algodClient: algodClient,
ownerAddress: creatorAccount.addr,
});
if (lockerID === undefined) {
const response = await subtopiaRegistryClient.createLocker({
creator: creatorSignerAccount,
lockerType: LockerType.CREATOR,
});
lockerID = response.lockerID;
}
const { subtopiaRegistryClient, lockerID } =
await setupSubtopiaRegistryClient(creatorSignerAccount);

// Test
const response = await subtopiaRegistryClient.createInfrastructure({
Expand Down Expand Up @@ -218,34 +230,30 @@ describe("subtopia", () => {

// Assert
expect(content.price.value).toBe(algos(1).microAlgos);

const disableProductResponse = await productClient.disable();

expect(disableProductResponse.txID).toBeDefined();

const deleteProductResponse =
await subtopiaRegistryClient.deleteInfrastructure({
infrastructureID: productClient.appID,
lockerID: lockerID,
});

expect(deleteProductResponse.txID).toBeDefined();
},
{ timeout: 10e6 }
);

it(
"should correctly add product, create discount, transfer product and delete discount",
"should handle product and discount operations correctly",
async () => {
const subtopiaRegistryClient = await SubtopiaRegistryClient.init(
algodClient,
creatorSignerAccount,
ChainType.TESTNET
);
const { subtopiaRegistryClient, lockerID } =
await setupSubtopiaRegistryClient(creatorSignerAccount);

const newOwner = bobTestAccount;

let lockerID = await SubtopiaRegistryClient.getLocker({
registryID: subtopiaRegistryClient.appID,
algodClient: algodClient,
ownerAddress: creatorAccount.addr,
});
if (lockerID === undefined) {
const response = await subtopiaRegistryClient.createLocker({
creator: creatorSignerAccount,
lockerType: LockerType.CREATOR,
});
lockerID = response.lockerID;
}

// Test
const response = await subtopiaRegistryClient.createInfrastructure({
productName: "Hooli",
Expand Down Expand Up @@ -308,6 +316,35 @@ describe("subtopia", () => {
);

expect(deleteDiscountResponse.txID).toBeDefined();

const disableProductResponse = await newOwnerProductClient.disable();

expect(disableProductResponse.txID).toBeDefined();

const newOwnerLockerID = await SubtopiaRegistryClient.getLocker({
registryID: subtopiaRegistryClient.appID,
algodClient: algodClient,
ownerAddress: newOwner.addr,
});

expect(newOwnerLockerID).toBeGreaterThan(0);

const newOwnerRegistryClient = await SubtopiaRegistryClient.init(
algodClient,
transactionSignerAccount(
makeBasicAccountTransactionSigner(newOwner),
newOwner.addr
),
ChainType.TESTNET
);

const deleteProductResponse =
await newOwnerRegistryClient.deleteInfrastructure({
infrastructureID: productClient.appID,
lockerID: newOwnerLockerID as number,
});

expect(deleteProductResponse.txID).toBeDefined();
},
{
timeout: 10e6,
Expand Down

0 comments on commit a635e4b

Please sign in to comment.