Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Unit tests for virtual cards backend #351

Merged
merged 74 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
59b48b6
chore: add vitest unit testing packages
EresDev Oct 24, 2024
2707875
fix: import correct ethers utils
EresDev Oct 24, 2024
fa67d62
chore: add vitest config
EresDev Oct 24, 2024
ffe2253
test: retrieve the best available virtual card
EresDev Oct 24, 2024
c218921
Merge branch 'vitest-tests' into development
EresDev Oct 24, 2024
42a0425
chore: add msw to mock reloadly in tests
EresDev Oct 28, 2024
e7d6c48
fix: make tests work with mocked reloadly
EresDev Oct 28, 2024
effb3c4
chore: add tsconfig to improve intellisense
EresDev Oct 28, 2024
377c774
refactor: improve json response test
EresDev Oct 28, 2024
efcc0a2
docs: add reason for alias in vitest config
EresDev Oct 28, 2024
5fd510a
refactor: remove tsconfig module resolution
EresDev Oct 28, 2024
222fcba
chore: add separate wrangler config for tests
EresDev Oct 28, 2024
ab3d700
refactor: rename virtual card to payment card
EresDev Oct 28, 2024
161819a
refactor: move vitest config under /tests dir
EresDev Oct 28, 2024
83b41f2
Merge branch 'vitest-tests' into development
EresDev Oct 28, 2024
f780677
Merge branch 'development' of https://github.com/ubiquity/pay.ubq.fi …
EresDev Oct 28, 2024
cf8650e
fix: prevent loading production cards on sandbox
EresDev Oct 30, 2024
5bc0b29
refactor: improve naming of mocked payment card
EresDev Oct 30, 2024
a48f83b
refactor: move vitest config to root dir
EresDev Oct 30, 2024
7fdad7c
fix: stop any http request that is not mocked
EresDev Oct 30, 2024
aa87294
refactor: cleanup event context
EresDev Oct 30, 2024
885495e
refactor: remove redundant handling of no card
EresDev Oct 30, 2024
3cd2f2e
refactor: extract card api urls to reuse
EresDev Oct 30, 2024
39ace8b
refactor: reuse existing context type
EresDev Oct 30, 2024
3f7c331
fix: use correct env type
EresDev Oct 30, 2024
96e8b64
test: find best card on production
EresDev Oct 30, 2024
6a35f70
refactor: add path to args of event context to reuse
EresDev Oct 31, 2024
6df9e57
test: offer no card for unsupported country
EresDev Oct 31, 2024
9b3f53a
fix: prevent msw server closing to rerun tests
EresDev Oct 31, 2024
160e058
test: offer no card for too low permit amount
EresDev Oct 31, 2024
0424a90
fix: improve msw server start and close in tests
EresDev Oct 31, 2024
14bf047
test: offer fallback mastercard when eligible
EresDev Oct 31, 2024
621adce
refactor: remove duplication
EresDev Oct 31, 2024
c7f202d
test: get order placed
EresDev Oct 31, 2024
313797c
test: respond with err for invalid order id
EresDev Oct 31, 2024
fb54ffd
refactor: move fixtures to correct dir
EresDev Nov 1, 2024
f1d246a
refactor: rename handler to http-mocks
EresDev Nov 1, 2024
7ea14f3
refactor: arrange backend function files
EresDev Nov 1, 2024
ef72309
test: get redeem code
EresDev Nov 1, 2024
e0ad048
fix: import findBestCard from correct location
EresDev Nov 1, 2024
aea6e27
test: prevent redeem code for wrong user
EresDev Nov 1, 2024
6932b77
test: give redeem code err on empty tx id
EresDev Nov 1, 2024
8926b58
refactor: reword describe of tests
EresDev Nov 1, 2024
c37bc04
refactor: allow tests to customize http req
EresDev Nov 3, 2024
2954784
test: post payment card order
EresDev Nov 3, 2024
03c7bd7
docs: add caution to wrangler test config
EresDev Nov 3, 2024
460ffae
fix: add strict strategy for outgoing req in tests
EresDev Nov 3, 2024
3f21a7e
test: post payment card order on production
EresDev Nov 3, 2024
4ebf5d2
test: return err on ordering wrong card
EresDev Nov 3, 2024
4b794de
test: return err for post order on unsupported blockchain
EresDev Nov 4, 2024
b4d235d
test: return err for post order for too low permit amount
EresDev Nov 4, 2024
76912d5
test: return err for post order for too high permit amount
EresDev Nov 4, 2024
2066e66
test: return err for post order for expired permit
EresDev Nov 4, 2024
4e64ff1
test: return err for post order for wrong contract interaction
EresDev Nov 4, 2024
822be09
test: return err for post order for wrong method call
EresDev Nov 4, 2024
dbaca99
test: return err for post order on wrong token transfer
EresDev Nov 4, 2024
d56f40d
fix: expect correct output on sandbox post order
EresDev Nov 4, 2024
9cc5e8f
test: return err for post order on transfer to wrong treasury
EresDev Nov 4, 2024
7dde73b
refactor: extract mocks to func to reduce duplication
EresDev Nov 5, 2024
987c099
fix: add console err expectations in tests
EresDev Nov 5, 2024
f69c816
test: mint card with ubiquity dollar
EresDev Nov 5, 2024
1a388c6
ci: add vitest unit test github workflow
EresDev Nov 5, 2024
8551b59
fix: run unit test workflow on push
EresDev Nov 5, 2024
7bf7fba
chore: cause a test to fail to debug workflow
EresDev Nov 5, 2024
a1fc277
Revert "chore: cause a test to fail to debug workflow"
EresDev Nov 5, 2024
84c5a34
ci: remove unnecessary permission from unit test workflow
EresDev Nov 5, 2024
3e83ba8
refactor: rename helpers to shared utils
EresDev Nov 5, 2024
fcdd361
refactor: rename getEventContext to createEventContext
EresDev Nov 5, 2024
cea1fca
chore: fix spells
EresDev Nov 5, 2024
bcc2329
chore: add explicit dependencies per knip rules
EresDev Nov 6, 2024
7be3cc2
chore: ignore cloudflare:test dependency by knip
EresDev Nov 6, 2024
0b04595
refactor: remove unnecessary code
EresDev Nov 6, 2024
34f74a3
refactor: remove duplicate test
EresDev Nov 6, 2024
e080d5d
chore: add unit test run script to packages.json
EresDev Nov 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"ubiquibot",
"UBIQUIBOT",
"URLSAFE",
"UUSD",
"vitest",
"WXDAI",
"XDAI",
"xmark"
Expand Down
2 changes: 1 addition & 1 deletion .github/knip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const config: KnipConfig = {
ignore: ["src/types/config.ts", "**/__mocks__/**", "**/__fixtures__/**", "lib/**/*"],
ignoreExportsUsedInFile: true,
// eslint can also be safely ignored as per the docs: https://knip.dev/guides/handling-issues#eslint--jest
ignoreDependencies: ["eslint-config-prettier", "eslint-plugin-prettier"],
ignoreDependencies: ["eslint-config-prettier", "eslint-plugin-prettier", "cloudflare"],
eslint: true,
ignoreBinaries: ["forge"],
};
Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/vitest-unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Run unit tests for pages functions
on:
workflow_dispatch:
pull_request:
push:

