Skip to content

Commit

Permalink
Updating mock usages
Browse files Browse the repository at this point in the history
  • Loading branch information
erdimaden committed May 21, 2024
1 parent ce39bc7 commit a4e4896
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 130 deletions.
123 changes: 63 additions & 60 deletions src/coinbase/tests/address_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,36 @@ import { InternalError } from "../errors";
import {
VALID_ADDRESS_BALANCE_LIST,
VALID_ADDRESS_MODEL,
VALID_BALANCE_MODEL,
addressesApiMock,
mockFn,
mockReturnRejectedValue,
} from "./utils";

// Test suite for Address class
describe("Address", () => {
const transactionHash = "0xdeadbeef";
let address: Address;
const getAddressBalance = jest.fn().mockResolvedValue({ data: VALID_BALANCE_MODEL });
const listAddressBalances = jest.fn().mockResolvedValue({ data: VALID_ADDRESS_BALANCE_LIST });
const requestFaucetFunds = jest
.fn()
.mockResolvedValue({ data: { transaction_hash: transactionHash } });
let address: Address, balanceModel;

beforeAll(() => {
Coinbase.apiClients.address = addressesApiMock;
Coinbase.apiClients.address!.getAddressBalance = mockFn(req => {
const { asset_id } = req;
balanceModel = {
amount: "1000000000000000000",
asset: {
asset_id,
network_id: Coinbase.networkList.BaseSepolia,
},
};
return { data: balanceModel };
});
Coinbase.apiClients.address!.listAddressBalances = mockFn(() => {
return { data: VALID_ADDRESS_BALANCE_LIST };
});
Coinbase.apiClients.address!.requestFaucetFunds = mockFn(() => {
return { data: { transaction_hash: transactionHash } };
});
});

beforeEach(() => {
address = new Address(VALID_ADDRESS_MODEL);
Expand All @@ -40,67 +57,59 @@ describe("Address", () => {
});

it("should return the correct list of balances", async () => {
Coinbase.apiClients.address = {
...addressesApiMock,
listAddressBalances,
};
const balances = await address.listBalances();
expect(balances.get(Coinbase.assetList.Eth)).toEqual(new Decimal(1));
expect(balances.get("usdc")).toEqual(new Decimal(5000));
expect(balances.get("weth")).toEqual(new Decimal(3));
expect(listAddressBalances).toHaveBeenCalledWith(address.getWalletId(), address.getId());
expect(listAddressBalances).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.listAddressBalances).toHaveBeenCalledWith(
address.getWalletId(),
address.getId(),
);
expect(Coinbase.apiClients.address!.listAddressBalances).toHaveBeenCalledTimes(1);
});

it("should return the correct ETH balance", async () => {
Coinbase.apiClients.address = {
...addressesApiMock,
getAddressBalance,
};
const ethBalance = await address.getBalance(Coinbase.assetList.Eth);
expect(ethBalance).toBeInstanceOf(Decimal);
expect(ethBalance).toEqual(new Decimal(1));
expect(getAddressBalance).toHaveBeenCalledWith(
expect(Coinbase.apiClients.address!.getAddressBalance).toHaveBeenCalledWith(
address.getWalletId(),
address.getId(),
Coinbase.assetList.Eth,
);
expect(getAddressBalance).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.getAddressBalance).toHaveBeenCalledTimes(1);
});

it("should return the correct Gwei balance", async () => {
const assetId = "gwei";
Coinbase.apiClients.address = {
...addressesApiMock,
getAddressBalance,
};
const ethBalance = await address.getBalance(assetId);
expect(ethBalance).toBeInstanceOf(Decimal);
expect(ethBalance).toEqual(new Decimal(1000000000));
expect(getAddressBalance).toHaveBeenCalledWith(address.getWalletId(), address.getId(), assetId);
expect(getAddressBalance).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.getAddressBalance).toHaveBeenCalledWith(
address.getWalletId(),
address.getId(),
assetId,
);
expect(Coinbase.apiClients.address!.getAddressBalance).toHaveBeenCalledTimes(1);
});

it("should return the correct Wei balance", async () => {
const assetId = "wei";
Coinbase.apiClients.address = {
...addressesApiMock,
getAddressBalance,
};
const ethBalance = await address.getBalance(assetId);
expect(ethBalance).toBeInstanceOf(Decimal);
expect(ethBalance).toEqual(new Decimal(1000000000000000000));
expect(getAddressBalance).toHaveBeenCalledWith(address.getWalletId(), address.getId(), assetId);
expect(getAddressBalance).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.getAddressBalance).toHaveBeenCalledWith(
address.getWalletId(),
address.getId(),
assetId,
);
expect(Coinbase.apiClients.address!.getAddressBalance).toHaveBeenCalledTimes(1);
});

it("should return an error for an unsupported asset", async () => {
const getAddressBalance = jest.fn().mockRejectedValue(new APIError(""));
const assetId = "unsupported-asset";
Coinbase.apiClients.address = {
...addressesApiMock,
getAddressBalance,
};
Coinbase.apiClients.address!.getAddressBalance = getAddressBalance;
await expect(address.getBalance(assetId)).rejects.toThrow(APIError);
expect(getAddressBalance).toHaveBeenCalledWith(address.getWalletId(), address.getId(), assetId);
expect(getAddressBalance).toHaveBeenCalledTimes(1);
Expand All @@ -115,46 +124,40 @@ describe("Address", () => {
});

it("should request funds from the faucet and returns the faucet transaction", async () => {
Coinbase.apiClients.address = {
...addressesApiMock,
requestFaucetFunds,
};
const faucetTransaction = await address.faucet();
expect(faucetTransaction).toBeInstanceOf(FaucetTransaction);
expect(faucetTransaction.getTransactionHash()).toBe(transactionHash);
expect(requestFaucetFunds).toHaveBeenCalledWith(address.getWalletId(), address.getId());
expect(requestFaucetFunds).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.requestFaucetFunds).toHaveBeenCalledWith(
address.getWalletId(),
address.getId(),
);
expect(Coinbase.apiClients.address!.requestFaucetFunds).toHaveBeenCalledTimes(1);
});

it("should throw an APIError when the request is unsuccesful", async () => {
const requestFaucetFunds = jest.fn().mockRejectedValue(new APIError(""));
Coinbase.apiClients.address = {
...addressesApiMock,
requestFaucetFunds,
};
Coinbase.apiClients.address!.requestFaucetFunds = mockReturnRejectedValue(new APIError(""));
await expect(address.faucet()).rejects.toThrow(APIError);
expect(requestFaucetFunds).toHaveBeenCalledWith(address.getWalletId(), address.getId());
expect(requestFaucetFunds).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.requestFaucetFunds).toHaveBeenCalledWith(
address.getWalletId(),
address.getId(),
);
expect(Coinbase.apiClients.address!.requestFaucetFunds).toHaveBeenCalledTimes(1);
});

it("should throw a FaucetLimitReachedError when the faucet limit is reached", async () => {
const requestFaucetFunds = jest.fn().mockRejectedValue(new FaucetLimitReachedError(""));
Coinbase.apiClients.address = {
...addressesApiMock,
requestFaucetFunds,
};
Coinbase.apiClients.address!.requestFaucetFunds = mockReturnRejectedValue(
new FaucetLimitReachedError(""),
);
await expect(address.faucet()).rejects.toThrow(FaucetLimitReachedError);
expect(requestFaucetFunds).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.requestFaucetFunds).toHaveBeenCalledTimes(1);
});

