diff --git a/bun.lock b/bun.lock index 184b17866..ed0e97eec 100644 --- a/bun.lock +++ b/bun.lock @@ -5,17 +5,17 @@ "name": "autumn", "dependencies": { "@wooorm/starry-night": "^3.8.0", - "ag-charts-react": "^12.0.1", - "chalk": "^5.3.0", - "drizzle-kit": "^0.31.1", + "ag-charts-react": "^12.3.0", + "chalk": "^5.6.2", + "drizzle-kit": "^0.31.5", "tailwind-scrollbar-hide": "^4.0.0", }, "devDependencies": { - "@biomejs/biome": "^2.2.4", - "@types/node": "^24.0.3", - "concurrently": "^9.1.2", - "dotenv": "^16.5.0", - "inquirer": "^12.6.3", + "@biomejs/biome": "^2.2.7", + "@types/node": "^24.9.1", + "concurrently": "^9.2.1", + "dotenv": "^16.6.1", + "inquirer": "^12.10.0", }, }, "scripts": { @@ -26,6 +26,8 @@ "chalk": "^5.3.0", "dotenv": "^16.5.0", "inquirer": "^12.6.3", + "ora": "^9.0.0", + "p-limit": "^7.2.0", }, "devDependencies": { "@types/inquirer": "^9.0.7", @@ -1584,7 +1586,7 @@ "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], - "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + "cli-spinners": ["cli-spinners@3.3.0", "", {}, "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ=="], "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], @@ -2096,7 +2098,7 @@ "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - "is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], @@ -2216,7 +2218,7 @@ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], + "log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], "logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="], @@ -2370,9 +2372,9 @@ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], + "ora": ["ora@9.0.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", "string-width": "^8.1.0", "strip-ansi": "^7.1.2" } }, "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A=="], - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + "p-limit": ["p-limit@7.2.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-ATHLtwoTNDloHRFFxFJdHnG6n2WUeFjaR8XQMFdKIv0xkXjrER8/iG9iu265jOM95zXHAfv9oTkqhrfbIzosrQ=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], @@ -2708,7 +2710,7 @@ "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2922,7 +2924,7 @@ "yn": ["yn@3.1.1", "", {}, "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="], - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], @@ -3022,6 +3024,8 @@ "@hyperdx/node-opentelemetry/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg=="], + "@hyperdx/node-opentelemetry/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + "@hyperdx/node-opentelemetry/ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], "@inquirer/external-editor/iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="], @@ -3422,6 +3426,8 @@ "bun-types/@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="], + "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -3454,8 +3460,6 @@ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "figures/is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], - "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -3464,10 +3468,10 @@ "html-minifier-terser/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], - "log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "mocha/log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], "nodemon/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], @@ -3478,11 +3482,7 @@ "openai/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], - "ora/is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], - - "ora/log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], - - "ora/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "pino/pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="], @@ -3504,7 +3504,7 @@ "react-email/glob": ["glob@11.0.3", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="], - "react-email/log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], + "react-email/ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], "react-promise-suspense/fast-deep-equal": ["fast-deep-equal@2.0.1", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="], @@ -3514,6 +3514,8 @@ "recaseai/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "recaseai/ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], + "renderkid/htmlparser2": ["htmlparser2@6.1.0", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", "domutils": "^2.5.2", "entities": "^2.0.0" } }, "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A=="], "renderkid/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -3532,8 +3534,6 @@ "socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], - "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -3554,10 +3554,16 @@ "winston-transport/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "@anthropic-ai/sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@autumn/vite/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], @@ -3742,6 +3748,10 @@ "@hyperdx/node-opentelemetry/ora/is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="], + "@hyperdx/node-opentelemetry/ora/is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], + + "@hyperdx/node-opentelemetry/ora/log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], + "@hyperdx/node-opentelemetry/ora/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -3978,7 +3988,9 @@ "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "mocha/log-symbols/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "mocha/log-symbols/is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], "next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], @@ -3992,9 +4004,7 @@ "openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "ora/log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], - - "ora/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "prebuild-install/tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], @@ -4008,10 +4018,20 @@ "react-email/glob/path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], - "react-email/log-symbols/is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + "react-email/ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "react-email/ora/log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], + + "react-email/ora/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "recaseai/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "recaseai/ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "recaseai/ora/log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], + + "recaseai/ora/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "renderkid/htmlparser2/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], "renderkid/htmlparser2/domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], @@ -4024,8 +4044,6 @@ "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "tsc-alias/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "tsc-alias/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], @@ -4036,6 +4054,8 @@ "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@hyperdx/node-opentelemetry/@opentelemetry/auto-instrumentations-node/@opentelemetry/instrumentation-aws-lambda/@types/aws-lambda": ["@types/aws-lambda@8.10.147", "", {}, "sha512-nD0Z9fNIZcxYX5Mai2CTmFD7wX7UldCkW2ezCF8D1T5hdiLsnTWDGRpfRYntU6VjTdLQjOvyszru7I1c1oCQew=="], "@hyperdx/node-opentelemetry/@opentelemetry/auto-instrumentations-node/@opentelemetry/instrumentation-aws-sdk/@opentelemetry/propagation-utils": ["@opentelemetry/propagation-utils@0.30.16", "", { "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-ZVQ3Z/PQ+2GQlrBfbMMMT0U7MzvYZLCPP800+ooyaBqm4hMvuQHfP028gB9/db0mwkmyEAMad9houukUVxhwcw=="], @@ -4112,16 +4132,28 @@ "css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], + "mocha/log-symbols/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "nodemon/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "prebuild-install/tar-fs/tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "react-email/glob/path-scurry/lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="], + "react-email/ora/log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + + "react-email/ora/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "recaseai/ora/log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + + "recaseai/ora/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "renderkid/htmlparser2/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], "tsc-alias/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@hyperdx/node-opentelemetry/@opentelemetry/auto-instrumentations-node/@opentelemetry/instrumentation-mysql/@types/mysql/@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="], "@hyperdx/node-opentelemetry/@opentelemetry/auto-instrumentations-node/@opentelemetry/instrumentation-pg/@types/pg/@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="], diff --git a/scripts/package.json b/scripts/package.json index 2da9851be..cfe6dd785 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -11,7 +11,9 @@ "@autumn/shared": "workspace:*", "chalk": "^5.3.0", "dotenv": "^16.5.0", - "inquirer": "^12.6.3" + "inquirer": "^12.6.3", + "ora": "^9.0.0", + "p-limit": "^7.2.0" }, "devDependencies": { "@types/inquirer": "^9.0.7", diff --git a/scripts/testGroups/README.md b/scripts/testGroups/README.md new file mode 100644 index 000000000..27b415c6c --- /dev/null +++ b/scripts/testGroups/README.md @@ -0,0 +1,133 @@ +# Test Groups + +Organized test suites for the Autumn test framework. + +## Usage + +From the project root: + +```bash +# Run a specific test group +./scripts/testGroups/g1.sh + +# Run with setup +./scripts/testGroups/g1.sh setup +``` + +From the server directory (legacy): + +```bash +./run.sh /path/to/test/group/script.sh +``` + +## Test Groups + +### G1: Upgrade & Downgrade Tests ✅ (Migrated to Bun) +**Status:** Fully migrated to Bun + TypeScript runner +**Tests:** +- `server/tests/check/basic/*.test.ts` +- `server/tests/attach/basic/*.test.ts` +- `server/tests/attach/upgrade/*.test.ts` +- `server/tests/attach/downgrade/*.test.ts` +- `server/tests/attach/free/*.test.ts` +- `server/tests/attach/addOn/*.test.ts` +- `server/tests/attach/entities/*.test.ts` +- `server/tests/attach/checkout/*.test.ts` + +**Features:** +- Live spinner showing current test +- Beautiful success/failure indicators +- Concise error reports +- **Compact mode** for large test suites (reduces screen overflow) + +### G2: Migrations, Versions & Others ⏳ (Mocha) +**Status:** Uses Mocha (pending migration) +**Tests:** +- Migrations +- Version updates +- Prepaid features +- Interval upgrades + +### G3: Continuous Use Tests ⏳ (Mocha) +**Status:** Uses Mocha (pending migration) +**Tests:** +- Entity management +- Usage tracking +- Updates +- Roles + +### G4: Merged & Core Tests ⏳ (Mocha) +**Status:** Uses Mocha (pending migration) +**Tests:** +- Merged subscriptions +- Core cancellation +- Multi-attach scenarios + +### G5: Advanced Features ⏳ (Mocha) +**Status:** Uses Mocha (pending migration) +**Tests:** +- Multi-feature +- Coupons +- Referrals +- Rollovers +- Usage limits + +### G6: Alex Integration Tests ⏳ (Mocha) +**Status:** Uses Mocha (pending migration) +**Tests:** +- End-to-end scenarios +- Product switching +- Topups + +## Migration Progress + +- ✅ Test runner built (TypeScript + Bun) +- ✅ G1 tests migrated to Bun +- ⏳ G2-G6 pending migration + +## Compact Mode 📦 + +When running many tests (dozens of test files), the standard output can overflow your terminal screen. Use compact mode to see only: +- Recently completed tests (last 3 with tick marks) +- Failed tests summary (updated in real-time) +- Currently running tests (up to 6) with their active test case +- A single line with test stats (progress, passed, failed) +- Full error details at the end + +### Usage + +**In shell scripts (recommended):** +```bash +# Use BUN_PARALLEL_COMPACT instead of BUN_PARALLEL +BUN_PARALLEL_COMPACT \ + 'server/tests/check/basic' \ + 'server/tests/attach/basic' +``` + +**Direct command line:** +```bash +# Add --compact flag +bun scripts/testScripts/runTests.ts server/tests/attach/upgrade --compact + +# Combine with other options +bun scripts/testScripts/runTests.ts server/tests/attach/upgrade --compact --max=10 +``` + +### When to Use Compact Mode + +- ✅ Running 20+ test files (like in g1.sh) +- ✅ CI/CD pipelines with limited scrollback +- ✅ When you only care about failures +- ❌ Debugging specific tests (use full mode to see progress) +- ❌ Running < 10 test files + +## Adding New Test Groups + +1. Create a new script in `scripts/testGroups/` +2. Use the TypeScript runner for new tests: + ```bash + bun scripts/testScripts/runTests.ts server/tests/your/tests + ``` +3. For large test suites, use `BUN_PARALLEL_COMPACT` instead of `BUN_PARALLEL` +4. Update this README with the test group details + diff --git a/scripts/testGroups/config.sh b/scripts/testGroups/config.sh new file mode 100755 index 000000000..a6141c57d --- /dev/null +++ b/scripts/testGroups/config.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Shared configuration for test groups + +# Get project root directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +SERVER_DIR="$PROJECT_ROOT/server" + +# Find bun executable (check common locations) +if command -v bun &> /dev/null; then + BUN_CMD="bun" +elif [ -f "$HOME/.bun/bin/bun" ]; then + BUN_CMD="$HOME/.bun/bin/bun" +elif [ -f "/usr/local/bin/bun" ]; then + BUN_CMD="/usr/local/bin/bun" +else + echo "Error: bun not found. Please install bun or add it to PATH." + exit 1 +fi + +# Test runner function +BUN_PARALLEL() { + cd "$PROJECT_ROOT" && $BUN_CMD scripts/testScripts/runTests.ts "$@" +} + +# Test runner with compact mode (recommended for many tests) +BUN_PARALLEL_COMPACT() { + cd "$PROJECT_ROOT" && $BUN_CMD scripts/testScripts/runTests.ts "$@" --compact +} + +# Setup function +BUN_SETUP() { + cd "$SERVER_DIR" && $BUN_CMD tests/setupMain.ts +} + +# Mocha function (for tests not yet migrated) +MOCHA_CMD() { + cd "$SERVER_DIR" && npx mocha --parallel -j 6 --timeout 10000000 --ignore tests/00_setup.ts "$@" +} + diff --git a/scripts/testGroups/g1.sh b/scripts/testGroups/g1.sh new file mode 100755 index 000000000..02b2ce875 --- /dev/null +++ b/scripts/testGroups/g1.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Test Group 1: Upgrade & Downgrade Tests +# Description: Tests for product upgrades and downgrades + +# Source shared configuration +source "$(dirname "$0")/config.sh" + +# Setup if requested +if [[ "$1" == *"setup"* ]]; then + echo "Running test setup..." + BUN_SETUP +fi + +# Run tests using TypeScript runner with compact mode +# Adjust --max to control concurrency (default: 6) +BUN_PARALLEL_COMPACT \ + 'server/tests/check/basic' \ + 'server/tests/attach/basic' \ + 'server/tests/attach/upgrade' \ + 'server/tests/attach/downgrade' \ + 'server/tests/attach/free' \ + 'server/tests/attach/addOn' \ + 'server/tests/attach/entities' \ + 'server/tests/attach/checkout' \ + --max=6 \ + + + diff --git a/scripts/testGroups/g2.sh b/scripts/testGroups/g2.sh new file mode 100755 index 000000000..437d60e6a --- /dev/null +++ b/scripts/testGroups/g2.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Test Group 2: Migrations, Versions & Others +# Description: Tests for migrations, version updates, and miscellaneous features + +# Source shared configuration +source "$(dirname "$0")/config.sh" + +# Setup if requested +if [[ "$1" == *"setup"* ]]; then + echo "Running test setup..." + BUN_SETUP +fi + +# These tests still use Mocha - will be migrated later + +MOCHA_CMD \ +'tests/attach/migrations/*.ts' \ +'tests/attach/newVersion/*.ts' \ +'tests/attach/upgradeOld/*.ts' \ +'tests/attach/others/*.ts' \ +'tests/attach/updateEnts/*.ts' \ +'tests/advanced/check/*.ts' + +MOCHA_CMD 'tests/attach/prepaid/*.ts' \ +'tests/interval/upgrade/*.ts' \ +'tests/interval/multiSub/*.ts' + diff --git a/scripts/testGroups/g3.sh b/scripts/testGroups/g3.sh new file mode 100755 index 000000000..1b8545c7f --- /dev/null +++ b/scripts/testGroups/g3.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Test Group 3: Continuous Use Tests +# Description: Tests for continuous usage tracking, entities, and roles + +# Source shared configuration +source "$(dirname "$0")/config.sh" + +# Setup if requested +if [[ "$1" == *"setup"* ]]; then + echo "Running test setup..." + BUN_SETUP +fi + +# These tests still use Mocha - will be migrated later + +MOCHA_CMD 'tests/contUse/entities/*.ts' +MOCHA_CMD 'tests/contUse/update/*.ts' +MOCHA_CMD 'tests/contUse/track/*.ts' +MOCHA_CMD 'tests/contUse/roles/*.ts' + diff --git a/scripts/testGroups/g4.sh b/scripts/testGroups/g4.sh new file mode 100755 index 000000000..0c74e1173 --- /dev/null +++ b/scripts/testGroups/g4.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Test Group 4: Merged & Core Tests +# Description: Tests for merged subscriptions and core functionality + +# Source shared configuration +source "$(dirname "$0")/config.sh" + +# Setup if requested +if [[ "$1" == *"setup"* ]]; then + echo "Running test setup..." + BUN_SETUP +fi + +# These tests still use Mocha - will be migrated later + +MOCHA_CMD 'tests/merged/group/*.ts' + +MOCHA_CMD 'tests/merged/add/*.ts' \ +'tests/merged/downgrade/*.ts' \ +'tests/merged/prepaid/*.ts' \ +'tests/merged/separate/*.ts' \ +'tests/merged/upgrade/*.ts' \ +'tests/merged/trial/*.ts' + +MOCHA_CMD 'tests/merged/addOn/*.ts' \ +'tests/core/cancel/*.ts' \ +'tests/core/multiAttach/*.ts' \ +'tests/core/multiAttach/multiInvoice/*.ts' \ +'tests/core/multiAttach/multiUpgrade/*.ts' + diff --git a/scripts/testGroups/g5.sh b/scripts/testGroups/g5.sh new file mode 100755 index 000000000..405f6d9cd --- /dev/null +++ b/scripts/testGroups/g5.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Test Group 5: Advanced Features +# Description: Tests for advanced features like coupons, referrals, usage limits + +# Source shared configuration +source "$(dirname "$0")/config.sh" + +# Setup if requested +if [[ "$1" == *"setup"* ]]; then + echo "Running test setup..." + BUN_SETUP +fi + +# These tests still use Mocha - will be migrated later + +MOCHA_CMD 'tests/advanced/multiFeature/*.ts' \ + 'tests/advanced/coupons/*.ts' \ + 'tests/attach/updateQuantity/*.ts' \ + 'tests/advanced/referrals/*.ts' \ + 'tests/advanced/referrals/paid/*.ts' \ + 'tests/advanced/rollovers/*.ts' \ + 'tests/advanced/customInterval/*.ts' + +MOCHA_CMD 'tests/attach/multiProduct/*.ts' \ + 'tests/advanced/usageLimit/*.ts' + +MOCHA_CMD 'tests/advanced/usage/*.ts' + diff --git a/scripts/testGroups/g6.sh b/scripts/testGroups/g6.sh new file mode 100755 index 000000000..7ab049ba7 --- /dev/null +++ b/scripts/testGroups/g6.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Test Group 6: Alex Tests +# Description: Integration tests for Alex scenarios + +# Source shared configuration +source "$(dirname "$0")/config.sh" + +# Setup if requested +if [[ "$1" == *"setup"* ]]; then + echo "Running test setup..." + BUN_SETUP +fi + +# These tests still use Mocha - will be migrated later +cd "$SERVER_DIR" + +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' + diff --git a/scripts/testScripts/runTests.ts b/scripts/testScripts/runTests.ts new file mode 100755 index 000000000..e85a1771a --- /dev/null +++ b/scripts/testScripts/runTests.ts @@ -0,0 +1,672 @@ +#!/usr/bin/env bun + +import { spawn } from "bun"; +import chalk from "chalk"; +import { readdir } from "fs/promises"; +import pLimit from "p-limit"; +import { basename, resolve } from "path"; + +interface TestResult { + file: string; + status: "pending" | "running" | "passed" | "failed"; + output: string; + duration: number; + error?: string; + lastTestName?: string; +} + +class TestRunner { + private results: Map = new Map(); + private testFiles: string[] = []; + private maxParallel: number = 6; + private spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; + private spinnerIndex = 0; + private renderInterval?: Timer; + private startLine = 0; + private compactMode: boolean = false; + private lastRenderedLines = 0; + + constructor(maxParallel?: number, compactMode?: boolean) { + if (maxParallel) this.maxParallel = maxParallel; + if (compactMode) this.compactMode = compactMode; + } + + async collectTestFiles(directories: string[]): Promise { + const testFiles: string[] = []; + + for (const dir of directories) { + const resolvedDir = resolve(process.cwd(), dir); + try { + const files = await readdir(resolvedDir); + for (const file of files) { + if (file.endsWith(".test.ts")) { + testFiles.push(resolve(resolvedDir, file)); + } + } + } catch (error) { + console.error(chalk.red(`Error reading directory ${dir}:`), error); + } + } + + return testFiles; + } + + private extractLastTest(output: string): string | null { + const lines = output.split("\n"); + + for (let i = lines.length - 1; i >= 0; i--) { + const line = lines[i].trim(); + + const testMatch = line.match(/^[✓✗]\s+(.+?)(?:\s+\[\d+\.\d+m?s\])?$/); + if (testMatch) { + return testMatch[1]; + } + + const bunTestMatch = line.match(/test\s+"([^"]+)"/); + if (bunTestMatch) { + return bunTestMatch[1]; + } + } + + return null; + } + + private truncateTestName(name: string, maxLength: number = 50): string { + if (name.length <= maxLength) return name; + return name.substring(0, maxLength - 3) + "..."; + } + + private hideCursor() { + process.stdout.write("\x1B[?25l"); + } + + private showCursor() { + process.stdout.write("\x1B[?25h"); + } + + private moveCursor(line: number, col: number = 0) { + process.stdout.write(`\x1B[${line};${col}H`); + } + + private clearLine() { + process.stdout.write("\x1B[2K"); + } + + private getSpacesNeeded(): number { + if (!this.compactMode) { + return this.testFiles.length + 3; + } + + // Compact mode: dynamically calculate based on content + // Base: 10 lines for headers, stats, spacing + // + 3 lines for recently completed + // + failed tests * 4 (name + 2 error lines + spacing) + // + running tests + const failedCount = Array.from(this.results.values()).filter( + (r) => r.status === "failed", + ).length; + const runningCount = Array.from(this.results.values()).filter( + (r) => r.status === "running", + ).length; + + return Math.min( + 10 + 3 + failedCount * 4 + Math.min(runningCount, 6), + 30, // Cap at 30 lines + ); + } + + private render() { + this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length; + const spinner = this.spinnerFrames[this.spinnerIndex]; + + if (this.compactMode) { + // Compact mode: show completed, failed, running tests, then stats + let lineNum = this.startLine; + + const completed = Array.from(this.results.values()).filter( + (r) => r.status === "passed" || r.status === "failed", + ).length; + const passed = Array.from(this.results.values()).filter( + (r) => r.status === "passed", + ).length; + const failed = Array.from(this.results.values()).filter( + (r) => r.status === "failed", + ).length; + + // Show recently completed tests (last 3) + const passedTests = Array.from(this.results.entries()) + .filter(([_, result]) => result.status === "passed") + .slice(-3); // Get last 3 completed + + if (passedTests.length > 0) { + this.moveCursor(lineNum, 0); + this.clearLine(); + process.stdout.write( + chalk.green.bold(`Recently Completed (${passed} total):\n`), + ); + lineNum++; + + for (const [file] of passedTests) { + this.moveCursor(lineNum, 0); + this.clearLine(); + const testName = basename(file); + process.stdout.write( + ` ${chalk.green("✓")} ${chalk.dim(testName)}\n`, + ); + lineNum++; + } + + // Add blank line + this.moveCursor(lineNum, 0); + this.clearLine(); + process.stdout.write("\n"); + lineNum++; + } + + // Show failed tests + const failedTests = Array.from(this.results.entries()).filter( + ([_, result]) => result.status === "failed", + ); + + if (failedTests.length > 0) { + this.moveCursor(lineNum, 0); + this.clearLine(); + process.stdout.write( + chalk.red.bold(`Failed (${failedTests.length}):\n`), + ); + lineNum++; + + for (const [file, result] of failedTests) { + this.moveCursor(lineNum, 0); + this.clearLine(); + const testName = basename(file); + process.stdout.write(` ${chalk.red("✗")} ${testName}\n`); + lineNum++; + + // Show first 2 lines of error + if (result.error) { + const errorLines = result.error.split("\n").filter((l) => l.trim()); + const displayLines = errorLines.slice(0, 2); + for (const line of displayLines) { + this.moveCursor(lineNum, 0); + this.clearLine(); + const truncated = + line.length > 80 ? line.substring(0, 77) + "..." : line; + process.stdout.write(` ${chalk.dim(truncated)}\n`); + lineNum++; + } + } + } + + // Add blank line after failed tests + this.moveCursor(lineNum, 0); + this.clearLine(); + process.stdout.write("\n"); + lineNum++; + } + + // Show currently running tests + const runningTests = Array.from(this.results.entries()).filter( + ([_, result]) => result.status === "running", + ); + + if (runningTests.length > 0) { + this.moveCursor(lineNum, 0); + this.clearLine(); + process.stdout.write( + chalk.cyan.bold(`Running (${runningTests.length}):\n`), + ); + lineNum++; + + for (const [file, result] of runningTests) { + this.moveCursor(lineNum, 0); + this.clearLine(); + const testName = basename(file); + let displayText = ` ${chalk.cyan(spinner)} ${testName}`; + if (result.lastTestName) { + const truncated = this.truncateTestName(result.lastTestName, 40); + displayText += chalk.dim(` › ${truncated}`); + } + process.stdout.write(`${displayText}\n`); + lineNum++; + } + + // Add blank line after running tests + this.moveCursor(lineNum, 0); + this.clearLine(); + process.stdout.write("\n"); + lineNum++; + } + + // Show stats line + this.moveCursor(lineNum, 0); + this.clearLine(); + process.stdout.write( + `${chalk.cyan(spinner)} Progress: ${chalk.bold(`${completed}/${this.testFiles.length}`)} | ` + + `${chalk.green(`✓ ${passed}`)} | ` + + `${failed > 0 ? chalk.red(`✗ ${failed}`) : chalk.dim(`✗ ${failed}`)}\n`, + ); + lineNum++; + + // Clear any remaining lines from previous renders + const maxLines = this.getSpacesNeeded(); + while (lineNum < maxLines) { + this.moveCursor(lineNum, 0); + this.clearLine(); + lineNum++; + } + + // Track how many lines we actually used + this.lastRenderedLines = lineNum - this.startLine; + } else { + // Full mode: show all tests + let lineNum = this.startLine; + + for (const file of this.testFiles) { + const result = this.results.get(file); + if (!result) continue; + + this.moveCursor(lineNum, 0); + this.clearLine(); + + const testName = basename(file); + let statusIcon: string; + let displayText: string; + + switch (result.status) { + case "pending": + statusIcon = chalk.dim("⋯"); + displayText = chalk.dim(testName); + break; + case "running": + statusIcon = chalk.cyan(spinner); + displayText = testName; + if (result.lastTestName) { + const truncated = this.truncateTestName(result.lastTestName); + displayText += chalk.dim(` › ${truncated}`); + } + break; + case "passed": + statusIcon = chalk.green("✓"); + displayText = chalk.dim(testName); + break; + case "failed": + statusIcon = chalk.red("✗"); + displayText = testName; + break; + } + + process.stdout.write(`${statusIcon} ${displayText}\n`); + lineNum++; + } + + // Summary line + const completed = Array.from(this.results.values()).filter( + (r) => r.status === "passed" || r.status === "failed", + ).length; + const failed = Array.from(this.results.values()).filter( + (r) => r.status === "failed", + ).length; + const running = Array.from(this.results.values()).filter( + (r) => r.status === "running", + ).length; + + this.moveCursor(lineNum + 1, 0); + this.clearLine(); + if (running > 0) { + process.stdout.write( + chalk.dim( + `Running: ${running} | Completed: ${completed}/${this.testFiles.length} | Failed: ${failed}`, + ), + ); + } + + // Track how many lines we actually used + this.lastRenderedLines = lineNum + 2 - this.startLine; + } + } + + async runTest(file: string): Promise { + const startTime = performance.now(); + + // Initialize as running + const result: TestResult = { + file, + status: "running", + output: "", + duration: 0, + }; + this.results.set(file, result); + + try { + const proc = spawn(["bun", "test", "--timeout", "0", file], { + stdout: "pipe", + stderr: "pipe", + }); + + let output = ""; + const decoder = new TextDecoder(); + + if (proc.stdout) { + for await (const chunk of proc.stdout) { + const text = decoder.decode(chunk); + output += text; + + // Update last test name + const lastTest = this.extractLastTest(output); + if (lastTest) { + result.lastTestName = lastTest; + result.output = output; + this.results.set(file, result); + } + } + } + + if (proc.stderr) { + for await (const chunk of proc.stderr) { + output += decoder.decode(chunk); + } + } + + await proc.exited; + const duration = performance.now() - startTime; + + if (proc.exitCode === 0) { + this.results.set(file, { + ...result, + status: "passed", + output, + duration, + }); + } else { + this.results.set(file, { + ...result, + status: "failed", + output, + duration, + error: this.extractError(output), + }); + } + } catch (error) { + const duration = performance.now() - startTime; + this.results.set(file, { + ...result, + status: "failed", + output: "", + duration, + error: String(error), + }); + } + } + + private extractError(output: string): string { + const lines = output.split("\n"); + const errorLines: string[] = []; + let inError = false; + let capturedLines = 0; + + for (const line of lines) { + if ( + line.includes("error:") || + line.includes("Error:") || + line.includes("Expected:") || + line.includes("Received:") || + line.includes("AssertionError") + ) { + inError = true; + } + + if (inError) { + errorLines.push(line); + capturedLines++; + + if (capturedLines > 20) break; + } + + if (line.match(/^[\s]*✗/)) { + errorLines.push(line); + } + } + + return errorLines.length > 0 ? errorLines.join("\n").trim() : output; + } + + private cleanup() { + if (this.renderInterval) { + clearInterval(this.renderInterval); + } + this.showCursor(); + } + + private handleInterrupt() { + this.cleanup(); + + // Move cursor past all output (use actual rendered lines in compact mode) + const linesToMove = this.compactMode + ? this.lastRenderedLines + : this.getSpacesNeeded(); + process.stdout.write(`\x1B[${linesToMove}B`); + console.log("\n"); + + console.log(chalk.yellow.bold("\n⚠ Tests interrupted by user (Ctrl+C)\n")); + + // Print summary of what we have so far + const failedTests = Array.from(this.results.values()).filter( + (t) => t.status === "failed", + ); + const completedTests = Array.from(this.results.values()).filter( + (t) => t.status === "passed" || t.status === "failed", + ); + + console.log( + chalk.dim( + `Completed: ${completedTests.length}/${this.testFiles.length} tests before interruption`, + ), + ); + + if (failedTests.length > 0) { + console.log( + chalk.red.bold( + `\n${"═".repeat(70)}\n FAILED TESTS (${failedTests.length})\n${"═".repeat(70)}\n`, + ), + ); + + for (const test of failedTests) { + const testName = basename(test.file); + console.log(chalk.red.bold(`\n✗ ${testName}`)); + console.log(chalk.dim("─".repeat(70))); + + if (test.error) { + const errorLines = test.error.split("\n"); + for (const line of errorLines) { + if (line.trim()) { + if (line.includes("Expected:") || line.includes("Received:")) { + console.log(chalk.yellow(line)); + } else if (line.includes("✗")) { + console.log(chalk.red(line)); + } else { + console.log(chalk.dim(line)); + } + } + } + } + } + + console.log( + chalk.red.bold( + `\n${"═".repeat(70)}\n ${failedTests.length} test file(s) failed\n${"═".repeat(70)}\n`, + ), + ); + } + + process.exit(130); // Standard exit code for SIGINT + } + + async run(directories: string[]): Promise { + this.testFiles = await this.collectTestFiles(directories); + + if (this.testFiles.length === 0) { + console.log(chalk.yellow("No test files found in specified directories")); + return; + } + + console.log( + chalk.bold(`\nRunning ${this.testFiles.length} test file(s)...\n`), + ); + + // Initialize all tests as pending + for (const file of this.testFiles) { + this.results.set(file, { + file, + status: "pending", + output: "", + duration: 0, + }); + } + + // Setup SIGINT handler + const sigintHandler = () => this.handleInterrupt(); + process.on("SIGINT", sigintHandler); + + // Hide cursor and create space for all tests + this.hideCursor(); + this.startLine = 1; // Start from line 1 + + // Create space - less space needed in compact mode + const spacesNeeded = this.getSpacesNeeded(); + this.lastRenderedLines = spacesNeeded; // Initialize to full space + for (let i = 0; i < spacesNeeded; i++) { + console.log(); + } + + // Move cursor back up to start rendering + process.stdout.write(`\x1B[${spacesNeeded}A`); + + // Start rendering loop + this.renderInterval = setInterval(() => this.render(), 100); + + // Run tests with concurrency limit + const limit = pLimit(this.maxParallel); + const promises = this.testFiles.map((file) => + limit(() => this.runTest(file)), + ); + + await Promise.all(promises); + + // Remove SIGINT handler + process.off("SIGINT", sigintHandler); + + // Final render + this.cleanup(); + this.render(); + + // Move cursor past all output (use actual rendered lines in compact mode) + const linesToMove = this.compactMode + ? this.lastRenderedLines + : this.getSpacesNeeded(); + process.stdout.write(`\x1B[${linesToMove}B`); + console.log("\n"); + + // Print summary + this.printSummary(); + } + + private printSummary() { + const failedTests = Array.from(this.results.values()).filter( + (t) => t.status === "failed", + ); + + if (failedTests.length === 0) { + console.log( + chalk.green.bold(`✓ All ${this.testFiles.length} test file(s) passed!`), + ); + process.exit(0); + } + + console.log( + chalk.red.bold( + `\n${"═".repeat(70)}\n FAILED TESTS (${failedTests.length}/${this.testFiles.length})\n${"═".repeat(70)}\n`, + ), + ); + + for (const test of failedTests) { + const testName = basename(test.file); + console.log(chalk.red.bold(`\n✗ ${testName}`)); + console.log(chalk.dim("─".repeat(70))); + + if (test.error) { + const errorLines = test.error.split("\n"); + for (const line of errorLines) { + if (line.trim()) { + if (line.includes("Expected:") || line.includes("Received:")) { + console.log(chalk.yellow(line)); + } else if (line.includes("✗")) { + console.log(chalk.red(line)); + } else { + console.log(chalk.dim(line)); + } + } + } + } + } + + console.log( + chalk.red.bold( + `\n${"═".repeat(70)}\n ${failedTests.length} test file(s) failed\n${"═".repeat(70)}\n`, + ), + ); + process.exit(1); + } +} + +// Parse CLI arguments +const args = process.argv.slice(2); +const directories: string[] = []; +let maxParallel = 6; +let compactMode = false; + +for (const arg of args) { + if (arg.startsWith("--max=")) { + maxParallel = Number.parseInt(arg.split("=")[1], 10); + } else if (arg === "--compact") { + compactMode = true; + } else if (arg.startsWith("-")) { + console.error(chalk.red(`Unknown option: ${arg}`)); + console.log( + "Usage: bun scripts/testScripts/runTests.ts [dir2] [...] [--max=N] [--compact]", + ); + process.exit(1); + } else { + directories.push(arg); + } +} + +if (directories.length === 0) { + console.error(chalk.red("Error: No test directories specified")); + console.log( + "Usage: bun scripts/testScripts/runTests.ts [dir2] [...] [--max=N] [--compact]", + ); + console.log("\nOptions:"); + console.log(" --max=N Set maximum parallel test files (default: 6)"); + console.log( + " --compact Use compact mode (only show summary and failures)", + ); + console.log("\nExamples:"); + console.log( + " bun scripts/testScripts/runTests.ts server/tests/attach/upgrade", + ); + console.log( + " bun scripts/testScripts/runTests.ts server/tests/attach/upgrade server/tests/attach/downgrade", + ); + console.log( + " bun scripts/testScripts/runTests.ts server/tests/attach/upgrade --max=10", + ); + console.log( + " bun scripts/testScripts/runTests.ts server/tests/attach/upgrade --compact", + ); + process.exit(1); +} + +// Run tests +const runner = new TestRunner(maxParallel, compactMode); +await runner.run(directories); diff --git a/server/shell/config.sh b/server/shell/config.sh index 639703e83..92eff5806 100755 --- a/server/shell/config.sh +++ b/server/shell/config.sh @@ -1,10 +1,33 @@ #!/bin/bash -BUN_SETUP="bun tests/setupMain.ts" -MOCHA_CMD="npx mocha --parallel -j 6 --timeout 10000000 --ignore tests/00_setup.ts" - -BUN_CMD="bun test --concurrent" - -# Get the directory where this script is located +# Get project root directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -BUN_PARALLEL="$SCRIPT_DIR/run-parallel.sh" \ No newline at end of file +SERVER_DIR="$SCRIPT_DIR/.." +PROJECT_ROOT="$SERVER_DIR/.." + +# Find bun executable +if command -v bun &> /dev/null; then + BUN_CMD="bun" +elif [ -f "$HOME/.bun/bin/bun" ]; then + BUN_CMD="$HOME/.bun/bin/bun" +elif [ -f "/usr/local/bin/bun" ]; then + BUN_CMD="/usr/local/bin/bun" +else + echo "Error: bun not found. Please install bun or add it to PATH." + exit 1 +fi + +# Setup function +BUN_SETUP="$BUN_CMD tests/setupMain.ts" + +# Test runner functions (using new TypeScript runner) +BUN_PARALLEL() { + cd "$PROJECT_ROOT" && $BUN_CMD scripts/testScripts/runTests.ts "$@" +} + +BUN_PARALLEL_COMPACT() { + cd "$PROJECT_ROOT" && $BUN_CMD scripts/testScripts/runTests.ts "$@" --compact +} + +# Mocha command (for tests not yet migrated) +MOCHA_CMD="npx mocha --parallel -j 6 --timeout 10000000 --ignore tests/00_setup.ts" \ No newline at end of file diff --git a/server/shell/g1.sh b/server/shell/g1.sh index a211f1a64..77638166c 100755 --- a/server/shell/g1.sh +++ b/server/shell/g1.sh @@ -1,23 +1,26 @@ #!/bin/bash +# Test Group 1: Upgrade & Downgrade Tests +# Description: Tests for product upgrades and downgrades + # Source shared configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/config.sh" -# If contains setup then run $MOCHA_SETUP +# Setup if requested if [[ "$1" == *"setup"* ]]; then - MOCHA_PARALLEL=true $BUN_SETUP + echo "Running test setup..." + $BUN_SETUP fi -# Run parallel basic tests -$BUN_PARALLEL tests/attach/basic -$BUN_PARALLEL tests/check/basic - -# # TODO: Refactor these tests to use Bun -# $MOCHA_CMD 'tests/attach/upgrade/*.ts' 'tests/attach/downgrade/*.ts' - -# $MOCHA_CMD \ -# 'tests/attach/checkout/*.ts' \ -# 'tests/attach/entities/*.ts' \ -# 'tests/attach/free/*.ts'\ -# 'tests/attach/addOn/*.ts' \ No newline at end of file +# Run tests using TypeScript runner with compact mode +# Adjust --max to control concurrency (default: 6) +$BUN_PARALLEL_COMPACT \ + 'tests/check/basic' \ + 'tests/attach/basic' \ + 'tests/attach/upgrade' \ + 'tests/attach/downgrade' \ + 'tests/attach/free' \ + 'tests/attach/addOn' \ + 'tests/attach/entities' \ + 'tests/attach/checkout' \ No newline at end of file diff --git a/server/shell/run-parallel.sh b/server/shell/run-parallel.sh index 5f0487c40..ad50bbf14 100755 --- a/server/shell/run-parallel.sh +++ b/server/shell/run-parallel.sh @@ -1,25 +1,39 @@ -#!/bin/bash +#!/usr/bin/env bash # Run Bun test files in parallel with proper error reporting -# Usage: ./run-parallel.sh [max_parallel] +# Usage: ./run-parallel.sh [test_directory2] [...] [--max=N] -TEST_DIR="$1" -MAX_PARALLEL="${2:-6}" - -if [ -z "$TEST_DIR" ]; then - echo "Error: No test directory specified" - echo "Usage: ./run-parallel.sh [max_parallel]" +if [ $# -eq 0 ]; then + echo "Error: No test directories specified" + echo "Usage: ./run-parallel.sh [test_directory2] [...] [--max=N]" exit 1 fi -if [ ! -d "$TEST_DIR" ]; then - echo "Error: Directory '$TEST_DIR' not found" +# Parse arguments +TEST_DIRS=() +MAX_PARALLEL=6 + +for arg in "$@"; do + if [[ "$arg" == --max=* ]]; then + MAX_PARALLEL="${arg#*=}" + else + if [ ! -d "$arg" ]; then + echo "Error: Directory '$arg' not found" + exit 1 + fi + TEST_DIRS+=("$arg") + fi +done + +if [ ${#TEST_DIRS[@]} -eq 0 ]; then + echo "Error: No valid test directories specified" exit 1 fi TEMP_DIR=$(mktemp -d) FAILED_DIR="$TEMP_DIR/failed" -mkdir -p "$FAILED_DIR" +STATUS_DIR="$TEMP_DIR/status" +mkdir -p "$FAILED_DIR" "$STATUS_DIR" # Colors GREEN='\033[0;32m' @@ -28,21 +42,69 @@ YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' +DIM='\033[2m' NC='\033[0m' # No Color +# Spinner frames +SPINNER_FRAMES=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') +SPINNER_FRAME=0 +NUM_SPINNER_FRAMES=${#SPINNER_FRAMES[@]} + +# Collect all test files first +TEST_FILES=() +for TEST_DIR in "${TEST_DIRS[@]}"; do + for test_file in "$TEST_DIR"/*.test.ts; do + if [ -f "$test_file" ]; then + TEST_FILES+=("$test_file") + # Create status file + echo "pending" > "$STATUS_DIR/$(basename "$test_file").status" + fi + done +done + +# Check if any tests were found +if [ ${#TEST_FILES[@]} -eq 0 ]; then + echo "No test files found in specified directories" + rm -rf "$TEMP_DIR" + exit 0 +fi + +# Helper function to get status +get_status() { + local test_file="$1" + local test_name=$(basename "$test_file") + local status_file="$STATUS_DIR/${test_name}.status" + if [ -f "$status_file" ]; then + cat "$status_file" + else + echo "pending" + fi +} + +# Helper function to set status +set_status() { + local test_file="$1" + local status="$2" + local test_name=$(basename "$test_file") + echo "$status" > "$STATUS_DIR/${test_name}.status" +} + # Cleanup function cleanup() { - echo -e "\n${YELLOW}Stopping all tests...${NC}" + # Stop spinner + if [ ! -z "$SPINNER_PID" ]; then + kill $SPINNER_PID 2>/dev/null || true + wait $SPINNER_PID 2>/dev/null || true + fi - # Kill all descendant processes (including bun test processes) + # Kill all descendant processes pkill -P $$ 2>/dev/null || true - - # Also kill any bun test processes that might be running pkill -f "bun test" 2>/dev/null || true - - # Kill all background jobs jobs -p | while read pid; do kill -9 $pid 2>/dev/null || true; done + # Show cursor again + tput cnorm 2>/dev/null || true + # Clean up temp directory rm -rf "$TEMP_DIR" exit 130 @@ -51,36 +113,98 @@ cleanup() { # Set up signal handlers trap cleanup SIGINT SIGTERM EXIT -# Function to run a test and capture output +# Function to render the test list +render_tests() { + local line_num=1 + + # Save cursor position + tput sc 2>/dev/null || true + + for test_file in "${TEST_FILES[@]}"; do + local test_name=$(basename "$test_file") + local status=$(get_status "$test_file") + local display_name="${test_name}" + + # Move to the line + tput cup $((line_num - 1)) 0 2>/dev/null || true + + # Clear line + tput el 2>/dev/null || true + + case "$status" in + "pending") + echo -ne "${DIM}⋯${NC} ${DIM}${display_name}${NC}" + ;; + "running") + local frame_idx=$((SPINNER_FRAME % NUM_SPINNER_FRAMES)) + local spinner_char="${SPINNER_FRAMES[$frame_idx]}" + echo -ne "${CYAN}${spinner_char}${NC} ${display_name}" + ;; + "passed") + echo -ne "${GREEN}✓${NC} ${DIM}${display_name}${NC}" + ;; + "failed") + echo -ne "${RED}✗${NC} ${display_name}" + ;; + esac + + ((line_num++)) + done + + # Restore cursor position + tput rc 2>/dev/null || true +} + +# Function to update spinner animation +animate_spinner() { + while true; do + SPINNER_FRAME=$((SPINNER_FRAME + 1)) + render_tests + sleep 0.1 + done +} + +# Function to run a test run_test() { local test_file=$1 local test_name=$(basename "$test_file") local output_file="$TEMP_DIR/$test_name.log" - echo -e "${CYAN}Running:${NC} $test_name" - # Force color output from bun test using script to simulate TTY + # Mark as running + set_status "$test_file" "running" + + # Run the test if script -q /dev/null bash -c "FORCE_COLOR=3 bun test --timeout 0 '$test_file' 2>&1" > "$output_file"; then - echo -e "${GREEN}✓ PASSED:${NC} $test_name" + set_status "$test_file" "passed" return 0 else - echo -e "${RED}✗ FAILED:${NC} $test_name" - # Create a marker file for failed test + set_status "$test_file" "failed" echo "$test_file|$output_file" > "$FAILED_DIR/$test_name.failed" return 1 fi } +# Hide cursor +tput civis 2>/dev/null || true + +# Initial render - create space for all tests +echo "" +for test_file in "${TEST_FILES[@]}"; do + echo "" +done + +# Move cursor back up +tput cuu ${#TEST_FILES[@]} 2>/dev/null || true + +# Start spinner animation in background +animate_spinner & +SPINNER_PID=$! + +# Run tests in parallel count=0 -for test_file in "$TEST_DIR"/*.test.ts; do - # Skip if no test files found - if [ ! -f "$test_file" ]; then - echo "No test files found in $TEST_DIR" - rm -rf "$TEMP_DIR" - exit 0 - fi - +for test_file in "${TEST_FILES[@]}"; do # Wait if we've hit max parallel - while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL ]; do + while [ $(jobs -r | wc -l) -ge $((MAX_PARALLEL + 1)) ]; do sleep 0.1 done @@ -88,40 +212,55 @@ for test_file in "$TEST_DIR"/*.test.ts; do ((count++)) done -# Wait for all remaining jobs -wait +# Wait for all tests to complete (exclude spinner process) +for job in $(jobs -p); do + if [ "$job" != "$SPINNER_PID" ]; then + wait $job 2>/dev/null || true + fi +done + +# Stop spinner +if [ ! -z "$SPINNER_PID" ]; then + kill $SPINNER_PID 2>/dev/null || true + wait $SPINNER_PID 2>/dev/null || true +fi + +# Final render +render_tests + +# Move cursor below test list +echo "" +echo "" + +# Show cursor again +tput cnorm 2>/dev/null || true # Report failures FAILED_COUNT=$(ls "$FAILED_DIR"/*.failed 2>/dev/null | wc -l) if [ $FAILED_COUNT -gt 0 ]; then - echo "" echo -e "${RED}${BOLD}========================================" - echo -e "FAILED TESTS ($FAILED_COUNT total):" + echo -e "FAILED TESTS ($FAILED_COUNT/${count}):" echo -e "========================================${NC}" - - # First, list all failed test names - for failure_file in "$FAILED_DIR"/*.failed; do - IFS='|' read -r test_file output_file < "$failure_file" - echo -e "${RED} ✗${NC} $(basename $test_file)" - done - - echo "" - echo -e "${YELLOW}${BOLD}Detailed Errors:${NC}" echo "" - # Then show detailed errors + # Show detailed errors for failure_file in "$FAILED_DIR"/*.failed; do IFS='|' read -r test_file output_file < "$failure_file" - echo -e "${YELLOW}--- FAILURE: ${BOLD}$(basename $test_file)${NC}${YELLOW} ---${NC}" + echo -e "${RED}${BOLD}✗ $(basename $test_file)${NC}" + echo -e "${DIM}─────────────────────────────────────────${NC}" cat "$output_file" echo "" done rm -rf "$TEMP_DIR" + + # Remove trap before exit to prevent double cleanup + trap - SIGINT SIGTERM EXIT exit 1 else - echo "" echo -e "${GREEN}${BOLD}✓ All tests passed!${NC} ${CYAN}($count tests)${NC}" rm -rf "$TEMP_DIR" + + # Remove trap before exit to prevent double cleanup + trap - SIGINT SIGTERM EXIT exit 0 fi - diff --git a/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts b/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts index 35696d5bc..6ca220003 100644 --- a/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts +++ b/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts @@ -1,16 +1,16 @@ import { - getFeatureBalance, + type Feature, + FeatureType, + type FullCustomerEntitlement, + type Organization, +} from "@autumn/shared"; +import { cusEntsContainFeature, - getUnlimitedAndUsageAllowed, + getFeatureBalance, getPaidFeatureBalance, + getUnlimitedAndUsageAllowed, } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; import { featureToCreditSystem } from "@/internal/features/creditSystemUtils.js"; -import { - FullCustomerEntitlement, - Feature, - FeatureType, - Organization, -} from "@autumn/shared"; const getRequiredAndActualBalance = ({ cusEnts, @@ -84,7 +84,7 @@ export const getV1CheckResponse = ({ } // 2. Handle unlimited / usage allowed features - let { unlimited, usageAllowed } = getUnlimitedAndUsageAllowed({ + const { unlimited, usageAllowed } = getUnlimitedAndUsageAllowed({ cusEnts, internalFeatureId: feature.internal_id!, }); @@ -116,12 +116,12 @@ export const getV1CheckResponse = ({ entityId, }); - let totalPaidAllowance = getPaidFeatureBalance({ + const totalPaidAllowance = getPaidFeatureBalance({ cusEnts, internalFeatureId: feature.internal_id!, }); - let newBalance: any = { + const newBalance: any = { feature_id: feature.id, required, balance: actual, diff --git a/server/src/internal/api/events/eventRouter.ts b/server/src/internal/api/events/eventRouter.ts index a0558f96d..02c803f42 100644 --- a/server/src/internal/api/events/eventRouter.ts +++ b/server/src/internal/api/events/eventRouter.ts @@ -207,6 +207,8 @@ eventsRouter.post("", async (req: any, res: any) => { try { const body = req.body; + console.log("body", body); + if (!body.event_name && !body.feature_id) { throw new RecaseError({ message: "event_name or feature_id is required", diff --git a/server/src/utils/scriptUtils/constructItem.ts b/server/src/utils/scriptUtils/constructItem.ts index 66436a76f..bf22f79f5 100644 --- a/server/src/utils/scriptUtils/constructItem.ts +++ b/server/src/utils/scriptUtils/constructItem.ts @@ -1,11 +1,11 @@ import { - LimitedItem, + type LimitedItem, OnDecrease, OnIncrease, - ProductItem, - ProductItemConfig, + type ProductItem, + type ProductItemConfig, ProductItemInterval, - RolloverConfig, + type RolloverConfig, UsageModel, } from "@autumn/shared"; @@ -16,6 +16,7 @@ export const constructFeatureItem = ({ intervalCount = 1, entityFeatureId, isBoolean = false, + unlimited = false, rolloverConfig, }: { featureId: string; @@ -25,6 +26,7 @@ export const constructFeatureItem = ({ entityFeatureId?: string; isBoolean?: boolean; rolloverConfig?: RolloverConfig; + unlimited?: boolean; }) => { if (isBoolean) { return { @@ -32,7 +34,14 @@ export const constructFeatureItem = ({ entity_feature_id: entityFeatureId, }; } - let item: LimitedItem = { + + if (unlimited) { + return { + feature_id: featureId, + included_usage: "inf", + } as ProductItem; + } + const item: LimitedItem = { feature_id: featureId, included_usage: includedUsage, entity_feature_id: entityFeatureId, @@ -75,7 +84,7 @@ export const constructPrepaidItem = ({ usageLimit?: number; intervalCount?: number; }) => { - let item: ProductItem = { + const item: ProductItem = { feature_id: featureId, usage_model: UsageModel.Prepaid, @@ -118,7 +127,7 @@ export const constructArrearItem = ({ usageLimit?: number; intervalCount?: number; }) => { - let item: ProductItem = { + const item: ProductItem = { feature_id: featureId, usage_model: UsageModel.PayPerUse, included_usage: includedUsage, @@ -153,7 +162,7 @@ export const constructArrearProratedItem = ({ usageLimit?: number; rolloverConfig?: RolloverConfig; }) => { - let item: ProductItem = { + const item: ProductItem = { feature_id: featureId, usage_model: UsageModel.PayPerUse, included_usage: includedUsage, diff --git a/server/src/utils/scriptUtils/createTestProducts.ts b/server/src/utils/scriptUtils/createTestProducts.ts index 7dddbc4de..6333dce2e 100644 --- a/server/src/utils/scriptUtils/createTestProducts.ts +++ b/server/src/utils/scriptUtils/createTestProducts.ts @@ -74,6 +74,7 @@ export const constructRawProduct = ({ version: 1, group: "", created_at: Date.now(), + env: AppEnv.Sandbox, }; }; @@ -107,11 +108,11 @@ export const constructProduct = ({ forcePaidDefault?: boolean; }) => { let price = 0; - if (type == "pro") { + if (type === "pro") { price = 20; - } else if (type == "premium") { + } else if (type === "premium") { price = 50; - } else if (type == "growth") { + } else if (type === "growth") { price = 100; } @@ -129,7 +130,7 @@ export const constructProduct = ({ ); } - if (type == "one_off") { + if (type === "one_off") { items.push( constructPriceItem({ price: 10, @@ -152,8 +153,9 @@ export const constructProduct = ({ ? `${keyToTitle(type)} (${interval})` : keyToTitle(type), items, + env: AppEnv.Sandbox, is_add_on: isAddOn, - is_default: (type == "free" && isDefault) || forcePaidDefault, + is_default: (type === "free" && isDefault) || forcePaidDefault, version: 1, group: group || "", free_trial: diff --git a/server/tests/MOCHA_TO_BUN_MIGRATION.md b/server/tests/MOCHA_TO_BUN_MIGRATION.md index 0642021a5..fd6c360fb 100644 --- a/server/tests/MOCHA_TO_BUN_MIGRATION.md +++ b/server/tests/MOCHA_TO_BUN_MIGRATION.md @@ -58,6 +58,11 @@ beforeAll(async () => { **Before:** ```typescript +addPrefixToProducts({ + products: [product1, product2], + prefix: testCase, +}); + await createProducts({ db, orgId: org.id, @@ -76,6 +81,8 @@ await initProductsV0({ }); ``` +**Important:** `initProductsV0` combines both `addPrefixToProducts` and `createProducts` into a single call. + ### 4. Update Test Functions Replace `it` with `test`: @@ -207,10 +214,43 @@ beforeAll(async () => { - [ ] Remove `this` context references - [ ] Test runs successfully +## Critical Rules + +### Order of Operations +**YOU MUST MAINTAIN THE EXACT ORDER** of initialization calls from the original test: + +1. **If products are created BEFORE customer init** → Keep that order in migration +2. **If customer is created BEFORE products** → Keep that order in migration + +**Example 1: Products First** +```typescript +// Original (Mocha) +addPrefixToProducts({ products: [pro, premium], prefix: testCase }); +await createProducts({ autumn, products: [pro, premium], db, orgId: org.id, env }); +await initCustomer({ autumn: autumnJs, customerId, db, org, env, attachPm: "success" }); + +// Migrated (Bun) - SAME ORDER +await initProductsV0({ ctx, products: [pro, premium], prefix: testCase }); +await initCustomerV3({ ctx, customerId, customerData: {}, attachPm: "success", withTestClock: true }); +``` + +**Example 2: Customer First** +```typescript +// Original (Mocha) +await initCustomer({ autumn: autumnJs, customerId, db, org, env, attachPm: "success" }); +addPrefixToProducts({ products: [pro, premium], prefix: testCase }); +await createProducts({ autumn, products: [pro, premium], db, orgId: org.id, env }); + +// Migrated (Bun) - SAME ORDER +await initCustomerV3({ ctx, customerId, customerData: {}, attachPm: "success", withTestClock: true }); +await initProductsV0({ ctx, products: [pro, premium], prefix: testCase }); +``` + ## Notes - **No timeout needed**: Tests run with `--timeout 0` globally - **Keep AutumnCli**: Don't replace with AutumnInt for API calls - **Preserve test logic**: Only change framework, not test behavior - **ctx is global**: Imported from createTestContext, contains db/org/env/stripeCli +- **initProductsV0 is a helper**: It combines `addPrefixToProducts` + `createProducts` into one call diff --git a/server/tests/attach/addOn/addOn1.ts b/server/tests/attach/addOn/addOn1.test.ts similarity index 62% rename from server/tests/attach/addOn/addOn1.ts rename to server/tests/attach/addOn/addOn1.test.ts index 16eb67780..01172972e 100644 --- a/server/tests/attach/addOn/addOn1.ts +++ b/server/tests/attach/addOn/addOn1.test.ts @@ -1,21 +1,19 @@ -import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; -import { expect } from "chai"; +import { beforeAll, describe, expect, test } from "bun:test"; +import { LegacyVersion } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct, constructRawProduct, } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts, replaceItems } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; +import { replaceItems } from "../utils.js"; export const pro = constructProduct({ items: [ @@ -43,60 +41,34 @@ const testCase = "addOn1"; describe(`${chalk.yellowBright(`${testCase}: Testing free add on, and updating free add on`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_2 }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - addPrefixToProducts({ + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + attachPm: "success", + withTestClock: true, + }); + await initProductsV0({ + ctx, products: [pro, addOn], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro, addOn], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - - testClockId = testClockId1!; }); - it("should should attach pro product, then add on product", async () => { + test("should should attach pro product, then add on product", async () => { await attachAndExpectCorrect({ autumn, - db, - org, - env, - stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, + stripeCli: ctx.stripeCli, customerId, product: pro, }); }); - it("should should attach add on product", async () => { + test("should should attach add on product", async () => { const preview = await autumn.attachPreview({ customer_id: customerId, product_id: addOn.id, @@ -109,7 +81,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free add on, and updating f const customer = await autumn.customers.get(customerId); - expect(customer.products.length).to.equal(3); + expect(customer.products.length).toBe(3); expectProductAttached({ customer, product: addOn, @@ -129,7 +101,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free add on, and updating f }), }); - it("should update add on product", async () => { + test("should update add on product", async () => { const preview = await autumn.attachPreview({ customer_id: customerId, product_id: addOn.id, @@ -146,7 +118,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free add on, and updating f const customer = await autumn.customers.get(customerId); - expect(customer.products.length).to.equal(3); + expect(customer.products.length).toBe(3); expectProductAttached({ customer, product: addOn, diff --git a/server/tests/attach/addOn/addOn2.test.ts b/server/tests/attach/addOn/addOn2.test.ts index 8141359a0..8dfdd33b3 100644 --- a/server/tests/attach/addOn/addOn2.test.ts +++ b/server/tests/attach/addOn/addOn2.test.ts @@ -1,12 +1,12 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; @@ -14,8 +14,8 @@ import { constructProduct, constructRawProduct, } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; export const pro = constructProduct({ type: "pro", @@ -40,39 +40,28 @@ describe(`${chalk.yellowBright(`${testCase}: Testing attach free add on twice (s let db: DrizzleCli, org: Organization, env: AppEnv; let stripeCli: Stripe; - before(async function () { - await setupBefore(this); - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; - - await initCustomer({ - db, - org, - env, - autumn: this.autumnJs, + await initCustomerV3({ + ctx, customerId, - fingerprint: "test", + customerData: { fingerprint: "test" }, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, addOn], prefix: testCase, }); - - await createProducts({ - db, - orgId: org.id, - env, - autumn, - products: [pro, addOn], - }); }); - it("should attach pro product and free add on", async () => { + test("should attach pro product and free add on", async () => { await attachAndExpectCorrect({ autumn, customerId, diff --git a/server/tests/attach/checkout/checkout3.ts b/server/tests/attach/checkout/checkout3.test.ts similarity index 62% rename from server/tests/attach/checkout/checkout3.ts rename to server/tests/attach/checkout/checkout3.test.ts index c47b55289..2c5efbe18 100644 --- a/server/tests/attach/checkout/checkout3.ts +++ b/server/tests/attach/checkout/checkout3.test.ts @@ -1,19 +1,17 @@ -import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; +import { beforeAll, describe, test } from "bun:test"; +import { LegacyVersion } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { completeCheckoutForm } from "tests/utils/stripeUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { timeout } from "@/utils/genUtils.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; export const pro = constructProduct({ items: [ @@ -40,47 +38,22 @@ const testCase = "checkout3"; describe(`${chalk.yellowBright(`${testCase}: Testing multi attach checkout, pro + one off`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + withTestClock: true, + }); - stripeCli = this.stripeCli; - - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, oneOff], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro, oneOff], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - // attachPm: "success", - }); - - testClockId = testClockId1!; }); - it("should attach pro and one off product", async () => { + test("should attach pro and one off product", async () => { const res = await autumn.attach({ customer_id: customerId, product_ids: [pro.id, oneOff.id], diff --git a/server/tests/attach/checkout/checkout4.ts b/server/tests/attach/checkout/checkout4.test.ts similarity index 56% rename from server/tests/attach/checkout/checkout4.ts rename to server/tests/attach/checkout/checkout4.test.ts index 1615d97dc..5583adee6 100644 --- a/server/tests/attach/checkout/checkout4.ts +++ b/server/tests/attach/checkout/checkout4.test.ts @@ -1,14 +1,12 @@ -import { type AppEnv, type Organization, RewardType } from "@autumn/shared"; -import { expect } from "chai"; +import { beforeAll, describe, expect, test } from "bun:test"; +import { RewardType } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts, createReward } from "tests/utils/productUtils.js"; +import { createReward } from "tests/utils/productUtils.js"; import { completeCheckoutForm } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { timeout } from "@/utils/genUtils.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; @@ -16,8 +14,8 @@ import { constructCoupon, constructProduct, } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const pro = constructProduct({ items: [ @@ -40,56 +38,31 @@ const testCase = "checkout4"; describe(`${chalk.yellowBright(`${testCase}: Testing attach coupon`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt(); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + withTestClock: true, + }); - stripeCli = this.stripeCli; - - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - // attachPm: "success", - }); - await createReward({ - orgId: org.id, - env, - db, + orgId: ctx.org.id, + env: ctx.env, + db: ctx.db, autumn, reward, productId: pro.id, }); - - testClockId = testClockId1!; }); - it("should attach pro and one off product", async () => { + test("should attach pro and one off product", async () => { const res = await autumn.attach({ customer_id: customerId, product_id: pro.id, @@ -106,8 +79,8 @@ describe(`${chalk.yellowBright(`${testCase}: Testing attach coupon`)}`, () => { product: pro, }); - expect(customer.invoices.length).to.equal(1); + expect(customer.invoices.length).toBe(1); const totalPrice = getBasePrice({ product: pro }); - expect(customer.invoices[0].total).to.equal(totalPrice * 0.5); + expect(customer.invoices[0].total).toBe(totalPrice * 0.5); }); }); diff --git a/server/tests/attach/checkout/checkout5.ts b/server/tests/attach/checkout/checkout5.test.ts similarity index 55% rename from server/tests/attach/checkout/checkout5.ts rename to server/tests/attach/checkout/checkout5.test.ts index 46f523565..d59c77fb1 100644 --- a/server/tests/attach/checkout/checkout5.ts +++ b/server/tests/attach/checkout/checkout5.test.ts @@ -1,20 +1,17 @@ -import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; -import { expect } from "chai"; +import { beforeAll, describe, expect, test } from "bun:test"; +import { LegacyVersion } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { completeInvoiceCheckout } from "tests/utils/stripeUtils/completeInvoiceCheckout.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; export const pro = constructProduct({ items: [ @@ -30,47 +27,21 @@ const testCase = "checkout5"; describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout, no product till paid`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_2 }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - addPrefixToProducts({ + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + withTestClock: true, + }); + await initProductsV0({ + ctx, products: [pro], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - // attachPm: "success", - }); - - testClockId = testClockId1!; }); - it("should attach pro product", async () => { + test("should attach pro product", async () => { const res = await autumn.attach({ customer_id: customerId, product_id: pro.id, @@ -81,12 +52,12 @@ describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout, no produc const invoice = customer.invoices?.[0]; - expect(invoice).to.exist; - expect(invoice.total).to.equal(getBasePrice({ product: pro })); - expect(invoice.status).to.equal("open"); + expect(invoice).toBeDefined(); + expect(invoice.total).toBe(getBasePrice({ product: pro })); + expect(invoice.status).toBe("open"); const product = customer.products.find((p) => p.id === pro.id); - expect(product).to.not.exist; + expect(product).toBeUndefined(); await completeInvoiceCheckout({ url: res.checkout_url, @@ -96,9 +67,9 @@ describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout, no produc const invoice2 = customer2.invoices?.[0]; - expect(customer2.invoices.length).to.equal(1); - expect(invoice2).to.exist; - expect(invoice2.status).to.equal("paid"); + expect(customer2.invoices.length).toBe(1); + expect(invoice2).toBeDefined(); + expect(invoice2.status).toBe("paid"); expectProductAttached({ customer: customer2, diff --git a/server/tests/attach/checkout/checkout6.ts b/server/tests/attach/checkout/checkout6.test.ts similarity index 63% rename from server/tests/attach/checkout/checkout6.ts rename to server/tests/attach/checkout/checkout6.test.ts index 41e9985ae..70a643916 100644 --- a/server/tests/attach/checkout/checkout6.ts +++ b/server/tests/attach/checkout/checkout6.test.ts @@ -1,21 +1,18 @@ -import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; -import { expect } from "chai"; +import { beforeAll, describe, expect, test } from "bun:test"; +import { LegacyVersion } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js"; import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { completeInvoiceCheckout } from "tests/utils/stripeUtils/completeInvoiceCheckout.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; export const pro = constructProduct({ items: [ @@ -41,54 +38,30 @@ const testCase = "checkout6"; describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout via checkout endpoint`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_2 }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + attachPm: "success", + withTestClock: true, + }); - stripeCli = this.stripeCli; - - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro, premium], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - - testClockId = testClockId1!; }); - it("should attach pro product via invoice checkout", async () => { + test("should attach pro product via invoice checkout", async () => { const res = await autumn.checkout({ customer_id: customerId, product_id: pro.id, invoice: true, }); - expect(res.url).to.exist; + expect(res.url).toBeDefined(); await completeInvoiceCheckout({ url: res.url!, @@ -107,7 +80,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout via checko }); }); - it("should have no URL returned if try to attach premium (with invoice true)", async () => { + test("should have no URL returned if try to attach premium (with invoice true)", async () => { await expectAutumnError({ func: async () => { await autumn.attach({ @@ -124,10 +97,10 @@ describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout via checko invoice: true, }); - expect(res.url).to.not.exist; + expect(res.url).toBeNull(); }); - it("should attach premium product via invoice enable immediately", async () => { + test("should attach premium product via invoice enable immediately", async () => { const res = await autumn.attach({ customer_id: customerId, product_id: premium.id, @@ -148,9 +121,9 @@ describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout via checko }); const invoices = customer.invoices; - expect(invoices.length).to.equal(2); - expect(invoices[0].status).to.equal("draft"); - expect(invoices[0].total).to.equal( + expect(invoices.length).toBe(2); + expect(invoices[0].status).toBe("draft"); + expect(invoices[0].total).toBe( getBasePrice({ product: premium }) - getBasePrice({ product: pro }), ); // proration... }); diff --git a/server/tests/attach/checkout/checkout7.ts b/server/tests/attach/checkout/checkout7.test.ts similarity index 66% rename from server/tests/attach/checkout/checkout7.ts rename to server/tests/attach/checkout/checkout7.test.ts index 14a2df46c..441166a7e 100644 --- a/server/tests/attach/checkout/checkout7.ts +++ b/server/tests/attach/checkout/checkout7.test.ts @@ -1,14 +1,11 @@ -import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; -import { expect } from "chai"; +import { beforeAll, describe, expect, test } from "bun:test"; +import { LegacyVersion } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { completeInvoiceCheckout } from "tests/utils/stripeUtils/completeInvoiceCheckout.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructFeatureItem, @@ -18,8 +15,8 @@ import { constructProduct, constructRawProduct, } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; export const pro = constructProduct({ items: [ @@ -47,47 +44,23 @@ const testCase = "checkout7"; describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout with one off product`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_2 }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + attachPm: "success", + withTestClock: true, + }); - stripeCli = this.stripeCli; - - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, addOn], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro, addOn], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - - testClockId = testClockId1!; }); - it("should attach pro product, then add on product via invoice checkout", async () => { + test("should attach pro product, then add on product via invoice checkout", async () => { await autumn.attach({ customer_id: customerId, product_id: pro.id, @@ -107,7 +80,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing invoice checkout with one o options, }); - expect(res2.url).to.exist; + expect(res2.url).toBeDefined(); await completeInvoiceCheckout({ url: res2.url!, diff --git a/server/tests/attach/downgrade/downgrade1.ts b/server/tests/attach/downgrade/downgrade1.test.ts similarity index 70% rename from server/tests/attach/downgrade/downgrade1.ts rename to server/tests/attach/downgrade/downgrade1.test.ts index 3a3295582..6019edca8 100644 --- a/server/tests/attach/downgrade/downgrade1.ts +++ b/server/tests/attach/downgrade/downgrade1.test.ts @@ -1,20 +1,20 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectDowngradeCorrect, expectNextCycleCorrect, } from "tests/utils/expectUtils/expectScheduleUtils.js"; -import { createProducts } from "tests/utils/productUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "downgrade1"; @@ -35,42 +35,30 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade from premium -> p let db: DrizzleCli, org: Organization, env: AppEnv; let stripeCli: Stripe; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, + customerId, + customerData: {}, + attachPm: "success", + withTestClock: true, + }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro, premium], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - testClockId = testClockId1!; }); - it("should attach premium product", async () => { + test("should attach premium product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -85,7 +73,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade from premium -> p // let nextCycle = Date.now(); let preview = null; - it("should downgrade to pro", async () => { + test("should downgrade to pro", async () => { const { preview: preview_ } = await expectDowngradeCorrect({ autumn, customerId, @@ -100,7 +88,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade from premium -> p preview = preview_; }); - it("should have pro attached on next cycle", async () => { + test("should have pro attached on next cycle", async () => { await expectNextCycleCorrect({ preview: preview!, autumn, diff --git a/server/tests/attach/downgrade/downgrade2.ts b/server/tests/attach/downgrade/downgrade2.test.ts similarity index 72% rename from server/tests/attach/downgrade/downgrade2.ts rename to server/tests/attach/downgrade/downgrade2.test.ts index 511c0a8d3..f66293c13 100644 --- a/server/tests/attach/downgrade/downgrade2.ts +++ b/server/tests/attach/downgrade/downgrade2.test.ts @@ -1,21 +1,21 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectDowngradeCorrect, expectNextCycleCorrect, } from "tests/utils/expectUtils/expectScheduleUtils.js"; -import { createProducts } from "tests/utils/productUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructFeatureItem } from "@/internal/products/product-items/productItemUtils.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "downgrade2"; @@ -44,42 +44,30 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade from premium -> f const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, + customerId, + customerData: {}, + attachPm: "success", + withTestClock: true, + }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [free, premium], prefix: testCase, }); - await createProducts({ - autumn, - products: [free, premium], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - testClockId = testClockId1!; }); - it("should attach premium product", async () => { + test("should attach premium product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -93,7 +81,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade from premium -> f // let nextCycle = Date.now(); let preview = null; - it("should downgrade to free", async () => { + test("should downgrade to free", async () => { const { preview: preview_ } = await expectDowngradeCorrect({ autumn, customerId, @@ -108,7 +96,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade from premium -> f preview = preview_; }); - it("should have pro attached on next cycle", async () => { + test("should have pro attached on next cycle", async () => { await expectNextCycleCorrect({ preview: preview!, autumn, diff --git a/server/tests/attach/downgrade/downgrade3.ts b/server/tests/attach/downgrade/downgrade3.test.ts similarity index 77% rename from server/tests/attach/downgrade/downgrade3.ts rename to server/tests/attach/downgrade/downgrade3.test.ts index 6ea933771..4276b1dc2 100644 --- a/server/tests/attach/downgrade/downgrade3.ts +++ b/server/tests/attach/downgrade/downgrade3.test.ts @@ -1,3 +1,4 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, type Customer, @@ -6,19 +7,18 @@ import { } from "@autumn/shared"; import chalk from "chalk"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; import { expectDowngradeCorrect } from "tests/utils/expectUtils/expectScheduleUtils.js"; -import { createProducts } from "tests/utils/productUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructFeatureItem } from "@/internal/products/product-items/productItemUtils.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "downgrade3"; @@ -53,44 +53,32 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro - const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; + const { testClockId: testClockId1, customer: customer_ } = + await initCustomerV3({ + ctx, + customerId, + customerData: {}, + attachPm: "success", + withTestClock: true, + }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [free, pro, premium], prefix: testCase, }); - await createProducts({ - autumn, - products: [free, pro, premium], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1, customer: customer_ } = - await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - testClockId = testClockId1!; customer = customer_!; }); - it("should attach premium product", async () => { + test("should attach premium product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -104,7 +92,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro - // let nextCycle = Date.now(); let preview = null; - it("should downgrade to pro", async () => { + test("should downgrade to pro", async () => { const { preview: preview_ } = await expectDowngradeCorrect({ autumn, customerId, @@ -119,7 +107,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro - preview = preview_; }); - it("should downgrade to free", async () => { + test("should downgrade to free", async () => { const { preview: preview_ } = await expectDowngradeCorrect({ autumn, customerId, @@ -134,7 +122,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro - preview = preview_; }); - it("should change downgrade to pro", async () => { + test("should change downgrade to pro", async () => { const { preview: preview_ } = await expectDowngradeCorrect({ autumn, customerId, @@ -147,7 +135,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: premium -> pro - }); }); - it("should renew premium", async () => { + test("should renew premium", async () => { await autumn.attach({ customer_id: customerId, product_id: premium.id, diff --git a/server/tests/attach/downgrade/downgrade4.ts b/server/tests/attach/downgrade/downgrade4.test.ts similarity index 76% rename from server/tests/attach/downgrade/downgrade4.ts rename to server/tests/attach/downgrade/downgrade4.test.ts index 5b956657a..3b71a8ff1 100644 --- a/server/tests/attach/downgrade/downgrade4.ts +++ b/server/tests/attach/downgrade/downgrade4.test.ts @@ -1,3 +1,4 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, BillingInterval, @@ -7,22 +8,21 @@ import { } from "@autumn/shared"; import chalk from "chalk"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectDowngradeCorrect, expectNextCycleCorrect, } from "tests/utils/expectUtils/expectScheduleUtils.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { advanceMonths } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { timeout } from "@/utils/genUtils.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "downgrade4"; @@ -52,44 +52,32 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: pro-quarter -> p const curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; + const { testClockId: testClockId1, customer: customer_ } = + await initCustomerV3({ + ctx, + customerId, + customerData: {}, + attachPm: "success", + withTestClock: true, + }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [proQuarter, pro, premium], prefix: testCase, }); - await createProducts({ - autumn, - products: [proQuarter, pro, premium], - customerId, - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1, customer: customer_ } = - await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - testClockId = testClockId1!; customer = customer_!; }); - it("should attach pro quarterly product", async () => { + test("should attach pro quarterly product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -101,7 +89,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: pro-quarter -> p }); }); - it("should downgrade to premium", async () => { + test("should downgrade to premium", async () => { await expectDowngradeCorrect({ autumn, customerId, @@ -116,7 +104,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: pro-quarter -> p let preview = null; - it("should downgrade to pro", async () => { + test("should downgrade to pro", async () => { const { preview: preview_ } = await expectDowngradeCorrect({ autumn, customerId, @@ -131,7 +119,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing downgrade: pro-quarter -> p preview = preview_; }); - it("should have correct invoice after cycle", async () => { + test("should have correct invoice after cycle", async () => { await advanceMonths({ stripeCli, testClockId, numberOfMonths: 3 }); await timeout(10000); diff --git a/server/tests/attach/downgrade/downgrade5.ts b/server/tests/attach/downgrade/downgrade5.test.ts similarity index 71% rename from server/tests/attach/downgrade/downgrade5.ts rename to server/tests/attach/downgrade/downgrade5.test.ts index 8a6ac61f1..a48d5c599 100644 --- a/server/tests/attach/downgrade/downgrade5.ts +++ b/server/tests/attach/downgrade/downgrade5.test.ts @@ -1,16 +1,16 @@ +import { beforeAll, describe, expect, test } from "bun:test"; import { CusProductStatus } from "@autumn/shared"; -import { expect } from "chai"; import chalk from "chalk"; import { addHours, addMonths } from "date-fns"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { AutumnCli } from "tests/cli/AutumnCli.js"; import { products } from "tests/global.js"; import { compareMainProduct } from "tests/utils/compare.js"; import { hoursToFinalizeInvoice } from "tests/utils/constants.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; const testCase = "downgrade5"; describe(`${chalk.yellowBright(`${testCase}: testing basic downgrade (paid to paid)`)}`, () => { @@ -19,39 +19,35 @@ describe(`${chalk.yellowBright(`${testCase}: testing basic downgrade (paid to pa let testClockId: string; let stripeCli: Stripe; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - stripeCli = this.stripeCli; + beforeAll(async () => { + stripeCli = ctx.stripeCli; - const { customer: customer_, testClockId: testClockId_ } = - await initCustomer({ - customerId, - db: this.db, - org: this.org, - env: this.env, - attachPm: "success", - autumn: autumnJs, - }); + const { testClockId: testClockId_ } = await initCustomerV3({ + ctx, + customerId, + customerData: {}, + attachPm: "success", + withTestClock: true, + }); testClockId = testClockId_; }); - it("should attach premium", async () => { + test("should attach premium", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.premium.id, }); }); - it("should attach pro", async () => { + test("should attach pro", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.pro.id, }); }); - it("should have correct product and entitlements for scheduled pro", async () => { + test("should have correct product and entitlements for scheduled pro", async () => { const res = await AutumnCli.getCustomer(customerId); compareMainProduct({ @@ -66,10 +62,10 @@ describe(`${chalk.yellowBright(`${testCase}: testing basic downgrade (paid to pa p.id === products.pro.id && p.status === CusProductStatus.Scheduled, ); - expect(resPro).to.exist; + expect(resPro).toBeDefined(); }); - it("should attach premium and remove scheduled pro", async () => { + test("should attach premium and remove scheduled pro", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.premium.id, @@ -81,7 +77,7 @@ describe(`${chalk.yellowBright(`${testCase}: testing basic downgrade (paid to pa p.id === products.pro.id && p.status === CusProductStatus.Scheduled, ); - expect(resPro).to.not.exist; + expect(resPro).toBeUndefined(); compareMainProduct({ sent: products.premium, @@ -90,7 +86,7 @@ describe(`${chalk.yellowBright(`${testCase}: testing basic downgrade (paid to pa }); // Advance time 1 month - it("should attach pro, advance stripe clock and have pro is attached", async () => { + test("should attach pro, advance stripe clock and have pro is attached", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.pro.id, diff --git a/server/tests/attach/downgrade/downgrade6.ts b/server/tests/attach/downgrade/downgrade6.test.ts similarity index 64% rename from server/tests/attach/downgrade/downgrade6.ts rename to server/tests/attach/downgrade/downgrade6.test.ts index 54d82e3f2..2bb182d75 100644 --- a/server/tests/attach/downgrade/downgrade6.ts +++ b/server/tests/attach/downgrade/downgrade6.test.ts @@ -1,44 +1,42 @@ -import { Customer } from "@autumn/shared"; +import { beforeAll, describe, test } from "bun:test"; +import type { Customer } from "@autumn/shared"; +import chalk from "chalk"; import { AutumnCli } from "tests/cli/AutumnCli.js"; import { products } from "tests/global.js"; -import chalk from "chalk"; import { compareMainProduct } from "tests/utils/compare.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { setupBefore } from "tests/before.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; const testCase = "downgrade6"; describe(`${chalk.yellowBright(`${testCase}: testing expire button`)}`, () => { - let customerId = testCase; + const customerId = testCase; let testClockId: string; - let autumn: AutumnInt = new AutumnInt(); + const autumn: AutumnInt = new AutumnInt(); let customer: Customer; - before(async function () { - await setupBefore(this); - + beforeAll(async () => { const { testClockId: testClockId_, customer: customer_ } = - await initCustomer({ + await initCustomerV3({ + ctx, customerId, - db: this.db, - org: this.org, - env: this.env, - autumn: this.autumnJs, + customerData: {}, attachPm: "success", + withTestClock: true, }); customer = customer_; testClockId = testClockId_; }); - it("should attach premium", async function () { + test("should attach premium", async () => { await autumn.attach({ customer_id: customerId, product_id: products.premium.id, }); }); - it("should expire premium", async function () { + test("should expire premium", async () => { // const cusProduct = await getMainCusProduct({ // db: this.db, // internalCustomerId: customer.internal_id, @@ -52,7 +50,7 @@ describe(`${chalk.yellowBright(`${testCase}: testing expire button`)}`, () => { }); }); - it("should have correct product and entitlements after expiration", async function () { + test("should have correct product and entitlements after expiration", async () => { const res = await AutumnCli.getCustomer(customerId); compareMainProduct({ diff --git a/server/tests/attach/downgrade/downgrade7.ts b/server/tests/attach/downgrade/downgrade7.test.ts similarity index 62% rename from server/tests/attach/downgrade/downgrade7.ts rename to server/tests/attach/downgrade/downgrade7.test.ts index 736baede1..5fc263f38 100644 --- a/server/tests/attach/downgrade/downgrade7.ts +++ b/server/tests/attach/downgrade/downgrade7.test.ts @@ -1,33 +1,33 @@ +import { beforeAll, describe, expect, test } from "bun:test"; +import type { Customer } from "@autumn/shared"; import chalk from "chalk"; -import { Customer } from "@autumn/shared"; +import type Stripe from "stripe"; import { AutumnCli } from "tests/cli/AutumnCli.js"; import { products } from "tests/global.js"; import { compareMainProduct } from "tests/utils/compare.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { setupBefore } from "tests/before.js"; -import { findCusProductById } from "@/internal/customers/cusProducts/cusProductUtils/findCusProduct.js"; -import { expect } from "chai"; import { getSubsFromCusId } from "tests/utils/expectUtils/expectSubUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; const testCase = "downgrade7"; describe(`${chalk.yellowBright(`${testCase}: testing expire scheduled product`)}`, () => { - let customerId = testCase; + const customerId = testCase; let testClockId: string; let customer: Customer; - let autumn: AutumnInt = new AutumnInt(); + const autumn: AutumnInt = new AutumnInt(); + let stripeCli: Stripe; - before(async function () { - await setupBefore(this); + beforeAll(async () => { + stripeCli = ctx.stripeCli; const { testClockId: testClockId_, customer: customer_ } = - await initCustomer({ + await initCustomerV3({ + ctx, customerId, - db: this.db, - org: this.org, - env: this.env, - autumn: this.autumnJs, + customerData: {}, attachPm: "success", + withTestClock: true, }); customer = customer_; @@ -35,7 +35,7 @@ describe(`${chalk.yellowBright(`${testCase}: testing expire scheduled product`)} }); // 2. Get premium - it("should attach premium, then attach pro", async function () { + test("should attach premium, then attach pro", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.premium.id, @@ -47,7 +47,7 @@ describe(`${chalk.yellowBright(`${testCase}: testing expire scheduled product`)} }); }); - it("should expire scheduled product (pro)", async function () { + test("should expire scheduled product (pro)", async () => { // const cusProduct = await findCusProductById({ // db: this.db, // internalCustomerId: customer.internal_id, @@ -63,8 +63,7 @@ describe(`${chalk.yellowBright(`${testCase}: testing expire scheduled product`)} // await AutumnCli.expire(cusProduct!.id); }); - it("should have correct product and entitlements (premium)", async function () { - this.timeout(30000); + test("should have correct product and entitlements (premium)", async () => { // Check that free is attached const res = await AutumnCli.getCustomer(customerId); compareMainProduct({ @@ -73,14 +72,14 @@ describe(`${chalk.yellowBright(`${testCase}: testing expire scheduled product`)} }); const { subs } = await getSubsFromCusId({ - stripeCli: this.stripeCli, + stripeCli, customerId: customerId, productId: products.premium.id, - db: this.db, - org: this.org, - env: this.env, + db: ctx.db, + org: ctx.org, + env: ctx.env, }); - expect(subs).to.have.lengthOf(1); - expect(subs[0].canceled_at).to.be.null; + expect(subs).toHaveLength(1); + expect(subs[0].canceled_at).toBe(null); }); }); diff --git a/server/tests/attach/downgrade/downgrade9.ts b/server/tests/attach/downgrade/downgrade9.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/tests/attach/entities/entity1.ts b/server/tests/attach/entities/entity1.test.ts similarity index 56% rename from server/tests/attach/entities/entity1.ts rename to server/tests/attach/entities/entity1.test.ts index 6f3164213..190cf9c9c 100644 --- a/server/tests/attach/entities/entity1.ts +++ b/server/tests/attach/entities/entity1.test.ts @@ -1,17 +1,15 @@ -import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; +import { beforeAll, describe, test } from "bun:test"; +import { LegacyVersion } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "aentity1"; @@ -28,44 +26,19 @@ const pro = constructProduct({ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach to entity via checkout`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - await initCustomer({ - autumn: autumnJs, + beforeAll(async () => { + await initCustomerV3({ + ctx, customerId, - db, - org, - env, - // attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro], - db, - orgId: org.id, - env, - }); - - // testClockId = testClockId1!; }); const newEntities = [ @@ -76,7 +49,7 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach to entity via }, ]; - it("should attach pro product to entity 1", async () => { + test("should attach pro product to entity 1", async () => { await autumn.entities.create(customerId, newEntities); const entityId = newEntities[0].id; @@ -84,10 +57,10 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach to entity via autumn, customerId, product: pro, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, entityId, }); diff --git a/server/tests/attach/entities/entity2.ts b/server/tests/attach/entities/entity2.test.ts similarity index 62% rename from server/tests/attach/entities/entity2.ts rename to server/tests/attach/entities/entity2.test.ts index 865a7d415..add58aeb1 100644 --- a/server/tests/attach/entities/entity2.ts +++ b/server/tests/attach/entities/entity2.test.ts @@ -1,27 +1,24 @@ -import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { AppEnv, Organization } from "@autumn/shared"; +import { beforeAll, describe, test } from "bun:test"; import chalk from "chalk"; -import Stripe from "stripe"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { setupBefore } from "tests/before.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import { addPrefixToProducts } from "../utils.js"; -import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; -import { TestFeature } from "tests/setup/v2Features.js"; -import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { defaultApiVersion } from "tests/constants.js"; -import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; -import { advanceTestClock } from "tests/utils/stripeUtils.js"; import { addHours, addMonths } from "date-fns"; +import { defaultApiVersion } from "tests/constants.js"; +import { TestFeature } from "tests/setup/v2Features.js"; import { hoursToFinalizeInvoice } from "tests/utils/constants.js"; -import { timeout } from "@/utils/genUtils.js"; +import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; +import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; import { expectInvoiceAfterUsage } from "tests/utils/expectUtils/expectSingleUse/expectUsageInvoice.js"; +import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { timeout } from "@/utils/genUtils.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "aentity2"; -export let proAnnual = constructProduct({ +export const proAnnual = constructProduct({ items: [ constructArrearItem({ featureId: TestFeature.Words, @@ -33,46 +30,26 @@ export let proAnnual = constructProduct({ }); describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to entity via checkout`)}`, () => { - let customerId = testCase; - let autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); + const customerId = testCase; + const autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; let curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + beforeAll(async () => { + const result = await initCustomerV3({ + ctx, customerId, - db, - org, - env, - // attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + testClockId = result.testClockId!; + + await initProductsV0({ + ctx, products: [proAnnual], prefix: testCase, }); - - await createProducts({ - autumn, - products: [proAnnual], - db, - orgId: org.id, - env, - }); - - testClockId = testClockId1; }); const newEntities = [ @@ -84,7 +61,7 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to ]; let entityId = newEntities[0].id; - it("should attach pro annual product to entity 2", async function () { + test("should attach pro annual product to entity 2", async () => { await autumn.entities.create(customerId, newEntities); entityId = newEntities[0].id; @@ -92,16 +69,16 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to autumn, customerId, product: proAnnual, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, entityId, }); }); - let usage = 1250130; - it("should track usage", async function () { + const usage = 1250130; + test("should track usage", async () => { await autumn.track({ customer_id: customerId, entity_id: entityId, @@ -110,7 +87,7 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to }); await timeout(5000); - let entity = await autumn.entities.get(customerId, entityId); + const entity = await autumn.entities.get(customerId, entityId); expectFeaturesCorrect({ customer: entity, @@ -124,9 +101,9 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to }); }); - it("should have correct invoice after cycle", async function () { + test("should have correct invoice after cycle", async () => { curUnix = await advanceTestClock({ - stripeCli, + stripeCli: ctx.stripeCli, testClockId, advanceTo: addHours( addMonths(curUnix, 1), @@ -142,10 +119,10 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to featureId: TestFeature.Words, product: proAnnual, usage, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, numInvoices: 2, }); }); diff --git a/server/tests/attach/entities/entity3.ts b/server/tests/attach/entities/entity3.test.ts similarity index 63% rename from server/tests/attach/entities/entity3.ts rename to server/tests/attach/entities/entity3.test.ts index c1211d093..7dd97d0a8 100644 --- a/server/tests/attach/entities/entity3.ts +++ b/server/tests/attach/entities/entity3.test.ts @@ -1,26 +1,23 @@ +import { beforeAll, describe, test } from "bun:test"; import chalk from "chalk"; -import Stripe from "stripe"; import { addHours, addMonths } from "date-fns"; -import { timeout } from "@/utils/genUtils.js"; -import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { AppEnv, Organization } from "@autumn/shared"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { setupBefore } from "tests/before.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import { addPrefixToProducts } from "../utils.js"; -import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; -import { TestFeature } from "tests/setup/v2Features.js"; -import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { defaultApiVersion } from "tests/constants.js"; -import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import { TestFeature } from "tests/setup/v2Features.js"; import { hoursToFinalizeInvoice } from "tests/utils/constants.js"; +import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectInvoiceAfterUsage } from "tests/utils/expectUtils/expectSingleUse/expectUsageInvoice.js"; +import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { timeout } from "@/utils/genUtils.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "aentity3"; -export let proAnnual = constructProduct({ +export const proAnnual = constructProduct({ items: [ constructArrearItem({ featureId: TestFeature.Words, @@ -31,46 +28,27 @@ export let proAnnual = constructProduct({ }); describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to entity via checkout`)}`, () => { - let customerId = testCase; - let autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); + const customerId = testCase; + const autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; let curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + beforeAll(async () => { + const result = await initCustomerV3({ + ctx, customerId, - db, - org, - env, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + testClockId = result.testClockId!; + + await initProductsV0({ + ctx, products: [proAnnual], prefix: testCase, }); - - await createProducts({ - autumn, - products: [proAnnual], - db, - orgId: org.id, - env, - }); - - testClockId = testClockId1; }); const newEntities = [ @@ -82,7 +60,7 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to ]; let entityId = newEntities[0].id; - it("should attach pro product to entity 2", async function () { + test("should attach pro product to entity 2", async () => { await autumn.entities.create(customerId, newEntities); entityId = newEntities[0].id; @@ -90,16 +68,16 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to autumn, customerId, product: proAnnual, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, entityId, }); }); - let nextUsage = 1032100; - it("should cancel and have correct final invoice", async function () { + const nextUsage = 1032100; + test("should cancel and have correct final invoice", async () => { await autumn.track({ customer_id: customerId, entity_id: entityId, @@ -116,7 +94,7 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to await timeout(5000); curUnix = await advanceTestClock({ - stripeCli, + stripeCli: ctx.stripeCli, testClockId, advanceTo: addHours( addMonths(curUnix, 1), @@ -131,10 +109,10 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro annual to featureId: TestFeature.Words, product: proAnnual, usage: nextUsage, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, numInvoices: 2, expectExpired: true, }); diff --git a/server/tests/attach/entities/entity4.ts b/server/tests/attach/entities/entity4.test.ts similarity index 56% rename from server/tests/attach/entities/entity4.ts rename to server/tests/attach/entities/entity4.test.ts index d692b7c20..00f85af8d 100644 --- a/server/tests/attach/entities/entity4.ts +++ b/server/tests/attach/entities/entity4.test.ts @@ -1,23 +1,20 @@ +import { beforeAll, describe, test } from "bun:test"; import chalk from "chalk"; -import Stripe from "stripe"; -import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { AppEnv, Organization } from "@autumn/shared"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { setupBefore } from "tests/before.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import { addPrefixToProducts } from "../utils.js"; -import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; -import { TestFeature } from "tests/setup/v2Features.js"; -import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { defaultApiVersion } from "tests/constants.js"; +import { TestFeature } from "tests/setup/v2Features.js"; +import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { timeout } from "@/utils/genUtils.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "aentity4"; -export let pro = constructProduct({ +export const pro = constructProduct({ items: [ constructArrearItem({ featureId: TestFeature.Words, @@ -28,46 +25,22 @@ export let pro = constructProduct({ }); describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro diff entities and testing track / check`)}`, () => { - let customerId = testCase; - let autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; + const customerId = testCase; + const autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); - let curUnix = new Date().getTime(); - - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + beforeAll(async () => { + await initCustomerV3({ + ctx, customerId, - db, - org, - env, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro], - db, - orgId: org.id, - env, - }); - - testClockId = testClockId1; }); const newEntities = [ @@ -83,41 +56,41 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro diff enti }, ]; - let entity1 = newEntities[0]; - let entity2 = newEntities[1]; + const entity1 = newEntities[0]; + const entity2 = newEntities[1]; - it("should attach pro product to entity 1", async function () { + test("should attach pro product to entity 1", async () => { await autumn.entities.create(customerId, newEntities); await attachAndExpectCorrect({ autumn, customerId, product: pro, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, entityId: entity1.id, numSubs: 1, }); }); - it("should attach pro product to entity 2", async function () { + test("should attach pro product to entity 2", async () => { await attachAndExpectCorrect({ autumn, customerId, product: pro, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, entityId: entity2.id, numSubs: 2, }); }); - let entity1Usage = Math.random() * 1000000; - it("should track usage on entity 1", async function () { + const entity1Usage = Math.random() * 1000000; + test("should track usage on entity 1", async () => { await autumn.track({ customer_id: customerId, entity_id: entity1.id, @@ -126,8 +99,8 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro diff enti }); await timeout(3000); - let entity1Res = await autumn.entities.get(customerId, entity1.id); - let entity2Res = await autumn.entities.get(customerId, entity2.id); + const entity1Res = await autumn.entities.get(customerId, entity1.id); + const entity2Res = await autumn.entities.get(customerId, entity2.id); expectFeaturesCorrect({ customer: entity1Res, @@ -146,8 +119,8 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro diff enti }); }); - let entity2Usage = Math.random() * 1000000; - it("should track usage on entity 2", async function () { + const entity2Usage = Math.random() * 1000000; + test("should track usage on entity 2", async () => { await autumn.track({ customer_id: customerId, entity_id: entity2.id, @@ -157,8 +130,8 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing attach pro diff enti await timeout(3000); - let entity1Res = await autumn.entities.get(customerId, entity1.id); - let entity2Res = await autumn.entities.get(customerId, entity2.id); + const entity1Res = await autumn.entities.get(customerId, entity1.id); + const entity2Res = await autumn.entities.get(customerId, entity2.id); expectFeaturesCorrect({ customer: entity1Res, diff --git a/server/tests/attach/entities/entity5.ts b/server/tests/attach/entities/entity5.test.ts similarity index 54% rename from server/tests/attach/entities/entity5.ts rename to server/tests/attach/entities/entity5.test.ts index 12cf425c2..b7579306c 100644 --- a/server/tests/attach/entities/entity5.ts +++ b/server/tests/attach/entities/entity5.test.ts @@ -1,25 +1,22 @@ +import { beforeAll, describe, expect, test } from "bun:test"; +import { CusProductStatus } from "@autumn/shared"; import chalk from "chalk"; -import Stripe from "stripe"; -import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { AppEnv, CusProductStatus, Organization } from "@autumn/shared"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { setupBefore } from "tests/before.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import { addPrefixToProducts } from "../utils.js"; -import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; -import { TestFeature } from "tests/setup/v2Features.js"; -import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { defaultApiVersion } from "tests/constants.js"; -import { expect } from "chai"; -import { advanceTestClock } from "tests/utils/stripeUtils.js"; -import { hoursToFinalizeInvoice } from "tests/utils/constants.js"; import { addHours, addMonths } from "date-fns"; +import { defaultApiVersion } from "tests/constants.js"; +import { TestFeature } from "tests/setup/v2Features.js"; +import { hoursToFinalizeInvoice } from "tests/utils/constants.js"; +import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; +import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "aentity5"; -export let pro = constructProduct({ +export const pro = constructProduct({ items: [ constructArrearItem({ featureId: TestFeature.Words, @@ -28,7 +25,7 @@ export let pro = constructProduct({ ], type: "pro", }); -export let premium = constructProduct({ +export const premium = constructProduct({ items: [ constructArrearItem({ featureId: TestFeature.Words, @@ -39,46 +36,25 @@ export let premium = constructProduct({ }); describe(`${chalk.yellowBright(`attach/${testCase}: Testing downgrade entity product`)}`, () => { - let customerId = testCase; - let autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); + const customerId = testCase; + const autumn: AutumnInt = new AutumnInt({ version: defaultApiVersion }); let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - let curUnix = new Date().getTime(); - - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + beforeAll(async () => { + const result = await initCustomerV3({ + ctx, customerId, - db, - org, - env, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + testClockId = result.testClockId!; + + await initProductsV0({ + ctx, products: [pro, premium], prefix: testCase, }); - - await createProducts({ - autumn, - products: [pro, premium], - db, - orgId: org.id, - env, - }); - - testClockId = testClockId1; }); const newEntities = [ @@ -94,40 +70,40 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing downgrade entity pro }, ]; - let entity1 = newEntities[0]; - let entity2 = newEntities[1]; + const entity1 = newEntities[0]; + const entity2 = newEntities[1]; - it("should attach premium product to entity 1", async function () { + test("should attach premium product to entity 1", async () => { await autumn.entities.create(customerId, newEntities); await attachAndExpectCorrect({ autumn, customerId, product: premium, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, entityId: entity1.id, numSubs: 1, }); }); - it("should attach premium product to entity 2", async function () { + test("should attach premium product to entity 2", async () => { await attachAndExpectCorrect({ autumn, customerId, product: premium, - stripeCli, - db, - org, - env, + stripeCli: ctx.stripeCli, + db: ctx.db, + org: ctx.org, + env: ctx.env, entityId: entity2.id, numSubs: 2, }); }); - it("should attach pro product to entity 1", async function () { + test("should attach pro product to entity 1", async () => { await autumn.attach({ customer_id: customerId, entity_id: entity1.id, @@ -136,13 +112,13 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing downgrade entity pro const entity = await autumn.entities.get(customerId, entity1.id); const proProd = entity.products.find((p: any) => p.id == pro.id); - expect(proProd).to.exist; - expect(proProd.status).to.equal(CusProductStatus.Scheduled); + expect(proProd).toBeDefined(); + expect(proProd.status).toBe(CusProductStatus.Scheduled); }); - it("should advance test clock and have pro attached to entity 1", async function () { + test("should advance test clock and have pro attached to entity 1", async () => { await advanceTestClock({ - stripeCli, + stripeCli: ctx.stripeCli, testClockId, advanceTo: addHours( addMonths(new Date(), 1), @@ -153,16 +129,16 @@ describe(`${chalk.yellowBright(`attach/${testCase}: Testing downgrade entity pro const entity = await autumn.entities.get(customerId, entity1.id); const proProd = entity.products.find((p: any) => p.id == pro.id); - expect(proProd).to.exist; - expect(proProd.status).to.equal(CusProductStatus.Active); - expect(entity.products.length).to.equal(2); + expect(proProd).toBeDefined(); + expect(proProd.status).toBe(CusProductStatus.Active); + expect(entity.products.length).toBe(2); const entity2Res = await autumn.entities.get(customerId, entity2.id); const premiumProd = entity2Res.products.find( (p: any) => p.id == premium.id, ); - expect(premiumProd).to.exist; - expect(premiumProd.status).to.equal(CusProductStatus.Active); - expect(entity2Res.products.length).to.equal(2); + expect(premiumProd).toBeDefined(); + expect(premiumProd.status).toBe(CusProductStatus.Active); + expect(entity2Res.products.length).toBe(2); }); }); diff --git a/server/tests/attach/free/free1.ts b/server/tests/attach/free/free1.test.ts similarity index 50% rename from server/tests/attach/free/free1.ts rename to server/tests/attach/free/free1.test.ts index c736eb0ae..19c9814d3 100644 --- a/server/tests/attach/free/free1.ts +++ b/server/tests/attach/free/free1.test.ts @@ -1,28 +1,19 @@ +import { beforeAll, describe, expect, test } from "bun:test"; import { - type AppEnv, CreateFreeTrialSchema, CusProductStatus, FreeTrialDuration, LegacyVersion, - type Organization, - organizations, } from "@autumn/shared"; -import { expect } from "chai"; import chalk from "chalk"; import { addDays } from "date-fns"; -import { eq } from "drizzle-orm"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { CacheManager } from "@/external/caching/CacheManager.js"; -import { clearOrgCache } from "@/internal/orgs/orgUtils/clearOrgCache.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "free1"; @@ -58,66 +49,31 @@ export const addOn = constructProduct({ describe(`${chalk.yellowBright(`${testCase}: Testing free product with trial and attaching add on`)}`, () => { const customerId = testCase; - const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 }); + const autumn: AutumnInt = new AutumnInt({ + version: LegacyVersion.v1_4, + orgConfig: { multiple_trials: true }, + }); let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - const numUsers = 0; - - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - await db - .update(organizations) - .set({ - config: { - ...this.org.config, - multiple_trials: true, - }, - }) - .where(eq(organizations.id, org.id)); - - await clearOrgCache({ - db, - orgId: org.id, - env, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + beforeAll(async () => { + const result = await initCustomerV3({ + ctx, customerId, - db, - org, - env, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + testClockId = result.testClockId!; + + await initProductsV0({ + ctx, products: [free, addOn], prefix: testCase, }); - - await createProducts({ - autumn, - products: [free, addOn], - db, - orgId: org.id, - env, - }); - - testClockId = testClockId1!; }); - const approximateDiff = 1000 * 60 * 30; // 30 minutes - it("should attach free product with trial", async () => { + const approximateDiff = 1000 * 60 * 80; // 60 minutes + test("should attach free product with trial", async () => { const attachPreview = await autumn.attachPreview({ customer_id: customerId, product_id: free.id, @@ -131,11 +87,13 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free product with trial and const customer = await autumn.customers.get(customerId); const freeProduct = customer.products.find((p) => p.id === free.id); - expect(freeProduct).to.exist; - expect(freeProduct?.status).to.equal(CusProductStatus.Trialing); - expect(freeProduct?.current_period_end).to.approximately( - addDays(Date.now(), trial1.length).getTime(), - approximateDiff, + expect(freeProduct).toBeDefined(); + expect(freeProduct?.status).toBe(CusProductStatus.Trialing); + expect(freeProduct?.current_period_end).toBeGreaterThanOrEqual( + addDays(Date.now(), trial1.length).getTime() - approximateDiff, + ); + expect(freeProduct?.current_period_end).toBeLessThanOrEqual( + addDays(Date.now(), trial1.length).getTime() + approximateDiff, ); }); @@ -144,7 +102,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free product with trial and duration: FreeTrialDuration.Day, }); - it("should update free product's trial end date", async () => { + test("should update free product's trial end date", async () => { const attachPreview = await autumn.attachPreview({ customer_id: customerId, product_id: free.id, @@ -162,14 +120,16 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free product with trial and const customer = await autumn.customers.get(customerId); const freeProduct = customer.products.find((p) => p.id === free.id); - expect(freeProduct?.status).to.equal(CusProductStatus.Trialing); - expect(freeProduct?.current_period_end).to.approximately( - addDays(Date.now(), trial2.length).getTime(), - approximateDiff, + expect(freeProduct?.status).toBe(CusProductStatus.Trialing); + expect(freeProduct?.current_period_end).toBeGreaterThanOrEqual( + addDays(Date.now(), trial2.length).getTime() - approximateDiff, + ); + expect(freeProduct?.current_period_end).toBeLessThanOrEqual( + addDays(Date.now(), trial2.length).getTime() + approximateDiff, ); }); - it("should attach add on product", async () => { + test("should attach add on product", async () => { const attachPreview = await autumn.attachPreview({ customer_id: customerId, product_id: addOn.id, @@ -184,28 +144,8 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free product with trial and const addOnProduct = customer.products.find((p) => p.id === addOn.id); const freeProduct = customer.products.find((p) => p.id === free.id); - expect(addOnProduct).to.exist; - expect(addOnProduct?.status).to.equal(CusProductStatus.Active); - expect(freeProduct?.status).to.equal(CusProductStatus.Trialing); - }); - - after(async function () { - await db - .update(organizations) - .set({ - config: { - ...this.org.config, - multiple_trials: false, - }, - }) - .where(eq(organizations.id, org.id)); - - await clearOrgCache({ - db, - orgId: org.id, - env, - }); - - await CacheManager.disconnect(); + expect(addOnProduct).toBeDefined(); + expect(addOnProduct?.status).toBe("active"); + expect(freeProduct?.status).toBe("trialing"); }); }); diff --git a/server/tests/attach/free/free2.ts b/server/tests/attach/free/free2.test.ts similarity index 63% rename from server/tests/attach/free/free2.ts rename to server/tests/attach/free/free2.test.ts index e85805233..10b3a354f 100644 --- a/server/tests/attach/free/free2.ts +++ b/server/tests/attach/free/free2.test.ts @@ -1,23 +1,16 @@ -import { - type AppEnv, - BillingInterval, - LegacyVersion, - type Organization, -} from "@autumn/shared"; +import { beforeAll, describe, test } from "bun:test"; +import { BillingInterval, LegacyVersion } from "@autumn/shared"; import chalk from "chalk"; -import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; -import { createProducts } from "tests/utils/productUtils.js"; -import type { DrizzleCli } from "@/db/initDrizzle.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructPriceItem } from "@/internal/products/product-items/productItemUtils.js"; import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; export const free = constructProduct({ items: [ @@ -47,49 +40,24 @@ const testCase = "free2"; describe(`${chalk.yellowBright(`${testCase}: Testing free product with trial and attaching add on`)}`, () => { const customerId = testCase; const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 }); - let testClockId: string; - let db: DrizzleCli, org: Organization, env: AppEnv; - let stripeCli: Stripe; - const curUnix = new Date().getTime(); - const numUsers = 0; - - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + beforeAll(async () => { + await initCustomerV3({ + ctx, customerId, - db, - org, - env, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [free], prefix: testCase, }); - - await createProducts({ - autumn, - products: [free], - db, - orgId: org.id, - env, - }); - - testClockId = testClockId1!; }); const approximateDiff = 1000 * 60 * 30; // 30 minutes - it("should attach free product", async () => { + test("should attach free product", async () => { await autumn.attach({ customer_id: customerId, product_id: free.id, @@ -109,14 +77,14 @@ describe(`${chalk.yellowBright(`${testCase}: Testing free product with trial and interval: BillingInterval.Month, }), ]; - it("should update free product with price", async () => { + test("should update free product with price", async () => { await attachAndExpectCorrect({ autumn, customerId, product: free, - db, - org, - env, + db: ctx.db, + org: ctx.org, + env: ctx.env, attachParams: { // @ts-expect-error is_custom: true, diff --git a/server/tests/attach/others/others5.ts b/server/tests/attach/others/others5.ts index aaa438495..d403b067f 100644 --- a/server/tests/attach/others/others5.ts +++ b/server/tests/attach/others/others5.ts @@ -1,10 +1,10 @@ -import chalk from "chalk"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { features, products } from "tests/global.js"; -import { AutumnCli } from "tests/cli/AutumnCli.js"; -import { timeout } from "../../utils/genUtils.js"; import { expect } from "chai"; +import chalk from "chalk"; import { setupBefore } from "tests/before.js"; +import { AutumnCli } from "tests/cli/AutumnCli.js"; +import { features, products } from "tests/global.js"; +import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { timeout } from "../../utils/genUtils.js"; const checkEntitledOnProduct = async ({ customerId, @@ -112,7 +112,7 @@ describe(`${chalk.yellowBright( let curAllowance = 0; const oneTimeBillingUnits = products.oneTimeAddOnMetered1.prices[0].config.billing_units!; - let oneTimeQuantity = 2 * oneTimeBillingUnits; + const oneTimeQuantity = 2 * oneTimeBillingUnits; before(async function () { await setupBefore(this); @@ -134,14 +134,14 @@ describe(`${chalk.yellowBright( // }); // }); - it("should attach pro", async function () { + it("should attach pro", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.pro.id, }); }); - it("should have correct entitlements (pro)", async function () { + it("should have correct entitlements (pro)", async () => { const used = await checkEntitledOnProduct({ customerId: customerId, product: products.pro, @@ -151,7 +151,7 @@ describe(`${chalk.yellowBright( curAllowance = products.pro.entitlements.metered1.allowance! - used; }); - it("should attach one time top up", async function () { + it("should attach one time top up", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.oneTimeAddOnMetered1.id, @@ -164,7 +164,7 @@ describe(`${chalk.yellowBright( }); }); - it("should have correct entitlements (one time top up)", async function () { + it("should have correct entitlements (one time top up)", async () => { // const oneTimeAmt = oneTimeBillingUnits * oneTimeQuantity; await checkEntitledOnProduct({ @@ -195,14 +195,14 @@ describe(`${chalk.yellowBright( }); // PRO WITH OVERAGE - it("should attach pro (with overage)", async function () { + it("should attach pro (with overage)", async () => { await AutumnCli.attach({ customerId: customerId, productId: products.proWithOverage.id, }); }); - it("should have correct entitlements (pro with overage)", async function () { + it("should have correct entitlements (pro with overage)", async () => { await checkEntitledOnProduct({ customerId: customerId, product: products.proWithOverage, @@ -212,7 +212,7 @@ describe(`${chalk.yellowBright( }); }); - it("should have correct usage-based balance (balance < 0)", async function () { + it("should have correct usage-based balance (balance < 0)", async () => { const { allowed, balanceObj }: any = await AutumnCli.entitled( customerId, features.metered1.id, diff --git a/server/tests/attach/upgrade/upgrade1.ts b/server/tests/attach/upgrade/upgrade1.test.ts similarity index 74% rename from server/tests/attach/upgrade/upgrade1.ts rename to server/tests/attach/upgrade/upgrade1.test.ts index eaf2e3d0d..139e7aff4 100644 --- a/server/tests/attach/upgrade/upgrade1.ts +++ b/server/tests/attach/upgrade/upgrade1.test.ts @@ -1,18 +1,18 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import { addWeeks } from "date-fns"; import type { Stripe } from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { advanceTestClock } from "tests/utils/stripeUtils.js"; -import { addPrefixToProducts } from "tests/utils/testProductUtils/testProductUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; // UNCOMMENT FROM HERE const pro = constructProduct({ @@ -42,42 +42,30 @@ describe(`${chalk.yellowBright("upgrade1: Testing usage upgrades")}`, () => { let org: Organization; let env: AppEnv; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, + customerId, + customerData: {}, + attachPm: "success", + withTestClock: true, + }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium, growth], prefix: customerId, }); - await createProducts({ - autumn: autumnJs, - products: [pro, premium, growth], - db, - orgId: org.id, - env, - customerId, - }); - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, - customerId, - db, - org, - env, - attachPm: "success", - }); - testClockId = testClockId1!; }); - it("should attach pro product", async () => { + test("should attach pro product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -89,7 +77,7 @@ describe(`${chalk.yellowBright("upgrade1: Testing usage upgrades")}`, () => { }); }); - it("should attach premium product", async () => { + test("should attach premium product", async () => { const wordsUsage = 100000; await autumn.track({ customer_id: customerId, @@ -115,7 +103,7 @@ describe(`${chalk.yellowBright("upgrade1: Testing usage upgrades")}`, () => { }); }); - it("should attach growth product", async () => { + test("should attach growth product", async () => { const wordsUsage = 200000; await autumn.track({ customer_id: customerId, diff --git a/server/tests/attach/upgrade/upgrade2.ts b/server/tests/attach/upgrade/upgrade2.test.ts similarity index 77% rename from server/tests/attach/upgrade/upgrade2.ts rename to server/tests/attach/upgrade/upgrade2.test.ts index d4081eecc..23863077a 100644 --- a/server/tests/attach/upgrade/upgrade2.ts +++ b/server/tests/attach/upgrade/upgrade2.test.ts @@ -1,18 +1,18 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import { addWeeks } from "date-fns"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { advanceTestClock } from "tests/utils/stripeUtils.js"; -import { addPrefixToProducts } from "tests/utils/testProductUtils/testProductUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; // Shared products for attach tests const testCase = "upgrade2"; @@ -54,42 +54,30 @@ describe(`${chalk.yellowBright("upgrade2: Testing usage upgrades with monthly -> let curUnix = new Date().getTime(); - before(async function () { - await setupBefore(this); - const { autumnJs } = this; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - db = this.db; - org = this.org; - env = this.env; - - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, customerId, - db, - org, - env, + customerData: {}, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, proAnnual, premiumAnnual], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro, proAnnual, premiumAnnual], - db, - orgId: org.id, - env, - }); - testClockId = testClockId1!; }); - it("should attach pro product", async () => { + test("should attach pro product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -101,7 +89,7 @@ describe(`${chalk.yellowBright("upgrade2: Testing usage upgrades with monthly -> }); }); - it("should attach pro annual product", async () => { + test("should attach pro annual product", async () => { await autumn.track({ customer_id: customerId, feature_id: TestFeature.Words, @@ -127,7 +115,7 @@ describe(`${chalk.yellowBright("upgrade2: Testing usage upgrades with monthly -> }); return; - it("should attach premium annual product", async () => { + test("should attach premium annual product", async () => { await autumn.track({ customer_id: customerId, feature_id: TestFeature.Words, diff --git a/server/tests/attach/upgrade/upgrade3.ts b/server/tests/attach/upgrade/upgrade3.test.ts similarity index 79% rename from server/tests/attach/upgrade/upgrade3.ts rename to server/tests/attach/upgrade/upgrade3.test.ts index 612a065fa..f9b9a76ed 100644 --- a/server/tests/attach/upgrade/upgrade3.ts +++ b/server/tests/attach/upgrade/upgrade3.test.ts @@ -1,19 +1,19 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import { addWeeks } from "date-fns"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { timeout } from "@/utils/genUtils.js"; import { constructArrearProratedItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "upgrade3"; @@ -69,41 +69,30 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with arrear prorat let curUnix = new Date().getTime(); let numUsers = 0; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, customerId, - db, - org, - env, + customerData: {}, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium, proAnnual], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro, premium, proAnnual], - db, - orgId: org.id, - env, - }); - testClockId = testClockId1!; }); - it("should attach pro product (arrear prorated)", async () => { + test("should attach pro product (arrear prorated)", async () => { // 1. Create multiple entities const entities = await autumn.entities.create(customerId, [ { @@ -136,7 +125,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with arrear prorat }); }); - it("should create entity, then upgrade to premium product (arrear prorated)", async () => { + test("should create entity, then upgrade to premium product (arrear prorated)", async () => { curUnix = await advanceTestClock({ stripeCli, testClockId, @@ -172,7 +161,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with arrear prorat }); }); - it("should upgrade to pro-annual product (arrear prorated)", async () => { + test("should upgrade to pro-annual product (arrear prorated)", async () => { curUnix = await advanceTestClock({ stripeCli, testClockId, diff --git a/server/tests/attach/upgrade/upgrade4.ts b/server/tests/attach/upgrade/upgrade4.test.ts similarity index 77% rename from server/tests/attach/upgrade/upgrade4.ts rename to server/tests/attach/upgrade/upgrade4.test.ts index 48996f0d4..5d5fbf52d 100644 --- a/server/tests/attach/upgrade/upgrade4.ts +++ b/server/tests/attach/upgrade/upgrade4.test.ts @@ -1,18 +1,18 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import { addWeeks } from "date-fns"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructPrepaidItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "upgrade4"; @@ -71,37 +71,26 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid conti let curUnix = new Date().getTime(); const numUsers = 0; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, customerId, - db, - org, - env, + customerData: {}, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium, proAnnual], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro, premium, proAnnual], - db, - orgId: org.id, - env, - }); - testClockId = testClockId1!; }); @@ -112,7 +101,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid conti }, ]; - it("should attach pro product (arrear prorated)", async () => { + test("should attach pro product (arrear prorated)", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -132,7 +121,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid conti }, ]; - it("should create entity, then upgrade to premium product (arrear prorated)", async () => { + test("should create entity, then upgrade to premium product (arrear prorated)", async () => { curUnix = await advanceTestClock({ stripeCli, testClockId, @@ -158,7 +147,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid conti }, ]; - it("should upgrade to pro-annual product (arrear prorated)", async () => { + test("should upgrade to pro-annual product (arrear prorated)", async () => { curUnix = await advanceTestClock({ stripeCli, testClockId, diff --git a/server/tests/attach/upgrade/upgrade5.ts b/server/tests/attach/upgrade/upgrade5.test.ts similarity index 73% rename from server/tests/attach/upgrade/upgrade5.ts rename to server/tests/attach/upgrade/upgrade5.test.ts index 0c1d0f74c..1db7f5e3b 100644 --- a/server/tests/attach/upgrade/upgrade5.ts +++ b/server/tests/attach/upgrade/upgrade5.test.ts @@ -1,18 +1,18 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import { addWeeks } from "date-fns"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { createProducts } from "tests/utils/productUtils.js"; import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructPrepaidItem } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "upgrade5"; @@ -48,37 +48,26 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid singl let curUnix = new Date().getTime(); const numUsers = 0; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, customerId, - db, - org, - env, + customerData: {}, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro, premium], - db, - orgId: org.id, - env, - }); - testClockId = testClockId1!; }); @@ -89,7 +78,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid singl }, ]; - it("should attach pro product (prepaid single use)", async () => { + test("should attach pro product (prepaid single use)", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -109,7 +98,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrades with prepaid singl }, ]; - it("should upgrade to premium product (prepaid single use)", async () => { + test("should upgrade to premium product (prepaid single use)", async () => { curUnix = await advanceTestClock({ stripeCli, testClockId, diff --git a/server/tests/attach/upgrade/upgrade6.ts b/server/tests/attach/upgrade/upgrade6.test.ts similarity index 81% rename from server/tests/attach/upgrade/upgrade6.ts rename to server/tests/attach/upgrade/upgrade6.test.ts index 53c650656..55c8ad3c9 100644 --- a/server/tests/attach/upgrade/upgrade6.ts +++ b/server/tests/attach/upgrade/upgrade6.test.ts @@ -1,14 +1,14 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { TestFeature } from "tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; import { expectAutumnError } from "tests/utils/expectUtils/expectErrUtils.js"; import { expectFeaturesCorrect } from "tests/utils/expectUtils/expectFeaturesCorrect.js"; import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; import { expectSubItemsCorrect } from "tests/utils/expectUtils/expectSubUtils.js"; -import { createProducts } from "tests/utils/productUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { attachFailedPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; @@ -19,8 +19,8 @@ import { constructArrearProratedItem, } from "@/utils/scriptUtils/constructItem.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "upgrade6"; @@ -53,41 +53,30 @@ describe(`${chalk.yellowBright(`${testCase}: Testing failed upgrades`)}`, () => let db: DrizzleCli, org: Organization, env: AppEnv; let stripeCli: Stripe; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, customerId, - db, - org, - env, + customerData: {}, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro, premium], - db, - orgId: org.id, - env, - }); - testClockId = testClockId1!; }); - it("should attach pro product", async () => { + test("should attach pro product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -100,7 +89,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing failed upgrades`)}`, () => }); const usage = 100012; - it("should upgrade to premium product and fail", async () => { + test("should upgrade to premium product and fail", async () => { await autumn.track({ customer_id: customerId, feature_id: TestFeature.Words, diff --git a/server/tests/attach/upgrade/upgrade7.ts b/server/tests/attach/upgrade/upgrade7.test.ts similarity index 65% rename from server/tests/attach/upgrade/upgrade7.ts rename to server/tests/attach/upgrade/upgrade7.test.ts index 3ce18ed83..019c6c3d5 100644 --- a/server/tests/attach/upgrade/upgrade7.ts +++ b/server/tests/attach/upgrade/upgrade7.test.ts @@ -1,14 +1,14 @@ +import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; import chalk from "chalk"; import type Stripe from "stripe"; -import { setupBefore } from "tests/before.js"; import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; -import { createProducts } from "tests/utils/productUtils.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; -import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; -import { addPrefixToProducts } from "../utils.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; const testCase = "upgrade7"; @@ -29,41 +29,30 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrade via cancel + attach let db: DrizzleCli, org: Organization, env: AppEnv; let stripeCli: Stripe; - before(async function () { - await setupBefore(this); - const { autumnJs } = this; - db = this.db; - org = this.org; - env = this.env; + beforeAll(async () => { + db = ctx.db; + org = ctx.org; + env = ctx.env; + stripeCli = ctx.stripeCli; - stripeCli = this.stripeCli; - - const { testClockId: testClockId1 } = await initCustomer({ - autumn: autumnJs, + const { testClockId: testClockId1 } = await initCustomerV3({ + ctx, customerId, - db, - org, - env, + customerData: {}, attachPm: "success", + withTestClock: true, }); - addPrefixToProducts({ + await initProductsV0({ + ctx, products: [pro, premium], prefix: testCase, }); - await createProducts({ - autumn, - products: [pro, premium], - db, - orgId: org.id, - env, - }); - testClockId = testClockId1!; }); - it("should attach pro product", async () => { + test("should attach pro product", async () => { await attachAndExpectCorrect({ autumn, customerId, @@ -75,7 +64,7 @@ describe(`${chalk.yellowBright(`${testCase}: Testing upgrade via cancel + attach }); }); - it("should cancel than attach premium product", async () => { + test("should cancel than attach premium product", async () => { await autumn.cancel({ customer_id: customerId, product_id: pro.id, diff --git a/server/tests/check/basic/check2.test.ts b/server/tests/check/basic/check2.test.ts index 18e073385..2cc09a36c 100644 --- a/server/tests/check/basic/check2.test.ts +++ b/server/tests/check/basic/check2.test.ts @@ -3,6 +3,7 @@ import { ApiVersion, type CheckResponse, type CheckResponseV0, + SuccessCode, } from "@autumn/shared"; import chalk from "chalk"; import { TestFeature } from "tests/setup/v2Features.js"; @@ -31,7 +32,7 @@ const freeProd = constructProduct({ const testCase = "check2"; -describe(`${chalk.yellowBright("check2: test /check when feature attached")}`, () => { +describe(`${chalk.yellowBright("check2: test /check on boolean feature")}`, () => { const customerId = "check2"; const autumnV0: AutumnInt = new AutumnInt({ version: ApiVersion.V0_2 }); const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 }); @@ -48,29 +49,39 @@ describe(`${chalk.yellowBright("check2: test /check when feature attached")}`, ( products: [freeProd], prefix: testCase, }); + + await autumnV1.attach({ + customer_id: customerId, + product_id: freeProd.id, + }); }); - test("should have correct check response when feature not attached (v0)", async () => { + test("v0 response", async () => { const res = (await autumnV0.check({ customer_id: customerId, - feature_id: TestFeature.Messages, + feature_id: TestFeature.Dashboard, })) as unknown as CheckResponseV0; - expect(res.allowed).toBe(false); + expect(res.allowed).toBe(true); expect(res.balances).toBeDefined(); - expect(res.balances).toHaveLength(0); + expect(res.balances).toHaveLength(1); + expect(res.balances[0]).toStrictEqual({ + feature_id: TestFeature.Dashboard, + balance: null, + }); }); - test("should have correct check response when feature not attached (v1)", async () => { + test("v1 response", async () => { const res = (await autumnV1.check({ customer_id: customerId, - feature_id: TestFeature.Messages, + feature_id: TestFeature.Dashboard, })) as unknown as CheckResponse; - expect(res.allowed).toBe(false); - expect(res.customer_id).toBe(customerId); - expect(res.feature_id).toBe(TestFeature.Messages); - expect(res.code).toBeDefined(); - expect(res.required_balance).toBe(1); + expect(res).toStrictEqual({ + customer_id: customerId, + feature_id: TestFeature.Dashboard, + code: SuccessCode.FeatureFound, + allowed: true, + }); }); }); diff --git a/server/tests/check/basic/check3.test.ts b/server/tests/check/basic/check3.test.ts index 18e073385..38f14dcd0 100644 --- a/server/tests/check/basic/check3.test.ts +++ b/server/tests/check/basic/check3.test.ts @@ -3,6 +3,8 @@ import { ApiVersion, type CheckResponse, type CheckResponseV0, + EntInterval, + SuccessCode, } from "@autumn/shared"; import chalk from "chalk"; import { TestFeature } from "tests/setup/v2Features.js"; @@ -13,11 +15,6 @@ import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; -const dashboardFeature = constructFeatureItem({ - featureId: TestFeature.Dashboard, - isBoolean: true, -}); - const messagesFeature = constructFeatureItem({ featureId: TestFeature.Messages, includedUsage: 1000, @@ -26,13 +23,13 @@ const messagesFeature = constructFeatureItem({ const freeProd = constructProduct({ type: "free", isDefault: false, - items: [dashboardFeature, messagesFeature], + items: [messagesFeature], }); -const testCase = "check2"; +const testCase = "check3"; -describe(`${chalk.yellowBright("check2: test /check when feature attached")}`, () => { - const customerId = "check2"; +describe(`${chalk.yellowBright("check3: test /check on metered feature")}`, () => { + const customerId = "check3"; const autumnV0: AutumnInt = new AutumnInt({ version: ApiVersion.V0_2 }); const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 }); @@ -48,29 +45,55 @@ describe(`${chalk.yellowBright("check2: test /check when feature attached")}`, ( products: [freeProd], prefix: testCase, }); + + await autumnV1.attach({ + customer_id: customerId, + product_id: freeProd.id, + }); }); - test("should have correct check response when feature not attached (v0)", async () => { + test("v0 response", async () => { const res = (await autumnV0.check({ customer_id: customerId, feature_id: TestFeature.Messages, })) as unknown as CheckResponseV0; - expect(res.allowed).toBe(false); + expect(res.allowed).toBe(true); expect(res.balances).toBeDefined(); - expect(res.balances).toHaveLength(0); + expect(res.balances).toHaveLength(1); + expect(res.balances[0]).toStrictEqual({ + feature_id: TestFeature.Messages, + required: 1, + balance: 1000, + }); }); - test("should have correct check response when feature not attached (v1)", async () => { + test("v1 response", async () => { const res = (await autumnV1.check({ customer_id: customerId, feature_id: TestFeature.Messages, })) as unknown as CheckResponse; - expect(res.allowed).toBe(false); - expect(res.customer_id).toBe(customerId); - expect(res.feature_id).toBe(TestFeature.Messages); - expect(res.code).toBeDefined(); - expect(res.required_balance).toBe(1); + const expectedRes = { + allowed: true, + customer_id: customerId, + feature_id: TestFeature.Messages, + required_balance: 1, + code: SuccessCode.FeatureFound, + interval: EntInterval.Month, + interval_count: 1, + unlimited: false, + balance: 1000, + usage: 0, + included_usage: 1000, + // next_reset_at: 1763833597035, + overage_allowed: false, + }; + + for (const key in expectedRes) { + expect(res[key as keyof CheckResponse]).toBe( + expectedRes[key as keyof typeof expectedRes], + ); + } }); }); diff --git a/server/tests/check/basic/check4.test.ts b/server/tests/check/basic/check4.test.ts index 18e073385..f61d41263 100644 --- a/server/tests/check/basic/check4.test.ts +++ b/server/tests/check/basic/check4.test.ts @@ -3,6 +3,7 @@ import { ApiVersion, type CheckResponse, type CheckResponseV0, + SuccessCode, } from "@autumn/shared"; import chalk from "chalk"; import { TestFeature } from "tests/setup/v2Features.js"; @@ -13,26 +14,21 @@ import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; -const dashboardFeature = constructFeatureItem({ - featureId: TestFeature.Dashboard, - isBoolean: true, -}); - const messagesFeature = constructFeatureItem({ featureId: TestFeature.Messages, - includedUsage: 1000, + unlimited: true, }); const freeProd = constructProduct({ type: "free", isDefault: false, - items: [dashboardFeature, messagesFeature], + items: [messagesFeature], }); -const testCase = "check2"; +const testCase = "check4"; -describe(`${chalk.yellowBright("check2: test /check when feature attached")}`, () => { - const customerId = "check2"; +describe(`${chalk.yellowBright("check4: test /check on unlimited feature")}`, () => { + const customerId = "check4"; const autumnV0: AutumnInt = new AutumnInt({ version: ApiVersion.V0_2 }); const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 }); @@ -48,29 +44,53 @@ describe(`${chalk.yellowBright("check2: test /check when feature attached")}`, ( products: [freeProd], prefix: testCase, }); + + await autumnV1.attach({ + customer_id: customerId, + product_id: freeProd.id, + }); }); - test("should have correct check response when feature not attached (v0)", async () => { + test("v0 response", async () => { const res = (await autumnV0.check({ customer_id: customerId, feature_id: TestFeature.Messages, })) as unknown as CheckResponseV0; - expect(res.allowed).toBe(false); + expect(res.allowed).toBe(true); expect(res.balances).toBeDefined(); - expect(res.balances).toHaveLength(0); + expect(res.balances).toHaveLength(1); + expect(res.balances[0]).toStrictEqual({ + balance: null, + feature_id: TestFeature.Messages, + unlimited: true, + usage_allowed: false, + required: null, + }); }); - test("should have correct check response when feature not attached (v1)", async () => { + test("v1 response", async () => { const res = (await autumnV1.check({ customer_id: customerId, feature_id: TestFeature.Messages, })) as unknown as CheckResponse; - expect(res.allowed).toBe(false); - expect(res.customer_id).toBe(customerId); - expect(res.feature_id).toBe(TestFeature.Messages); - expect(res.code).toBeDefined(); - expect(res.required_balance).toBe(1); + console.log(res); + + const expectedRes = { + allowed: true, + customer_id: customerId, + feature_id: TestFeature.Messages as string, + required_balance: 1, + code: SuccessCode.FeatureFound, + unlimited: true, + balance: null, + usage: 0, + included_usage: 0, + next_reset_at: null, + overage_allowed: false, + }; + + expect(expectedRes).toMatchObject(res); }); }); diff --git a/server/tests/check/basic/check5.test.ts b/server/tests/check/basic/check5.test.ts new file mode 100644 index 000000000..9fbb08d9d --- /dev/null +++ b/server/tests/check/basic/check5.test.ts @@ -0,0 +1,99 @@ +import { beforeAll, describe, expect, test } from "bun:test"; +import { + ApiVersion, + type CheckResponse, + type CheckResponseV0, + type LimitedItem, + SuccessCode, +} from "@autumn/shared"; +import chalk from "chalk"; +import { TestFeature } from "tests/setup/v2Features.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; + +const messagesFeature = constructArrearItem({ + featureId: TestFeature.Messages, + price: 0.5, + includedUsage: 100, +}) as LimitedItem; + +const proProd = constructProduct({ + type: "free", + isDefault: false, + items: [messagesFeature], +}); + +const testCase = "check5"; + +describe(`${chalk.yellowBright("check5: test /check on usage-based feature")}`, () => { + const customerId = "check5"; + const autumnV0: AutumnInt = new AutumnInt({ version: ApiVersion.V0_2 }); + const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 }); + + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + attachPm: "success", + withTestClock: false, + }); + + await initProductsV0({ + ctx, + products: [proProd], + prefix: testCase, + }); + + await autumnV1.attach({ + customer_id: customerId, + product_id: proProd.id, + }); + }); + + test("v0 response", async () => { + const res = (await autumnV0.check({ + customer_id: customerId, + feature_id: TestFeature.Messages, + })) as unknown as CheckResponseV0; + + expect(res.allowed).toBe(true); + expect(res.balances).toBeDefined(); + expect(res.balances).toHaveLength(1); + expect(res.balances[0]).toMatchObject({ + balance: messagesFeature.included_usage, + feature_id: TestFeature.Messages, + unlimited: false, + usage_allowed: true, + required: null, + }); + }); + + test("v1 response", async () => { + const res = (await autumnV1.check({ + customer_id: customerId, + feature_id: TestFeature.Messages, + })) as unknown as CheckResponse; + + const expectedRes = { + allowed: true, + customer_id: customerId, + feature_id: TestFeature.Messages as string, + required_balance: 1, + code: SuccessCode.FeatureFound, + unlimited: false, + balance: messagesFeature.included_usage, + usage: 0, + included_usage: messagesFeature.included_usage, + overage_allowed: true, + interval: messagesFeature.interval, + interval_count: 1, + }; + + expect(res).toMatchObject(expectedRes); + expect(res.next_reset_at).toBeDefined(); + }); +}); diff --git a/server/tests/check/basic/check6.test.ts b/server/tests/check/basic/check6.test.ts new file mode 100644 index 000000000..f6490345d --- /dev/null +++ b/server/tests/check/basic/check6.test.ts @@ -0,0 +1,130 @@ +import { beforeAll, describe, expect, test } from "bun:test"; +import { + ApiVersion, + type CheckResponse, + type CheckResponseV0, + type LimitedItem, + SuccessCode, +} from "@autumn/shared"; +import chalk from "chalk"; +import { TestFeature } from "tests/setup/v2Features.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { + constructArrearItem, + constructFeatureItem, +} from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; + +const monthlyMessages = constructArrearItem({ + featureId: TestFeature.Messages, + price: 0.5, + includedUsage: 100, +}) as LimitedItem; + +const lifetimeMessages = constructFeatureItem({ + featureId: TestFeature.Messages, + interval: null, + includedUsage: 1000, +}) as LimitedItem; + +const proProd = constructProduct({ + type: "pro", + isDefault: false, + items: [monthlyMessages, lifetimeMessages], +}); + +const testCase = "check6"; + +describe(`${chalk.yellowBright("check6: test /check on feature with multiple balances (one off + monthly)")}`, () => { + const customerId = "check6"; + const autumnV0: AutumnInt = new AutumnInt({ version: ApiVersion.V0_2 }); + const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 }); + + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + attachPm: "success", + withTestClock: false, + }); + + await initProductsV0({ + ctx, + products: [proProd], + prefix: testCase, + }); + + await autumnV1.attach({ + customer_id: customerId, + product_id: proProd.id, + }); + }); + + test("v0 response", async () => { + const res = (await autumnV0.check({ + customer_id: customerId, + feature_id: TestFeature.Messages, + })) as unknown as CheckResponseV0; + + console.log(res); + + expect(res.allowed).toBe(true); + expect(res.balances).toBeDefined(); + expect(res.balances).toHaveLength(1); + expect(res.balances[0]).toMatchObject({ + balance: monthlyMessages.included_usage + lifetimeMessages.included_usage, + feature_id: TestFeature.Messages, + required: null, + unlimited: false, + usage_allowed: true, + }); + }); + + test("v1 response", async () => { + const res = (await autumnV1.check({ + customer_id: customerId, + feature_id: TestFeature.Messages, + })) as unknown as CheckResponse; + + const totalIncludedUsage = + monthlyMessages.included_usage + lifetimeMessages.included_usage; + + const lifetimeBreakdown = { + balance: lifetimeMessages.included_usage, + included_usage: lifetimeMessages.included_usage, + interval: "lifetime", + interval_count: 1, + next_reset_at: null, + usage: 0, + }; + + const monthlyBreakdown = { + balance: monthlyMessages.included_usage, + included_usage: monthlyMessages.included_usage, + interval: "month", + interval_count: 1, + usage: 0, + }; + + const expectedRes = { + allowed: true, + customer_id: customerId, + feature_id: TestFeature.Messages as string, + required_balance: 1, + code: SuccessCode.FeatureFound, + unlimited: false, + balance: totalIncludedUsage, + interval: "multiple", + interval_count: null, + usage: 0, + included_usage: totalIncludedUsage, + overage_allowed: true, + breakdown: [lifetimeBreakdown, monthlyBreakdown], + }; + + expect(res).toMatchObject(expectedRes); + }); +}); diff --git a/server/tests/check/basic/check7.test.ts b/server/tests/check/basic/check7.test.ts new file mode 100644 index 000000000..53188301f --- /dev/null +++ b/server/tests/check/basic/check7.test.ts @@ -0,0 +1,102 @@ +import { beforeAll, describe, expect, test } from "bun:test"; +import { + ApiVersion, + type CheckResponse, + type CheckResponseV0, + type LimitedItem, + SuccessCode, +} from "@autumn/shared"; +import chalk from "chalk"; +import { TestFeature } from "tests/setup/v2Features.js"; +import ctx from "tests/utils/testInitUtils/createTestContext.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js"; +import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js"; + +const messagesFeature = constructArrearItem({ + featureId: TestFeature.Messages, + price: 0.5, + includedUsage: 100, + usageLimit: 500, +}) as LimitedItem; + +const proProd = constructProduct({ + type: "pro", + isDefault: false, + items: [messagesFeature], +}); + +const testCase = "check7"; + +describe(`${chalk.yellowBright("check7: test /check on feature with usage limits")}`, () => { + const customerId = "check7"; + const autumnV0: AutumnInt = new AutumnInt({ version: ApiVersion.V0_2 }); + const autumnV1: AutumnInt = new AutumnInt({ version: ApiVersion.V1_2 }); + + beforeAll(async () => { + await initCustomerV3({ + ctx, + customerId, + attachPm: "success", + withTestClock: false, + }); + + await initProductsV0({ + ctx, + products: [proProd], + prefix: testCase, + }); + + await autumnV1.attach({ + customer_id: customerId, + product_id: proProd.id, + }); + }); + + test("v0 response", async () => { + const res = (await autumnV0.check({ + customer_id: customerId, + feature_id: TestFeature.Messages, + required_balance: messagesFeature.usage_limit! + 1, + })) as unknown as CheckResponseV0; + + expect(res.allowed).toBe(false); + expect(res.balances).toBeDefined(); + expect(res.balances).toHaveLength(1); + expect(res.balances[0]).toMatchObject({ + balance: messagesFeature.included_usage, + required: messagesFeature.usage_limit! + 1, + feature_id: TestFeature.Messages, + }); + }); + + test("v1 response", async () => { + const res = (await autumnV1.check({ + customer_id: customerId, + feature_id: TestFeature.Messages, + required_balance: messagesFeature.usage_limit! + 1, + })) as unknown as CheckResponse; + + const expectedRes = { + allowed: false, + customer_id: customerId, + balance: messagesFeature.included_usage, + feature_id: TestFeature.Messages as string, + required_balance: messagesFeature.usage_limit! + 1, + code: SuccessCode.FeatureFound, + unlimited: false, + usage: 0, + included_usage: messagesFeature.included_usage, + overage_allowed: false, + + usage_limit: messagesFeature.usage_limit!, + interval: "month", + interval_count: 1, + }; + + expect(res).toMatchObject(expectedRes); + expect(res.next_reset_at).toBeDefined(); + }); +}); diff --git a/server/tests/global.ts b/server/tests/global.ts index d3ff60898..3a1b68c7a 100644 --- a/server/tests/global.ts +++ b/server/tests/global.ts @@ -981,6 +981,8 @@ export const cleanFeatures = async () => { } }; +await cleanFeatures(); + // before(async function () { // try { // this.env = AppEnv.Sandbox; diff --git a/server/tests/utils/stripeUtils.ts b/server/tests/utils/stripeUtils.ts index e6218366b..b0762b42b 100644 --- a/server/tests/utils/stripeUtils.ts +++ b/server/tests/utils/stripeUtils.ts @@ -34,19 +34,24 @@ export const completeCheckoutForm = async ( ) => { let browser; - if (process.env.NODE_ENV === "development" && !isLocal) { - const session = await client.sessions.create(); - browser = await puppeteer.connect({ - browserWSEndpoint: session!.wsEndpoint, - defaultViewport: null, - }); - } else { - browser = await puppeteer.launch({ - headless: false, - executablePath: "/Applications/Chromium.app/Contents/MacOS/Chromium", - args: ["--no-sandbox", "--disable-setuid-sandbox"], - }); - } + // if (process.env.NODE_ENV === "development" && !isLocal) { + // const session = await client.sessions.create(); + // browser = await puppeteer.connect({ + // browserWSEndpoint: session!.wsEndpoint, + // defaultViewport: null, + // }); + // } else { + // browser = await puppeteer.launch({ + // headless: false, + // executablePath: "/Applications/Chromium.app/Contents/MacOS/Chromium", + // args: ["--no-sandbox", "--disable-setuid-sandbox"], + // }); + // } + browser = await puppeteer.launch({ + headless: false, + executablePath: "/Applications/Chromium.app/Contents/MacOS/Chromium", + args: ["--no-sandbox", "--disable-setuid-sandbox"], + }); try { const page = await browser.newPage(); diff --git a/server/tests/utils/stripeUtils/completeInvoiceCheckout.ts b/server/tests/utils/stripeUtils/completeInvoiceCheckout.ts index 7da20dcc8..d8b13e54d 100644 --- a/server/tests/utils/stripeUtils/completeInvoiceCheckout.ts +++ b/server/tests/utils/stripeUtils/completeInvoiceCheckout.ts @@ -1,15 +1,11 @@ import "dotenv/config"; -import { Stripe } from "stripe"; import puppeteer from "puppeteer-core"; -import Browserbase from "@browserbasehq/sdk"; - -import { Hyperbrowser } from "@hyperbrowser/sdk"; import { timeout } from "../genUtils.js"; -const client = new Hyperbrowser({ - apiKey: process.env.HYPERBROWSER_API_KEY, -}); +// const client = new Hyperbrowser({ +// apiKey: process.env.HYPERBROWSER_API_KEY, +// }); export const completeInvoiceCheckout = async ({ url, @@ -20,19 +16,20 @@ export const completeInvoiceCheckout = async ({ }) => { let browser; - if (process.env.NODE_ENV === "development" && !isLocal) { - const session = await client.sessions.create(); - browser = await puppeteer.connect({ - browserWSEndpoint: session!.wsEndpoint, - defaultViewport: null, - }); - } else { - browser = await puppeteer.launch({ - headless: false, - executablePath: "/Applications/Chromium.app/Contents/MacOS/Chromium", - args: ["--no-sandbox", "--disable-setuid-sandbox"], - }); - } + // if (process.env.NODE_ENV === "development" && !isLocal) { + // const session = await client.sessions.create(); + // browser = await puppeteer.connect({ + // browserWSEndpoint: session!.wsEndpoint, + // defaultViewport: null, + // }); + // } else { + + // } + browser = await puppeteer.launch({ + headless: false, + executablePath: "/Applications/Chromium.app/Contents/MacOS/Chromium", + args: ["--no-sandbox", "--disable-setuid-sandbox"], + }); try { const page = await browser.newPage(); diff --git a/shared/api/balances/check/previousVersions/CheckResponseV0.ts b/shared/api/balances/check/previousVersions/CheckResponseV0.ts index dcb010a6c..b47cd4dd8 100644 --- a/shared/api/balances/check/previousVersions/CheckResponseV0.ts +++ b/shared/api/balances/check/previousVersions/CheckResponseV0.ts @@ -1,4 +1,3 @@ -import { EntInterval } from "@models/productModels/entModels/entEnums.js"; import { z } from "zod/v4"; export const CheckResponseV0Schema = z.object({ @@ -6,10 +5,14 @@ export const CheckResponseV0Schema = z.object({ balances: z.array( z.object({ feature_id: z.string(), - unlimited: z.boolean().nullish(), - interval: z.enum(EntInterval).nullish(), + required: z.number().nullish(), balance: z.number().nullish(), - used: z.number().nullish(), + unlimited: z.boolean().optional(), + usage_allowed: z.boolean().optional(), + // unlimited: z.boolean().nullish(), + // interval: z.enum(EntInterval).nullish(), + // balance: z.number().nullish(), + // used: z.number().nullish(), }), ), }); diff --git a/shared/api/core/checkModels.ts b/shared/api/core/checkModels.ts index 5eb44275a..451903c36 100644 --- a/shared/api/core/checkModels.ts +++ b/shared/api/core/checkModels.ts @@ -91,10 +91,13 @@ export const CheckResultSchema = z description: "The ID of the entity (if provided)", example: "entity_123", }), - required_balance: z.number().meta({ - description: "The required balance for this check", - example: 1, - }), + required_balance: z + .number() + .meta({ + description: "The required balance for this check", + example: 1, + }) + .optional(), // not present for boolean features code: z.string().meta({ description: "Response code indicating the result", example: "allowed",