chore: solving type errors in test files
This commit is contained in:
@@ -1 +1 @@
|
||||
1.2.19
|
||||
1.3.2
|
||||
@@ -5,6 +5,7 @@
|
||||
- JS Doc comments should be SHORT and SWEET. Don't need examples unless ABSOLUTELY necessary
|
||||
- When writing DB queries, for the `customers`, `products` and `features` tables (and others possibly not mentioned here), the primary key when updating is `internal_id`, not `id`
|
||||
- When using db schemas in Drizzle, import them from '@autumn/shared', and don't do schemas.
|
||||
- Do NOT use "any" type.
|
||||
|
||||
# Testing
|
||||
- When writing tests, ALWAYS read:
|
||||
|
||||
37
bun.lock
37
bun.lock
@@ -120,7 +120,7 @@
|
||||
"recaseai": "^0.0.37",
|
||||
"resend": "^4.1.1",
|
||||
"semver": "^7.7.2",
|
||||
"stripe": "18.4.0-beta.2",
|
||||
"stripe": "catalog:",
|
||||
"svix": "^1.45.1",
|
||||
"tsc-alias": "^1.8.16",
|
||||
"ws": "^8.18.0",
|
||||
@@ -159,13 +159,13 @@
|
||||
"dotenv": "^16.5.0",
|
||||
"drizzle-kit": "catalog:",
|
||||
"drizzle-orm": "catalog:",
|
||||
"stripe": "catalog:",
|
||||
"yaml": "^2.8.1",
|
||||
"zod-openapi": "^5.4.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/node": "^24.0.3",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20250731.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"nodemon": "^3.1.7",
|
||||
"tsx": "^4.19.4",
|
||||
@@ -262,6 +262,7 @@
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@typescript/native-preview": "^7.0.0-dev.20251111.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
@@ -270,12 +271,14 @@
|
||||
"typescript": "~5.7.2",
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vite": "^6.2.0",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
"catalog": {
|
||||
"drizzle-kit": "^0.31.1",
|
||||
"drizzle-orm": "0.43.1",
|
||||
"stripe": "18.4.0-beta.2",
|
||||
},
|
||||
"packages": {
|
||||
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="],
|
||||
@@ -486,23 +489,23 @@
|
||||
|
||||
"@better-fetch/fetch": ["@better-fetch/fetch@1.1.18", "", {}, "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@2.3.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.4", "@biomejs/cli-darwin-x64": "2.3.4", "@biomejs/cli-linux-arm64": "2.3.4", "@biomejs/cli-linux-arm64-musl": "2.3.4", "@biomejs/cli-linux-x64": "2.3.4", "@biomejs/cli-linux-x64-musl": "2.3.4", "@biomejs/cli-win32-arm64": "2.3.4", "@biomejs/cli-win32-x64": "2.3.4" }, "bin": { "biome": "bin/biome" } }, "sha512-TU08LXjBHdy0mEY9APtEtZdNQQijXUDSXR7IK1i45wgoPD5R0muK7s61QcFir6FpOj/RP1+YkPx5QJlycXUU3w=="],
|
||||
"@biomejs/biome": ["@biomejs/biome@2.3.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.5", "@biomejs/cli-darwin-x64": "2.3.5", "@biomejs/cli-linux-arm64": "2.3.5", "@biomejs/cli-linux-arm64-musl": "2.3.5", "@biomejs/cli-linux-x64": "2.3.5", "@biomejs/cli-linux-x64-musl": "2.3.5", "@biomejs/cli-win32-arm64": "2.3.5", "@biomejs/cli-win32-x64": "2.3.5" }, "bin": { "biome": "bin/biome" } }, "sha512-HvLhNlIlBIbAV77VysRIBEwp55oM/QAjQEin74QQX9Xb259/XP/D5AGGnZMOyF1el4zcvlNYYR3AyTMUV3ILhg=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-w40GvlNzLaqmuWYiDU6Ys9FNhJiclngKqcGld3iJIiy2bpJ0Q+8n3haiaC81uTPY/NA0d8Q/I3Z9+ajc14102Q=="],
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fLdTur8cJU33HxHUUsii3GLx/TR0BsfQx8FkeqIiW33cGMtUD56fAtrh+2Fx1uhiCsVZlFh6iLKUU3pniZREQw=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-3s7TLVtjJ7ni1xADXsS7x7GMUrLBZXg8SemXc3T0XLslzvqKj/dq1xGeBQ+pOWQzng9MaozfacIHdK2UlJ3jGA=="],
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-qpT8XDqeUlzrOW8zb4k3tjhT7rmvVRumhi2657I2aGcY4B+Ft5fNwDdZGACzn8zj7/K1fdWjgwYE3i2mSZ+vOA=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-y7efHyyM2gYmHy/AdWEip+VgTMe9973aP7XYKPzu/j8JxnPHuSUXftzmPhkVw0lfm4ECGbdBdGD6+rLmTgNZaA=="],
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-u/pybjTBPGBHB66ku4pK1gj+Dxgx7/+Z0jAriZISPX1ocTO8aHh8x8e7Kb1rB4Ms0nA/SzjtNOVJ4exVavQBCw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-IruVGQRwMURivWazchiq7gKAqZSFs5so6gi0hJyxk7x6HR+iwZbO2IxNOqyLURBvL06qkIHs7Wffl6Bw30vCbQ=="],
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-eGUG7+hcLgGnMNl1KHVZUYxahYAhC462jF/wQolqu4qso2MSk32Q+QrpN7eN4jAHAg7FUMIo897muIhK4hXhqg=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gKfjWR/6/dfIxPJCw8REdEowiXCkIpl9jycpNVHux8aX2yhWPLjydOshkDL6Y/82PcQJHn95VCj7J+BRcE5o1Q=="],
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-XrIVi9YAW6ye0CGQ+yax0gLfx+BFOtKaNX74n+xHWla6Cl6huUmcKNO7HPx7BiKnJUzrxXY1qYlm7xMvi08X4g=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.4", "", { "os": "linux", "cpu": "x64" }, "sha512-mzKFFv/w66e4/jCobFmD3kymCqG+FuWE7sVa4Yjqd9v7qt2UhXo67MSZKY9Ih18V2IwPzRKQPCw6KwdZs6AXSA=="],
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.5", "", { "os": "linux", "cpu": "x64" }, "sha512-awVuycTPpVTH/+WDVnEEYSf6nbCBHf/4wB3lquwT7puhNg8R4XvonWNZzUsfHZrCkjkLhFH/vCZK5jHatD9FEg=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-5TJ6JfVez+yyupJ/iGUici2wzKf0RrSAxJhghQXtAEsc67OIpdwSKAQboemILrwKfHDi5s6mu7mX+VTCTUydkw=="],
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-DlBiMlBZZ9eIq4H7RimDSGsYcOtfOIfZOaI5CqsWiSlbTfqbPVfWtCf92wNzx8GNMbu1s7/g3ZZESr6+GwM/SA=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.4", "", { "os": "win32", "cpu": "x64" }, "sha512-FGCijXecmC4IedQ0esdYNlMpx0Jxgf4zceCaMu6fkjWyjgn50ZQtMiqZZQ0Q/77yqPxvtkgZAvt5uGw0gAAjig=="],
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.5", "", { "os": "win32", "cpu": "x64" }, "sha512-nUmR8gb6yvrKhtRgzwo/gDimPwnO5a4sCydf8ZS2kHIJhEmSmk+STsusr1LHTuM//wXppBawvSQi2xFXJCdgKQ=="],
|
||||
|
||||
"@browserbasehq/sdk": ["@browserbasehq/sdk@2.6.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA=="],
|
||||
|
||||
@@ -1684,7 +1687,7 @@
|
||||
|
||||
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="],
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.26", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-73lC1ugzwoaWCLJ1LvOgrR5xsMLTqSKIEoMHVtL9E/HNk0PXtTM76ZIm84856/SF7Nv8mPZxKoBsgpm0tR1u1Q=="],
|
||||
|
||||
"basic-ftp": ["basic-ftp@5.0.5", "", {}, "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg=="],
|
||||
|
||||
@@ -2154,6 +2157,8 @@
|
||||
|
||||
"globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
|
||||
|
||||
"globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="],
|
||||
|
||||
"goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="],
|
||||
|
||||
"google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
|
||||
@@ -2182,7 +2187,7 @@
|
||||
|
||||
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
|
||||
|
||||
"hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="],
|
||||
"hono": ["hono@4.10.5", "", {}, "sha512-h/MXuTkoAK8NG1EfDp0jI1YLf6yGdDnfkebRO2pwEh5+hE3RAJFXkCsnD0vamSiARK4ZrB6MY+o3E/hCnOyHrQ=="],
|
||||
|
||||
"hono-rate-limiter": ["hono-rate-limiter@0.4.2", "", { "peerDependencies": { "hono": "^4.1.1" } }, "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw=="],
|
||||
|
||||
@@ -2976,6 +2981,8 @@
|
||||
|
||||
"tsc-alias": ["tsc-alias@1.8.16", "", { "dependencies": { "chokidar": "^3.5.3", "commander": "^9.0.0", "get-tsconfig": "^4.10.0", "globby": "^11.0.4", "mylas": "^2.1.9", "normalize-path": "^3.0.0", "plimit-lit": "^1.2.6" }, "bin": { "tsc-alias": "dist/bin/index.js" } }, "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g=="],
|
||||
|
||||
"tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="],
|
||||
|
||||
"tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
@@ -3048,6 +3055,8 @@
|
||||
|
||||
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||
|
||||
"vite-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="],
|
||||
|
||||
"vscode-oniguruma": ["vscode-oniguruma@2.0.1", "", {}, "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ=="],
|
||||
|
||||
"vscode-textmate": ["vscode-textmate@9.2.1", "", {}, "sha512-eXiUi2yYFv9bdvgrYtJynA7UemCEkpVNE50S9iBBA08LYG5t9+/TB+8IRS/YoYOubCez2OkSyZ1Q12eQMwzbrw=="],
|
||||
@@ -3938,8 +3947,6 @@
|
||||
|
||||
"cloudflare/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"cmdk/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="],
|
||||
|
||||
"concurrently/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"convex/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
||||
|
||||
@@ -126,9 +126,7 @@ fi
|
||||
# echo "✅ Copied migration files"
|
||||
# fi
|
||||
|
||||
# Build shared workspace (required for other workspaces)
|
||||
echo "🔨 Building shared workspace..."
|
||||
bun -F @autumn/shared build
|
||||
# Shared workspace is now used directly from source (no build needed)
|
||||
|
||||
# # Run database migrations if DATABASE_URL exists
|
||||
# if grep -q "DATABASE_URL=" server/.env 2>/dev/null; then
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
services:
|
||||
valkey:
|
||||
image: docker.io/valkey/valkey:8.0
|
||||
environment:
|
||||
- ALLOW_EMPTY_PASSWORD=yes
|
||||
- VALKEY_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
|
||||
volumes:
|
||||
- valkey-data:/bitnami/valkey/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', '-h', 'localhost', '-p', '6379', 'ping']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
ports:
|
||||
- "6379:6379"
|
||||
restart: unless-stopped
|
||||
|
||||
vite:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/prod.dockerfile
|
||||
target: vite-prod
|
||||
ports:
|
||||
- "3000:3000"
|
||||
restart: always
|
||||
|
||||
server:
|
||||
environment:
|
||||
- REDIS_URL=redis://valkey:6379
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/prod.dockerfile
|
||||
target: server-prod
|
||||
ports:
|
||||
- "8080:8080"
|
||||
restart: always
|
||||
|
||||
localtunnel:
|
||||
image: oven/bun:latest
|
||||
build:
|
||||
dockerfile: docker/prod.dockerfile
|
||||
context: .
|
||||
target: localtunnel
|
||||
volumes:
|
||||
- ./server:/app/server
|
||||
depends_on:
|
||||
- server
|
||||
restart: unless-stopped
|
||||
|
||||
workers:
|
||||
environment:
|
||||
- REDIS_URL=redis://valkey:6379
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/prod.dockerfile
|
||||
target: workers-prod
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
valkey-data:
|
||||
shared-dist:
|
||||
shared-node-modules:
|
||||
root-node-modules:
|
||||
@@ -21,32 +21,25 @@ WORKDIR /app
|
||||
COPY localtunnel-start.sh ./
|
||||
CMD ["sh", "localtunnel-start.sh"]
|
||||
|
||||
# Stage 2: /shared
|
||||
FROM base AS shared
|
||||
COPY shared/ ./shared/
|
||||
WORKDIR /app/shared
|
||||
RUN bun run build
|
||||
CMD ["bun", "dev"]
|
||||
|
||||
# Stage 3: /vite
|
||||
# Stage 2: /vite
|
||||
FROM base AS vite
|
||||
COPY --from=shared /app/shared/dist ./shared/dist
|
||||
COPY shared/ ./shared/
|
||||
WORKDIR /app/vite
|
||||
COPY vite/ ./
|
||||
EXPOSE 3000
|
||||
CMD ["bun", "dev"]
|
||||
|
||||
# Stage 4: /server
|
||||
# Stage 3: /server
|
||||
FROM base AS server
|
||||
COPY --from=shared /app/shared/dist ./shared/dist
|
||||
COPY shared/ ./shared/
|
||||
COPY server/ ./server/
|
||||
WORKDIR /app/server
|
||||
EXPOSE 8080
|
||||
CMD ["bun", "dev"]
|
||||
|
||||
# Stage 5: Workers
|
||||
# Stage 4: Workers
|
||||
FROM base AS workers
|
||||
COPY --from=shared /app/shared/dist ./shared/dist
|
||||
COPY shared/ ./shared/
|
||||
COPY server/ ./server/
|
||||
WORKDIR /app/server
|
||||
CMD ["bun", "workers:dev"]
|
||||
@@ -1,52 +0,0 @@
|
||||
# ---- Base dependencies ----
|
||||
FROM oven/bun:latest AS base
|
||||
WORKDIR /app
|
||||
RUN bun install -g serve typescript tsc tsc-alias tsx
|
||||
|
||||
COPY package.json bun.lock ./
|
||||
COPY shared/package*.json ./shared/
|
||||
COPY server/package*.json ./server/
|
||||
COPY vite/package*.json ./vite/
|
||||
|
||||
RUN bun install
|
||||
|
||||
FROM base AS localtunnel
|
||||
WORKDIR /app
|
||||
COPY localtunnel-start.sh ./
|
||||
CMD ["sh", "localtunnel-start.sh"]
|
||||
|
||||
# ---- Build shared package ----
|
||||
FROM base AS shared-build
|
||||
COPY shared/ ./shared/
|
||||
WORKDIR /app/shared
|
||||
RUN bun run build
|
||||
|
||||
# ---- Build frontend (vite) ----
|
||||
FROM base AS vite-build
|
||||
COPY --from=shared-build /app/shared/dist ./shared/dist
|
||||
COPY . .
|
||||
WORKDIR /app
|
||||
RUN bun run build
|
||||
|
||||
# ---- Build backend (server) ----
|
||||
FROM base AS server-build
|
||||
COPY --from=shared-build /app/shared/dist ./shared/dist
|
||||
COPY . .
|
||||
WORKDIR /app
|
||||
# RUN bun run server:build:bun
|
||||
|
||||
# ---- Production frontend image ----
|
||||
FROM vite-build AS vite-prod
|
||||
EXPOSE 3000
|
||||
WORKDIR /app
|
||||
CMD ["bun", "start"]
|
||||
|
||||
FROM server-build AS server-prod
|
||||
EXPOSE 8080
|
||||
WORKDIR /app/server
|
||||
CMD ["bun", "start"]
|
||||
|
||||
FROM server-build AS workers-prod
|
||||
EXPOSE 8080
|
||||
WORKDIR /app/server
|
||||
CMD ["bun", "workers"]
|
||||
19
package.json
19
package.json
@@ -9,6 +9,7 @@
|
||||
"scripts"
|
||||
],
|
||||
"catalog": {
|
||||
"stripe": "18.4.0-beta.2",
|
||||
"drizzle-orm": "0.43.1",
|
||||
"drizzle-kit": "^0.31.1"
|
||||
}
|
||||
@@ -16,32 +17,18 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun scripts/start-dev.js",
|
||||
|
||||
"d": "ENV_FILE=.env infisical run --env=dev -- bun scripts/start-dev.js",
|
||||
"p": "ENV_FILE=.env.prod infisical run --env=prod -- bun scripts/start-dev.js",
|
||||
|
||||
"vite:build": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
||||
"vite:start": "bun -F @autumn/vite start:bun",
|
||||
"shared": "bun -F @autumn/shared build",
|
||||
"server": "cd server && bun start",
|
||||
"workers": "bun -F @autumn/shared build && bun -F @autumn/server workers",
|
||||
"cron": "bun -F @autumn/shared build && bun -F @autumn/server cron",
|
||||
"check": "bun -F @autumn/shared build && bun -F @autumn/server check",
|
||||
"server:cron": "pnpm -F server cron:start",
|
||||
"server:check": "NODE_ENV=production pnpm -F server check",
|
||||
"setup": "node scripts/setup.js",
|
||||
"setup:test": "bun scripts/setup-test.ts",
|
||||
"tests": "bun scripts/test.ts",
|
||||
|
||||
"setupci": "node scripts/setupci.js",
|
||||
"replicate": "bun scripts/replicate.ts",
|
||||
"db:push": " bun -F @autumn/shared db:push",
|
||||
"db:generate": "bun -F @autumn/shared db:generate",
|
||||
"db:migrate": " bun -F @autumn/shared db:migrate",
|
||||
"docker:up": "docker compose -f docker-compose.dev.yml up --build",
|
||||
"docker:up:unix": "docker compose -f docker-compose.unix.yml up --build",
|
||||
"docker:up:ci": "docker compose -f docker-compose.ci.yml up --build",
|
||||
"vite:build:bun": "bun -F @autumn/shared build && bun -F @autumn/vite build:bun",
|
||||
"vite:start:bun": "bun -F @autumn/shared build && bun -F @autumn/vite start:bun"
|
||||
"db:migrate": " bun -F @autumn/shared db:migrate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wooorm/starry-night": "^3.8.0",
|
||||
|
||||
@@ -145,27 +145,6 @@ async function startDev() {
|
||||
// await handlePorts();
|
||||
}
|
||||
|
||||
// Step 1: Build shared package first (initial build)
|
||||
console.log("\n📦 Building shared package...\n");
|
||||
const buildShared = spawn("bun", ["run", "build"], {
|
||||
cwd: "shared",
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
buildShared.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`Shared package build failed with code ${code}`));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
buildShared.on("error", reject);
|
||||
});
|
||||
|
||||
console.log("\n✅ Shared package built successfully!\n");
|
||||
|
||||
// Clear Vite cache to prevent dep optimization issues
|
||||
const viteCachePath = path.join(
|
||||
projectRoot,
|
||||
@@ -180,19 +159,18 @@ async function startDev() {
|
||||
|
||||
console.log("🚀 Starting development servers in watch mode...\n");
|
||||
|
||||
// Step 2: Start server, workers, and vite first (they'll use the built shared package)
|
||||
// Start server, workers, and vite (they'll use the shared package source files directly)
|
||||
const concurrentlyCmd = spawn(
|
||||
"bunx",
|
||||
[
|
||||
"concurrently",
|
||||
"-n",
|
||||
"server,workers,vite,shared",
|
||||
"server,workers,vite",
|
||||
"-c",
|
||||
"green,yellow,blue,cyan",
|
||||
"green,yellow,blue",
|
||||
`"cd server && SERVER_PORT=${SERVER_PORT} bun dev"`,
|
||||
`"cd server && bun workers:dev"`,
|
||||
`"cd vite && VITE_PORT=${VITE_PORT} bun dev"`,
|
||||
`"cd shared && bun run dev:watch"`,
|
||||
],
|
||||
{
|
||||
stdio: "inherit",
|
||||
|
||||
1
server/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
Symbolic link
1
server/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
Symbolic link
@@ -0,0 +1 @@
|
||||
../../CLAUDE.md
|
||||
111
server/CLAUDE.md
Normal file
111
server/CLAUDE.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
description: Use Bun instead of Node.js, npm, pnpm, or vite.
|
||||
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
Default to using Bun instead of Node.js.
|
||||
|
||||
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
||||
- Use `bun test` instead of `jest` or `vitest`
|
||||
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
||||
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
||||
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
||||
- Bun automatically loads .env, so don't use dotenv.
|
||||
|
||||
## APIs
|
||||
|
||||
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
||||
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
||||
- `Bun.redis` for Redis. Don't use `ioredis`.
|
||||
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
||||
- `WebSocket` is built-in. Don't use `ws`.
|
||||
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
||||
- Bun.$`ls` instead of execa.
|
||||
|
||||
## Testing
|
||||
|
||||
Use `bun test` to run tests.
|
||||
|
||||
```ts#index.test.ts
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
test("hello world", () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
```
|
||||
|
||||
## Frontend
|
||||
|
||||
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
||||
|
||||
Server:
|
||||
|
||||
```ts#index.ts
|
||||
import index from "./index.html"
|
||||
|
||||
Bun.serve({
|
||||
routes: {
|
||||
"/": index,
|
||||
"/api/users/:id": {
|
||||
GET: (req) => {
|
||||
return new Response(JSON.stringify({ id: req.params.id }));
|
||||
},
|
||||
},
|
||||
},
|
||||
// optional websocket support
|
||||
websocket: {
|
||||
open: (ws) => {
|
||||
ws.send("Hello, world!");
|
||||
},
|
||||
message: (ws, message) => {
|
||||
ws.send(message);
|
||||
},
|
||||
close: (ws) => {
|
||||
// handle close
|
||||
}
|
||||
},
|
||||
development: {
|
||||
hmr: true,
|
||||
console: true,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
||||
|
||||
```html#index.html
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello, world!</h1>
|
||||
<script type="module" src="./frontend.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
With the following `frontend.tsx`:
|
||||
|
||||
```tsx#frontend.tsx
|
||||
import React from "react";
|
||||
|
||||
// import .css files directly and it works
|
||||
import './index.css';
|
||||
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
const root = createRoot(document.body);
|
||||
|
||||
export default function Frontend() {
|
||||
return <h1>Hello, world!</h1>;
|
||||
}
|
||||
|
||||
root.render(<Frontend />);
|
||||
```
|
||||
|
||||
Then, run index.ts
|
||||
|
||||
```sh
|
||||
bun --hot ./index.ts
|
||||
```
|
||||
|
||||
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
|
||||
15
server/README.md
Normal file
15
server/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# @autumn/server
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.js
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.3.2. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||
@@ -1,173 +0,0 @@
|
||||
import { Redis } from "ioredis";
|
||||
import { AutumnInt } from "../src/external/autumn/autumnCli.js";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import crypto from "node:crypto";
|
||||
|
||||
const client = new Redis("redis://localhost:6379");
|
||||
|
||||
// Load Lua scripts
|
||||
const setCustomerScript = readFileSync(
|
||||
join(import.meta.dir, "setCustomer.lua"),
|
||||
"utf-8",
|
||||
);
|
||||
const getCustomerScript = readFileSync(
|
||||
join(import.meta.dir, "getCustomer.lua"),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
// Calculate SHA1 hashes for script caching
|
||||
const setCustomerSha = crypto
|
||||
.createHash("sha1")
|
||||
.update(setCustomerScript)
|
||||
.digest("hex");
|
||||
const getCustomerSha = crypto
|
||||
.createHash("sha1")
|
||||
.update(getCustomerScript)
|
||||
.digest("hex");
|
||||
|
||||
// Load scripts into Redis
|
||||
await client.script("LOAD", setCustomerScript);
|
||||
await client.script("LOAD", getCustomerScript);
|
||||
|
||||
/**
|
||||
* Atomically stores a customer object in Redis with features as HSETs
|
||||
*/
|
||||
async function setCustomer({ customerData }: { customerData: any }) {
|
||||
const customerId = customerData.id;
|
||||
try {
|
||||
const result = await client.evalsha(
|
||||
setCustomerSha,
|
||||
1,
|
||||
customerId,
|
||||
JSON.stringify(customerData),
|
||||
);
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
// If script not found, reload and retry
|
||||
if (error.message.includes("NOSCRIPT")) {
|
||||
await client.script("LOAD", setCustomerScript);
|
||||
return await client.evalsha(
|
||||
setCustomerSha,
|
||||
1,
|
||||
customerId,
|
||||
JSON.stringify(customerData),
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically retrieves a customer object from Redis, reconstructing from HSETs
|
||||
*/
|
||||
async function getCustomer({ customerId }: { customerId: string }) {
|
||||
try {
|
||||
const result = await client.evalsha(getCustomerSha, 1, customerId);
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(result as string);
|
||||
} catch (error: any) {
|
||||
// If script not found, reload and retry
|
||||
if (error.message.includes("NOSCRIPT")) {
|
||||
await client.script("LOAD", getCustomerScript);
|
||||
const retryResult = await client.evalsha(getCustomerSha, 1, customerId);
|
||||
if (!retryResult) {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(retryResult as string);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates a feature balance using HINCRBYFLOAT
|
||||
*/
|
||||
async function updateFeatureBalance({
|
||||
customerId,
|
||||
featureId,
|
||||
amount,
|
||||
breakdownIndex,
|
||||
}: {
|
||||
customerId: string;
|
||||
featureId: string;
|
||||
amount: number;
|
||||
breakdownIndex?: number;
|
||||
}) {
|
||||
// Update breakdown-specific balance if index provided
|
||||
if (breakdownIndex !== undefined) {
|
||||
const breakdownKey = `customer:${customerId}:features:${featureId}:breakdown:${breakdownIndex}`;
|
||||
await client.hincrbyfloat(breakdownKey, "balance", amount);
|
||||
}
|
||||
|
||||
// Always update aggregate feature balance
|
||||
const featureKey = `customer:${customerId}:features:${featureId}`;
|
||||
await client.hincrbyfloat(featureKey, "balance", amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically updates a feature usage using HINCRBYFLOAT
|
||||
*/
|
||||
async function updateFeatureUsage({
|
||||
customerId,
|
||||
featureId,
|
||||
amount,
|
||||
breakdownIndex,
|
||||
}: {
|
||||
customerId: string;
|
||||
featureId: string;
|
||||
amount: number;
|
||||
breakdownIndex?: number;
|
||||
}) {
|
||||
// Update breakdown-specific usage if index provided
|
||||
if (breakdownIndex !== undefined) {
|
||||
const breakdownKey = `customer:${customerId}:features:${featureId}:breakdown:${breakdownIndex}`;
|
||||
await client.hincrbyfloat(breakdownKey, "usage", amount);
|
||||
}
|
||||
|
||||
// Always update aggregate feature usage
|
||||
const featureKey = `customer:${customerId}:features:${featureId}`;
|
||||
await client.hincrbyfloat(featureKey, "usage", amount);
|
||||
}
|
||||
|
||||
const main = async () => {
|
||||
const autumn = new AutumnInt({ secretKey: process.env.JDEV! });
|
||||
|
||||
const customer = await autumn.customers.get("john");
|
||||
|
||||
// 1. Set customer
|
||||
const setStart = performance.now();
|
||||
await setCustomer({
|
||||
customerData: customer,
|
||||
});
|
||||
const setEnd = performance.now();
|
||||
console.log(`✓ Stored customer in Redis (${(setEnd - setStart).toFixed(2)}ms)`);
|
||||
|
||||
// 2. Get customer
|
||||
console.time("Get cached customer");
|
||||
const cachedCustomer = await getCustomer({ customerId: "john" });
|
||||
console.timeEnd("Get cached customer");
|
||||
|
||||
// Compare features
|
||||
console.log("\n=== Comparison ===");
|
||||
console.log(`Original credits feature balance: `, cachedCustomer?.features?.credits?.balance);
|
||||
|
||||
// Time the decrement of lifetime balance using HDECRBYFLOAT
|
||||
console.time("Decrement lifetime balance");
|
||||
await client.hincrbyfloat("customer:john:features:credits", "balance", -1.42513);
|
||||
console.timeEnd("Decrement lifetime balance");
|
||||
|
||||
// Get updated customer
|
||||
const updatedCustomer = await getCustomer({ customerId: "john" });
|
||||
console.log('Updated credits feature balance:', updatedCustomer?.features?.credits?.balance);
|
||||
|
||||
// Time getting the customer object
|
||||
console.time("Get base customer");
|
||||
await client.get("customer:john");
|
||||
console.timeEnd("Get base customer");
|
||||
};
|
||||
|
||||
await main();
|
||||
process.exit(0);
|
||||
@@ -1,72 +0,0 @@
|
||||
import { AppEnv } from "@autumn/shared";
|
||||
import {
|
||||
buildCachedApiCustomerKey,
|
||||
getCachedApiCustomer,
|
||||
} from "../src/internal/customers/cusUtils/apiCusCacheUtils/getCachedApiCustomer.js";
|
||||
import { initDrizzle } from "../src/db/initDrizzle.js";
|
||||
import { initScript } from "../src/utils/scriptUtils/scriptUtils.js";
|
||||
import { redis } from "../src/external/redis/initRedis.js";
|
||||
import { AutumnInt } from "../src/external/autumn/autumnCli.js";
|
||||
|
||||
const DEDUCTION_COUNT = 15_000;
|
||||
const DEDUCTION_AMOUNT = 1;
|
||||
|
||||
const logCredits = (label: string, customer: Awaited<ReturnType<typeof getCachedApiCustomer>>) => {
|
||||
const credits = customer?.features?.credits;
|
||||
console.log(`\n${label}`);
|
||||
console.log(` Total Balance: ${credits?.balance ?? "N/A"}`);
|
||||
console.log(` Monthly Credits: ${credits?.breakdown?.[0]?.balance ?? "N/A"}`);
|
||||
console.log(` Lifetime Credits: ${credits?.breakdown?.[1]?.balance ?? "N/A"}`);
|
||||
};
|
||||
|
||||
const main = async () => {
|
||||
const orgId = "org_2s4vfEyYVgFZDlOwcMHjsHR0eef";
|
||||
const env = AppEnv.Sandbox;
|
||||
const customerId = "john";
|
||||
|
||||
|
||||
const { db } = initDrizzle();
|
||||
const { req } = await initScript({ orgId, env });
|
||||
const autumn = new AutumnInt({
|
||||
secretKey: process.env.JDEV!,
|
||||
});
|
||||
|
||||
await redis.del(buildCachedApiCustomerKey({ customerId, orgId, env }));
|
||||
|
||||
const customerBefore = await getCachedApiCustomer({
|
||||
ctx: req as any,
|
||||
customerId,
|
||||
});
|
||||
logCredits("📊 Credits Before:", customerBefore);
|
||||
|
||||
console.log(`\n⏳ Processing ${DEDUCTION_COUNT.toLocaleString()} deductions...`);
|
||||
const start = Date.now();
|
||||
|
||||
|
||||
const promises = Array.from({ length: DEDUCTION_COUNT }, () =>
|
||||
autumn.track({
|
||||
customer_id: customerId,
|
||||
feature_id: "credits",
|
||||
value: DEDUCTION_AMOUNT,
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(promises);
|
||||
const elapsed = Date.now() - start;
|
||||
|
||||
const customerAfter = await getCachedApiCustomer({
|
||||
ctx: req as any,
|
||||
customerId,
|
||||
});
|
||||
logCredits("📊 Credits After:", customerAfter);
|
||||
|
||||
const deductionDiff = (customerBefore?.features?.credits?.balance ?? 0) - (customerAfter?.features?.credits?.balance ?? 0);
|
||||
|
||||
console.log("\n✅ Test Complete!");
|
||||
console.log(` Time Elapsed: ${elapsed.toLocaleString()}ms`);
|
||||
console.log(` Total Deducted: ${deductionDiff}`);
|
||||
console.log(` Avg per Deduction: ${(elapsed / DEDUCTION_COUNT).toFixed(3)}ms\n`);
|
||||
};
|
||||
|
||||
await main();
|
||||
process.exit(0);
|
||||
@@ -1,41 +0,0 @@
|
||||
import { globalBatchingManager } from "../src/internal/balances/track/redisTrackUtils/BatchingManager.js";
|
||||
|
||||
/**
|
||||
* Test script to debug the simplified batchDeduction.lua
|
||||
* This will show you what the Lua script can retrieve about a feature
|
||||
*/
|
||||
async function testLuaDebug() {
|
||||
// Replace these with real values from your test data
|
||||
const customerId = "your-customer-id";
|
||||
const orgId = "your-org-id";
|
||||
const env = "development";
|
||||
|
||||
console.log("Testing Lua script with:");
|
||||
console.log("- Customer ID:", customerId);
|
||||
console.log("- Org ID:", orgId);
|
||||
console.log("- Environment:", env);
|
||||
console.log("");
|
||||
|
||||
try {
|
||||
const result = await globalBatchingManager.deduct({
|
||||
customerId,
|
||||
featureDeductions: [
|
||||
{ featureId: "credits", amount: 10 },
|
||||
{ featureId: "api_calls", amount: 5 },
|
||||
],
|
||||
orgId,
|
||||
env,
|
||||
overageBehavior: "cap",
|
||||
});
|
||||
|
||||
console.log("📦 Batching manager result:");
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error("❌ Error:", error);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testLuaDebug();
|
||||
|
||||
1
server/index.js
Normal file
1
server/index.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log("Hello via Bun!");
|
||||
@@ -23,7 +23,9 @@
|
||||
"parallel-tests:v1": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts",
|
||||
"parallel-tests:verbose": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts --verbose",
|
||||
"parallel-tests:debug": "ENV_FILE=.env infisical run --env=dev -- bun tests/testRunner/runParallelGroups.ts --debug",
|
||||
"clear-master": "ENV_FILE=.env infisical run --env=dev -- bun tests/clearMasterOrg.ts"
|
||||
"clear-master": "ENV_FILE=.env infisical run --env=dev -- bun tests/clearMasterOrg.ts",
|
||||
|
||||
"ts": "bunx tsgo --build --noEmit"
|
||||
},
|
||||
"mocha": {
|
||||
"node-option": [
|
||||
@@ -113,7 +115,7 @@
|
||||
"recaseai": "^0.0.37",
|
||||
"resend": "^4.1.1",
|
||||
"semver": "^7.7.2",
|
||||
"stripe": "18.4.0-beta.2",
|
||||
"stripe": "catalog:",
|
||||
"svix": "^1.45.1",
|
||||
"tsc-alias": "^1.8.16",
|
||||
"ws": "^8.18.0",
|
||||
|
||||
@@ -36,16 +36,16 @@ import { checkCusSubCorrect } from "./utils/checkUtils/checkCustomerCorrect.js";
|
||||
|
||||
const { db } = initDrizzle({ maxConnections: 5 });
|
||||
|
||||
let orgSlugs = process.env.ORG_SLUGS!.split(",");
|
||||
const orgSlugs = process.env.ORG_SLUGS!.split(",");
|
||||
const skipEmails = process.env.SKIP_EMAILS!.split(",");
|
||||
const skipIds = [
|
||||
"cus_2tXCCwC6iyiftgA6ndSo1Ubb2dx",
|
||||
"DxG668K7uDd0Vahk54YWjvCGVgf2",
|
||||
];
|
||||
|
||||
orgSlugs = ["welcome-back-1747670264"];
|
||||
let customerId = null;
|
||||
customerId = "56";
|
||||
// orgSlugs = ["welcome-back-1747670264"];
|
||||
// let customerId = null;
|
||||
const customerId = null;
|
||||
|
||||
const getSingleCustomer = async ({
|
||||
stripeCli,
|
||||
|
||||
2
server/src/external/autumn/autumnCli.ts
vendored
2
server/src/external/autumn/autumnCli.ts
vendored
@@ -18,6 +18,7 @@ import {
|
||||
type RewardRedemption,
|
||||
type TrackParams,
|
||||
} from "@autumn/shared";
|
||||
import { defaultApiVersion } from "@tests/constants.js";
|
||||
import type {
|
||||
CancelParams,
|
||||
CheckoutParams,
|
||||
@@ -27,7 +28,6 @@ import type {
|
||||
Customer,
|
||||
UsageParams,
|
||||
} from "autumn-js";
|
||||
import { defaultApiVersion } from "tests/constants.js";
|
||||
|
||||
export default class AutumnError extends Error {
|
||||
message: string;
|
||||
|
||||
4
server/src/external/autumn/autumnCliV2.ts
vendored
4
server/src/external/autumn/autumnCliV2.ts
vendored
@@ -5,7 +5,7 @@ dotenv.config();
|
||||
|
||||
import {
|
||||
type AttachBody,
|
||||
type CreateEntity,
|
||||
type CreateEntityParams,
|
||||
type CreateRewardProgram,
|
||||
CusExpand,
|
||||
EntityExpand,
|
||||
@@ -321,7 +321,7 @@ export class AutumnCliV2 {
|
||||
|
||||
create: async (
|
||||
customerId: string,
|
||||
entity: CreateEntity | CreateEntity[],
|
||||
entity: CreateEntityParams | CreateEntityParams[],
|
||||
) => {
|
||||
return await this.post(
|
||||
`/customers/${customerId}/entities?with_autumn_id=true`,
|
||||
|
||||
6
server/src/external/llm/llmUtils.ts
vendored
6
server/src/external/llm/llmUtils.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { createAnthropic } from "@ai-sdk/anthropic";
|
||||
import { Feature } from "@autumn/shared";
|
||||
import type { Feature } from "@autumn/shared";
|
||||
import { generateObject } from "ai";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -20,8 +20,8 @@ export const generateFeatureDisplay = async (feature: Feature) => {
|
||||
</feature>
|
||||
`;
|
||||
|
||||
let { object } = await generateObject({
|
||||
model: anthropic("claude-3-5-sonnet-latest"),
|
||||
const { object } = await generateObject({
|
||||
model: anthropic("claude-haiku-4-5"),
|
||||
schema: z.object({
|
||||
singular: z.string(),
|
||||
plural: z.string(),
|
||||
|
||||
@@ -155,7 +155,7 @@ export const handleStripeWebhookEvent = async ({
|
||||
case "customer.subscription.updated": {
|
||||
const subscription = event.data.object;
|
||||
await handleSubscriptionUpdated({
|
||||
req: ctx as ExtendedRequest,
|
||||
req: ctx as unknown as ExtendedRequest,
|
||||
db,
|
||||
org,
|
||||
subscription,
|
||||
@@ -168,7 +168,7 @@ export const handleStripeWebhookEvent = async ({
|
||||
|
||||
case "customer.subscription.deleted":
|
||||
await handleSubDeleted({
|
||||
req: ctx as ExtendedRequest,
|
||||
req: ctx as unknown as ExtendedRequest,
|
||||
stripeCli,
|
||||
data: event.data.object,
|
||||
logger,
|
||||
@@ -178,7 +178,7 @@ export const handleStripeWebhookEvent = async ({
|
||||
case "checkout.session.completed": {
|
||||
const checkoutSession = event.data.object;
|
||||
await handleCheckoutSessionCompleted({
|
||||
req: ctx as ExtendedRequest,
|
||||
req: ctx as unknown as ExtendedRequest,
|
||||
db,
|
||||
data: checkoutSession,
|
||||
org,
|
||||
@@ -196,7 +196,7 @@ export const handleStripeWebhookEvent = async ({
|
||||
invoiceData: invoice,
|
||||
env,
|
||||
event,
|
||||
req: ctx as ExtendedRequest,
|
||||
req: ctx as unknown as ExtendedRequest,
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -206,7 +206,7 @@ export const handleStripeWebhookEvent = async ({
|
||||
stripeCli,
|
||||
env,
|
||||
event,
|
||||
req: ctx as ExtendedRequest,
|
||||
req: ctx as unknown as ExtendedRequest,
|
||||
});
|
||||
break;
|
||||
|
||||
|
||||
6
server/src/external/stripe/stripeSubUtils.ts
vendored
6
server/src/external/stripe/stripeSubUtils.ts
vendored
@@ -141,7 +141,7 @@ export const getUsageBasedSub = async ({
|
||||
let usageFeatures: string[] | null = null;
|
||||
|
||||
// 1. Check if there's autumn sub
|
||||
const autumnSub = autumnSubs?.find((sub) => sub.stripe_id == stripeSub.id);
|
||||
const autumnSub = autumnSubs?.find((sub) => sub.stripe_id === stripeSub.id);
|
||||
if (autumnSub) {
|
||||
const containsFeature = autumnSub.usage_features.includes(
|
||||
feature.internal_id!,
|
||||
@@ -153,14 +153,14 @@ export const getUsageBasedSub = async ({
|
||||
|
||||
try {
|
||||
usageFeatures = JSON.parse(stripeSub.metadata.usage_features);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!usageFeatures ||
|
||||
usageFeatures.find(
|
||||
(feat: any) => feat.internal_id == feature.internal_id,
|
||||
(feat: any) => feat.internal_id === feature.internal_id,
|
||||
) === undefined
|
||||
) {
|
||||
continue;
|
||||
|
||||
@@ -301,8 +301,6 @@ export const handleInvoiceCreated = async ({
|
||||
|
||||
const subId = invoiceToSubId({ invoice });
|
||||
|
||||
console.log("HANDLING INVOICE CREATED", invoice.id);
|
||||
|
||||
if (subId) {
|
||||
const activeProducts = await CusProductService.getByStripeSubId({
|
||||
db,
|
||||
|
||||
@@ -4,16 +4,33 @@ import type { HonoEnv } from "@/honoUtils/HonoEnv.js";
|
||||
/**
|
||||
* Extracts expand from validated query and sets it in context.
|
||||
* Must run AFTER versionedValidator so expand has been transformed.
|
||||
* Uses c.req.query("expand") to access the parsed expand field.
|
||||
*/
|
||||
export const expandMiddleware = (): MiddlewareHandler<HonoEnv> => {
|
||||
return async (c, next) => {
|
||||
const validatedQuery = c.req.valid("query");
|
||||
const expand = validatedQuery?.expand || [];
|
||||
// Query is parsed by queryMiddleware and validated by versionedValidator/validator
|
||||
// queryStringArray normalizes expand to an array during validation, but we access
|
||||
// the parsed query which may still be a string for single values
|
||||
const expandValue = c.req.query("expand");
|
||||
// queryMiddleware converts "true"/"false" strings to boolean values
|
||||
// Handle both boolean (from queryMiddleware) and string (fallback) cases
|
||||
const skipCacheQuery = c.req.query("skip_cache");
|
||||
const skipCacheValue =
|
||||
(typeof skipCacheQuery === "boolean" && skipCacheQuery === true) ||
|
||||
(typeof skipCacheQuery === "string" && skipCacheQuery === "true");
|
||||
|
||||
// Normalize to array: undefined -> [], string -> [string], array -> array
|
||||
const expand: string[] = !expandValue
|
||||
? []
|
||||
: Array.isArray(expandValue)
|
||||
? expandValue
|
||||
: [expandValue];
|
||||
|
||||
const ctx = c.get("ctx");
|
||||
c.set("ctx", {
|
||||
...ctx,
|
||||
expand: Array.isArray(expand) ? expand : [expand],
|
||||
expand,
|
||||
skipCache: skipCacheValue,
|
||||
});
|
||||
|
||||
await next();
|
||||
|
||||
@@ -65,7 +65,7 @@ export const versionedValidator = ({
|
||||
throw result.error;
|
||||
}
|
||||
validatedData = result.data as Record<string, unknown>;
|
||||
c.req.addValidatedData(target, result.data);
|
||||
c.req.addValidatedData(target, validatedData);
|
||||
} else {
|
||||
// For other targets, use zValidator
|
||||
const validatorMiddleware = zValidator(target, schema, (result, _c) => {
|
||||
|
||||
@@ -30,6 +30,7 @@ export type RequestContext = {
|
||||
|
||||
// Query params
|
||||
expand: string[];
|
||||
skipCache: boolean;
|
||||
};
|
||||
|
||||
export type AutumnContext = RequestContext;
|
||||
|
||||
@@ -80,7 +80,7 @@ analyticsRouter.post("", (req, res) =>
|
||||
}
|
||||
|
||||
events.data.forEach((event: any) => {
|
||||
event["period"] = parseInt(format(new Date(event["period"]), "T"));
|
||||
event.period = parseInt(format(new Date(event.period), "T"));
|
||||
});
|
||||
|
||||
const usageList = events.data.filter(
|
||||
|
||||
@@ -404,6 +404,7 @@ export const createFullCusProduct = async ({
|
||||
isDowngrade,
|
||||
entities: attachParams.entities,
|
||||
features: attachParams.features,
|
||||
prices,
|
||||
});
|
||||
|
||||
// 4. Get new rollovers
|
||||
|
||||
@@ -102,7 +102,6 @@ export const handleScheduleFunction2 = async ({
|
||||
});
|
||||
|
||||
const currentPhaseIndex = getCurrentPhaseIndex({
|
||||
// biome-ignore lint/suspicious/noExplicitAny: ok
|
||||
schedule: { phases: newItems.phases } as any,
|
||||
now: attachParams.now,
|
||||
});
|
||||
|
||||
@@ -122,7 +122,7 @@ export const getPricesAndEnts = async ({
|
||||
optionsInput: optionsInput || [],
|
||||
features,
|
||||
prices,
|
||||
curCusProduct: curMainProduct,
|
||||
curCusProduct,
|
||||
}),
|
||||
prices,
|
||||
entitlements: getEntsWithFeature({
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
intervalToValue,
|
||||
ProrationBehavior,
|
||||
} from "@autumn/shared";
|
||||
import { isDefaultTrialFullProduct } from "@/internal/products/productUtils/classifyProduct.js";
|
||||
import { isFreeProduct } from "@/internal/products/productUtils.js";
|
||||
import { notNullish, nullish } from "@/utils/genUtils.js";
|
||||
import type { AttachParams } from "../../cusProducts/AttachParams.js";
|
||||
@@ -16,7 +17,6 @@ import {
|
||||
attachParamsToProduct,
|
||||
attachParamToCusProducts,
|
||||
} from "./convertAttachParams.js";
|
||||
import { isDefaultTrialFullProduct } from "@/internal/products/productUtils/classifyProduct.js";
|
||||
|
||||
export const intervalsAreSame = ({
|
||||
attachParams,
|
||||
@@ -118,10 +118,11 @@ export const getAttachConfig = async ({
|
||||
let isUpgradingFromDefaultTrial = false;
|
||||
if (curMainProduct && branch === AttachBranch.Upgrade) {
|
||||
const product = cusProductToProduct({ cusProduct: curMainProduct });
|
||||
isUpgradingFromDefaultTrial = isDefaultTrialFullProduct({
|
||||
product,
|
||||
skipDefault: true,
|
||||
}) || false;
|
||||
isUpgradingFromDefaultTrial =
|
||||
isDefaultTrialFullProduct({
|
||||
product,
|
||||
skipDefault: true,
|
||||
}) || false;
|
||||
}
|
||||
|
||||
const checkoutFlow =
|
||||
@@ -172,7 +173,6 @@ export const getAttachConfig = async ({
|
||||
? attachBody.finalize_invoice!
|
||||
: true,
|
||||
requirePaymentMethod: paymentMethodRequired,
|
||||
|
||||
};
|
||||
|
||||
return { flags, config };
|
||||
|
||||
@@ -99,6 +99,10 @@ export const getNewProductPreview = async ({
|
||||
|
||||
let trialEnds: number | undefined;
|
||||
|
||||
if (config.disableTrial) {
|
||||
attachParams.freeTrial = null;
|
||||
}
|
||||
|
||||
// Scenario where we update a current sub with new product (so no create sub)
|
||||
let anchor: number | undefined;
|
||||
if (mergeSub && !config.disableMerge) {
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import Stripe from "stripe";
|
||||
import { AttachParams } from "../../cusProducts/AttachParams.js";
|
||||
import {
|
||||
type AttachConfig,
|
||||
CusProductStatus,
|
||||
cusProductToPrices,
|
||||
cusProductToProduct,
|
||||
type FullCusProduct,
|
||||
} from "@autumn/shared";
|
||||
import type Stripe from "stripe";
|
||||
import { getStripeSubItems2 } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js";
|
||||
import { AttachConfig, CusProductStatus, FullCusProduct } from "@autumn/shared";
|
||||
import { getExistingCusProducts } from "../../cusProducts/cusProductUtils/getExistingCusProducts.js";
|
||||
import { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import { cusProductToPrices, cusProductToProduct } from "@autumn/shared";
|
||||
import { isArrearPrice } from "@/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.js";
|
||||
import {
|
||||
findStripeItemForPrice,
|
||||
subItemInCusProduct,
|
||||
} from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
||||
import { mergeNewSubItems } from "./mergeNewSubItems.js";
|
||||
import { isArrearPrice } from "@/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.js";
|
||||
import { formatPrice } from "@/internal/products/prices/priceUtils.js";
|
||||
import { getQuantityToRemove } from "./mergeUtils.js";
|
||||
import { notNullish } from "@/utils/genUtils.js";
|
||||
import { ItemSet } from "@/utils/models/ItemSet.js";
|
||||
import type { ItemSet } from "@/utils/models/ItemSet.js";
|
||||
import type { ExtendedRequest } from "@/utils/models/Request.js";
|
||||
import type { AttachParams } from "../../cusProducts/AttachParams.js";
|
||||
import { getExistingCusProducts } from "../../cusProducts/cusProductUtils/getExistingCusProducts.js";
|
||||
import { mergeNewSubItems } from "./mergeNewSubItems.js";
|
||||
import { getQuantityToRemove } from "./mergeUtils.js";
|
||||
|
||||
export const getCusProductsToRemove = ({
|
||||
attachParams,
|
||||
@@ -53,12 +58,12 @@ export const getCusProductsToRemove = ({
|
||||
cusProductsToRemove.push(curScheduledProduct);
|
||||
}
|
||||
|
||||
// 1. If product is an add on, and there's current same, add it
|
||||
else if (curSameProduct) {
|
||||
// 2. If cancelling, and same product exists (add on / main), add it.
|
||||
else if (curSameProduct && attachParams.fromCancel) {
|
||||
cusProductsToRemove.push(curSameProduct);
|
||||
}
|
||||
|
||||
// 2. If product is a main product, add curMain
|
||||
// 3. If product is a main product, add curMain
|
||||
else if (!product.is_add_on && curMainProduct) {
|
||||
cusProductsToRemove.push(curMainProduct);
|
||||
}
|
||||
@@ -100,8 +105,7 @@ export const paramsToSubItems = async ({
|
||||
removeCusProducts?: FullCusProduct[];
|
||||
addItemSet?: ItemSet;
|
||||
}) => {
|
||||
const { logger } = req;
|
||||
let curSubItems = sub?.items.data || [];
|
||||
const curSubItems = sub?.items.data || [];
|
||||
|
||||
const itemSet = notNullish(addItemSet)
|
||||
? addItemSet!
|
||||
@@ -120,7 +124,7 @@ export const paramsToSubItems = async ({
|
||||
cusProductsToRemove.map((cp) => cp.product.name),
|
||||
);
|
||||
|
||||
let newSubItems = mergeNewSubItems({
|
||||
const newSubItems = mergeNewSubItems({
|
||||
itemSet,
|
||||
curSubItems,
|
||||
});
|
||||
@@ -165,7 +169,7 @@ export const paramsToSubItems = async ({
|
||||
allCusProducts.some((cp) => {
|
||||
if (cp.id === cusProduct.id) return false;
|
||||
|
||||
if (cp.status == CusProductStatus.Scheduled) return false;
|
||||
if (cp.status === CusProductStatus.Scheduled) return false;
|
||||
|
||||
return subItemInCusProduct({
|
||||
cusProduct: cp,
|
||||
@@ -177,7 +181,7 @@ export const paramsToSubItems = async ({
|
||||
}
|
||||
|
||||
if (
|
||||
itemSet.subItems.some((si) => si.price == existingSubItem.price?.id)
|
||||
itemSet.subItems.some((si) => si.price === existingSubItem.price?.id)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -33,9 +33,10 @@ export const subToNewSchedule = async ({
|
||||
attachParams,
|
||||
includeCanceled: true,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`REMOVING CUS PRODUCTS: ${cusProductsToRemove.map((cp) => `${cp.product.id} (E: ${cp.entity_id})`).join(", ")}`,
|
||||
); // See mergedDowngrade5.test.ts -- if there's no schedule and creating one, might need to include canceled cus products
|
||||
);
|
||||
|
||||
const res = await paramsToScheduleItems({
|
||||
req,
|
||||
|
||||
@@ -158,6 +158,7 @@ export const handleCancelProduct = async ({
|
||||
replaceables: [],
|
||||
entities: fullCus.entities,
|
||||
features: req.features,
|
||||
fromCancel: true,
|
||||
},
|
||||
config: getDefaultAttachConfig(),
|
||||
skipInsertCusProduct,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { getEntOptions } from "@/internal/products/prices/priceUtils.js";
|
||||
import { performDeductionOnCusEnt } from "@/trigger/updateBalanceTask.js";
|
||||
import { notNullish, nullish } from "@/utils/genUtils.js";
|
||||
import { isOneOff } from "../../../../products/productUtils.js";
|
||||
import {
|
||||
getRelatedCusPrice,
|
||||
getUnlimitedAndUsageAllowed,
|
||||
@@ -161,6 +162,7 @@ export const getExistingUsages = ({
|
||||
export const addExistingUsagesToCusEnts = ({
|
||||
cusEnts,
|
||||
entitlements,
|
||||
prices,
|
||||
curCusProduct,
|
||||
carryExistingUsages = false,
|
||||
printLogs = false,
|
||||
@@ -170,6 +172,7 @@ export const addExistingUsagesToCusEnts = ({
|
||||
}: {
|
||||
cusEnts: CustomerEntitlement[];
|
||||
entitlements: EntitlementWithFeature[];
|
||||
prices: Price[];
|
||||
curCusProduct: FullCusProduct;
|
||||
carryExistingUsages?: boolean;
|
||||
printLogs?: boolean;
|
||||
@@ -177,9 +180,7 @@ export const addExistingUsagesToCusEnts = ({
|
||||
entities: Entity[];
|
||||
features: Feature[];
|
||||
}) => {
|
||||
if (isDowngrade) {
|
||||
return cusEnts;
|
||||
}
|
||||
if (isDowngrade || isOneOff(prices)) return cusEnts;
|
||||
|
||||
const existingUsages = getExistingUsages({
|
||||
curCusProduct,
|
||||
|
||||
@@ -63,13 +63,6 @@ export const initNextResetAt = ({
|
||||
entitlement.interval_count || 1,
|
||||
).getTime();
|
||||
|
||||
// console.log(`--------------------------------`);
|
||||
// console.log(`Interval: `, entitlement.interval);
|
||||
// console.log(`Interval count: `, entitlement.interval_count);
|
||||
// console.log(`Now: `, formatUnixToDateTime(now));
|
||||
// console.log(`Next reset at: `, formatUnixToDateTime(nextResetAtCalculated));
|
||||
// console.log(`Anchor to unix: `, formatUnixToDateTime(anchorToUnix));
|
||||
|
||||
// If anchorToUnix, align next reset at to anchorToUnix...
|
||||
if (
|
||||
anchorToUnix &&
|
||||
|
||||
@@ -38,17 +38,15 @@ export const buildCachedApiCustomerKey = ({
|
||||
export const getCachedApiCustomer = async ({
|
||||
ctx,
|
||||
customerId,
|
||||
skipCache = false,
|
||||
skipEntityMerge = false,
|
||||
source,
|
||||
}: {
|
||||
ctx: AutumnContext;
|
||||
customerId: string;
|
||||
skipCache?: boolean;
|
||||
skipEntityMerge?: boolean; // If true, returns only customer's own features (no entity merging)
|
||||
source?: string;
|
||||
}): Promise<{ apiCustomer: ApiCustomer; legacyData: CustomerLegacyData }> => {
|
||||
const { org, env, db } = ctx;
|
||||
const { org, env, db, skipCache } = ctx;
|
||||
|
||||
const getExpandedApiCustomer = async () => {
|
||||
// await redis.del(
|
||||
|
||||
@@ -166,12 +166,12 @@ export const getApiBalance = ({
|
||||
}),
|
||||
);
|
||||
|
||||
const totalUnused = sumValues(
|
||||
cusEnts.map((cusEnt) => {
|
||||
const { unused } = getCusEntBalance({ cusEnt, entityId });
|
||||
return unused;
|
||||
}),
|
||||
);
|
||||
// const totalUnused = sumValues(
|
||||
// cusEnts.map((cusEnt) => {
|
||||
// const { unused } = getCusEntBalance({ cusEnt, entityId });
|
||||
// return unused;
|
||||
// }),
|
||||
// );
|
||||
|
||||
const totalAdditionalBalance = sumValues(
|
||||
cusEnts.map((cusEnt) => {
|
||||
@@ -183,25 +183,6 @@ export const getApiBalance = ({
|
||||
const totalMaxPurchase = sumValues(
|
||||
cusEnts.map((cusEnt) => cusEntToMaxPurchase({ cusEnt })),
|
||||
);
|
||||
// const totalUsageLimit = sumValues(
|
||||
// cusEnts.map((cusEnt) => cusEntToUsageLimit({ cusEnt, entityId })),
|
||||
// );
|
||||
|
||||
// const totalIncludedUsage = sumValues(
|
||||
// cusEnts.map((cusEnt) => cusEntToIncludedUsage({ cusEnt, entityId })),
|
||||
// );
|
||||
|
||||
// const totalIncludedUsageWithRollovers = sumValues(
|
||||
// cusEnts.map((cusEnt) =>
|
||||
// cusEntToIncludedUsage({ cusEnt, entityId, withRollovers: true }),
|
||||
// ),
|
||||
// );
|
||||
|
||||
// const totalUsage = new Decimal(totalIncludedUsageWithRollovers)
|
||||
// .add(totalAdjustment)
|
||||
// .sub(totalBalanceWithRollovers)
|
||||
// .sub(totalUnused)
|
||||
// .toNumber();
|
||||
|
||||
// 1. Granted balance
|
||||
const totalGrantedBalanceWithRollovers = sumValues(
|
||||
@@ -257,10 +238,6 @@ export const getApiBalance = ({
|
||||
const reset = cusEntsToReset({ cusEnts, feature });
|
||||
const rollovers = cusEntsToRollovers({ cusEnts, entityId });
|
||||
|
||||
// console.log(
|
||||
// `Feature: ${feature.id}, Total Max Purchase: ${totalMaxPurchase}, Usage Allowed: ${usageAllowed}`,
|
||||
// );
|
||||
|
||||
const { data: apiBalance, error } = ApiBalanceSchema.safeParse({
|
||||
feature: ctx.expand.includes(CusExpand.BalanceFeature)
|
||||
? apiFeature
|
||||
|
||||
@@ -82,13 +82,13 @@ export const handleUpdateEntitlement = async (req: any, res: any) => {
|
||||
env: req.env,
|
||||
});
|
||||
|
||||
if (balance < 0 && !cusEnt.usage_allowed) {
|
||||
throw new RecaseError({
|
||||
message: "Entitlement does not allow usage",
|
||||
code: ErrCode.InvalidRequest,
|
||||
statusCode: StatusCodes.BAD_REQUEST,
|
||||
});
|
||||
}
|
||||
// if (balance < 0 && !cusEnt.usage_allowed) {
|
||||
// throw new RecaseError({
|
||||
// message: "Entitlement does not allow usage",
|
||||
// code: ErrCode.InvalidRequest,
|
||||
// statusCode: StatusCodes.BAD_REQUEST,
|
||||
// });
|
||||
// }
|
||||
|
||||
if (cusEnt.unlimited) {
|
||||
throw new RecaseError({
|
||||
@@ -115,7 +115,7 @@ export const handleUpdateEntitlement = async (req: any, res: any) => {
|
||||
},
|
||||
toDeduct: deducted,
|
||||
addAdjustment: true,
|
||||
allowNegativeBalance: cusEnt.usage_allowed || false,
|
||||
allowNegativeBalance: true,
|
||||
entityId: entity_id,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -24,6 +24,18 @@ export const handleFeatureTypeChanged = async ({
|
||||
const { linkedEntitlements, entitlements, prices, creditSystems, cusEnts } =
|
||||
objectsUsingFeature;
|
||||
|
||||
// Cannot switch between credit system <-> metered / boolean
|
||||
if (
|
||||
(feature.type === FeatureType.CreditSystem &&
|
||||
newType !== FeatureType.CreditSystem) ||
|
||||
(feature.type !== FeatureType.CreditSystem &&
|
||||
newType === FeatureType.CreditSystem)
|
||||
) {
|
||||
throw new RecaseError({
|
||||
message: `Cannot change type of feature ${feature.id} from ${feature.type} to ${newType}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (cusEnts.length > 0) {
|
||||
throw new RecaseError({
|
||||
message: `Cannot change type of feature ${feature.id} because it has been attached to a customer before`,
|
||||
|
||||
@@ -60,11 +60,17 @@ export class InvoiceService {
|
||||
with: {
|
||||
customer: {
|
||||
with: {
|
||||
org: true,
|
||||
org: {
|
||||
with: {
|
||||
master: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})) as Invoice & { customer: Customer & { org: Organization } };
|
||||
})) as Invoice & {
|
||||
customer: Customer & { org: Organization & { master: Organization } };
|
||||
};
|
||||
}
|
||||
|
||||
static async list({
|
||||
|
||||
@@ -2,12 +2,13 @@ import {
|
||||
type EntitlementWithFeature,
|
||||
type Feature,
|
||||
type FullProduct,
|
||||
getProductItemDisplay,
|
||||
type Organization,
|
||||
type Price,
|
||||
} from "@autumn/shared";
|
||||
import { mapToProductItems } from "@/internal/products/productV2Utils.js";
|
||||
import { isFeatureItem } from "../products/product-items/productItemUtils/getItemType.js";
|
||||
import { getProductItemDisplay } from "../products/productUtils/productResponseUtils/getProductItemDisplay.js";
|
||||
// import { getProductItemDisplay } from "../products/productUtils/productResponseUtils/getProductItemDisplay.js";
|
||||
|
||||
export const buildInvoiceMemo = async ({
|
||||
org,
|
||||
|
||||
@@ -63,10 +63,12 @@ mainRouter.use(
|
||||
try {
|
||||
const org = invoice.customer.org;
|
||||
const env = invoice.customer.env;
|
||||
|
||||
const stripeCli = createStripeCli({
|
||||
org,
|
||||
env,
|
||||
});
|
||||
|
||||
const stripeInvoice = await stripeCli.invoices.retrieve(
|
||||
invoice.stripe_id,
|
||||
);
|
||||
|
||||
@@ -22,16 +22,12 @@ export const getReplaceables = ({
|
||||
}
|
||||
|
||||
const numReplaceables = prevOverage - newOverage;
|
||||
const newReplaceables = Array.from(
|
||||
{ length: numReplaceables },
|
||||
(_, i) =>
|
||||
({
|
||||
id: generateId("rep"),
|
||||
cus_ent_id: cusEnt.id,
|
||||
created_at: Date.now(),
|
||||
delete_next_cycle: deleteNextCycle,
|
||||
}) satisfies InsertReplaceable,
|
||||
);
|
||||
const newReplaceables = Array.from({ length: numReplaceables }, (_, i) => ({
|
||||
id: generateId("rep"),
|
||||
cus_ent_id: cusEnt.id,
|
||||
created_at: Date.now(),
|
||||
delete_next_cycle: deleteNextCycle,
|
||||
}));
|
||||
|
||||
return newReplaceables;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,26 @@ export const shouldBillNow = (onIncrease: OnIncrease | OnDecrease) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const shouldProrateDowngradeNow = ({
|
||||
onIncrease,
|
||||
onDecrease,
|
||||
}: {
|
||||
onIncrease: OnIncrease;
|
||||
onDecrease: OnDecrease;
|
||||
}) => {
|
||||
if (
|
||||
onDecrease === OnDecrease.NoProrations ||
|
||||
onDecrease === OnDecrease.None
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
onIncrease === OnIncrease.ProrateImmediately ||
|
||||
onIncrease === OnIncrease.BillImmediately
|
||||
);
|
||||
};
|
||||
|
||||
export const shouldProrate = (onIncrease?: OnIncrease | OnDecrease) => {
|
||||
if (!onIncrease) {
|
||||
return true;
|
||||
|
||||
@@ -256,7 +256,8 @@ export const toFeatureAndPrice = ({
|
||||
|
||||
if (shouldProrate(onDecrease) || onDecrease === OnDecrease.Prorate) {
|
||||
onDecrease =
|
||||
onIncrease === OnIncrease.ProrateImmediately
|
||||
onIncrease === OnIncrease.ProrateImmediately ||
|
||||
onIncrease === OnIncrease.BillImmediately
|
||||
? OnDecrease.ProrateImmediately
|
||||
: OnDecrease.ProrateNextCycle;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ErrCode } from "@autumn/shared";
|
||||
import { type Response } from "express";
|
||||
import type { Response } from "express";
|
||||
import { nanoid } from "nanoid";
|
||||
import { CacheManager } from "@/utils/cacheUtils/CacheManager.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
|
||||
@@ -74,10 +74,9 @@ const initWorker = ({ id, db }: { id: number; db: DrizzleCli }) => {
|
||||
|
||||
if (actionHandlers.includes(job.name as JobName)) {
|
||||
await runActionHandlerTask({
|
||||
queue: queue,
|
||||
job,
|
||||
logger: workerLogger,
|
||||
db,
|
||||
jobName: job.name as JobName,
|
||||
payload: job.data,
|
||||
ctx,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ import type Stripe from "stripe";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
||||
import { findStripeItemForPrice } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js";
|
||||
import { getUsageBasedSub } from "@/external/stripe/stripeSubUtils.js";
|
||||
import { getRelatedCusPrice } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js";
|
||||
import { getBillingType } from "@/internal/products/prices/priceUtils.js";
|
||||
import RecaseError from "@/utils/errorUtils.js";
|
||||
import { cusProductToSub } from "../internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
|
||||
import { handleProratedDowngrade } from "./arrearProratedUsage/handleProratedDowngrade.js";
|
||||
import { handleProratedUpgrade } from "./arrearProratedUsage/handleProratedUpgrade.js";
|
||||
|
||||
@@ -115,13 +115,18 @@ export const adjustAllowance = async ({
|
||||
logger.info(`Customer: ${customer.name}, Org: ${org.slug}`);
|
||||
|
||||
const stripeCli = createStripeCli({ org, env });
|
||||
const sub = await getUsageBasedSub({
|
||||
db,
|
||||
const sub = await cusProductToSub({
|
||||
cusProduct,
|
||||
stripeCli,
|
||||
subIds: cusProduct.subscription_ids!,
|
||||
feature: affectedFeature,
|
||||
});
|
||||
|
||||
// const sub = await getUsageBasedSub({
|
||||
// db,
|
||||
// stripeCli,
|
||||
// subIds: cusProduct.subscription_ids!,
|
||||
// feature: affectedFeature,
|
||||
// });
|
||||
|
||||
if (!sub) {
|
||||
logger.error("adjustAllowance: no usage-based sub found");
|
||||
return { newReplaceables: null, invoice: null, deletedReplaceables: null };
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getFeatureInvoiceDescription,
|
||||
type InsertReplaceable,
|
||||
OnDecrease,
|
||||
OnIncrease,
|
||||
type Organization,
|
||||
type Product,
|
||||
type UsagePriceConfig,
|
||||
@@ -21,12 +22,12 @@ import { priceToInvoiceAmount } from "@/internal/products/prices/priceUtils/pric
|
||||
import {
|
||||
shouldBillNow,
|
||||
shouldProrate,
|
||||
shouldProrateDowngradeNow,
|
||||
} from "@/internal/products/prices/priceUtils/prorationConfigUtils.js";
|
||||
import { roundUsage } from "@/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.js";
|
||||
import { formatUnixToDate } from "@/utils/genUtils.js";
|
||||
import { getStripeNow } from "@/utils/scriptUtils/testClockUtils.js";
|
||||
import { getUsageFromBalance } from "../adjustAllowance.js";
|
||||
|
||||
export const createDowngradeProrationInvoice = async ({
|
||||
org,
|
||||
cusPrice,
|
||||
@@ -38,6 +39,7 @@ export const createDowngradeProrationInvoice = async ({
|
||||
newRoundedUsage,
|
||||
feature,
|
||||
product,
|
||||
onIncrease,
|
||||
onDecrease,
|
||||
logger,
|
||||
}: {
|
||||
@@ -51,6 +53,7 @@ export const createDowngradeProrationInvoice = async ({
|
||||
newRoundedUsage: number;
|
||||
feature: Feature;
|
||||
product: Product;
|
||||
onIncrease: OnIncrease;
|
||||
onDecrease: OnDecrease;
|
||||
logger: any;
|
||||
}) => {
|
||||
@@ -102,7 +105,12 @@ export const createDowngradeProrationInvoice = async ({
|
||||
await stripeCli.invoiceItems.create(invoiceItem);
|
||||
let invoice = null;
|
||||
|
||||
if (shouldBillNow(onDecrease)) {
|
||||
if (
|
||||
shouldProrateDowngradeNow({
|
||||
onIncrease,
|
||||
onDecrease,
|
||||
})
|
||||
) {
|
||||
const { invoice: finalInvoice } = await createAndFinalizeInvoice({
|
||||
stripeCli,
|
||||
paymentMethod: null,
|
||||
@@ -196,6 +204,9 @@ export const handleProratedDowngrade = async ({
|
||||
}),
|
||||
feature,
|
||||
product,
|
||||
onIncrease:
|
||||
cusPrice.price.proration_config?.on_increase ||
|
||||
OnIncrease.ProrateImmediately,
|
||||
onDecrease,
|
||||
logger,
|
||||
});
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import "dotenv/config";
|
||||
|
||||
import { invitation } from "@autumn/shared";
|
||||
import { betterAuth } from "better-auth";
|
||||
import { betterAuth, type User } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { admin, emailOTP, organization } from "better-auth/plugins";
|
||||
import {
|
||||
admin,
|
||||
emailOTP,
|
||||
type Organization,
|
||||
organization,
|
||||
} from "better-auth/plugins";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "@/db/initDrizzle.js";
|
||||
import { logger } from "@/external/logtail/logtailUtils.js";
|
||||
@@ -104,7 +109,11 @@ export const auth = betterAuth({
|
||||
}),
|
||||
|
||||
organization({
|
||||
async sendInvitationEmail(data) {
|
||||
async sendInvitationEmail(data: {
|
||||
id: string;
|
||||
email: string;
|
||||
organization: Organization;
|
||||
}) {
|
||||
const inviteLink = `${process.env.CLIENT_URL}/accept?id=${data.id}`;
|
||||
await sendInvitationEmail({
|
||||
email: data.email,
|
||||
@@ -135,7 +144,13 @@ export const auth = betterAuth({
|
||||
|
||||
organizationCreation: {
|
||||
disabled: false,
|
||||
afterCreate: async ({ organization, user }) => {
|
||||
afterCreate: async ({
|
||||
organization,
|
||||
user,
|
||||
}: {
|
||||
organization: Organization;
|
||||
user: User;
|
||||
}) => {
|
||||
await afterOrgCreated({ org: organization, user });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiVersion, type ProductV2 } from "@autumn/shared";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import type { TestContext } from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.js";
|
||||
import type { TestContext } from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import { addPrefixToProducts } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
export const initProductsV0 = async ({
|
||||
|
||||
@@ -3,7 +3,7 @@ import dotenv from "dotenv";
|
||||
dotenv.config();
|
||||
|
||||
import { AppEnv } from "@autumn/shared";
|
||||
import { clearOrg, setupOrg } from "tests/utils/setup.js";
|
||||
import { clearOrg, setupOrg } from "@tests/utils/setup.js";
|
||||
import { initDrizzle } from "@/db/initDrizzle.js";
|
||||
import {
|
||||
advanceProducts,
|
||||
@@ -33,16 +33,6 @@ describe("Initialize org for tests", () => {
|
||||
await setupOrg({
|
||||
orgId: this.org.id,
|
||||
env: DEFAULT_ENV,
|
||||
features: { ...features, ...creditSystems } as any,
|
||||
products: {
|
||||
...products,
|
||||
...advanceProducts,
|
||||
...attachProducts,
|
||||
...oneTimeProducts,
|
||||
...entityProducts,
|
||||
} as any,
|
||||
rewards: { ...rewards } as any,
|
||||
rewardTriggers: { ...referralPrograms } as any,
|
||||
});
|
||||
|
||||
console.log("--------------------------------");
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import {
|
||||
ApiVersion,
|
||||
type CheckResponse,
|
||||
type CheckResponseV0,
|
||||
type CheckResponseV1,
|
||||
SuccessCode,
|
||||
} from "@autumn/shared";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
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 { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||
@@ -44,7 +44,7 @@ describe(`${chalk.yellowBright("temp: temporary script for testing")}`, () => {
|
||||
const res = (await autumnV1.check({
|
||||
customer_id: customerId,
|
||||
feature_id: TestFeature.Messages,
|
||||
})) as unknown as CheckResponse;
|
||||
})) as unknown as CheckResponseV1;
|
||||
|
||||
expect(res).toStrictEqual({
|
||||
allowed: false,
|
||||
|
||||
118
server/tests/_temp/temp1.test.ts
Normal file
118
server/tests/_temp/temp1.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { beforeAll, describe, test } from "bun:test";
|
||||
import {
|
||||
type AppEnv,
|
||||
LegacyVersion,
|
||||
OnDecrease,
|
||||
OnIncrease,
|
||||
type Organization,
|
||||
} from "@autumn/shared";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import {
|
||||
constructArrearProratedItem,
|
||||
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";
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const addOn = constructProduct({
|
||||
type: "pro",
|
||||
isAddOn: true,
|
||||
items: [
|
||||
// constructFeatureItem({ featureId: TestFeature.Words, includedUsage: 100 }),
|
||||
constructArrearProratedItem({
|
||||
featureId: TestFeature.Users,
|
||||
includedUsage: 1,
|
||||
pricePerUnit: 10,
|
||||
config: {
|
||||
on_increase: OnIncrease.BillImmediately,
|
||||
on_decrease: OnDecrease.Prorate,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe(`${chalk.yellowBright("temp1: Testing add ons")}`, () => {
|
||||
const customerId = "temp1";
|
||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
let curUnix: number;
|
||||
let db: DrizzleCli;
|
||||
let org: Organization;
|
||||
let env: AppEnv;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = ctx.db;
|
||||
org = ctx.org;
|
||||
env = ctx.env;
|
||||
stripeCli = ctx.stripeCli;
|
||||
|
||||
const { testClockId: testClockId1 } = await initCustomerV3({
|
||||
ctx,
|
||||
customerId,
|
||||
customerData: {},
|
||||
attachPm: "success",
|
||||
withTestClock: true,
|
||||
});
|
||||
|
||||
await initProductsV0({
|
||||
ctx,
|
||||
products: [addOn],
|
||||
prefix: customerId,
|
||||
});
|
||||
|
||||
testClockId = testClockId1!;
|
||||
});
|
||||
|
||||
test("should attach pro product", async () => {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: addOn.id,
|
||||
});
|
||||
|
||||
await autumn.entities.create(customerId, [
|
||||
{
|
||||
id: "1",
|
||||
name: "Entity 1",
|
||||
feature_id: TestFeature.Users,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
name: "Entity 2",
|
||||
feature_id: TestFeature.Users,
|
||||
},
|
||||
]);
|
||||
|
||||
await autumn.entities.delete(customerId, "1");
|
||||
// await autumn.attach({
|
||||
// customer_id: customerId,
|
||||
// product_id: addOn.id,
|
||||
// });
|
||||
// await autumn.attach({
|
||||
// customer_id: customerId,
|
||||
// product_id: addOn.id,
|
||||
// });
|
||||
});
|
||||
|
||||
// test("should cancel one add on", async () => {
|
||||
// await autumn.cancel({
|
||||
// customer_id: customerId,
|
||||
// product_id: addOn.id,
|
||||
// });
|
||||
|
||||
// await expectSubToBeCorrect({
|
||||
// customerId,
|
||||
// db,
|
||||
// org,
|
||||
// env,
|
||||
// });
|
||||
// });
|
||||
});
|
||||
@@ -1,234 +0,0 @@
|
||||
import {
|
||||
type AppEnv,
|
||||
type Customer,
|
||||
LegacyVersion,
|
||||
type Organization,
|
||||
} 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 { rewards } from "tests/global.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import {
|
||||
advanceTestClock,
|
||||
completeCheckoutForm,
|
||||
getDiscount,
|
||||
} from "tests/utils/stripeUtils.js";
|
||||
import {
|
||||
addPrefixToProducts,
|
||||
getBasePrice,
|
||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
|
||||
const testCase = "coupon1";
|
||||
|
||||
const pro = constructProduct({
|
||||
type: "pro",
|
||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
||||
});
|
||||
|
||||
const simulateOneCycle = async ({
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
autumn,
|
||||
testClockId,
|
||||
couponAmount,
|
||||
curUnix,
|
||||
}: {
|
||||
customerId: string;
|
||||
db: DrizzleCli;
|
||||
org: Organization;
|
||||
env: AppEnv;
|
||||
stripeCli: Stripe;
|
||||
autumn: AutumnInt;
|
||||
testClockId: string;
|
||||
couponAmount: number;
|
||||
curUnix: number;
|
||||
}) => {
|
||||
const usage = Math.random() * 100000 + 10000;
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
feature_id: TestFeature.Words,
|
||||
value: usage,
|
||||
});
|
||||
|
||||
// Expected invoice total
|
||||
const expectedTotal = await getExpectedInvoiceTotal({
|
||||
usage: [{ featureId: TestFeature.Words, value: usage }],
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
});
|
||||
|
||||
couponAmount -= expectedTotal;
|
||||
|
||||
curUnix = await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addHours(
|
||||
addMonths(curUnix, 1),
|
||||
hoursToFinalizeInvoice,
|
||||
).getTime(),
|
||||
waitForSeconds: 30,
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expect(customer.invoices![0].total).to.equal(0);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli: stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
expect(cusDiscount).to.exist;
|
||||
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(
|
||||
Math.round(couponAmount * 100),
|
||||
`Expected stripe cus to have coupon amount ${couponAmount * 100}`,
|
||||
);
|
||||
|
||||
return {
|
||||
couponAmount,
|
||||
curUnix,
|
||||
};
|
||||
};
|
||||
|
||||
describe(
|
||||
chalk.yellow(
|
||||
`${testCase} - Testing invoice credits reward, apply to all product`,
|
||||
),
|
||||
() => {
|
||||
const customerId = "coupon1";
|
||||
let stripeCli: Stripe;
|
||||
let customer: Customer;
|
||||
let testClockId: string;
|
||||
let db: DrizzleCli;
|
||||
let org: Organization;
|
||||
let env: AppEnv;
|
||||
|
||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||
|
||||
let couponAmount = rewards.rolloverAll.discount_config.discount_value;
|
||||
let curUnix = new Date().getTime();
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const res = await initCustomer({
|
||||
customerId,
|
||||
org,
|
||||
env,
|
||||
db,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
|
||||
addPrefixToProducts({
|
||||
products: [pro],
|
||||
prefix: testCase,
|
||||
});
|
||||
|
||||
await createProducts({
|
||||
products: [pro],
|
||||
orgId: org.id,
|
||||
env,
|
||||
db,
|
||||
autumn,
|
||||
});
|
||||
|
||||
testClockId = res.testClockId;
|
||||
customer = res.customer;
|
||||
});
|
||||
|
||||
// CYCLE 0
|
||||
it("should attach pro", async () => {
|
||||
const res = await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(
|
||||
res.checkout_url,
|
||||
undefined,
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
|
||||
await timeout(10000);
|
||||
|
||||
couponAmount -= getBasePrice({ product: pro });
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expectProductAttached({ customer, product: pro });
|
||||
|
||||
expect(customer.invoices![0].total).to.equal(0);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
expect(cusDiscount).to.exist;
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(
|
||||
rewards.rolloverAll.id,
|
||||
);
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
});
|
||||
|
||||
it("should run one cycle and have correct invoice + coupon amount", async () => {
|
||||
const res = await simulateOneCycle({
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
autumn,
|
||||
testClockId,
|
||||
couponAmount,
|
||||
curUnix: new Date().getTime(),
|
||||
});
|
||||
|
||||
couponAmount = res.couponAmount;
|
||||
curUnix = res.curUnix;
|
||||
});
|
||||
|
||||
// CYCLE 1
|
||||
it("should run another cycle and have correct invoice + coupon amount", async () => {
|
||||
const res = await simulateOneCycle({
|
||||
customerId,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
stripeCli,
|
||||
autumn,
|
||||
testClockId,
|
||||
couponAmount,
|
||||
curUnix,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -7,22 +7,22 @@ import {
|
||||
type Organization,
|
||||
RewardType,
|
||||
} from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { createReward } from "tests/utils/productUtils.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { timeout } from "@tests/utils/genUtils.js";
|
||||
import { createReward } from "@tests/utils/productUtils.js";
|
||||
import {
|
||||
advanceTestClock,
|
||||
completeCheckoutForm,
|
||||
getDiscount,
|
||||
} from "tests/utils/stripeUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
} from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
import {
|
||||
type AppEnv,
|
||||
CouponDurationType,
|
||||
type CreateReward,
|
||||
LegacyVersion,
|
||||
type Organization,
|
||||
RewardType,
|
||||
} from "@autumn/shared";
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type Stripe from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
||||
import { completeCheckoutForm, getDiscount } from "tests/utils/stripeUtils.js";
|
||||
import {
|
||||
addPrefixToProducts,
|
||||
getBasePrice,
|
||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import { advanceTestClock } from "@/utils/scriptUtils/testClockUtils.js";
|
||||
|
||||
const pro = constructProduct({
|
||||
type: "pro",
|
||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
||||
});
|
||||
|
||||
const testCase = "coupon2";
|
||||
|
||||
// Create reward input
|
||||
const reward: CreateReward = {
|
||||
id: "usage",
|
||||
name: "usage",
|
||||
promo_codes: [{ code: "usage" }],
|
||||
type: RewardType.InvoiceCredits,
|
||||
discount_config: {
|
||||
discount_value: 10000,
|
||||
duration_type: CouponDurationType.Forever,
|
||||
duration_value: 1,
|
||||
should_rollover: true,
|
||||
apply_to_all: false,
|
||||
price_ids: [],
|
||||
},
|
||||
};
|
||||
|
||||
describe(
|
||||
chalk.yellow(`${testCase} - Testing one-off rollover, apply to usage only`),
|
||||
() => {
|
||||
const customerId = testCase;
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
|
||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||
let org: Organization;
|
||||
let env: AppEnv;
|
||||
let db: DrizzleCli;
|
||||
|
||||
let couponAmount = reward.discount_config?.discount_value ?? 0;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
db = this.db;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { testClockId: testClockId1 } = await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn: this.autumnJs,
|
||||
});
|
||||
|
||||
testClockId = testClockId1;
|
||||
|
||||
addPrefixToProducts({
|
||||
products: [pro],
|
||||
prefix: testCase,
|
||||
});
|
||||
|
||||
await createProducts({
|
||||
orgId: this.org.id,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn,
|
||||
products: [pro],
|
||||
});
|
||||
|
||||
await createReward({
|
||||
orgId: org.id,
|
||||
env,
|
||||
db,
|
||||
autumn,
|
||||
reward,
|
||||
productId: pro.id,
|
||||
onlyUsage: true,
|
||||
});
|
||||
});
|
||||
|
||||
// CYCLE 0
|
||||
it("should attach pro with promo code", async () => {
|
||||
const res = await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
});
|
||||
|
||||
await completeCheckoutForm(res.checkout_url, undefined, reward.id);
|
||||
|
||||
await timeout(10000);
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expectProductAttached({
|
||||
customer,
|
||||
product: pro,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have fixed price invoice and correct remaining coupon amount", async () => {
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
const fixedPrice = getBasePrice({ product: pro });
|
||||
expect(customer.invoices![0].total).to.equal(fixedPrice);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(reward.id);
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(couponAmount * 100);
|
||||
});
|
||||
|
||||
// CYCLE 1
|
||||
it("should track usage and have correct invoice amount", async () => {
|
||||
const usage = new Decimal(Math.random() * 1250120 + 10000)
|
||||
.toDecimalPlaces(2)
|
||||
.toNumber();
|
||||
|
||||
await autumn.track({
|
||||
customer_id: customerId,
|
||||
feature_id: TestFeature.Words,
|
||||
value: usage,
|
||||
});
|
||||
|
||||
const usageTotal = await getExpectedInvoiceTotal({
|
||||
org,
|
||||
env,
|
||||
db,
|
||||
customerId,
|
||||
productId: pro.id,
|
||||
usage: [{ featureId: TestFeature.Words, value: usage }],
|
||||
stripeCli,
|
||||
onlyIncludeUsage: true,
|
||||
});
|
||||
|
||||
const basePrice = getBasePrice({ product: pro });
|
||||
|
||||
couponAmount = couponAmount - usageTotal;
|
||||
|
||||
await advanceTestClock({
|
||||
stripeCli,
|
||||
testClockId,
|
||||
advanceTo: addHours(
|
||||
addMonths(new Date(), 1),
|
||||
hoursToFinalizeInvoice,
|
||||
).getTime(),
|
||||
waitForSeconds: 20,
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expect(customer.invoices![0].total).to.equal(basePrice);
|
||||
|
||||
const cusDiscount = await getDiscount({
|
||||
stripeCli,
|
||||
stripeId: customer.stripe_id!,
|
||||
});
|
||||
|
||||
expect(getOriginalCouponId(cusDiscount.coupon?.id)).to.equal(reward.id);
|
||||
|
||||
expect(cusDiscount.coupon?.amount_off).to.equal(
|
||||
Math.round(couponAmount * 100),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -11,18 +11,18 @@ import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import { Decimal } from "decimal.js";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
||||
import { completeCheckoutForm, getDiscount } from "tests/utils/stripeUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { timeout } from "@tests/utils/genUtils.js";
|
||||
import { createProducts, createReward } from "@tests/utils/productUtils.js";
|
||||
import { completeCheckoutForm, getDiscount } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import {
|
||||
addPrefixToProducts,
|
||||
getBasePrice,
|
||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
} from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { getOriginalCouponId } from "@/internal/rewards/rewardUtils.js";
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
import {
|
||||
type AppEnv,
|
||||
CouponDurationType,
|
||||
type CreateReward,
|
||||
LegacyVersion,
|
||||
type Organization,
|
||||
RewardType,
|
||||
} from "@autumn/shared";
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import type Stripe from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { expectAttachCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
||||
import {
|
||||
addPrefixToProducts,
|
||||
getBasePrice,
|
||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import {
|
||||
constructArrearItem,
|
||||
constructFeatureItem,
|
||||
} from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
|
||||
|
||||
const pro = constructProduct({
|
||||
type: "pro",
|
||||
items: [constructArrearItem({ featureId: TestFeature.Words })],
|
||||
});
|
||||
|
||||
const oneOff = constructProduct({
|
||||
type: "one_off",
|
||||
items: [
|
||||
constructFeatureItem({
|
||||
featureId: TestFeature.Words,
|
||||
includedUsage: 500,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// Create reward input
|
||||
const rewardId = "attach_coupon";
|
||||
const promoCode = "attach_coupon_code";
|
||||
const reward: CreateReward = {
|
||||
id: rewardId,
|
||||
name: "attach_coupon",
|
||||
promo_codes: [{ code: promoCode }],
|
||||
type: RewardType.FixedDiscount,
|
||||
discount_config: {
|
||||
discount_value: 5,
|
||||
duration_type: CouponDurationType.OneOff,
|
||||
duration_value: 1,
|
||||
should_rollover: true,
|
||||
apply_to_all: true,
|
||||
price_ids: [],
|
||||
},
|
||||
};
|
||||
|
||||
const testCase = "coupon3";
|
||||
describe(chalk.yellow(`${testCase} - Testing attach coupon`), () => {
|
||||
const customerId = testCase;
|
||||
let stripeCli: Stripe;
|
||||
let testClockId: string;
|
||||
|
||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||
let org: Organization;
|
||||
let env: AppEnv;
|
||||
let db: DrizzleCli;
|
||||
|
||||
const couponAmount = reward.discount_config!.discount_value;
|
||||
|
||||
before(async function () {
|
||||
await setupBefore(this);
|
||||
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
db = this.db;
|
||||
stripeCli = this.stripeCli;
|
||||
|
||||
const { testClockId: testClockId1 } = await initCustomer({
|
||||
customerId,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn: this.autumnJs,
|
||||
attachPm: "success",
|
||||
});
|
||||
|
||||
testClockId = testClockId1;
|
||||
|
||||
addPrefixToProducts({
|
||||
products: [pro, oneOff],
|
||||
prefix: testCase,
|
||||
});
|
||||
|
||||
await createProducts({
|
||||
orgId: this.org.id,
|
||||
env: this.env,
|
||||
db: this.db,
|
||||
autumn,
|
||||
products: [pro, oneOff],
|
||||
});
|
||||
|
||||
await createReward({
|
||||
orgId: org.id,
|
||||
env,
|
||||
db,
|
||||
autumn,
|
||||
reward,
|
||||
productId: pro.id,
|
||||
});
|
||||
});
|
||||
|
||||
// CYCLE 0
|
||||
it("should attach pro with reward ID", async () => {
|
||||
const res = await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
reward: rewardId,
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expectAttachCorrect({
|
||||
customer,
|
||||
product: pro,
|
||||
});
|
||||
|
||||
const invoice = customer.invoices![0];
|
||||
const basePrice = getBasePrice({ product: pro });
|
||||
expect(invoice.total).to.equal(basePrice - couponAmount);
|
||||
});
|
||||
|
||||
it("should attach one off with reward ID", async () => {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: oneOff.id,
|
||||
reward: rewardId,
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expectAttachCorrect({
|
||||
customer,
|
||||
product: oneOff,
|
||||
});
|
||||
|
||||
const invoice = customer.invoices![0];
|
||||
const basePrice = getBasePrice({ product: oneOff });
|
||||
expect(invoice.total).to.equal(basePrice - couponAmount);
|
||||
expect(invoice.product_ids).to.include(oneOff.id);
|
||||
});
|
||||
|
||||
it("should attach one off with promo code", async () => {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: oneOff.id,
|
||||
reward: promoCode,
|
||||
});
|
||||
|
||||
const customer = await autumn.customers.get(customerId);
|
||||
expectAttachCorrect({
|
||||
customer,
|
||||
product: oneOff,
|
||||
});
|
||||
|
||||
expect(customer.invoices!.length).to.equal(3);
|
||||
const basePrice = getBasePrice({ product: oneOff });
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const invoice = customer.invoices![i];
|
||||
expect(invoice.total).to.equal(basePrice - couponAmount);
|
||||
expect(invoice.product_ids).to.include(oneOff.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -9,14 +9,14 @@ import {
|
||||
} from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { expectAttachCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { createProducts, createReward } from "tests/utils/productUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { expectAttachCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||
import { createProducts, createReward } from "@tests/utils/productUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import {
|
||||
addPrefixToProducts,
|
||||
getBasePrice,
|
||||
} from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
} from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import {
|
||||
|
||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.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";
|
||||
|
||||
@@ -3,11 +3,11 @@ import { LegacyVersion } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
|
||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.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";
|
||||
|
||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
|
||||
@@ -3,11 +3,11 @@ import { LegacyVersion } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructRawProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
|
||||
@@ -3,14 +3,14 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addHours, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js";
|
||||
import { getExpectedInvoiceTotal } from "@tests/utils/expectUtils/expectInvoiceUtils.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
|
||||
@@ -3,13 +3,13 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addDays, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
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 { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
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 { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
||||
|
||||
@@ -3,10 +3,10 @@ import { LegacyVersion } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { addDays, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.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 ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
||||
import {
|
||||
|
||||
@@ -3,13 +3,13 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addDays, addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
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 { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
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 { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { calculateProrationAmount } from "@/internal/invoices/prorationUtils.js";
|
||||
|
||||
@@ -3,16 +3,16 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
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 { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
} from "@tests/utils/expectUtils/expectScheduleUtils.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.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";
|
||||
|
||||
@@ -3,14 +3,14 @@ import { LegacyVersion } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.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 ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
} from "@tests/utils/expectUtils/expectScheduleUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import { getBasePrice } from "@tests/utils/testProductUtils/testProductUtils.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
|
||||
@@ -3,16 +3,16 @@ import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addMonths } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
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 { getBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
|
||||
} from "@tests/utils/expectUtils/expectScheduleUtils.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.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";
|
||||
|
||||
@@ -3,11 +3,11 @@ import type { Customer } from "autumn-js";
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
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 { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
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 type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
|
||||
@@ -3,9 +3,9 @@ import { LegacyVersion } from "@autumn/shared";
|
||||
import type { Customer } from "autumn-js";
|
||||
import chalk from "chalk";
|
||||
import type Stripe from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.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";
|
||||
|
||||
@@ -3,11 +3,11 @@ import type { Customer } from "autumn-js";
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import type Stripe from "stripe";
|
||||
import { addPrefixToProducts } from "tests/attach/utils.js";
|
||||
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 { addPrefixToProducts } from "@tests/attach/utils.js";
|
||||
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 type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { timeout } from "@/utils/genUtils.js";
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
// Manual customer creation - not using initCustomer to control test clock properly
|
||||
import { beforeAll, describe, it } from "bun:test";
|
||||
import {
|
||||
type AppEnv,
|
||||
CusProductStatus,
|
||||
FreeTrialDuration,
|
||||
LegacyVersion,
|
||||
type Organization,
|
||||
ProductItemInterval,
|
||||
} from "@autumn/shared";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import chalk from "chalk";
|
||||
import type Stripe from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomerV2 } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import {
|
||||
defaultTrialFree,
|
||||
defaultTrialPro,
|
||||
setupDefaultTrialBefore,
|
||||
} from "./defaultTrialBefore.test.js";
|
||||
import { 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";
|
||||
|
||||
// Case 1: ✅
|
||||
// Pro product with default trial exists alongside a free default product
|
||||
@@ -28,35 +27,66 @@ import {
|
||||
// Pro product with default trial exists alone
|
||||
// -> Creating a new customer should attach the pro product with default trial
|
||||
|
||||
const defaultTrialPro = constructProduct({
|
||||
items: [
|
||||
constructFeatureItem({
|
||||
featureId: TestFeature.Words,
|
||||
includedUsage: 1500,
|
||||
interval: ProductItemInterval.Month,
|
||||
}),
|
||||
],
|
||||
isDefault: true,
|
||||
forcePaidDefault: true,
|
||||
id: "defaultTrial_pro",
|
||||
group: "defaultTrial",
|
||||
type: "pro",
|
||||
freeTrial: {
|
||||
length: 7,
|
||||
duration: FreeTrialDuration.Day,
|
||||
unique_fingerprint: false,
|
||||
card_required: false,
|
||||
},
|
||||
});
|
||||
|
||||
const defaultTrialFree = constructProduct({
|
||||
items: [
|
||||
constructFeatureItem({
|
||||
featureId: TestFeature.Words,
|
||||
includedUsage: 500,
|
||||
interval: ProductItemInterval.Month,
|
||||
}),
|
||||
],
|
||||
id: "defaultTrial_free",
|
||||
group: "defaultTrial",
|
||||
type: "free",
|
||||
isDefault: true,
|
||||
});
|
||||
|
||||
const testCase = "defaultTrial1";
|
||||
|
||||
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure default trials are attached when creating a customer`)}`, () => {
|
||||
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 = Math.floor(new Date().getTime() / 1000);
|
||||
beforeAll(async () => {
|
||||
// Products must be initialized BEFORE customer creation for default products
|
||||
await initProductsV0({
|
||||
ctx,
|
||||
products: [defaultTrialPro, defaultTrialFree],
|
||||
prefix: testCase,
|
||||
customerId,
|
||||
});
|
||||
|
||||
beforeAll(async function () {
|
||||
await setupBefore(this);
|
||||
await setupDefaultTrialBefore({});
|
||||
const { autumnJs } = this;
|
||||
stripeCli = this.stripeCli;
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
const res = await initCustomerV2({
|
||||
autumn: autumnJs,
|
||||
customerId: testCase,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
const res = await initCustomerV3({
|
||||
ctx,
|
||||
customerId,
|
||||
withTestClock: true,
|
||||
});
|
||||
|
||||
testClockID = res.testClockId;
|
||||
stripeCli = ctx.stripeCli;
|
||||
});
|
||||
|
||||
it("should create a customer with the paid default trial", async () => {
|
||||
|
||||
@@ -1,59 +1,76 @@
|
||||
// Manual customer creation - not using initCustomer to control test clock properly
|
||||
import { beforeAll, describe, it } from "bun:test";
|
||||
import {
|
||||
type AppEnv,
|
||||
CusProductStatus,
|
||||
FreeTrialDuration,
|
||||
LegacyVersion,
|
||||
type Organization,
|
||||
ProductItemInterval,
|
||||
} from "@autumn/shared";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import chalk from "chalk";
|
||||
import { addDays, addHours } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomerV2 } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import {
|
||||
defaultTrialPro,
|
||||
setupDefaultTrialBefore,
|
||||
} from "./defaultTrialBefore.test.js";
|
||||
import { 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";
|
||||
|
||||
// 2.2:
|
||||
// -> Creating a new customer with a payment method should attach the pro product with default trial
|
||||
// --> Advancing the test clock should cancel the trial and attach the pro product
|
||||
|
||||
const defaultTrialPro = constructProduct({
|
||||
items: [
|
||||
constructFeatureItem({
|
||||
featureId: TestFeature.Words,
|
||||
includedUsage: 1500,
|
||||
interval: ProductItemInterval.Month,
|
||||
}),
|
||||
],
|
||||
isDefault: true,
|
||||
forcePaidDefault: true,
|
||||
id: "defaultTrial_pro",
|
||||
group: "defaultTrial",
|
||||
type: "pro",
|
||||
freeTrial: {
|
||||
length: 7,
|
||||
duration: FreeTrialDuration.Day,
|
||||
unique_fingerprint: false,
|
||||
card_required: false,
|
||||
},
|
||||
});
|
||||
|
||||
const testCase = "defaultTrial2";
|
||||
|
||||
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure trial transitions into full product if payment method is valid`)}`, () => {
|
||||
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 = Math.floor(new Date().getTime() / 1000);
|
||||
beforeAll(async () => {
|
||||
// Products must be initialized BEFORE customer creation for default products
|
||||
await initProductsV0({
|
||||
ctx,
|
||||
products: [defaultTrialPro],
|
||||
prefix: testCase,
|
||||
customerId,
|
||||
});
|
||||
|
||||
beforeAll(async function () {
|
||||
await setupBefore(this);
|
||||
await setupDefaultTrialBefore({});
|
||||
const { autumnJs } = this;
|
||||
stripeCli = this.stripeCli;
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
const res = await initCustomerV2({
|
||||
autumn: autumnJs,
|
||||
customerId: testCase,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
const res = await initCustomerV3({
|
||||
ctx,
|
||||
customerId,
|
||||
attachPm: "success",
|
||||
withTestClock: true,
|
||||
});
|
||||
|
||||
testClockID = res.testClockId;
|
||||
stripeCli = ctx.stripeCli;
|
||||
});
|
||||
|
||||
it("should create a customer with the paid default trial", async () => {
|
||||
|
||||
@@ -1,59 +1,76 @@
|
||||
// Manual customer creation - not using initCustomer to control test clock properly
|
||||
import { beforeAll, describe, it } from "bun:test";
|
||||
import {
|
||||
type AppEnv,
|
||||
CusProductStatus,
|
||||
FreeTrialDuration,
|
||||
LegacyVersion,
|
||||
type Organization,
|
||||
ProductItemInterval,
|
||||
} from "@autumn/shared";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { hoursToFinalizeInvoice } from "@tests/utils/constants.js";
|
||||
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import chalk from "chalk";
|
||||
import { addDays, addHours } from "date-fns";
|
||||
import type Stripe from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { hoursToFinalizeInvoice } from "tests/utils/constants.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { initCustomerV2 } from "@/utils/scriptUtils/initCustomer.js";
|
||||
import {
|
||||
defaultTrialPro,
|
||||
setupDefaultTrialBefore,
|
||||
} from "./defaultTrialBefore.test.js";
|
||||
import { 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";
|
||||
|
||||
// 2.3:
|
||||
// -> Creating a new customer with a fake payment method should attach the pro product with default trial
|
||||
// --> Advancing the test clock should cancel the trial and attach the free product
|
||||
|
||||
const defaultTrialPro = constructProduct({
|
||||
items: [
|
||||
constructFeatureItem({
|
||||
featureId: TestFeature.Words,
|
||||
includedUsage: 1500,
|
||||
interval: ProductItemInterval.Month,
|
||||
}),
|
||||
],
|
||||
isDefault: true,
|
||||
forcePaidDefault: true,
|
||||
id: "defaultTrial_pro",
|
||||
group: "defaultTrial",
|
||||
type: "pro",
|
||||
freeTrial: {
|
||||
length: 7,
|
||||
duration: FreeTrialDuration.Day,
|
||||
unique_fingerprint: false,
|
||||
card_required: false,
|
||||
},
|
||||
});
|
||||
|
||||
const testCase = "defaultTrial3";
|
||||
|
||||
describe(`${chalk.yellowBright(`advanced/${testCase}: ensure trials cancel with bad payment method`)}`, () => {
|
||||
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 = Math.floor(new Date().getTime() / 1000);
|
||||
beforeAll(async () => {
|
||||
// Products must be initialized BEFORE customer creation for default products
|
||||
await initProductsV0({
|
||||
ctx,
|
||||
products: [defaultTrialPro],
|
||||
prefix: testCase,
|
||||
customerId,
|
||||
});
|
||||
|
||||
beforeAll(async function () {
|
||||
await setupBefore(this);
|
||||
await setupDefaultTrialBefore({});
|
||||
const { autumnJs } = this;
|
||||
stripeCli = this.stripeCli;
|
||||
db = this.db;
|
||||
org = this.org;
|
||||
env = this.env;
|
||||
|
||||
const res = await initCustomerV2({
|
||||
autumn: autumnJs,
|
||||
customerId: testCase,
|
||||
db,
|
||||
org,
|
||||
env,
|
||||
const res = await initCustomerV3({
|
||||
ctx,
|
||||
customerId,
|
||||
attachPm: "fail",
|
||||
withTestClock: true,
|
||||
});
|
||||
|
||||
testClockID = res.testClockId;
|
||||
stripeCli = ctx.stripeCli;
|
||||
});
|
||||
|
||||
it("should create a customer with the paid default trial", async () => {
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import {
|
||||
FreeTrialDuration,
|
||||
LegacyVersion,
|
||||
ProductItemInterval,
|
||||
} from "@autumn/shared";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
|
||||
export const defaultTrialPro = constructProduct({
|
||||
items: [
|
||||
constructFeatureItem({
|
||||
featureId: TestFeature.Words,
|
||||
includedUsage: 1500,
|
||||
interval: ProductItemInterval.Month,
|
||||
}),
|
||||
],
|
||||
isDefault: true,
|
||||
forcePaidDefault: true,
|
||||
id: "defaultTrial_pro",
|
||||
group: "defaultTrial",
|
||||
type: "pro",
|
||||
freeTrial: {
|
||||
length: 7,
|
||||
duration: FreeTrialDuration.Day,
|
||||
unique_fingerprint: false,
|
||||
card_required: false,
|
||||
},
|
||||
});
|
||||
|
||||
export const defaultTrialFree = constructProduct({
|
||||
items: [
|
||||
constructFeatureItem({
|
||||
featureId: TestFeature.Words,
|
||||
includedUsage: 500,
|
||||
interval: ProductItemInterval.Month,
|
||||
}),
|
||||
],
|
||||
id: "defaultTrial_free",
|
||||
group: "defaultTrial",
|
||||
type: "free",
|
||||
isDefault: true,
|
||||
});
|
||||
|
||||
export const setupDefaultTrialBefore = async ({}: {}) => {
|
||||
const autumn = new AutumnInt({ version: LegacyVersion.v1_2 });
|
||||
for (const product of [defaultTrialPro, defaultTrialFree]) {
|
||||
const res = await autumn.products.get(product.id);
|
||||
|
||||
if (res.code === "product_not_found") {
|
||||
try {
|
||||
await autumn.products.create(product);
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2,10 +2,10 @@ import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { expectProductAttached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { cusProductToSub } from "@/internal/customers/cusProducts/cusProductUtils/convertCusProduct.js";
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import { ApiVersion, type AppEnv, LegacyVersion } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import {
|
||||
getPrepaidCusEnt,
|
||||
getUsageCusEnt,
|
||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.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";
|
||||
|
||||
@@ -7,14 +7,14 @@ import {
|
||||
} from "@autumn/shared";
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { features } from "tests/global.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { features } from "@tests/global.js";
|
||||
import {
|
||||
getPrepaidCusEnt,
|
||||
getUsageCusEnt,
|
||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructFeaturePriceItem } from "@/internal/products/product-items/productItemUtils.js";
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import { ApiVersion, type AppEnv, LegacyVersion } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import {
|
||||
getLifetimeFreeCusEnt,
|
||||
getUsageCusEnt,
|
||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.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";
|
||||
|
||||
@@ -8,14 +8,14 @@ import {
|
||||
} from "@autumn/shared";
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { features } from "tests/global.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { features } from "@tests/global.js";
|
||||
import {
|
||||
getLifetimeFreeCusEnt,
|
||||
getUsageCusEnt,
|
||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { createProduct } from "tests/utils/productUtils.js";
|
||||
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { createProduct } from "@tests/utils/productUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import {
|
||||
|
||||
@@ -4,13 +4,13 @@ import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import { ApiVersion, type AppEnv } from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { addMonths } from "date-fns";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import {
|
||||
getLifetimeFreeCusEnt,
|
||||
getUsageCusEnt,
|
||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.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";
|
||||
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
import { expect } from "chai";
|
||||
import chalk from "chalk";
|
||||
import { addMonths } from "date-fns";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { features } from "tests/global.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { features } from "@tests/global.js";
|
||||
import {
|
||||
getLifetimeFreeCusEnt,
|
||||
getUsageCusEnt,
|
||||
} from "tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { createProducts } from "tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
} from "@tests/utils/cusProductUtils/cusEntSearchUtils.js";
|
||||
import { getMainCusProduct } from "@tests/utils/cusProductUtils/cusProductUtils.js";
|
||||
import { createProducts } from "@tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import {
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
import { assert } from "chai";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
import { assert } from "chai";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
import { assert } from "chai";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import { setupBefore } from "tests/before.js";
|
||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import { setupBefore } from "@tests/before.js";
|
||||
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
|
||||
@@ -10,9 +10,9 @@ import {
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import chalk from "chalk";
|
||||
import type { Stripe } from "stripe";
|
||||
import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { CusService } from "@/internal/customers/CusService.js";
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
// import { assert } from "chai";
|
||||
// import chalk from "chalk";
|
||||
// import type { Stripe } from "stripe";
|
||||
// import { setupBefore } from "tests/before.js";
|
||||
// import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
// import { setupBefore } from "@tests/before.js";
|
||||
// import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
// import {
|
||||
// advanceTestClock,
|
||||
// completeCheckoutForm,
|
||||
// } from "tests/utils/stripeUtils.js";
|
||||
// } from "@tests/utils/stripeUtils.js";
|
||||
// import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
// import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
// import { CusService } from "@/internal/customers/CusService.js";
|
||||
|
||||
@@ -13,12 +13,12 @@
|
||||
// import { beforeAll, describe, expect, test } from "bun:test";
|
||||
// import chalk from "chalk";
|
||||
// import type { Stripe } from "stripe";
|
||||
// import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js";
|
||||
// import { expectProductV1Attached } from "@tests/utils/expectUtils/expectProductAttached.js";
|
||||
// import {
|
||||
// advanceTestClock,
|
||||
// completeCheckoutForm,
|
||||
// } from "tests/utils/stripeUtils.js";
|
||||
// import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
// } from "@tests/utils/stripeUtils.js";
|
||||
// import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
// import type { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
// import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
// import { CusService } from "@/internal/customers/CusService.js";
|
||||
|
||||
@@ -15,11 +15,11 @@ import {
|
||||
import chalk from "chalk";
|
||||
import { addDays } from "date-fns";
|
||||
import type { Stripe } from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { createReferralProgram } from "tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { timeout } from "@tests/utils/genUtils.js";
|
||||
import { createReferralProgram } 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 AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
|
||||
@@ -16,11 +16,11 @@ import {
|
||||
import chalk from "chalk";
|
||||
import { addDays } from "date-fns";
|
||||
import type { Stripe } from "stripe";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { createReferralProgram } from "tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "tests/utils/stripeUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { timeout } from "@tests/utils/genUtils.js";
|
||||
import { createReferralProgram } from "@tests/utils/productUtils.js";
|
||||
import { advanceTestClock } from "@tests/utils/stripeUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { createStripeCli } from "@/external/connect/createStripeCli.js";
|
||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
RewardType,
|
||||
} from "@autumn/shared";
|
||||
import chalk from "chalk";
|
||||
import { TestFeature } from "tests/setup/v2Features.js";
|
||||
import { timeout } from "tests/utils/genUtils.js";
|
||||
import { createReferralProgram } from "tests/utils/productUtils.js";
|
||||
import ctx from "tests/utils/testInitUtils/createTestContext.js";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import { timeout } from "@tests/utils/genUtils.js";
|
||||
import { createReferralProgram } from "@tests/utils/productUtils.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user