Skip to content

Commit

Permalink
Merge pull request #351 from EresDevOrg/development
Browse files Browse the repository at this point in the history
PR: Unit tests for virtual cards backend
  • Loading branch information
rndquu authored Nov 8, 2024
2 parents e555598 + e080d5d commit b4e9840
Show file tree
Hide file tree
Showing 60 changed files with 3,487 additions and 241 deletions.
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

1 comment on commit b4e9840

@ubiquity-os
Copy link
Contributor

@ubiquity-os ubiquity-os bot commented on b4e9840 Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rndquu Configuration is invalid.

Caution

approvalsRequired:

path: plugins/5/uses/0/with
value: {"approvalsRequired":{"collaborator":1},"mergeTimeout":{"collaborator":"3.5 days"},"repos":{"ignore":["ubiquibot","launch-party","staging","production"]}}
message: Property "approvalsRequired" does not match schema.

Caution

path: plugins/5/uses/0/with/approvalsRequired
value: {"collaborator":1}
message: Instance does not have required property "contributor".

Caution

approvalsRequired:

path: plugins/5/uses/0/with
value: {"approvalsRequired":{"collaborator":1},"mergeTimeout":{"collaborator":"3.5 days"},"repos":{"ignore":["ubiquibot","launch-party","staging","production"]}}
message: Property "mergeTimeout" does not match schema.

Caution

collaborator: "3.5 days"

path: plugins/5/uses/0/with/mergeTimeout
value: {"collaborator":"3.5 days"}
message: Instance does not have required property "contributor".

Caution

approvalsRequired:

path: plugins/5/uses/0/with
value: {"approvalsRequired":{"collaborator":1},"mergeTimeout":{"collaborator":"3.5 days"},"repos":{"ignore":["ubiquibot","launch-party","staging","production"]}}
message: Property "repos" does not match schema.

Caution

path: plugins/5/uses/0/with/repos
value: {"ignore":["ubiquibot","launch-party","staging","production"]}
message: Instance does not have required property "monitor".

Caution

reviewDelayTolerance: "3 Days"

path: plugins/6/uses/0/with
value: {"reviewDelayTolerance":"3 Days","taskStaleTimeoutDuration":"30 Days","startRequiresWallet":true}
message: Instance does not have required property "maxConcurrentTasks".

Caution

reviewDelayTolerance: "3 Days"

path: plugins/6/uses/0/with
value: {"reviewDelayTolerance":"3 Days","taskStaleTimeoutDuration":"30 Days","startRequiresWallet":true}
message: Instance does not have required property "emptyWalletText".

Caution

reviewDelayTolerance: "3 Days"

path: plugins/6/uses/0/with
value: {"reviewDelayTolerance":"3 Days","taskStaleTimeoutDuration":"30 Days","startRequiresWallet":true}
message: Instance does not have required property "rolesWithReviewAuthority".

Caution

path: plugins/7/uses/0/with
value: undefined
message: Instance does not have required property "matchThreshold".

Caution

path: plugins/7/uses/0/with
value: undefined
message: Instance does not have required property "warningThreshold".

Caution

path: plugins/7/uses/0/with
value: undefined
message: Instance does not have required property "jobMatchingThreshold".

Please sign in to comment.