# Testing

The Qiguard API runs on mainnets only; there are no testnets. Test in three steps: unit tests against recorded answers, a local fork for the full flow, then small trades on Base.

## 1. Recorded answers

Record real answers once (price, quote, errors) and replay them in your unit tests. Every answer is plain JSON with a `zid`; the [API reference](reference.md) lists every field. The SDK takes a `fetch` option, so a test can pass its own:

```js
import { QiguardClient } from './qiguard-sdk.mjs'; // https://docs.qiguard.com/sdk/qiguard-sdk.mjs

const recorded = { '/swap/chains': { chains: [{ chainId: 8453, chainName: 'Base' }], zid: '0x5f1c9a7e2b44d0c3a1e8f6b2' } };
const qg = new QiguardClient({
  apiKey: 'qg_sk_test',
  fetch: async (url) => new Response(JSON.stringify(recorded[new URL(url).pathname]), { status: 200 }),
});
```

## 2. Local fork

Run the full swap flow on a fork of Base, with a wallet you control on the fork. The example uses Foundry (`anvil`, `cast`).

```sh
# 1. fork Base at the latest block
anvil --fork-url https://mainnet.base.org --port 8545

# 2. act as a wallet that holds USDC on Base
export TAKER=0xYourTestWallet
cast rpc anvil_impersonateAccount $TAKER --rpc-url http://127.0.0.1:8545

# 3. quote with taker=$TAKER against the live API (quotes are read from mainnet)
curl -s "https://api.qiguard.com/swap/allowance-holder/quote?chainId=8453&sellToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&buyToken=0x4200000000000000000000000000000000000006&sellAmount=25000000&taker=$TAKER" \
  -H "qg-api-key: $QG_API_KEY" -H "qg-version: v1" > quote.json

# 4. approve the settlement contract for the exact amount on the fork
cast send 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 "approve(address,uint256)" 0x67433187Db1e57f66E88880b6Be833f6759362b1 25000000 \
  --from $TAKER --unlocked --rpc-url http://127.0.0.1:8545

# 5. quote again (step 3), then send transaction.data to transaction.to on the fork
cast send $(jq -r .transaction.to quote.json) $(jq -r .transaction.data quote.json) \
  --from $TAKER --unlocked --gas-limit $(jq -r .transaction.gas quote.json) --rpc-url http://127.0.0.1:8545
```

Fork again shortly before each run: the quote is priced at the current mainnet block and carries a 120-second deadline by default (`deadlineSeconds`).

## 3. Small trades on Base

Base has low gas costs. A trade of a few dollars of USDC to WETH tests the real path end to end, with the approval, the transaction and the dashboard usage graph. Gasless trades need at least 10 USD.

## Gasless before it is switched on

Until gasless is switched on for a chain, `POST /gasless/submit` answers `503 GASLESS_NOT_ENABLED` (`data.reason` `gasless_not_enabled`). Everything before the submit works: the gasless price and quote, the Permit2 approval and the signature. Handle the 503 in your flow, and read `GET /gasless/chains` to see when `enabled` turns `true`.

## Checks worth automating

- Pairs: every pair has an anchor on one side (`GET /tokens`).
- `issues.allowance` and `issues.balance` are handled before sending.
- The quote is refreshed after an approval (`expiresAt`).
- `429` answers wait for `Retry-After`.
- `warnings[]` is logged.