env:
NODE_ENV: "test"

jobs:
testing:
runs-on: ubuntu-latest

steps:
- uses: actions/setup-node@v4
with:
node-version: "20.10.0"

- name: Checkout code
uses: actions/checkout@v3

- name: Install dependencies
run: yarn

- name: Run tests
run: npx vitest --run
8 changes: 5 additions & 3 deletions functions/get-best-card.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BigNumber } from "ethers";
import { getAccessToken, findBestCard } from "./helpers";
import { Context } from "./types";
import { validateEnvVars, validateRequestMethod } from "./validators";
import { getBestCardParamsSchema } from "../shared/api-types";
import { findBestCard } from "./utils/best-card-finder";
import { getAccessToken } from "./utils/shared";
import { Context } from "./utils/types";
import { validateEnvVars, validateRequestMethod } from "./utils/validators";

export async function onRequest(ctx: Context): Promise<Response> {
try {
Expand All @@ -20,6 +21,7 @@ export async function onRequest(ctx: Context): Promise<Response> {
const { country, amount } = result.data;

const accessToken = await getAccessToken(ctx.env);

const bestCard = await findBestCard(country, BigNumber.from(amount), accessToken);

if (bestCard) {
Expand Down
8 changes: 4 additions & 4 deletions functions/get-order.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { OrderTransaction } from "../shared/types";
import { commonHeaders, getAccessToken, getBaseUrl } from "./helpers";
import { commonHeaders, getAccessToken, getReloadlyApiBaseUrl } from "./utils/shared";
import { getGiftCardById } from "./post-order";
import { AccessToken, Context, ReloadlyFailureResponse, ReloadlyGetTransactionResponse } from "./types";
import { validateEnvVars, validateRequestMethod } from "./validators";
import { AccessToken, Context, ReloadlyFailureResponse, ReloadlyGetTransactionResponse } from "./utils/types";
import { validateEnvVars, validateRequestMethod } from "./utils/validators";
import { getOrderParamsSchema } from "../shared/api-types";

export async function onRequest(ctx: Context): Promise<Response> {
Expand Down Expand Up @@ -46,7 +46,7 @@ export async function getTransactionFromOrderId(orderId: string, accessToken: Ac
const oneYearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
const oneYearAgoFormatted = oneYearAgo.toISOString().replace("T", " ").substring(0, 19);

const url = `${getBaseUrl(accessToken.isSandbox)}/reports/transactions?size=1&page=1&customIdentifier=${orderId}&startDate=${oneYearAgoFormatted}&endDate=${nowFormatted}`;
const url = `${getReloadlyApiBaseUrl(accessToken.isSandbox)}/reports/transactions?size=1&page=1&customIdentifier=${orderId}&startDate=${oneYearAgoFormatted}&endDate=${nowFormatted}`;
console.log(`Retrieving transaction from ${url}`);
const options = {
method: "GET",
Expand Down
14 changes: 7 additions & 7 deletions functions/get-redeem-code.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { verifyMessage } from "ethers/lib/utils";
import { verifyMessage } from "@ethersproject/wallet";
import { getGiftCardOrderId, getMessageToSign } from "../shared/helpers";
import { getRedeemCodeParamsSchema } from "../shared/api-types";
import { getTransactionFromOrderId } from "./get-order";
import { commonHeaders, getAccessToken, getBaseUrl } from "./helpers";
import { AccessToken, Context, ReloadlyFailureResponse, ReloadlyRedeemCodeResponse } from "./types";
import { validateEnvVars, validateRequestMethod } from "./validators";
import { commonHeaders, getAccessToken, getReloadlyApiBaseUrl } from "./utils/shared";
import { AccessToken, Context, ReloadlyFailureResponse, ReloadlyRedeemCodeResponse } from "./utils/types";
import { validateEnvVars, validateRequestMethod } from "./utils/validators";
import { RedeemCode } from "../shared/types";

export async function onRequest(ctx: Context): Promise<Response> {
Expand Down Expand Up @@ -42,12 +42,12 @@ export async function onRequest(ctx: Context): Promise<Response> {
const orderId = getGiftCardOrderId(wallet, permitSig);
const order = await getTransactionFromOrderId(orderId, accessToken);

if (order.transactionId != transactionId) {
if (order?.transactionId != transactionId) {
console.error(
`Given transaction does not match with retrieved transactionId using generated orderId: ${JSON.stringify({
transactionId,
orderId,
transactionIdFromOrder: order.transactionId,
transactionIdFromOrder: order?.transactionId,
})}`
);
return errorResponse;
Expand All @@ -62,7 +62,7 @@ export async function onRequest(ctx: Context): Promise<Response> {
}

export async function getRedeemCode(transactionId: number, accessToken: AccessToken): Promise<RedeemCode[]> {
const url = `${getBaseUrl(accessToken.isSandbox)}/orders/transactions/${transactionId}/cards`;
const url = `${getReloadlyApiBaseUrl(accessToken.isSandbox)}/orders/transactions/${transactionId}/cards`;
console.log(`Retrieving redeem codes from ${url}`);
const options = {
method: "GET",
Expand Down
200 changes: 0 additions & 200 deletions functions/helpers.ts

This file was deleted.

19 changes: 10 additions & 9 deletions functions/post-order.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { TransactionReceipt, TransactionResponse } from "@ethersproject/providers";
import { JsonRpcProvider } from "@ethersproject/providers/lib/json-rpc-provider";
import { JsonRpcProvider, TransactionReceipt, TransactionResponse } from "@ethersproject/providers";

import { BigNumber } from "ethers";
import { Interface, TransactionDescription } from "ethers/lib/utils";
import { Interface, TransactionDescription } from "@ethersproject/abi";
import { Tokens, chainIdToRewardTokenMap, giftCardTreasuryAddress, permit2Address } from "../shared/constants";
import { getFastestRpcUrl, getGiftCardOrderId } from "../shared/helpers";
import { getGiftCardValue, isClaimableForAmount } from "../shared/pricing";
import { ExchangeRate, GiftCard } from "../shared/types";
import { permit2Abi } from "../static/scripts/rewards/abis/permit2-abi";
import { erc20Abi } from "../static/scripts/rewards/abis/erc20-abi";
import { getTransactionFromOrderId } from "./get-order";
import { commonHeaders, findBestCard, getAccessToken, getBaseUrl } from "./helpers";
import { AccessToken, Context, ReloadlyFailureResponse, ReloadlyOrderResponse } from "./types";
import { validateEnvVars, validateRequestMethod } from "./validators";
import { commonHeaders, getAccessToken, getReloadlyApiBaseUrl } from "./utils/shared";
import { AccessToken, Context, ReloadlyFailureResponse, ReloadlyOrderResponse } from "./utils/types";
import { validateEnvVars, validateRequestMethod } from "./utils/validators";
import { postOrderParamsSchema } from "../shared/api-types";
import { permitAllowedChainIds, ubiquityDollarAllowedChainIds, ubiquityDollarChainAddresses } from "../shared/constants";
import { findBestCard } from "./utils/best-card-finder";

export async function onRequest(ctx: Context): Promise<Response> {
try {
Expand Down Expand Up @@ -110,7 +111,7 @@ export async function onRequest(ctx: Context): Promise<Response> {
}

export async function getGiftCardById(productId: number, accessToken: AccessToken): Promise<GiftCard> {
const url = `${getBaseUrl(accessToken.isSandbox)}/products/${productId}`;
const url = `${getReloadlyApiBaseUrl(accessToken.isSandbox)}/products/${productId}`;
console.log(`Retrieving gift cards from ${url}`);
const options = {
method: "GET",
Expand Down Expand Up @@ -138,7 +139,7 @@ export async function getGiftCardById(productId: number, accessToken: AccessToke
}

async function orderGiftCard(productId: number, cardValue: number, identifier: string, accessToken: AccessToken): Promise<ReloadlyOrderResponse> {
const url = `${getBaseUrl(accessToken.isSandbox)}/orders`;
const url = `${getReloadlyApiBaseUrl(accessToken.isSandbox)}/orders`;
console.log(`Placing order at url: ${url}`);

const requestBody = JSON.stringify({
Expand Down Expand Up @@ -189,7 +190,7 @@ async function isDuplicateOrder(orderId: string, accessToken: AccessToken): Prom
}

async function getExchangeRate(usdAmount: number, fromCurrency: string, accessToken: AccessToken): Promise<ExchangeRate> {
const url = `${getBaseUrl(accessToken.isSandbox)}/fx-rate?currencyCode=${fromCurrency}&amount=${usdAmount}`;
const url = `${getReloadlyApiBaseUrl(accessToken.isSandbox)}/fx-rate?currencyCode=${fromCurrency}&amount=${usdAmount}`;
console.log(`Retrieving url ${url}`);
const options = {
method: "GET",
Expand Down
Loading
Loading