it("should throw an InternalError when the request fails unexpectedly", async () => {
const requestFaucetFunds = jest.fn().mockRejectedValue(new InternalError(""));
Coinbase.apiClients.address = {
...addressesApiMock,
requestFaucetFunds,
};
Coinbase.apiClients.address!.requestFaucetFunds = mockReturnRejectedValue(
new InternalError(""),
);
await expect(address.faucet()).rejects.toThrow(InternalError);
expect(requestFaucetFunds).toHaveBeenCalledTimes(1);
expect(Coinbase.apiClients.address!.requestFaucetFunds).toHaveBeenCalledTimes(1);
});

it("should return the correct string representation", () => {
Expand Down
57 changes: 47 additions & 10 deletions src/coinbase/tests/coinbase_test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { randomUUID } from "crypto";
import { APIError } from "../api_error";
import { Coinbase } from "../coinbase";
import { VALID_WALLET_MODEL, addressesApiMock, usersApiMock, walletsApiMock } from "./utils";
import {
VALID_WALLET_MODEL,
addressesApiMock,
mockReturnRejectedValue,
mockReturnValue,
usersApiMock,
walletsApiMock,
} from "./utils";

const PATH_PREFIX = "./src/coinbase/tests/config";

Expand Down Expand Up @@ -34,35 +42,64 @@ describe("Coinbase tests", () => {
});

describe("should able to interact with the API", () => {
let user;
let user, walletId, publicKey, addressId, transactionHash;
const cbInstance = Coinbase.configureFromJson(
`${PATH_PREFIX}/coinbase_cloud_api_key.json`,
true,
);

it("should return the correct user ID", async () => {
beforeAll(async () => {
Coinbase.apiClients = {
user: usersApiMock,
wallet: walletsApiMock,
address: addressesApiMock,
};

walletId = randomUUID();
publicKey = "0x1234567890";
addressId = "0xd25d4aaf";
transactionHash = "0xdeadbeef";

const walletModel = {
id: walletId,
network_id: Coinbase.networkList.BaseSepolia,
default_address: {
wallet_id: walletId,
address_id: addressId,
public_key: publicKey,
network_id: Coinbase.networkList.BaseSepolia,
},
};

Coinbase.apiClients.user!.getCurrentUser = mockReturnValue({ id: 123 });
Coinbase.apiClients.wallet!.createWallet = mockReturnValue(walletModel);
Coinbase.apiClients.wallet!.getWallet = mockReturnValue(walletModel);
Coinbase.apiClients.address!.requestFaucetFunds = mockReturnValue({
transaction_hash: transactionHash,
});
Coinbase.apiClients.address!.createAddress = mockReturnValue(
VALID_WALLET_MODEL.default_address,
);

user = await cbInstance.getDefaultUser();
});

it("should return the correct user ID", async () => {
expect(user.getId()).toBe(123);
expect(user.toString()).toBe("User{ userId: 123 }");
expect(usersApiMock.getCurrentUser).toHaveBeenCalledWith();
expect(Coinbase.apiClients.user!.getCurrentUser).toHaveBeenCalledWith();
expect(usersApiMock.getCurrentUser).toHaveBeenCalledTimes(1);
});

it("should be able to get faucet funds", async () => {
const wallet = await user.createWallet();
expect(wallet.getId()).toBe(VALID_WALLET_MODEL.id);
expect(wallet.getId()).toBe(walletId);
const payload = { wallet: { network_id: Coinbase.networkList.BaseSepolia } };
expect(walletsApiMock.createWallet).toHaveBeenCalledWith(payload);
expect(walletsApiMock.createWallet).toHaveBeenCalledTimes(1);

const defaultAddress = wallet.defaultAddress();
expect(defaultAddress?.getId()).toBe(VALID_WALLET_MODEL.default_address?.address_id);
expect(defaultAddress?.getId()).toBe(addressId);

const faucetTransaction = await wallet?.faucet();
expect(faucetTransaction.getTransactionHash()).toBe("0xdeadbeef");
Expand All @@ -75,11 +112,11 @@ describe("Coinbase tests", () => {
});

it("should raise an error if the user is not found", async () => {
Coinbase.apiClients.user = {
...usersApiMock,
getCurrentUser: jest.fn().mockRejectedValue(new APIError("User not found")),
};
const cbInstance = Coinbase.configureFromJson(`${PATH_PREFIX}/coinbase_cloud_api_key.json`);
Coinbase.apiClients.user!.getCurrentUser = mockReturnRejectedValue(
new APIError("User not found"),
);

await expect(cbInstance.getDefaultUser()).rejects.toThrow(APIError);
expect(usersApiMock.getCurrentUser).toHaveBeenCalledWith();
expect(usersApiMock.getCurrentUser).toHaveBeenCalledTimes(1);
Expand Down
31 changes: 22 additions & 9 deletions src/coinbase/tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import axios, { AxiosInstance } from "axios";
import { ethers } from "ethers";
import { randomUUID } from "crypto";
Expand All @@ -12,8 +13,20 @@ import { BASE_PATH } from "../../client/base";
import { Coinbase } from "../coinbase";
import { registerAxiosInterceptors } from "../utils";

export const mockFn = (...args) => jest.fn(...args) as any;
export const mockReturnValue = data => jest.fn().mockResolvedValue({ data });
export const mockReturnRejectedValue = data => jest.fn().mockRejectedValue(data);

export const walletId = randomUUID();

export const generateRandomHash = (length = 8) => {
const characters = "abcdef0123456789";
let hash = "0x";
for (let i = 0; i < length; i++) {
hash += characters[Math.floor(Math.random() * characters.length)];
}
return hash;
};
// newAddressModel creates a new AddressModel with a random wallet ID and a random Ethereum address.
export const newAddressModel = (walletId: string): AddressModel => {
const ethAddress = ethers.Wallet.createRandom();
Expand Down Expand Up @@ -87,7 +100,7 @@ type AxiosMockType = [AxiosInstance, Configuration, string];
/**
* Returns an Axios instance with interceptors and configuration for testing.
*
* @returns {AxiosMockType} - The Axios instance, configuration, and base path.
* @returns The Axios instance, configuration, and base path.
*/
export const createAxiosMock = (): AxiosMockType => {
const axiosInstance = axios.create();
Expand All @@ -101,18 +114,18 @@ export const createAxiosMock = (): AxiosMockType => {
};

export const usersApiMock = {
getCurrentUser: jest.fn().mockResolvedValue({ data: { id: 123 } }),
getCurrentUser: jest.fn(),
};

export const walletsApiMock = {
getWallet: jest.fn().mockResolvedValue(Promise.resolve({ data: VALID_WALLET_MODEL })),
createWallet: jest.fn().mockResolvedValue(Promise.resolve({ data: VALID_WALLET_MODEL })),
getWallet: jest.fn(),
createWallet: jest.fn(),
};

export const addressesApiMock = {
requestFaucetFunds: jest.fn().mockResolvedValue({ data: { transaction_hash: "0xdeadbeef" } }),
getAddress: jest.fn().mockResolvedValue({ data: VALID_ADDRESS_BALANCE_LIST }),
getAddressBalance: jest.fn().mockResolvedValue({ data: { VALID_BALANCE_MODEL } }),
listAddressBalances: jest.fn().mockResolvedValue({ data: VALID_ADDRESS_BALANCE_LIST }),
createAddress: jest.fn().mockResolvedValue({ data: VALID_WALLET_MODEL.default_address }),
requestFaucetFunds: jest.fn(),
getAddress: jest.fn(),
getAddressBalance: jest.fn(),
listAddressBalances: jest.fn(),
createAddress: jest.fn(),
};
Loading

0 comments on commit a4e4896

Please sign in to comment.