From 31cf65d386fc3aa7bf60f90e8096c76b69034a93 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 20:50:25 +0000 Subject: [PATCH 1/2] chore(deps): update vitest monorepo to v4 --- package-lock.json | 357 +++++++++++++--------------------------------- package.json | 4 +- 2 files changed, 104 insertions(+), 257 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92195981..336350f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@octokit/request-error": "^7.0.2", "@octokit/tsconfig": "^4.0.0", "@types/node": "^24.0.0", - "@vitest/coverage-v8": "^3.0.0", + "@vitest/coverage-v8": "^4.0.0", "esbuild": "^0.25.0", "github-openapi-graphql-query": "^5.0.0", "npm-run-all2": "^8.0.0", @@ -26,7 +26,7 @@ "semantic-release-plugin-update-version-in-files": "^2.0.0", "tinyglobby": "^0.2.15", "typescript": "^5.0.0", - "vitest": "^3.0.0" + "vitest": "^4.0.0" }, "engines": { "node": ">= 20" @@ -35,20 +35,6 @@ "@octokit/core": "^7.0.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -633,27 +619,6 @@ "node": ">=12" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1207,6 +1172,13 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -1244,32 +1216,30 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", - "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.10.tgz", + "integrity": "sha512-g+brmtoKa/sAeIohNJnnWhnHtU6GuqqVOSQ4SxDIPcgZWZyhJs5RmF5LpqXs8Kq64lANP+vnbn5JLzhLj/G56g==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^1.0.2", - "ast-v8-to-istanbul": "^0.3.3", - "debug": "^4.4.1", + "@vitest/utils": "4.0.10", + "ast-v8-to-istanbul": "^0.3.8", + "debug": "^4.4.3", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", - "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "std-env": "^3.9.0", - "test-exclude": "^7.0.1", - "tinyrainbow": "^2.0.0" + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.1", + "std-env": "^3.10.0", + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.2.4", - "vitest": "3.2.4" + "@vitest/browser": "4.0.10", + "vitest": "4.0.10" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1278,39 +1248,40 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.10.tgz", + "integrity": "sha512-3QkTX/lK39FBNwARCQRSQr0TP9+ywSdxSX+LgbJ2M1WmveXP72anTbnp2yl5fH+dU6SUmBzNMrDHs80G8G2DZg==", "dev": true, "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" + "@vitest/spy": "4.0.10", + "@vitest/utils": "4.0.10", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.10.tgz", + "integrity": "sha512-e2OfdexYkjkg8Hh3L9NVEfbwGXq5IZbDovkf30qW2tOh7Rh9sVtmSr2ztEXOFbymNxS4qjzLXUQIvATvN4B+lg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.4", + "@vitest/spy": "4.0.10", "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" + "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -1322,42 +1293,41 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.10.tgz", + "integrity": "sha512-99EQbpa/zuDnvVjthwz5bH9o8iPefoQZ63WV8+bsRJZNw3qQSvSltfut8yu1Jc9mqOYi7pEbsKxYTi/rjaq6PA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^2.0.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.10.tgz", + "integrity": "sha512-EXU2iSkKvNwtlL8L8doCpkyclw0mc/t4t9SeOnfOFPyqLmQwuceMPA4zJBa6jw0MKsZYbw7kAn+gl7HxrlB8UQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" + "@vitest/utils": "4.0.10", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.10.tgz", + "integrity": "sha512-2N4X2ZZl7kZw0qeGdQ41H0KND96L3qX1RgwuCfy6oUsF2ISGD/HpSbmms+CkIOsQmg2kulwfhJ4CI0asnZlvkg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", + "@vitest/pretty-format": "4.0.10", + "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { @@ -1365,28 +1335,24 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.10.tgz", + "integrity": "sha512-AsY6sVS8OLb96GV5RoG8B6I35GAbNrC49AO+jNRF9YVGb/g9t+hzNm1H6kD0NDp8tt7VJLs6hb7YMkDXqu03iw==", "dev": true, "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.10.tgz", + "integrity": "sha512-kOuqWnEwZNtQxMKg3WmPK1vmhZu9WcoX69iwWjVz+jvKTsF1emzsv3eoPcDr6ykA3qP2bsCQE7CwqfNtAVzsmg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" + "@vitest/pretty-format": "4.0.10", + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1483,43 +1449,16 @@ "balanced-match": "^1.0.0" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { "node": ">=18" } }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1609,16 +1548,6 @@ } } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1948,13 +1877,6 @@ "dev": true, "license": "MIT" }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -1973,15 +1895,15 @@ } }, "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz", + "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "source-map-js": "^1.2.1" } }, "node_modules/make-dir": { @@ -2139,16 +2061,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2494,19 +2406,6 @@ "node": ">=8" } }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2520,21 +2419,6 @@ "node": ">=8" } }, - "node_modules/test-exclude": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", - "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^10.4.1", - "minimatch": "^9.0.4" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2566,30 +2450,10 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", "dev": true, "license": "MIT", "engines": { @@ -2654,6 +2518,7 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -2723,66 +2588,40 @@ } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.10.tgz", + "integrity": "sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", + "@vitest/expect": "4.0.10", + "@vitest/mocker": "4.0.10", + "@vitest/pretty-format": "4.0.10", + "@vitest/runner": "4.0.10", + "@vitest/snapshot": "4.0.10", + "@vitest/spy": "4.0.10", + "@vitest/utils": "4.0.10", + "debug": "^4.4.3", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2790,9 +2629,11 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.10", + "@vitest/browser-preview": "4.0.10", + "@vitest/browser-webdriverio": "4.0.10", + "@vitest/ui": "4.0.10", "happy-dom": "*", "jsdom": "*" }, @@ -2806,7 +2647,13 @@ "@types/node": { "optional": true }, - "@vitest/browser": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { "optional": true }, "@vitest/ui": { diff --git a/package.json b/package.json index 0826cc5e..7b496f92 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@octokit/request-error": "^7.0.2", "@octokit/tsconfig": "^4.0.0", "@types/node": "^24.0.0", - "@vitest/coverage-v8": "^3.0.0", + "@vitest/coverage-v8": "^4.0.0", "esbuild": "^0.25.0", "github-openapi-graphql-query": "^5.0.0", "npm-run-all2": "^8.0.0", @@ -42,7 +42,7 @@ "semantic-release-plugin-update-version-in-files": "^2.0.0", "tinyglobby": "^0.2.15", "typescript": "^5.0.0", - "vitest": "^3.0.0" + "vitest": "^4.0.0" }, "release": { "branches": [ From 4849b45131b70cbb2e3cbe9f5b1eb311d9424848 Mon Sep 17 00:00:00 2001 From: wolfy1339 Date: Wed, 19 Nov 2025 15:50:23 -0500 Subject: [PATCH 2/2] fix --- test/retry.test.ts | 783 ++++++++++++++++++++++----------------------- 1 file changed, 388 insertions(+), 395 deletions(-) diff --git a/test/retry.test.ts b/test/retry.test.ts index d8f0a221..1a2ead9e 100644 --- a/test/retry.test.ts +++ b/test/retry.test.ts @@ -12,458 +12,451 @@ import { createServer } from "node:http"; */ const restStatusCodes = [403, 429]; -describe( - "Retry", - function () { - describe.each(restStatusCodes)("REST", function (statusCode) { - it(`Should retry 'secondary-limit' and succeed when status ${statusCode}`, async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - fallbackSecondaryRateRetryAfter: 0, - retryAfterBaseValue: 50, - onSecondaryRateLimit: (retryAfter, options) => { - expect(options).toMatchObject({ - method: "GET", - url: "/route", - request: { retryCount: eventCount }, - }); - expect(retryAfter).toEqual(eventCount + 1); - eventCount++; - return true; +describe("Retry", { timeout: 20000 }, function () { + describe.each(restStatusCodes)("REST", function (statusCode) { + it(`Should retry 'secondary-limit' and succeed when status ${statusCode}`, async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + fallbackSecondaryRateRetryAfter: 0, + retryAfterBaseValue: 50, + onSecondaryRateLimit: (retryAfter, options) => { + expect(options).toMatchObject({ + method: "GET", + url: "/route", + request: { retryCount: eventCount }, + }); + expect(retryAfter).toEqual(eventCount + 1); + eventCount++; + return true; + }, + onRateLimit: () => 1, + }, + }); + + const res = await octokit.request("GET /route", { + request: { + responses: [ + { + status: statusCode, + headers: { "retry-after": "1" }, + data: { message: "You have exceeded a secondary rate limit" }, }, - onRateLimit: () => 1, + { status: 200, headers: {}, data: { message: "Success!" } }, + ], + }, + }); + + expect(res.status).toEqual(200); + expect(res.data).toMatchObject({ message: "Success!" }); + expect(eventCount).toEqual(1); + expect(octokit.__requestLog).toStrictEqual([ + "START GET /route", + "START GET /route", + "END GET /route", + ]); + const ms = octokit.__requestTimings[1] - octokit.__requestTimings[0]; + expect(ms).toBeLessThan(80); + expect(ms).toBeGreaterThan(20); + }); + + it("Should retry 'secondary-limit' twice and fail", async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + fallbackSecondaryRateRetryAfter: 0, + retryAfterBaseValue: 50, + onSecondaryRateLimit: (retryAfter, options) => { + expect(options).toMatchObject({ + method: "GET", + url: "/route", + request: { retryCount: eventCount }, + }); + expect(retryAfter).toEqual(eventCount + 1); + eventCount++; + return true; }, - }); + onRateLimit: () => 1, + }, + }); - const res = await octokit.request("GET /route", { + const message = "You have exceeded a secondary rate limit"; + try { + await octokit.request("GET /route", { request: { responses: [ { status: statusCode, headers: { "retry-after": "1" }, - data: { message: "You have exceeded a secondary rate limit" }, + data: { message }, + }, + { + status: statusCode, + headers: { "retry-after": "2" }, + data: { message }, + }, + { + status: 404, + headers: { "retry-after": "3" }, + data: { message: "Nope!" }, }, - { status: 200, headers: {}, data: { message: "Success!" } }, ], }, }); + throw new Error("Should not reach this point"); + } catch (error: any) { + expect(error.status).toEqual(404); + expect(error.message).toEqual("Nope!"); + } + + expect(eventCount).toEqual(2); + expect(octokit.__requestLog).toStrictEqual([ + "START GET /route", + "START GET /route", + "START GET /route", + ]); + + const ms1 = octokit.__requestTimings[1] - octokit.__requestTimings[0]; + expect(ms1).toBeLessThan(70); + expect(ms1).toBeGreaterThan(30); + + const ms2 = octokit.__requestTimings[2] - octokit.__requestTimings[1]; + expect(ms2).toBeLessThan(120); + expect(ms2).toBeGreaterThan(80); + }); - expect(res.status).toEqual(200); - expect(res.data).toMatchObject({ message: "Success!" }); - expect(eventCount).toEqual(1); - expect(octokit.__requestLog).toStrictEqual([ - "START GET /route", - "START GET /route", - "END GET /route", - ]); - const ms = octokit.__requestTimings[1] - octokit.__requestTimings[0]; - expect(ms).toBeLessThan(80); - expect(ms).toBeGreaterThan(20); + it("Should not leak retryCount between requests", async function () { + let counter = 1; + + const server = createServer((_, res) => { + if (counter++ % 3 === 0) { + res + .writeHead(200, { "Content-Type": "application/json" }) + .end(JSON.stringify({ message: "Success!" })); + } else { + res + .writeHead(statusCode, { + "Content-Type": "application/json", + "retry-after": "1", + }) + .end( + JSON.stringify({ + message: "You have exceeded a secondary rate limit", + }), + ); + } }); - it("Should retry 'secondary-limit' twice and fail", async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - fallbackSecondaryRateRetryAfter: 0, - retryAfterBaseValue: 50, - onSecondaryRateLimit: (retryAfter, options) => { - expect(options).toMatchObject({ - method: "GET", - url: "/route", - request: { retryCount: eventCount }, - }); - expect(retryAfter).toEqual(eventCount + 1); - eventCount++; + server.listen(0); + const { port } = server.address() as AddressInfo; + + const ThrottledOctokit = Octokit.plugin(throttling); + const octokit = new ThrottledOctokit({ + baseUrl: `http://localhost:${port}`, + throttle: { + fallbackSecondaryRateRetryAfter: 0, + retryAfterBaseValue: 50, + onRateLimit: () => true, + onSecondaryRateLimit: (retryCount) => { + if (retryCount < 5) { return true; - }, - onRateLimit: () => 1, + } }, - }); - - const message = "You have exceeded a secondary rate limit"; - try { - await octokit.request("GET /route", { - request: { - responses: [ - { - status: statusCode, - headers: { "retry-after": "1" }, - data: { message }, - }, - { - status: statusCode, - headers: { "retry-after": "2" }, - data: { message }, - }, - { - status: 404, - headers: { "retry-after": "3" }, - data: { message: "Nope!" }, - }, - ], - }, - }); - throw new Error("Should not reach this point"); - } catch (error: any) { - expect(error.status).toEqual(404); - expect(error.message).toEqual("Nope!"); - } - - expect(eventCount).toEqual(2); - expect(octokit.__requestLog).toStrictEqual([ - "START GET /route", - "START GET /route", - "START GET /route", - ]); - - const ms1 = octokit.__requestTimings[1] - octokit.__requestTimings[0]; - expect(ms1).toBeLessThan(70); - expect(ms1).toBeGreaterThan(30); - - const ms2 = octokit.__requestTimings[2] - octokit.__requestTimings[1]; - expect(ms2).toBeLessThan(120); - expect(ms2).toBeGreaterThan(80); + }, }); - it("Should not leak retryCount between requests", async function () { - let counter = 1; - - const server = createServer((_, res) => { - if (counter++ % 3 === 0) { - res - .writeHead(200, { "Content-Type": "application/json" }) - .end(JSON.stringify({ message: "Success!" })); - } else { - res - .writeHead(statusCode, { - "Content-Type": "application/json", - "retry-after": "1", - }) - .end( - JSON.stringify({ - message: "You have exceeded a secondary rate limit", - }), - ); - } - }); - - server.listen(0); - const { port } = server.address() as AddressInfo; - - const ThrottledOctokit = Octokit.plugin(throttling); - const octokit = new ThrottledOctokit({ - baseUrl: `http://localhost:${port}`, - throttle: { - fallbackSecondaryRateRetryAfter: 0, - retryAfterBaseValue: 50, - onRateLimit: () => true, - onSecondaryRateLimit: (retryCount) => { - if (retryCount < 5) { - return true; - } - }, - }, + try { + await octokit.request("GET /nope-nope-ok"); + await octokit.request("GET /nope-nope-ok"); + await octokit.request("GET /nope-nope-ok"); + } finally { + return new Promise((resolve, reject) => { + server.close((error) => { + if (error) { + return reject(error); + } + resolve("ok"); + }); }); + } + }); - try { - await octokit.request("GET /nope-nope-ok"); - await octokit.request("GET /nope-nope-ok"); - await octokit.request("GET /nope-nope-ok"); - } finally { - return new Promise((resolve, reject) => { - server.close((error) => { - if (error) { - return reject(error); - } - resolve("ok"); + it(`Should retry 'rate-limit' with status code ${statusCode} and succeed`, async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + onRateLimit: (retryAfter, options) => { + expect(options).toMatchObject({ + method: "GET", + url: "/route", + request: { retryCount: eventCount }, }); - }); - } - }); - - it(`Should retry 'rate-limit' with status code ${statusCode} and succeed`, async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - onRateLimit: (retryAfter, options) => { - expect(options).toMatchObject({ - method: "GET", - url: "/route", - request: { retryCount: eventCount }, - }); - expect(retryAfter).toEqual(0); - eventCount++; - return true; - }, - onSecondaryRateLimit: () => 1, + expect(retryAfter).toEqual(0); + eventCount++; + return true; }, - }); + onSecondaryRateLimit: () => 1, + }, + }); - const res = await octokit.request("GET /route", { - request: { - responses: [ - { - status: statusCode, - headers: { - "x-ratelimit-remaining": "0", - "x-ratelimit-reset": "123", - }, - data: {}, + const res = await octokit.request("GET /route", { + request: { + responses: [ + { + status: statusCode, + headers: { + "x-ratelimit-remaining": "0", + "x-ratelimit-reset": "123", }, - { status: 202, headers: {}, data: { message: "Yay!" } }, - ], - }, - }); - - expect(res.status).toEqual(202); - expect(res.data).toMatchObject({ message: "Yay!" }); - expect(eventCount).toEqual(1); - expect(octokit.__requestLog).toStrictEqual([ - "START GET /route", - "START GET /route", - "END GET /route", - ]); - expect( - octokit.__requestTimings[1] - octokit.__requestTimings[0], - ).toBeLessThan(20); + data: {}, + }, + { status: 202, headers: {}, data: { message: "Yay!" } }, + ], + }, }); - }); - describe("GraphQL", function () { - it("Should retry 'rate-limit' and succeed", async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - write: new Bottleneck.Group({ minTime: 50 }), - onRateLimit: (retryAfter, options) => { - expect(options).toMatchObject({ - method: "POST", - url: "/graphql", - request: { retryCount: eventCount }, - }); - expect(retryAfter).toEqual(0); - eventCount++; - return true; - }, - onSecondaryRateLimit: () => 1, + expect(res.status).toEqual(202); + expect(res.data).toMatchObject({ message: "Yay!" }); + expect(eventCount).toEqual(1); + expect(octokit.__requestLog).toStrictEqual([ + "START GET /route", + "START GET /route", + "END GET /route", + ]); + expect( + octokit.__requestTimings[1] - octokit.__requestTimings[0], + ).toBeLessThan(20); + }); + }); + + describe("GraphQL", function () { + it("Should retry 'rate-limit' and succeed", async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + write: new Bottleneck.Group({ minTime: 50 }), + onRateLimit: (retryAfter, options) => { + expect(options).toMatchObject({ + method: "POST", + url: "/graphql", + request: { retryCount: eventCount }, + }); + expect(retryAfter).toEqual(0); + eventCount++; + return true; }, - }); + onSecondaryRateLimit: () => 1, + }, + }); - const res = await octokit.request("POST /graphql", { - request: { - responses: [ - { - status: 200, - headers: { - // should retry based on response body, not header - // https://github.com/octokit/plugin-throttling.js/issues/632 - "x-ratelimit-remaining": "1", - "x-ratelimit-reset": "123", - }, - data: { errors: [{ type: "RATE_LIMITED" }] }, + const res = await octokit.request("POST /graphql", { + request: { + responses: [ + { + status: 200, + headers: { + // should retry based on response body, not header + // https://github.com/octokit/plugin-throttling.js/issues/632 + "x-ratelimit-remaining": "1", + "x-ratelimit-reset": "123", }, - { status: 200, headers: {}, data: { message: "Yay!" } }, - ], - }, - }); - - expect(res.status).toEqual(200); - expect(res.data).toMatchObject({ message: "Yay!" }); - expect(eventCount).toEqual(1); - expect(octokit.__requestLog).toStrictEqual([ - "START POST /graphql", - "END POST /graphql", - "START POST /graphql", - "END POST /graphql", - ]); - - const ms = octokit.__requestTimings[2] - octokit.__requestTimings[0]; - expect(ms).toBeLessThan(70); - expect(ms).toBeGreaterThan(30); + data: { errors: [{ type: "RATE_LIMITED" }] }, + }, + { status: 200, headers: {}, data: { message: "Yay!" } }, + ], + }, }); - it("Should work with full URL", async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - write: new Bottleneck.Group({ minTime: 50 }), - onRateLimit: (retryAfter, options) => { - expect(options).toMatchObject({ - method: "POST", - url: "https://api.github.com/graphql", - request: { retryCount: eventCount }, - }); - expect(retryAfter).toEqual(0); - eventCount++; - return true; - }, - onSecondaryRateLimit: () => 1, - }, - }); + expect(res.status).toEqual(200); + expect(res.data).toMatchObject({ message: "Yay!" }); + expect(eventCount).toEqual(1); + expect(octokit.__requestLog).toStrictEqual([ + "START POST /graphql", + "END POST /graphql", + "START POST /graphql", + "END POST /graphql", + ]); + + const ms = octokit.__requestTimings[2] - octokit.__requestTimings[0]; + expect(ms).toBeLessThan(70); + expect(ms).toBeGreaterThan(30); + }); - const res = await octokit.request( - "POST https://api.github.com/graphql", - { - request: { - responses: [ - { - status: 200, - headers: { - "x-ratelimit-remaining": "1", - "x-ratelimit-reset": "123", - }, - data: { errors: [{ type: "RATE_LIMITED" }] }, - }, - { status: 200, headers: {}, data: { message: "Yay!" } }, - ], - }, + it("Should work with full URL", async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + write: new Bottleneck.Group({ minTime: 50 }), + onRateLimit: (retryAfter, options) => { + expect(options).toMatchObject({ + method: "POST", + url: "https://api.github.com/graphql", + request: { retryCount: eventCount }, + }); + expect(retryAfter).toEqual(0); + eventCount++; + return true; }, - ); - - expect(res.status).toEqual(200); - expect(res.data).toMatchObject({ message: "Yay!" }); - expect(eventCount).toEqual(1); - expect(octokit.__requestLog).toStrictEqual([ - "START POST https://api.github.com/graphql", - "END POST https://api.github.com/graphql", - "START POST https://api.github.com/graphql", - "END POST https://api.github.com/graphql", - ]); - - const ms = octokit.__requestTimings[2] - octokit.__requestTimings[0]; - expect(ms).toBeLessThan(70); - expect(ms).toBeGreaterThan(30); + onSecondaryRateLimit: () => 1, + }, }); - it("Should ignore other error types", async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - write: new Bottleneck.Group({ minTime: 50 }), - onRateLimit: () => { - eventCount++; - return true; + const res = await octokit.request("POST https://api.github.com/graphql", { + request: { + responses: [ + { + status: 200, + headers: { + "x-ratelimit-remaining": "1", + "x-ratelimit-reset": "123", + }, + data: { errors: [{ type: "RATE_LIMITED" }] }, }, - onSecondaryRateLimit: () => 1, - }, - }); + { status: 200, headers: {}, data: { message: "Yay!" } }, + ], + }, + }); - const res = await octokit.request("POST /graphql", { - request: { - responses: [ - { - status: 200, - headers: { - "x-ratelimit-remaining": "0", - "x-ratelimit-reset": "123", - }, - data: { errors: [{ type: "HELLO_WORLD" }] }, - }, - { status: 200, headers: {}, data: { message: "Yay!" } }, - ], - }, - }); + expect(res.status).toEqual(200); + expect(res.data).toMatchObject({ message: "Yay!" }); + expect(eventCount).toEqual(1); + expect(octokit.__requestLog).toStrictEqual([ + "START POST https://api.github.com/graphql", + "END POST https://api.github.com/graphql", + "START POST https://api.github.com/graphql", + "END POST https://api.github.com/graphql", + ]); + + const ms = octokit.__requestTimings[2] - octokit.__requestTimings[0]; + expect(ms).toBeLessThan(70); + expect(ms).toBeGreaterThan(30); + }); - expect(res.status).toEqual(200); - expect(res.data).toStrictEqual({ errors: [{ type: "HELLO_WORLD" }] }); - expect(eventCount).toEqual(0); - expect(octokit.__requestLog).toStrictEqual([ - "START POST /graphql", - "END POST /graphql", - ]); + it("Should ignore other error types", async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + write: new Bottleneck.Group({ minTime: 50 }), + onRateLimit: () => { + eventCount++; + return true; + }, + onSecondaryRateLimit: () => 1, + }, }); - it("Should ignore 401 Unauthorized errors", async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - write: new Bottleneck.Group({ minTime: 50 }), - onRateLimit: () => { - eventCount++; - return true; + const res = await octokit.request("POST /graphql", { + request: { + responses: [ + { + status: 200, + headers: { + "x-ratelimit-remaining": "0", + "x-ratelimit-reset": "123", + }, + data: { errors: [{ type: "HELLO_WORLD" }] }, }, - onSecondaryRateLimit: () => 1, - }, - }); + { status: 200, headers: {}, data: { message: "Yay!" } }, + ], + }, + }); - try { - await octokit.request("POST /graphql", { - request: { - responses: [ - { - status: 401, - headers: { - "x-ratelimit-remaining": "0", - "x-ratelimit-reset": "123", - }, - data: { - message: "Bad credentials", - documentation_url: "https://docs.github.com/graphql", - }, - }, - ], - }, - }); - throw new Error("Should not reach this point"); - } catch (error: any) { - expect(error.status).toEqual(401); - expect(error.message).toEqual("Bad credentials"); - } + expect(res.status).toEqual(200); + expect(res.data).toStrictEqual({ errors: [{ type: "HELLO_WORLD" }] }); + expect(eventCount).toEqual(0); + expect(octokit.__requestLog).toStrictEqual([ + "START POST /graphql", + "END POST /graphql", + ]); + }); - expect(eventCount).toEqual(0); - expect(octokit.__requestLog).toStrictEqual(["START POST /graphql"]); + it("Should ignore 401 Unauthorized errors", async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + write: new Bottleneck.Group({ minTime: 50 }), + onRateLimit: () => { + eventCount++; + return true; + }, + onSecondaryRateLimit: () => 1, + }, }); - it("Should retry 403 Forbidden errors on SecondaryRate limit", async function () { - let eventCount = 0; - const octokit = new TestOctokit({ - throttle: { - write: new Bottleneck.Group({ minTime: 50 }), - onSecondaryRateLimit: () => { - eventCount++; - return true; - }, - onRateLimit: () => 1, - fallbackSecondaryRateRetryAfter: 0, - retryAfterBaseValue: 50, - }, - }); - const res = await octokit.request("POST /graphql", { + try { + await octokit.request("POST /graphql", { request: { responses: [ { - status: 403, + status: 401, headers: { - "retry-after": 1, + "x-ratelimit-remaining": "0", + "x-ratelimit-reset": "123", }, data: { - message: - "You have exceeded a secondary rate limit. Please wait a few minutes before you try again.", - documentation_url: - "https://developer.github.com/v3/#secondary-rate-limits", + message: "Bad credentials", + documentation_url: "https://docs.github.com/graphql", }, }, - { status: 200, headers: {}, data: { message: "Success!" } }, ], }, }); + throw new Error("Should not reach this point"); + } catch (error: any) { + expect(error.status).toEqual(401); + expect(error.message).toEqual("Bad credentials"); + } + + expect(eventCount).toEqual(0); + expect(octokit.__requestLog).toStrictEqual(["START POST /graphql"]); + }); - expect(res.status).toEqual(200); - expect(res.data).toMatchObject({ message: "Success!" }); - expect(eventCount).toEqual(1); - expect(octokit.__requestLog).toStrictEqual([ - "START POST /graphql", - "START POST /graphql", - "END POST /graphql", - ]); - - const ms = octokit.__requestTimings[1] - octokit.__requestTimings[0]; - expect(ms).toBeLessThan(80); - expect(ms).toBeGreaterThan(20); + it("Should retry 403 Forbidden errors on SecondaryRate limit", async function () { + let eventCount = 0; + const octokit = new TestOctokit({ + throttle: { + write: new Bottleneck.Group({ minTime: 50 }), + onSecondaryRateLimit: () => { + eventCount++; + return true; + }, + onRateLimit: () => 1, + fallbackSecondaryRateRetryAfter: 0, + retryAfterBaseValue: 50, + }, }); + const res = await octokit.request("POST /graphql", { + request: { + responses: [ + { + status: 403, + headers: { + "retry-after": 1, + }, + data: { + message: + "You have exceeded a secondary rate limit. Please wait a few minutes before you try again.", + documentation_url: + "https://developer.github.com/v3/#secondary-rate-limits", + }, + }, + { status: 200, headers: {}, data: { message: "Success!" } }, + ], + }, + }); + + expect(res.status).toEqual(200); + expect(res.data).toMatchObject({ message: "Success!" }); + expect(eventCount).toEqual(1); + expect(octokit.__requestLog).toStrictEqual([ + "START POST /graphql", + "START POST /graphql", + "END POST /graphql", + ]); + + const ms = octokit.__requestTimings[1] - octokit.__requestTimings[0]; + expect(ms).toBeLessThan(80); + expect(ms).toBeGreaterThan(20); }); - }, - { timeout: 20000 }, -); + }); +});