feat: set up test and run tests easier
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"terminals": []
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Autumn dev container",
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/node:18",
|
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
|
||||||
"forwardPorts": [8080],
|
|
||||||
// "postCreateCommand": "pnpm install",
|
|
||||||
"postCreateCommand": "apt update && apt install -y zsh",
|
|
||||||
"settings": {
|
|
||||||
"terminal.integrated.shell.linux": "/bin/zsh"
|
|
||||||
},
|
|
||||||
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
# name: Benchmark Server PR
|
|
||||||
|
|
||||||
# on:
|
|
||||||
# pull_request:
|
|
||||||
# types: [opened, synchronize, reopened]
|
|
||||||
# paths:
|
|
||||||
# - 'server/**'
|
|
||||||
# - 'pnpm-lock.yaml'
|
|
||||||
# - 'package.json'
|
|
||||||
|
|
||||||
# permissions:
|
|
||||||
# contents: read
|
|
||||||
# pull-requests: write
|
|
||||||
|
|
||||||
# jobs:
|
|
||||||
# benchmark:
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
# steps:
|
|
||||||
# - name: Checkout code
|
|
||||||
# uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# - name: Setup pnpm
|
|
||||||
# uses: pnpm/action-setup@v2
|
|
||||||
# with:
|
|
||||||
# version: latest
|
|
||||||
|
|
||||||
# - name: Setup Node.js
|
|
||||||
# uses: actions/setup-node@v4
|
|
||||||
# with:
|
|
||||||
# node-version: '20'
|
|
||||||
# cache: 'pnpm'
|
|
||||||
|
|
||||||
# - name: Install dependencies
|
|
||||||
# run: pnpm i --no-frozen-lockfile
|
|
||||||
|
|
||||||
# - name: Run benchmark
|
|
||||||
# id: benchmark
|
|
||||||
# working-directory: ./server
|
|
||||||
# run: |
|
|
||||||
# echo "BENCHMARK_OUTPUT<<EOF" >> $GITHUB_OUTPUT
|
|
||||||
# FULL_OUTPUT=$(pnpm run benchmark 2>&1)
|
|
||||||
# FILTERED_OUTPUT=$(echo "$FULL_OUTPUT" | grep -v "^> @.*benchmark" | grep -v "^> tsx benchmarks" | grep -v "Benchmark completed successfully")
|
|
||||||
# echo "$FILTERED_OUTPUT" >> $GITHUB_OUTPUT
|
|
||||||
# echo "EOF" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# if echo "$FULL_OUTPUT" | grep -q "Benchmark completed successfully"; then
|
|
||||||
# echo "BENCHMARK_STATUS=✅ Passed" >> $GITHUB_OUTPUT
|
|
||||||
# else
|
|
||||||
# echo "BENCHMARK_STATUS=❌ Failed" >> $GITHUB_OUTPUT
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# - name: Comment PR
|
|
||||||
# uses: actions/github-script@v7
|
|
||||||
# with:
|
|
||||||
# script: |
|
|
||||||
# const output = `${{ steps.benchmark.outputs.BENCHMARK_OUTPUT }}`;
|
|
||||||
# const status = `${{ steps.benchmark.outputs.BENCHMARK_STATUS }}`;
|
|
||||||
# const body = `## 📊 Benchmark Results
|
|
||||||
|
|
||||||
# **Benchmark CI:** ${status}
|
|
||||||
|
|
||||||
# <details>
|
|
||||||
# <summary>Click to view benchmark results</summary>
|
|
||||||
|
|
||||||
# \`\`\`javascript
|
|
||||||
# ${output}
|
|
||||||
# \`\`\`
|
|
||||||
|
|
||||||
# </details>
|
|
||||||
|
|
||||||
# *Benchmark run for commit ${{ github.sha }}*`;
|
|
||||||
|
|
||||||
# github.rest.issues.createComment({
|
|
||||||
# issue_number: context.issue.number,
|
|
||||||
# owner: context.repo.owner,
|
|
||||||
# repo: context.repo.repo,
|
|
||||||
# body: body
|
|
||||||
# });
|
|
||||||
21
bun.lock
21
bun.lock
@@ -18,6 +18,21 @@
|
|||||||
"inquirer": "^12.6.3",
|
"inquirer": "^12.6.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"scripts": {
|
||||||
|
"name": "@autumn/scripts",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@autumn/shared": "workspace:*",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
|
"inquirer": "^12.6.3",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/inquirer": "^9.0.7",
|
||||||
|
"tsx": "^4.19.2",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
"server": {
|
"server": {
|
||||||
"name": "@autumn/server",
|
"name": "@autumn/server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -318,6 +333,8 @@
|
|||||||
|
|
||||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.32.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg=="],
|
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.32.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg=="],
|
||||||
|
|
||||||
|
"@autumn/scripts": ["@autumn/scripts@workspace:scripts"],
|
||||||
|
|
||||||
"@autumn/server": ["@autumn/server@workspace:server"],
|
"@autumn/server": ["@autumn/server@workspace:server"],
|
||||||
|
|
||||||
"@autumn/shared": ["@autumn/shared@workspace:shared"],
|
"@autumn/shared": ["@autumn/shared@workspace:shared"],
|
||||||
@@ -1264,6 +1281,8 @@
|
|||||||
|
|
||||||
"@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="],
|
"@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="],
|
||||||
|
|
||||||
|
"@types/inquirer": ["@types/inquirer@9.0.9", "", { "dependencies": { "@types/through": "*", "rxjs": "^7.2.0" } }, "sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw=="],
|
||||||
|
|
||||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
"@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="],
|
"@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="],
|
||||||
@@ -1328,6 +1347,8 @@
|
|||||||
|
|
||||||
"@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="],
|
"@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="],
|
||||||
|
|
||||||
|
"@types/through": ["@types/through@0.0.33", "", { "dependencies": { "@types/node": "*" } }, "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ=="],
|
||||||
|
|
||||||
"@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="],
|
"@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="],
|
||||||
|
|
||||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -4,7 +4,8 @@
|
|||||||
"workspaces": [
|
"workspaces": [
|
||||||
"server",
|
"server",
|
||||||
"shared",
|
"shared",
|
||||||
"vite"
|
"vite",
|
||||||
|
"scripts"
|
||||||
],
|
],
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
"dev:simple": "concurrently \"cd shared && bun dev:watch\" \"cd server && bun dev\" \"cd server && bun workers:dev\" \"cd vite && bun dev\"",
|
"dev:simple": "concurrently \"cd shared && bun dev:watch\" \"cd server && bun dev\" \"cd server && bun workers:dev\" \"cd vite && bun dev\"",
|
||||||
"vite:build": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
"vite:build": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
||||||
"vite:start": "bun -F @autumn/vite start:bun",
|
"vite:start": "bun -F @autumn/vite start:bun",
|
||||||
|
|
||||||
"shared": "bun -F @autumn/shared build",
|
"shared": "bun -F @autumn/shared build",
|
||||||
"server": "bun -F @autumn/shared build && bun -F @autumn/server start",
|
"server": "bun -F @autumn/shared build && bun -F @autumn/server start",
|
||||||
"workers": "bun -F @autumn/shared build && bun -F @autumn/server workers",
|
"workers": "bun -F @autumn/shared build && bun -F @autumn/server workers",
|
||||||
@@ -20,14 +22,19 @@
|
|||||||
"server:cron": "pnpm -F server cron:start",
|
"server:cron": "pnpm -F server cron:start",
|
||||||
"server:check": "NODE_ENV=production pnpm -F server check",
|
"server:check": "NODE_ENV=production pnpm -F server check",
|
||||||
"setup": "node scripts/setup.js",
|
"setup": "node scripts/setup.js",
|
||||||
|
|
||||||
|
"setup:test": "bun scripts/setup-test.ts",
|
||||||
|
"tests": "bun scripts/test.ts",
|
||||||
|
|
||||||
"setupci": "node scripts/setupci.js",
|
"setupci": "node scripts/setupci.js",
|
||||||
"db:push": " bun -F @autumn/shared db:push",
|
"db:push": " bun -F @autumn/shared db:push",
|
||||||
"db:generate": "bun -F @autumn/shared db:generate",
|
"db:generate": "bun -F @autumn/shared db:generate",
|
||||||
"db:migrate": " bun -F @autumn/shared db:migrate",
|
"db:migrate": " bun -F @autumn/shared db:migrate",
|
||||||
|
|
||||||
"docker:up": "docker compose -f docker-compose.dev.yml up --build",
|
"docker:up": "docker compose -f docker-compose.dev.yml up --build",
|
||||||
"docker:up:unix": "docker compose -f docker-compose.unix.yml up --build",
|
"docker:up:unix": "docker compose -f docker-compose.unix.yml up --build",
|
||||||
"docker:up:ci": "docker compose -f docker-compose.ci.yml up --build",
|
"docker:up:ci": "docker compose -f docker-compose.ci.yml up --build",
|
||||||
"build:all": "pnpm -F shared build && pnpm -F server prod:build && pnpm -F vite build",
|
|
||||||
"vite:build:bun": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
"vite:build:bun": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
||||||
"vite:start:bun": "bun -F @autumn/shared build && bun -F @autumn/vite start:bun"
|
"vite:start:bun": "bun -F @autumn/shared build && bun -F @autumn/vite start:bun"
|
||||||
},
|
},
|
||||||
|
|||||||
21
scripts/package.json
Normal file
21
scripts/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "@autumn/scripts",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"setup": "tsx setup.js",
|
||||||
|
"setup-test": "tsx setup-test.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@autumn/shared": "workspace:*",
|
||||||
|
"chalk": "^5.3.0",
|
||||||
|
"dotenv": "^16.5.0",
|
||||||
|
"inquirer": "^12.6.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/inquirer": "^9.0.7",
|
||||||
|
"tsx": "^4.19.2",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
145
scripts/setup-test.ts
Normal file
145
scripts/setup-test.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import chalk from "chalk";
|
||||||
|
import inquirer from "inquirer";
|
||||||
|
import { createTestOrg, TEST_ORG_CONFIG } from "./setupTestUtils/createTestOrg.js";
|
||||||
|
import {
|
||||||
|
setupStripeTestKey,
|
||||||
|
setupTunnelUrl,
|
||||||
|
setupUpstash,
|
||||||
|
} from "./setupTestUtils/setupPrompts.js";
|
||||||
|
import { updateEnvFile } from "./setupTestUtils/updateEnvFile.js";
|
||||||
|
import {
|
||||||
|
updateSingleEnvVar,
|
||||||
|
updateMultipleEnvVars,
|
||||||
|
} from "./setupTestUtils/incrementalEnvUpdate.js";
|
||||||
|
|
||||||
|
async function showPreparationChecklist() {
|
||||||
|
console.log(
|
||||||
|
chalk.magentaBright("\n================ Autumn Test Setup ================\n"),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan("This script will set up a test organization for development.\n"),
|
||||||
|
);
|
||||||
|
console.log(chalk.yellowBright("Before you begin, please have the following ready:\n"));
|
||||||
|
|
||||||
|
console.log(chalk.whiteBright("1. Stripe Test API Key (sk_test_...)"));
|
||||||
|
console.log(
|
||||||
|
chalk.gray(" → Used to link Stripe to your test account for payment processing\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(chalk.whiteBright("2. Upstash Redis REST URL and Token"));
|
||||||
|
console.log(
|
||||||
|
chalk.gray(" → Used for caching customer objects and testing race conditions\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(chalk.whiteBright("3. Tunnel URL (e.g., ngrok URL)"));
|
||||||
|
console.log(
|
||||||
|
chalk.gray(
|
||||||
|
" → Points to localhost:8080 so Stripe webhooks can reach your server\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prompt user to continue
|
||||||
|
const { ready } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "confirm",
|
||||||
|
name: "ready",
|
||||||
|
message: chalk.cyan("Ready to begin setup?"),
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!ready) {
|
||||||
|
console.log(chalk.yellow("\nSetup cancelled. Run the script again when you're ready!\n"));
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// Show preparation checklist
|
||||||
|
await showPreparationChecklist();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Import db from server
|
||||||
|
const { db } = await import("../server/src/db/initDrizzle.js");
|
||||||
|
|
||||||
|
// Step 1: Create test organization in database and get API key
|
||||||
|
const autumnSecretKey = await createTestOrg({ db });
|
||||||
|
|
||||||
|
// Save org details immediately
|
||||||
|
updateMultipleEnvVars({
|
||||||
|
TESTS_ORG: TEST_ORG_CONFIG.slug,
|
||||||
|
TESTS_ORG_ID: TEST_ORG_CONFIG.id,
|
||||||
|
...(autumnSecretKey && { UNIT_TEST_AUTUMN_SECRET_KEY: autumnSecretKey }),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 2: Get Stripe test key
|
||||||
|
const stripeTestKey = await setupStripeTestKey();
|
||||||
|
|
||||||
|
// Save Stripe key immediately
|
||||||
|
updateSingleEnvVar({ key: "STRIPE_TEST_KEY", value: stripeTestKey });
|
||||||
|
|
||||||
|
// Step 3: Get Upstash configuration
|
||||||
|
const { upstashUrl, upstashToken } = await setupUpstash();
|
||||||
|
|
||||||
|
// Save Upstash credentials immediately
|
||||||
|
updateMultipleEnvVars({
|
||||||
|
UPSTASH_REDIS_REST_URL: upstashUrl,
|
||||||
|
UPSTASH_REDIS_REST_TOKEN: upstashToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 4: Get tunnel URL
|
||||||
|
const tunnelUrl = await setupTunnelUrl();
|
||||||
|
|
||||||
|
// Save tunnel URL immediately
|
||||||
|
updateSingleEnvVar({ key: "STRIPE_WEBHOOK_URL", value: tunnelUrl });
|
||||||
|
|
||||||
|
// Step 5: Final update to ensure proper formatting
|
||||||
|
updateEnvFile({
|
||||||
|
testOrgSlug: TEST_ORG_CONFIG.slug,
|
||||||
|
testOrgId: TEST_ORG_CONFIG.id,
|
||||||
|
autumnSecretKey,
|
||||||
|
stripeTestKey,
|
||||||
|
upstashUrl,
|
||||||
|
upstashToken,
|
||||||
|
tunnelUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
chalk.magentaBright(
|
||||||
|
"\n================ Setup Complete! ================\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(chalk.greenBright("🎉 Test organization setup complete! 🎉\n"));
|
||||||
|
console.log(chalk.cyan("Test Organization Details:"));
|
||||||
|
console.log(chalk.whiteBright(` Organization: ${TEST_ORG_CONFIG.slug}`));
|
||||||
|
console.log(chalk.whiteBright(` ID: ${TEST_ORG_CONFIG.id}`));
|
||||||
|
|
||||||
|
if (autumnSecretKey) {
|
||||||
|
console.log(chalk.whiteBright(` Secret Key: ${autumnSecretKey}\n`));
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
chalk.whiteBright(" Secret Key: (using existing key from .env)\n"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(chalk.cyan("Next steps:"));
|
||||||
|
console.log(
|
||||||
|
chalk.whiteBright("1. Start your tunnel (e.g., ngrok http 8080)"),
|
||||||
|
);
|
||||||
|
console.log(chalk.whiteBright("2. Start your development server"));
|
||||||
|
console.log(
|
||||||
|
chalk.whiteBright("3. Run tests with your new test organization!\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
chalk.red("\n❌ Setup failed:"),
|
||||||
|
error instanceof Error ? error.message : error,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
128
scripts/setupTestUtils/createTestOrg.ts
Normal file
128
scripts/setupTestUtils/createTestOrg.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { type OrgConfig, member, organizations, user } from "@autumn/shared";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import type { DrizzleCli } from "@server/db/initDrizzle.js";
|
||||||
|
import { createKey } from "@server/internal/dev/api-keys/apiKeyUtils.js";
|
||||||
|
|
||||||
|
const TEST_ORG_CONFIG = {
|
||||||
|
id: "org_2sWv2S8LJ9iaTjLI6UtNsfL88Kt",
|
||||||
|
slug: "unit-test-org",
|
||||||
|
name: "Unit Test Org",
|
||||||
|
createdAt: new Date(1738583937426).toISOString(),
|
||||||
|
created_at: 1738583937426,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a test organization in the database and generates an API key
|
||||||
|
*/
|
||||||
|
export async function createTestOrg({
|
||||||
|
db,
|
||||||
|
}: {
|
||||||
|
db: DrizzleCli;
|
||||||
|
}): Promise<string | null> {
|
||||||
|
console.log(
|
||||||
|
chalk.magentaBright(
|
||||||
|
"\n================ Creating Test Organization ================\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if org already exists
|
||||||
|
const existingOrg = await db.query.organizations.findFirst({
|
||||||
|
where: eq(organizations.id, TEST_ORG_CONFIG.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingOrg) {
|
||||||
|
console.log(
|
||||||
|
chalk.yellowBright(
|
||||||
|
`Test organization '${TEST_ORG_CONFIG.slug}' already exists. Creating new API key.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Always create a new API key for existing org
|
||||||
|
const apiKey = await createKey({
|
||||||
|
db,
|
||||||
|
env: "sandbox" as any,
|
||||||
|
name: "Unit Test Key",
|
||||||
|
orgId: TEST_ORG_CONFIG.id,
|
||||||
|
prefix: "am_sk_test",
|
||||||
|
meta: {
|
||||||
|
createdBy: "setup-test-script",
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
userId: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(chalk.greenBright("✅ Created API key for existing organization"));
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the test organization
|
||||||
|
const org = {
|
||||||
|
id: TEST_ORG_CONFIG.id,
|
||||||
|
slug: TEST_ORG_CONFIG.slug,
|
||||||
|
name: TEST_ORG_CONFIG.name,
|
||||||
|
createdAt: new Date(TEST_ORG_CONFIG.created_at),
|
||||||
|
created_at: TEST_ORG_CONFIG.created_at,
|
||||||
|
stripe_connected: false,
|
||||||
|
default_currency: "usd",
|
||||||
|
config: {} as OrgConfig,
|
||||||
|
onboarded: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
await db.insert(organizations).values(org);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
chalk.greenBright(
|
||||||
|
`✅ Created test organization: ${TEST_ORG_CONFIG.slug} (${TEST_ORG_CONFIG.id})`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get first 5 users from database and create memberships
|
||||||
|
const users = await db.select().from(user).limit(5);
|
||||||
|
|
||||||
|
if (users.length > 0) {
|
||||||
|
const { generateId } = await import("@server/utils/genUtils.js");
|
||||||
|
|
||||||
|
const memberships = users.map((u) => ({
|
||||||
|
id: generateId("mem"),
|
||||||
|
organizationId: TEST_ORG_CONFIG.id,
|
||||||
|
userId: u.id,
|
||||||
|
role: "owner",
|
||||||
|
createdAt: new Date(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
await db.insert(member).values(memberships);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
chalk.greenBright(
|
||||||
|
`✅ Created ${memberships.length} membership(s) for test organization`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
chalk.yellowBright(
|
||||||
|
"⚠ No users found in database. Skipping membership creation.",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create API key for the new org
|
||||||
|
const apiKey = await createKey({
|
||||||
|
db,
|
||||||
|
env: "sandbox" as any,
|
||||||
|
name: "Unit Test Key",
|
||||||
|
orgId: TEST_ORG_CONFIG.id,
|
||||||
|
prefix: "am_sk_test",
|
||||||
|
meta: {
|
||||||
|
createdBy: "setup-test-script",
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
userId: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(chalk.greenBright("✅ Created API key for test organization"));
|
||||||
|
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TEST_ORG_CONFIG };
|
||||||
87
scripts/setupTestUtils/incrementalEnvUpdate.ts
Normal file
87
scripts/setupTestUtils/incrementalEnvUpdate.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { envPath } from "./updateEnvFile.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incrementally updates a single env variable in the .env file
|
||||||
|
*/
|
||||||
|
export function updateSingleEnvVar({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const envContent = readFileSync(envPath, "utf-8");
|
||||||
|
const lines = envContent.split("\n");
|
||||||
|
|
||||||
|
// Check if the key already exists
|
||||||
|
let found = false;
|
||||||
|
const updatedLines = lines.map((line) => {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (trimmed.startsWith(`${key}=`)) {
|
||||||
|
found = true;
|
||||||
|
return `${key}=${value}`;
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
});
|
||||||
|
|
||||||
|
// If not found, add it to the end
|
||||||
|
if (!found) {
|
||||||
|
updatedLines.push(`${key}=${value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(envPath, updatedLines.join("\n"));
|
||||||
|
console.log(chalk.gray(` ✓ Saved ${key} to .env`));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
chalk.red(
|
||||||
|
` ⚠ Warning: Could not save ${key} to .env. You may need to add it manually.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates multiple env variables at once
|
||||||
|
*/
|
||||||
|
export function updateMultipleEnvVars(vars: Record<string, string>) {
|
||||||
|
try {
|
||||||
|
const envContent = readFileSync(envPath, "utf-8");
|
||||||
|
const lines = envContent.split("\n");
|
||||||
|
const keysToUpdate = Object.keys(vars);
|
||||||
|
const foundKeys = new Set<string>();
|
||||||
|
|
||||||
|
// Update existing keys
|
||||||
|
const updatedLines = lines.map((line) => {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
for (const key of keysToUpdate) {
|
||||||
|
if (trimmed.startsWith(`${key}=`)) {
|
||||||
|
foundKeys.add(key);
|
||||||
|
return `${key}=${vars[key]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add new keys that weren't found
|
||||||
|
for (const key of keysToUpdate) {
|
||||||
|
if (!foundKeys.has(key)) {
|
||||||
|
updatedLines.push(`${key}=${vars[key]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(envPath, updatedLines.join("\n"));
|
||||||
|
|
||||||
|
for (const key of keysToUpdate) {
|
||||||
|
console.log(chalk.gray(` ✓ Saved ${key} to .env`));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(
|
||||||
|
chalk.red(
|
||||||
|
" ⚠ Warning: Could not save variables to .env. You may need to add them manually.",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
136
scripts/setupTestUtils/setupPrompts.ts
Normal file
136
scripts/setupTestUtils/setupPrompts.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import inquirer from "inquirer";
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts user for Stripe test API key
|
||||||
|
*/
|
||||||
|
export async function setupStripeTestKey(): Promise<string> {
|
||||||
|
console.log(
|
||||||
|
chalk.magentaBright(
|
||||||
|
"\n================ Stripe Test API Key Setup ================\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
"This Stripe test API key will be used to link Stripe to your test account.",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
"You can find this in your Stripe Dashboard under Developers > API Keys (Test Mode).\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { stripeTestKey } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "stripeTestKey",
|
||||||
|
message: chalk.cyan("Enter your Stripe test secret key (sk_test_...):"),
|
||||||
|
validate: (input: string) => {
|
||||||
|
if (!input || input.length < 10) {
|
||||||
|
return "Please enter a valid Stripe test key";
|
||||||
|
}
|
||||||
|
if (!input.startsWith("sk_test_")) {
|
||||||
|
return "Stripe test keys should start with 'sk_test_'";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return stripeTestKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts user for Upstash configuration
|
||||||
|
*/
|
||||||
|
export async function setupUpstash(): Promise<{
|
||||||
|
upstashUrl: string;
|
||||||
|
upstashToken: string;
|
||||||
|
}> {
|
||||||
|
console.log(
|
||||||
|
chalk.magentaBright("\n================ Upstash Setup ================\n"),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
"Upstash is used for caching the customer object and is important for testing race conditions.",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
"You can create a free Upstash Redis instance at https://upstash.com/\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const { upstashUrl } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "upstashUrl",
|
||||||
|
message: chalk.cyan("Enter your Upstash Redis REST URL:"),
|
||||||
|
validate: (input: string) => {
|
||||||
|
if (!input || input.length < 10) {
|
||||||
|
return "Please enter a valid Upstash URL";
|
||||||
|
}
|
||||||
|
if (!input.startsWith("https://")) {
|
||||||
|
return "Upstash URL should start with 'https://'";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { upstashToken } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "upstashToken",
|
||||||
|
message: chalk.cyan("Enter your Upstash Redis REST token:"),
|
||||||
|
validate: (input: string) => {
|
||||||
|
if (!input || input.length < 10) {
|
||||||
|
return "Please enter a valid Upstash token";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { upstashUrl, upstashToken };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts user for tunnel URL
|
||||||
|
*/
|
||||||
|
export async function setupTunnelUrl(): Promise<string> {
|
||||||
|
console.log(
|
||||||
|
chalk.magentaBright(
|
||||||
|
"\n================ Tunnel URL Setup ================\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(
|
||||||
|
"You need a tunnel that points to localhost:8080 (your server URL) to receive Stripe webhooks.",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan("You can use tools like ngrok, localtunnel, or Cloudflare Tunnel."),
|
||||||
|
);
|
||||||
|
console.log(chalk.cyan("Example: https://your-subdomain.ngrok.io\n"));
|
||||||
|
|
||||||
|
const { tunnelUrl } = await inquirer.prompt([
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "tunnelUrl",
|
||||||
|
message: chalk.cyan("Enter your tunnel URL:"),
|
||||||
|
validate: (input: string) => {
|
||||||
|
if (!input || input.length < 10) {
|
||||||
|
return "Please enter a valid tunnel URL";
|
||||||
|
}
|
||||||
|
if (!input.startsWith("https://") && !input.startsWith("http://")) {
|
||||||
|
return "Tunnel URL should start with 'http://' or 'https://'";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return tunnelUrl;
|
||||||
|
}
|
||||||
183
scripts/setupTestUtils/updateEnvFile.ts
Normal file
183
scripts/setupTestUtils/updateEnvFile.ts
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { dirname, resolve } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import chalk from "chalk";
|
||||||
|
import { config } from "dotenv";
|
||||||
|
|
||||||
|
// Get the directory of this script file
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find server/.env file robustly - works whether running from root or scripts dir
|
||||||
|
*/
|
||||||
|
function findEnvPath(): string {
|
||||||
|
// Try from script directory (scripts/setup-test.ts -> ../server/.env)
|
||||||
|
const fromScriptDir = resolve(__dirname, "../../server/.env");
|
||||||
|
if (existsSync(fromScriptDir)) {
|
||||||
|
return fromScriptDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try from current working directory
|
||||||
|
const fromCwd = resolve(process.cwd(), "server/.env");
|
||||||
|
if (existsSync(fromCwd)) {
|
||||||
|
return fromCwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If neither exists, return the path from script dir (will fail later with clear error)
|
||||||
|
return fromScriptDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const envPath = findEnvPath();
|
||||||
|
|
||||||
|
// Load existing env vars
|
||||||
|
config({ path: envPath });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates server/.env with new test configuration
|
||||||
|
*/
|
||||||
|
export function updateEnvFile({
|
||||||
|
testOrgSlug,
|
||||||
|
testOrgId,
|
||||||
|
autumnSecretKey,
|
||||||
|
stripeTestKey,
|
||||||
|
upstashUrl,
|
||||||
|
upstashToken,
|
||||||
|
tunnelUrl,
|
||||||
|
}: {
|
||||||
|
testOrgSlug: string;
|
||||||
|
testOrgId: string;
|
||||||
|
autumnSecretKey: string | null;
|
||||||
|
stripeTestKey: string;
|
||||||
|
upstashUrl: string;
|
||||||
|
upstashToken: string;
|
||||||
|
tunnelUrl: string;
|
||||||
|
}) {
|
||||||
|
console.log(
|
||||||
|
chalk.magentaBright(
|
||||||
|
"\n================ Updating Environment Variables ================\n",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Read existing .env file
|
||||||
|
let envContent = "";
|
||||||
|
try {
|
||||||
|
envContent = readFileSync(envPath, "utf-8");
|
||||||
|
} catch {
|
||||||
|
console.log(
|
||||||
|
chalk.red(
|
||||||
|
`❌ Could not read server/.env file at ${envPath}. Make sure it exists.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse existing env vars
|
||||||
|
const envVars = new Map<string, string>();
|
||||||
|
const lines = envContent.split("\n");
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
// Skip empty lines and comments
|
||||||
|
if (!trimmed || trimmed.startsWith("#")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eqIndex = trimmed.indexOf("=");
|
||||||
|
if (eqIndex > 0) {
|
||||||
|
const key = trimmed.substring(0, eqIndex);
|
||||||
|
const value = trimmed.substring(eqIndex + 1);
|
||||||
|
envVars.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update with new test variables
|
||||||
|
envVars.set("TESTS_ORG", testOrgSlug);
|
||||||
|
envVars.set("TESTS_ORG_ID", testOrgId);
|
||||||
|
|
||||||
|
// Only update the secret key if a new one was generated
|
||||||
|
if (autumnSecretKey) {
|
||||||
|
envVars.set("UNIT_TEST_AUTUMN_SECRET_KEY", autumnSecretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
envVars.set("STRIPE_TEST_KEY", stripeTestKey);
|
||||||
|
envVars.set("UPSTASH_REDIS_REST_URL", upstashUrl);
|
||||||
|
envVars.set("UPSTASH_REDIS_REST_TOKEN", upstashToken);
|
||||||
|
envVars.set("STRIPE_WEBHOOK_URL", tunnelUrl);
|
||||||
|
|
||||||
|
// Build new env content, preserving structure
|
||||||
|
const sections: string[][] = [];
|
||||||
|
let currentSection: string[] = [];
|
||||||
|
let inTestSection = false;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
|
||||||
|
// Check if this is a section header
|
||||||
|
if (trimmed.startsWith("#")) {
|
||||||
|
if (currentSection.length > 0) {
|
||||||
|
sections.push(currentSection);
|
||||||
|
currentSection = [];
|
||||||
|
}
|
||||||
|
currentSection.push(line);
|
||||||
|
inTestSection = trimmed.toLowerCase().includes("test");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip test-related vars from existing content - we'll add them fresh
|
||||||
|
if (
|
||||||
|
trimmed.startsWith("TESTS_ORG") ||
|
||||||
|
trimmed.startsWith("UNIT_TEST_AUTUMN_SECRET_KEY") ||
|
||||||
|
trimmed.startsWith("STRIPE_TEST_KEY") ||
|
||||||
|
trimmed.startsWith("UPSTASH_REDIS_REST") ||
|
||||||
|
(trimmed.startsWith("STRIPE_WEBHOOK_URL") && inTestSection)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSection.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSection.length > 0) {
|
||||||
|
sections.push(currentSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add test configuration section
|
||||||
|
const testSection = [
|
||||||
|
"",
|
||||||
|
"# Test Configuration",
|
||||||
|
`TESTS_ORG=${testOrgSlug}`,
|
||||||
|
`TESTS_ORG_ID=${testOrgId}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Only add secret key if it was generated/updated
|
||||||
|
if (autumnSecretKey) {
|
||||||
|
testSection.push(`UNIT_TEST_AUTUMN_SECRET_KEY=${autumnSecretKey}`);
|
||||||
|
} else if (envVars.has("UNIT_TEST_AUTUMN_SECRET_KEY")) {
|
||||||
|
testSection.push(
|
||||||
|
`UNIT_TEST_AUTUMN_SECRET_KEY=${envVars.get("UNIT_TEST_AUTUMN_SECRET_KEY")}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
testSection.push(
|
||||||
|
`STRIPE_TEST_KEY=${stripeTestKey}`,
|
||||||
|
"",
|
||||||
|
"# Upstash (for caching)",
|
||||||
|
`UPSTASH_REDIS_REST_URL=${upstashUrl}`,
|
||||||
|
`UPSTASH_REDIS_REST_TOKEN=${upstashToken}`,
|
||||||
|
"",
|
||||||
|
"# Tunnel URL (for Stripe webhooks)",
|
||||||
|
`STRIPE_WEBHOOK_URL=${tunnelUrl}`,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
|
sections.push(testSection);
|
||||||
|
|
||||||
|
// Write back to file
|
||||||
|
const newContent = sections.map((s) => s.join("\n")).join("\n");
|
||||||
|
writeFileSync(envPath, newContent);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
chalk.greenBright(`✅ Environment variables updated in ${envPath}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
279
scripts/test.ts
Normal file
279
scripts/test.ts
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import { spawn } from "node:child_process";
|
||||||
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
||||||
|
import { join, relative, resolve } from "node:path";
|
||||||
|
import chalk from "chalk";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively finds all test files in a directory
|
||||||
|
*/
|
||||||
|
function findTestFiles({ dir }: { dir: string }): string[] {
|
||||||
|
const files: string[] = [];
|
||||||
|
const entries = readdirSync(dir);
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = join(dir, entry);
|
||||||
|
const stat = statSync(fullPath);
|
||||||
|
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
files.push(...findTestFiles({ dir: fullPath }));
|
||||||
|
} else if (
|
||||||
|
entry.endsWith(".ts") &&
|
||||||
|
!entry.includes("Utils") &&
|
||||||
|
!entry.includes("utils")
|
||||||
|
) {
|
||||||
|
files.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fuzzy matches a search term against test file paths and returns a score
|
||||||
|
*/
|
||||||
|
function fuzzyMatchScore({
|
||||||
|
search,
|
||||||
|
filePath,
|
||||||
|
}: {
|
||||||
|
search: string;
|
||||||
|
filePath: string;
|
||||||
|
}): number {
|
||||||
|
const searchLower = search.toLowerCase();
|
||||||
|
const pathLower = filePath.toLowerCase();
|
||||||
|
const fileName = pathLower.split("/").pop() || "";
|
||||||
|
|
||||||
|
// Check if search matches exactly in filename (highest priority)
|
||||||
|
if (fileName === `${searchLower}.ts`) {
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if filename starts with search (high priority)
|
||||||
|
if (fileName.startsWith(searchLower)) {
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if search is contained in filename
|
||||||
|
if (fileName.includes(searchLower)) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple fuzzy match - check if all characters appear in order
|
||||||
|
let searchIndex = 0;
|
||||||
|
let score = 0;
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < pathLower.length && searchIndex < searchLower.length;
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
if (pathLower[i] === searchLower[searchIndex]) {
|
||||||
|
searchIndex++;
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return 0 if not all characters matched
|
||||||
|
if (searchIndex !== searchLower.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs shell test scripts from server/shell/ directory or individual test files
|
||||||
|
*/
|
||||||
|
async function runTest() {
|
||||||
|
const scriptName = process.argv[2];
|
||||||
|
const additionalArgs = process.argv.slice(3);
|
||||||
|
|
||||||
|
if (!scriptName) {
|
||||||
|
console.log(
|
||||||
|
chalk.red("❌ Please provide a shell script or test file name"),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.cyan("\nUsage: bun tests <script-name|test-name> [args...]"),
|
||||||
|
);
|
||||||
|
console.log(chalk.gray("Examples:"));
|
||||||
|
console.log(chalk.gray(" bun tests g1"));
|
||||||
|
console.log(chalk.gray(" bun tests g1 setup"));
|
||||||
|
console.log(
|
||||||
|
chalk.gray(" bun tests basic1 # fuzzy matches test file"),
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
chalk.gray(" bun tests attach/basic1 # matches path pattern\n"),
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverDir = resolve(process.cwd(), "server");
|
||||||
|
const shellScript = resolve(serverDir, "shell", `${scriptName}.sh`);
|
||||||
|
|
||||||
|
// First try to find a shell script
|
||||||
|
if (existsSync(shellScript)) {
|
||||||
|
const argsDisplay =
|
||||||
|
additionalArgs.length > 0 ? ` ${additionalArgs.join(" ")}` : "";
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(`🧪 Running shell script: ${scriptName}.sh${argsDisplay}\n`),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a new process group by spawning with detached: true
|
||||||
|
const child = spawn("bash", [shellScript, ...additionalArgs], {
|
||||||
|
cwd: serverDir,
|
||||||
|
stdio: "inherit",
|
||||||
|
env: { ...process.env, NODE_ENV: "production" },
|
||||||
|
detached: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store the process group ID
|
||||||
|
const pgid = child.pid;
|
||||||
|
|
||||||
|
// Forward termination signals to entire process group
|
||||||
|
const killProcessGroup = () => {
|
||||||
|
if (pgid) {
|
||||||
|
try {
|
||||||
|
// Kill the entire process group with SIGKILL (force kill)
|
||||||
|
process.kill(-pgid, "SIGKILL");
|
||||||
|
} catch (_err) {
|
||||||
|
// Process group might already be dead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on("SIGINT", () => {
|
||||||
|
console.log(chalk.yellow("\n⚠️ Received SIGINT, killing all test processes...\n"));
|
||||||
|
killProcessGroup();
|
||||||
|
process.exit(130);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("SIGTERM", () => {
|
||||||
|
console.log(chalk.yellow("\n⚠️ Received SIGTERM, killing all test processes...\n"));
|
||||||
|
killProcessGroup();
|
||||||
|
process.exit(143);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("exit", () => {
|
||||||
|
killProcessGroup();
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on("exit", (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
console.log(
|
||||||
|
chalk.green(`\n✅ Test ${scriptName} completed successfully`),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
chalk.red(`\n❌ Test ${scriptName} failed with code ${code}`),
|
||||||
|
);
|
||||||
|
process.exit(code || 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on("error", (error) => {
|
||||||
|
console.log(chalk.red(`\n❌ Error running test: ${error.message}`));
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not a shell script, try fuzzy matching test files
|
||||||
|
console.log(
|
||||||
|
chalk.cyan(`🔍 Searching for test file matching: ${scriptName}\n`),
|
||||||
|
);
|
||||||
|
|
||||||
|
const testsDir = resolve(serverDir, "tests");
|
||||||
|
const allTestFiles = findTestFiles({ dir: testsDir });
|
||||||
|
|
||||||
|
// Find matches with scores
|
||||||
|
const matches = allTestFiles
|
||||||
|
.map((file) => ({
|
||||||
|
path: file,
|
||||||
|
relative: relative(serverDir, file),
|
||||||
|
score: fuzzyMatchScore({ search: scriptName, filePath: file }),
|
||||||
|
}))
|
||||||
|
.filter((match) => match.score > 0)
|
||||||
|
.sort((a, b) => b.score - a.score);
|
||||||
|
|
||||||
|
if (matches.length === 0) {
|
||||||
|
console.log(chalk.red(`❌ No test file found matching: ${scriptName}`));
|
||||||
|
console.log(chalk.gray(` Searched in: ${testsDir}\n`));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bestMatch = matches[0];
|
||||||
|
const otherMatches = matches.slice(1, 5);
|
||||||
|
|
||||||
|
if (otherMatches.length > 0) {
|
||||||
|
console.log(
|
||||||
|
chalk.yellow(`⚠️ Multiple matches found, picking best match:\n`),
|
||||||
|
);
|
||||||
|
console.log(chalk.green(` ✓ ${bestMatch.relative} (selected)`));
|
||||||
|
for (const match of otherMatches) {
|
||||||
|
console.log(chalk.gray(` ${match.relative}`));
|
||||||
|
}
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
const testFile = bestMatch;
|
||||||
|
console.log(chalk.green(`✓ Found: ${testFile.relative}\n`));
|
||||||
|
console.log(chalk.cyan(`🧪 Running test file...\n`));
|
||||||
|
|
||||||
|
// Run the test file with mocha
|
||||||
|
const child = spawn("bunx", ["mocha", "--timeout", "0", testFile.path], {
|
||||||
|
cwd: serverDir,
|
||||||
|
stdio: "inherit",
|
||||||
|
env: { ...process.env, NODE_ENV: "production" },
|
||||||
|
detached: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store the process group ID
|
||||||
|
const pgid = child.pid;
|
||||||
|
|
||||||
|
// Forward termination signals to entire process group
|
||||||
|
const killProcessGroup = () => {
|
||||||
|
if (pgid) {
|
||||||
|
try {
|
||||||
|
// Kill the entire process group with SIGKILL (force kill)
|
||||||
|
process.kill(-pgid, "SIGKILL");
|
||||||
|
} catch (_err) {
|
||||||
|
// Process group might already be dead
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.on("SIGINT", () => {
|
||||||
|
console.log(chalk.yellow("\n⚠️ Received SIGINT, killing test process...\n"));
|
||||||
|
killProcessGroup();
|
||||||
|
process.exit(130);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("SIGTERM", () => {
|
||||||
|
console.log(chalk.yellow("\n⚠️ Received SIGTERM, killing test process...\n"));
|
||||||
|
killProcessGroup();
|
||||||
|
process.exit(143);
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("exit", () => {
|
||||||
|
killProcessGroup();
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on("exit", (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
console.log(
|
||||||
|
chalk.green(`\n✅ Test ${scriptName} completed successfully`),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
chalk.red(`\n❌ Test ${scriptName} failed with code ${code}`),
|
||||||
|
);
|
||||||
|
process.exit(code || 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on("error", (error) => {
|
||||||
|
console.log(chalk.red(`\n❌ Error running test: ${error.message}`));
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
runTest();
|
||||||
25
scripts/tsconfig.json
Normal file
25
scripts/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": false,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"types": ["node"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@server/*": ["../server/src/*"],
|
||||||
|
"@shared/*": ["../shared/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts", "**/*.js"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
MOCHA_SETUP="bunx mocha tests/00_setup.ts"
|
MOCHA_SETUP="bunx mocha --timeout 10000000 tests/00_setup.ts"
|
||||||
MOCHA_CMD="bunx mocha --parallel -j 6 --timeout 10000000 --ignore tests/00_setup.ts"
|
MOCHA_CMD="bunx mocha --parallel -j 6 --timeout 10000000 --ignore tests/00_setup.ts"
|
||||||
@@ -5,9 +5,9 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
source "$SCRIPT_DIR/config.sh"
|
source "$SCRIPT_DIR/config.sh"
|
||||||
|
|
||||||
# If contains setup then run $MOCHA_SETUP
|
# If contains setup then run $MOCHA_SETUP
|
||||||
# if [[ "$1" == *"setup"* ]]; then
|
if [[ "$1" == *"setup"* ]]; then
|
||||||
# MOCHA_PARALLEL=true $MOCHA_SETUP
|
MOCHA_PARALLEL=true $MOCHA_SETUP
|
||||||
# fi
|
fi
|
||||||
|
|
||||||
$MOCHA_CMD \
|
$MOCHA_CMD \
|
||||||
'tests/attach/basic/*.ts' \
|
'tests/attach/basic/*.ts' \
|
||||||
|
|||||||
@@ -29,7 +29,13 @@ export const handleConnectWebhook = async (c: Context<HonoEnv>) => {
|
|||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
|
||||||
const masterStripe = initMasterStripe();
|
let masterStripe: Stripe;
|
||||||
|
try {
|
||||||
|
masterStripe = initMasterStripe();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to initialize master stripe client ${error}`);
|
||||||
|
return c.json(200);
|
||||||
|
}
|
||||||
let event: Stripe.Event;
|
let event: Stripe.Event;
|
||||||
|
|
||||||
// Step 1: Get webhook secret
|
// Step 1: Get webhook secret
|
||||||
|
|||||||
201
server/test.sh
201
server/test.sh
@@ -1,75 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
MOCHA_SETUP="npx mocha tests/00_setup.ts"
|
|
||||||
MOCHA_CMD="npx mocha --parallel --timeout 10000000 --ignore tests/00_setup.ts"
|
|
||||||
|
|
||||||
# Group 1
|
if [ "$1" == "custom" ]; then
|
||||||
if [ "$1" == "group1" ]; then
|
|
||||||
$MOCHA_SETUP \
|
|
||||||
&& $MOCHA_CMD \
|
|
||||||
'tests/attach/basic/*.ts' \
|
|
||||||
'tests/attach/upgrade/*.ts' \
|
|
||||||
'tests/attach/downgrade/*.ts'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Group 2
|
|
||||||
if [ "$1" == "g2" ]; then
|
|
||||||
$MOCHA_SETUP && $MOCHA_CMD \
|
|
||||||
'tests/attach/upgradeOld/*.ts' \
|
|
||||||
'tests/attach/entities/*.ts' \
|
|
||||||
'tests/attach/migrations/*.ts' \
|
|
||||||
'tests/attach/newVersion/*.ts' \
|
|
||||||
'tests/attach/others/*.ts' \
|
|
||||||
'tests/attach/updateEnts/*.ts' \
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" == "g3" ]; then
|
|
||||||
$MOCHA_SETUP \
|
|
||||||
&& $MOCHA_CMD 'tests/contUse/entities/*.ts' \
|
|
||||||
&& $MOCHA_CMD 'tests/contUse/update/*.ts'\
|
|
||||||
&& $MOCHA_CMD 'tests/contUse/track/*.ts' \
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" == "g4" ]; then
|
|
||||||
$MOCHA_SETUP && $MOCHA_CMD \
|
|
||||||
'tests/attach/updateQuantity/*.ts' \
|
|
||||||
'tests/attach/multiProduct/*.ts' \
|
|
||||||
'tests/advanced/multiFeature/*.ts' \
|
|
||||||
'tests/advanced/referrals/*.ts' \
|
|
||||||
'tests/advanced/coupons/*.ts'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Group 5 - Paid referrals
|
|
||||||
if [ "$1" == "paid-referrals" ]; then
|
|
||||||
$MOCHA_SETUP && $MOCHA_CMD \
|
|
||||||
'tests/advanced/referrals/paid/*.ts'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Group 4
|
|
||||||
if [ "$1" == "g4" ]; then
|
|
||||||
$MOCHA_SETUP && $MOCHA_CMD \
|
|
||||||
'tests/advanced/usage/*.ts'
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if [ "$1" == "alex-parallel" ]; then
|
|
||||||
MOCHA_PARALLEL=true npx mocha 'tests/alex/00_setup.ts' && npx mocha --parallel --timeout 10000000 \
|
|
||||||
'tests/alex/01_free.ts' 'tests/alex/02_pro.ts' 'tests/alex/03_premium.ts' \
|
|
||||||
'tests/alex/04_topups.ts' 'tests/alex/05_cancel.ts' 'tests/alex/06_switch.ts' \
|
|
||||||
--ignore 'tests/alex/00_setup.ts'
|
|
||||||
|
|
||||||
elif [ "$1" == "alex" ]; then
|
|
||||||
npx mocha 'tests/alex/00_setup.ts' && \
|
|
||||||
npx mocha --timeout 10000000 \
|
|
||||||
'tests/alex/01_free.ts' \
|
|
||||||
--ignore 'tests/alex/00_setup.ts'
|
|
||||||
|
|
||||||
elif [ "$1" == "alex-custom" ]; then
|
|
||||||
FILE_TO_TEST="$2"
|
|
||||||
npx mocha --timeout 10000000 "tests/alex/$FILE_TO_TEST.ts"
|
|
||||||
elif [ "$1" == "custom" ]; then
|
|
||||||
FILE_TO_TEST="$2"
|
FILE_TO_TEST="$2"
|
||||||
ARG3="$3"
|
ARG3="$3"
|
||||||
if [ "$ARG3" == "setup" ]; then
|
if [ "$ARG3" == "setup" ]; then
|
||||||
@@ -79,41 +10,123 @@ elif [ "$1" == "custom" ]; then
|
|||||||
else
|
else
|
||||||
npx mocha --bail --timeout 10000000 "tests/$FILE_TO_TEST.ts"
|
npx mocha --bail --timeout 10000000 "tests/$FILE_TO_TEST.ts"
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
npx mocha --timeout 10000000 'tests/00_setup.ts' && npx mocha --timeout 10000000 \
|
|
||||||
'tests/**/*.ts' \
|
|
||||||
--ignore 'tests/00_setup.ts' \
|
|
||||||
--ignore 'tests/alex/**/*.ts'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# # TEST PARALLEL
|
|
||||||
# if [ "$1" == "basic-parallel" ]; then
|
# MOCHA_SETUP="npx mocha tests/00_setup.ts"
|
||||||
# MOCHA_PARALLEL=true $MOCHA_SETUP \
|
# MOCHA_CMD="npx mocha --parallel --timeout 10000000 --ignore tests/00_setup.ts"
|
||||||
# && $MOCHA_CMD \
|
|
||||||
|
# # Group 1
|
||||||
|
# if [ "$1" == "group1" ]; then
|
||||||
|
# $MOCHA_SETUP \
|
||||||
|
# && $MOCHA_CMD \
|
||||||
# 'tests/attach/basic/*.ts' \
|
# 'tests/attach/basic/*.ts' \
|
||||||
# 'tests/attach/upgrade/*.ts' \
|
# 'tests/attach/upgrade/*.ts' \
|
||||||
# 'tests/attach/downgrade/*.ts' \
|
# 'tests/attach/downgrade/*.ts'
|
||||||
# && $MOCHA_CMD \
|
# fi
|
||||||
|
|
||||||
|
# # Group 2
|
||||||
|
# if [ "$1" == "g2" ]; then
|
||||||
|
# $MOCHA_SETUP && $MOCHA_CMD \
|
||||||
# 'tests/attach/upgradeOld/*.ts' \
|
# 'tests/attach/upgradeOld/*.ts' \
|
||||||
# 'tests/attach/entities/*.ts' \
|
# 'tests/attach/entities/*.ts' \
|
||||||
# 'tests/attach/migrations/*.ts' \
|
# 'tests/attach/migrations/*.ts' \
|
||||||
# 'tests/attach/multiProduct/*.ts' \
|
|
||||||
# 'tests/attach/newVersion/*.ts' \
|
# 'tests/attach/newVersion/*.ts' \
|
||||||
# 'tests/attach/others/*.ts' \
|
# 'tests/attach/others/*.ts' \
|
||||||
# 'tests/attach/updateEnts/*.ts' \
|
# 'tests/attach/updateEnts/*.ts' \
|
||||||
|
# exit 0
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# if [ "$1" == "g3" ]; then
|
||||||
|
# $MOCHA_SETUP \
|
||||||
|
# && $MOCHA_CMD 'tests/contUse/entities/*.ts' \
|
||||||
|
# && $MOCHA_CMD 'tests/contUse/update/*.ts'\
|
||||||
|
# && $MOCHA_CMD 'tests/contUse/track/*.ts' \
|
||||||
|
# exit 0
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# if [ "$1" == "g4" ]; then
|
||||||
|
# $MOCHA_SETUP && $MOCHA_CMD \
|
||||||
# 'tests/attach/updateQuantity/*.ts' \
|
# 'tests/attach/updateQuantity/*.ts' \
|
||||||
# 'tests/contUse/entities/*.ts' \
|
# 'tests/attach/multiProduct/*.ts' \
|
||||||
# 'tests/contUse/track/*.ts' \
|
|
||||||
# 'tests/contUse/update/*.ts' \
|
|
||||||
# && $MOCHA_CMD \
|
|
||||||
# 'tests/advanced/multiFeature/*.ts' \
|
# 'tests/advanced/multiFeature/*.ts' \
|
||||||
# 'tests/advanced/referrals/*.ts' \
|
# 'tests/advanced/referrals/*.ts' \
|
||||||
# 'tests/advanced/coupons/*.ts' \
|
# 'tests/advanced/coupons/*.ts'
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# # Group 5 - Paid referrals
|
||||||
|
# if [ "$1" == "paid-referrals" ]; then
|
||||||
|
# $MOCHA_SETUP && $MOCHA_CMD \
|
||||||
|
# 'tests/advanced/referrals/paid/*.ts'
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# # Group 4
|
||||||
|
# if [ "$1" == "g4" ]; then
|
||||||
|
# $MOCHA_SETUP && $MOCHA_CMD \
|
||||||
|
# 'tests/advanced/usage/*.ts'
|
||||||
|
# fi
|
||||||
|
|
||||||
|
|
||||||
# elif [ "$1" == "advanced-parallel" ]; then
|
|
||||||
# MOCHA_PARALLEL=true \
|
# if [ "$1" == "alex-parallel" ]; then
|
||||||
# $MOCHA_SETUP \
|
# MOCHA_PARALLEL=true npx mocha 'tests/alex/00_setup.ts' && npx mocha --parallel --timeout 10000000 \
|
||||||
# && $MOCHA_CMD 'tests/advanced/arrear_prorated/*.ts' 'tests/advanced/coupons/*.ts'\
|
# 'tests/alex/01_free.ts' 'tests/alex/02_pro.ts' 'tests/alex/03_premium.ts' \
|
||||||
# # && $MOCHA_CMD 'tests/advanced/usage/*.ts' \
|
# 'tests/alex/04_topups.ts' 'tests/alex/05_cancel.ts' 'tests/alex/06_switch.ts' \
|
||||||
# # && $MOCHA_CMD 'tests/advanced/coupons/*.ts'\
|
# --ignore 'tests/alex/00_setup.ts'
|
||||||
|
|
||||||
|
# elif [ "$1" == "alex" ]; then
|
||||||
|
# npx mocha 'tests/alex/00_setup.ts' && \
|
||||||
|
# npx mocha --timeout 10000000 \
|
||||||
|
# 'tests/alex/01_free.ts' \
|
||||||
|
# --ignore 'tests/alex/00_setup.ts'
|
||||||
|
|
||||||
|
# elif [ "$1" == "alex-custom" ]; then
|
||||||
|
# FILE_TO_TEST="$2"
|
||||||
|
# npx mocha --timeout 10000000 "tests/alex/$FILE_TO_TEST.ts"
|
||||||
|
# elif [ "$1" == "custom" ]; then
|
||||||
|
# FILE_TO_TEST="$2"
|
||||||
|
# ARG3="$3"
|
||||||
|
# if [ "$ARG3" == "setup" ]; then
|
||||||
|
# npx mocha --bail --timeout 10000000 'tests/00_setup.ts'
|
||||||
|
# elif [ "$ARG3" == "parallel" ]; then
|
||||||
|
# npx mocha --parallel --bail --timeout 10000000 "tests/$FILE_TO_TEST.ts"
|
||||||
|
# else
|
||||||
|
# npx mocha --bail --timeout 10000000 "tests/$FILE_TO_TEST.ts"
|
||||||
|
# fi
|
||||||
|
# else
|
||||||
|
# npx mocha --timeout 10000000 'tests/00_setup.ts' && npx mocha --timeout 10000000 \
|
||||||
|
# 'tests/**/*.ts' \
|
||||||
|
# --ignore 'tests/00_setup.ts' \
|
||||||
|
# --ignore 'tests/alex/**/*.ts'
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# # # TEST PARALLEL
|
||||||
|
# # if [ "$1" == "basic-parallel" ]; then
|
||||||
|
# # MOCHA_PARALLEL=true $MOCHA_SETUP \
|
||||||
|
# # && $MOCHA_CMD \
|
||||||
|
# # 'tests/attach/basic/*.ts' \
|
||||||
|
# # 'tests/attach/upgrade/*.ts' \
|
||||||
|
# # 'tests/attach/downgrade/*.ts' \
|
||||||
|
# # && $MOCHA_CMD \
|
||||||
|
# # 'tests/attach/upgradeOld/*.ts' \
|
||||||
|
# # 'tests/attach/entities/*.ts' \
|
||||||
|
# # 'tests/attach/migrations/*.ts' \
|
||||||
|
# # 'tests/attach/multiProduct/*.ts' \
|
||||||
|
# # 'tests/attach/newVersion/*.ts' \
|
||||||
|
# # 'tests/attach/others/*.ts' \
|
||||||
|
# # 'tests/attach/updateEnts/*.ts' \
|
||||||
|
# # 'tests/attach/updateQuantity/*.ts' \
|
||||||
|
# # 'tests/contUse/entities/*.ts' \
|
||||||
|
# # 'tests/contUse/track/*.ts' \
|
||||||
|
# # 'tests/contUse/update/*.ts' \
|
||||||
|
# # && $MOCHA_CMD \
|
||||||
|
# # 'tests/advanced/multiFeature/*.ts' \
|
||||||
|
# # 'tests/advanced/referrals/*.ts' \
|
||||||
|
# # 'tests/advanced/coupons/*.ts' \
|
||||||
|
|
||||||
|
|
||||||
|
# # elif [ "$1" == "advanced-parallel" ]; then
|
||||||
|
# # MOCHA_PARALLEL=true \
|
||||||
|
# # $MOCHA_SETUP \
|
||||||
|
# # && $MOCHA_CMD 'tests/advanced/arrear_prorated/*.ts' 'tests/advanced/coupons/*.ts'\
|
||||||
|
# # # && $MOCHA_CMD 'tests/advanced/usage/*.ts' \
|
||||||
|
# # # && $MOCHA_CMD 'tests/advanced/coupons/*.ts'\
|
||||||
|
|||||||
@@ -1,35 +1,31 @@
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
import { AppEnv } from "@autumn/shared";
|
import { AppEnv } from "@autumn/shared";
|
||||||
import { clearOrg, setupOrg } from "tests/utils/setup.js";
|
import { clearOrg, setupOrg } from "tests/utils/setup.js";
|
||||||
|
import { initDrizzle } from "@/db/initDrizzle.js";
|
||||||
import {
|
import {
|
||||||
features,
|
|
||||||
products,
|
|
||||||
creditSystems,
|
|
||||||
advanceProducts,
|
advanceProducts,
|
||||||
attachProducts,
|
attachProducts,
|
||||||
rewards,
|
creditSystems,
|
||||||
oneTimeProducts,
|
|
||||||
entityProducts,
|
entityProducts,
|
||||||
|
features,
|
||||||
|
oneTimeProducts,
|
||||||
|
products,
|
||||||
referralPrograms,
|
referralPrograms,
|
||||||
|
rewards,
|
||||||
} from "./global.js";
|
} from "./global.js";
|
||||||
import { initDrizzle } from "@/db/initDrizzle.js";
|
|
||||||
|
|
||||||
const ORG_SLUG = process.env.TESTS_ORG!;
|
const ORG_SLUG = process.env.TESTS_ORG!;
|
||||||
const DEFAULT_ENV = AppEnv.Sandbox;
|
const DEFAULT_ENV = AppEnv.Sandbox;
|
||||||
|
|
||||||
import { Hyperbrowser } from "@hyperbrowser/sdk";
|
|
||||||
const hyperbrowser = new Hyperbrowser({
|
|
||||||
apiKey: process.env.HYPERBROWSER_API_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Initialize org for tests", () => {
|
describe("Initialize org for tests", () => {
|
||||||
it("should initialize org", async function () {
|
it("should initialize org", async function () {
|
||||||
this.timeout(1000000000);
|
this.timeout(1000000000);
|
||||||
this.org = await clearOrg({ orgSlug: ORG_SLUG, env: DEFAULT_ENV });
|
this.org = await clearOrg({ orgSlug: ORG_SLUG, env: DEFAULT_ENV });
|
||||||
this.env = DEFAULT_ENV;
|
this.env = DEFAULT_ENV;
|
||||||
let { db, client } = initDrizzle();
|
const { db, client } = initDrizzle();
|
||||||
|
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|||||||
@@ -13,12 +13,6 @@ import { OrgService } from "@/internal/orgs/OrgService.js";
|
|||||||
const ORG_SLUG = process.env.TESTS_ORG!;
|
const ORG_SLUG = process.env.TESTS_ORG!;
|
||||||
const DEFAULT_ENV = AppEnv.Sandbox;
|
const DEFAULT_ENV = AppEnv.Sandbox;
|
||||||
|
|
||||||
import { Hyperbrowser } from "@hyperbrowser/sdk";
|
|
||||||
|
|
||||||
const hyperbrowser = new Hyperbrowser({
|
|
||||||
apiKey: process.env.HYPERBROWSER_API_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setupBefore = async (instance: any) => {
|
export const setupBefore = async (instance: any) => {
|
||||||
try {
|
try {
|
||||||
const { db, client } = initDrizzle();
|
const { db, client } = initDrizzle();
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"import": "./dist/index.js"
|
"import": "./dist/index.js",
|
||||||
|
"default": "./dist/index.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"author": "Recase Inc.",
|
"author": "Recase Inc.",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import {
|
||||||
@@ -9,8 +11,6 @@ import { useOrg } from "@/hooks/common/useOrg";
|
|||||||
import { authClient, useListOrganizations } from "@/lib/auth-client";
|
import { authClient, useListOrganizations } from "@/lib/auth-client";
|
||||||
import { useAxiosInstance } from "@/services/useAxiosInstance";
|
import { useAxiosInstance } from "@/services/useAxiosInstance";
|
||||||
import { getBackendErr } from "@/utils/genUtils";
|
import { getBackendErr } from "@/utils/genUtils";
|
||||||
import { useState } from "react";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
import { useMemberships } from "../hooks/useMemberships";
|
import { useMemberships } from "../hooks/useMemberships";
|
||||||
|
|
||||||
export const DeleteOrgPopover = () => {
|
export const DeleteOrgPopover = () => {
|
||||||
@@ -64,6 +64,7 @@ export const DeleteOrgPopover = () => {
|
|||||||
try {
|
try {
|
||||||
await deleteOrg();
|
await deleteOrg();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log("Error:", error);
|
||||||
toast.error(getBackendErr(error, "Failed to delete org"));
|
toast.error(getBackendErr(error, "Failed to delete org"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user