From 099044089379eb07ca2a47ae1f2a4e1e897bd89a Mon Sep 17 00:00:00 2001 From: Jonathan Samines Date: Fri, 28 Jun 2024 21:51:42 -0600 Subject: [PATCH] Replace nock with msw --- package-lock.json | 424 ++++++++++++++++-- package.json | 3 +- test/_authorization-server-mock.js | 159 ++++--- test/access-token-refresh.js | 65 ++- test/access-token-revoke.js | 63 ++- ...authorization-code-grant-type-get-token.js | 96 ++-- ...client-credentials-grant-type-get-token.js | 96 ++-- test/errors.js | 21 +- test/msw-matcher/_compare.js | 33 ++ test/msw-matcher/_index.js | 9 + test/msw-matcher/_matchers.js | 71 +++ test/msw-matcher/_request-body.js | 38 ++ test/msw-matcher/_request.js | 85 ++++ test/msw-matcher/_scope.js | 52 +++ ...rce-owner-password-grant-type-get-token.js | 101 +++-- 15 files changed, 1092 insertions(+), 224 deletions(-) create mode 100644 test/msw-matcher/_compare.js create mode 100644 test/msw-matcher/_index.js create mode 100644 test/msw-matcher/_matchers.js create mode 100644 test/msw-matcher/_request-body.js create mode 100644 test/msw-matcher/_request.js create mode 100644 test/msw-matcher/_scope.js diff --git a/package-lock.json b/package-lock.json index ccad6891..6ddbe96f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,11 +21,12 @@ "chance": "^1.1.9", "chance-access-token": "^2.1.0", "date-fns": "^3.6.0", + "diff": "^5.2.0", "doctoc": "^2.2.1", "eslint": "^8.26.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.26.0", - "nock": "^13.2.9" + "msw": "^2.3.1" } }, "node_modules/@bcoe/v8-coverage": { @@ -34,6 +35,24 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "dependencies": { + "statuses": "^2.0.1" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -153,6 +172,91 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@inquirer/confirm": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.10.tgz", + "integrity": "sha512-/aAHu83Njy6yf44T+ZrRPUkMcUqprrOiIKsyMvf9jOV+vF5BNb2ja1aLP33MK36W8eaf91MTL/mU/e6METuENg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^8.2.3", + "@inquirer/type": "^1.3.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.3.tgz", + "integrity": "sha512-WrpDVPAaxJQjHid3Ra4FhUO70YBzkHSYVyW5X48L5zHYdudoPISJqTRRWSeamHfaXda7PNNaC5Py5MEo7QwBNA==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.3", + "@inquirer/type": "^1.3.3", + "@types/mute-stream": "^0.0.4", + "@types/node": "^20.14.6", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.3.tgz", + "integrity": "sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.3.3.tgz", + "integrity": "sha512-xTUt0NulylX27/zMx04ZYar/kr1raaiFTVvQ5feljQsiAgdm0WPj4S73/ye0fbslh+15QrIuDvfCXTek7pMY5A==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -327,6 +431,32 @@ "semver": "bin/semver.js" } }, + "node_modules/@mswjs/cookies": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.1.tgz", + "integrity": "sha512-W68qOHEjx1iD+4VjQudlx26CPIoxmIAtK4ZCexU0/UJBG6jYhcuyzKJx+Iw8uhBIGd9eba64XgWVgo20it1qwA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.29.1.tgz", + "integrity": "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==", + "dev": true, + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -362,6 +492,28 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -455,6 +607,12 @@ "unified": "^9.2.2" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", @@ -476,12 +634,42 @@ "@types/unist": "*" } }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", "dev": true }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, "node_modules/@vercel/nft": { "version": "0.26.5", "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.26.5.tgz", @@ -593,6 +781,33 @@ "emoji-regex": "~10.1.0" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1583,6 +1798,18 @@ "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", "dev": true }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -1649,6 +1876,15 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1742,6 +1978,15 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1839,6 +2084,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/doctoc": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/doctoc/-/doctoc-2.2.1.tgz", @@ -2497,9 +2751,9 @@ "dev": true }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { @@ -2883,6 +3137,15 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -2958,6 +3221,12 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3279,6 +3548,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3537,12 +3812,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -4093,27 +4362,92 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "node_modules/msw": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.3.1.tgz", + "integrity": "sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@inquirer/confirm": "^3.0.0", + "@mswjs/cookies": "^1.1.0", + "@mswjs/interceptors": "^0.29.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.7.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } }, - "node_modules/nock": { - "version": "13.2.9", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.9.tgz", - "integrity": "sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==", + "node_modules/msw/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.21", - "propagate": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.20.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.20.1.tgz", + "integrity": "sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg==", + "dev": true, + "engines": { + "node": ">=16" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, "engines": { - "node": ">= 10.13" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -4284,6 +4618,12 @@ "node": ">= 0.8.0" } }, + "node_modules/outvariant": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==", + "dev": true + }, "node_modules/p-map": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.2.tgz", @@ -4409,6 +4749,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-to-regexp": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dev": true + }, "node_modules/path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", @@ -4472,15 +4818,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -4920,6 +5257,21 @@ "node": ">=8" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5344,6 +5696,12 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", diff --git a/package.json b/package.json index e375341c..497ae722 100644 --- a/package.json +++ b/package.json @@ -58,11 +58,12 @@ "chance": "^1.1.9", "chance-access-token": "^2.1.0", "date-fns": "^3.6.0", + "diff": "^5.2.0", "doctoc": "^2.2.1", "eslint": "^8.26.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.26.0", - "nock": "^13.2.9" + "msw": "^2.3.1" }, "volta": { "node": "18.20.3" diff --git a/test/_authorization-server-mock.js b/test/_authorization-server-mock.js index 697875d1..c0d45b66 100644 --- a/test/_authorization-server-mock.js +++ b/test/_authorization-server-mock.js @@ -1,8 +1,10 @@ 'use strict'; -const nock = require('nock'); const Hoek = require('@hapi/hoek'); const Boom = require('@hapi/boom'); +const { HttpResponse } = require('msw'); + +const msw = require('./msw-matcher/_index'); const accessToken = { access_token: '5683E74C-7514-4426-B64F-CF0C24223F69', @@ -13,87 +15,134 @@ const accessToken = { function createAuthorizationServer(authorizationServerUrl) { function tokenSuccessWithCustomPath(path, scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post(path, params) - .reply(200, accessToken, { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post(path) + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => HttpResponse.json(accessToken, { + status: 200, + })); } function tokenSuccess(scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/token', params) - .reply(200, accessToken, { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/token') + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => HttpResponse.json(accessToken, { + status: 200, + })); } function tokenError(scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/token', params) - .reply(500, Boom.badImplementation(), { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/token') + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => HttpResponse.json(Boom.badImplementation(), { + status: 500, + })); } function tokenAuthorizationError(scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/token', params) - .reply(401, Boom.unauthorized(), { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/token') + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => HttpResponse.json(Boom.unauthorized(), { + status: 401, + })); } function tokenRevokeSuccess(scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/revoke', params) - .reply(240, null, { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/revoke') + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => new HttpResponse(null, { + status: 204, + headers: { + 'Content-Type': 'application/json', + }, + })); } function tokenRevokeSuccessWithCustomPath(path, scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post(path, params) - .reply(204, null, { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post(path) + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => new HttpResponse(null, { + status: 204, + headers: { + 'Content-Type': 'application/json', + }, + })); } function tokenRevokeError(scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/revoke', params) - .reply(500, Boom.badImplementation(), { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/revoke') + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => HttpResponse.json(Boom.badImplementation(), { + status: 500, + })); } function tokenRevokeAllSuccess(scopeOptions, accessTokenParams, refreshTokenParams) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/revoke', accessTokenParams) - .reply(204, null, { - 'Content-Type': 'application/json', - }) - .post('/oauth/revoke', refreshTokenParams) - .reply(204, null, { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/revoke') + .matchBody(accessTokenParams) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => new HttpResponse(null, { + status: 204, + headers: { + 'Content-Type': 'application/json', + }, + })) + .post('/oauth/revoke') + .matchBody(refreshTokenParams) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => new HttpResponse(null, { + status: 204, + headers: { + 'Content-Type': 'application/json', + }, + })); } function tokenSuccessWithNonJSONContent(scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/token', params) - .reply(200, 'Sorry for not responding with a json response', { - 'Content-Type': 'application/html', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/token') + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => new HttpResponse('Sorry for not responding with a json response', { + status: 200, + headers: { + 'Content-Type': 'text/html', + }, + })); } function tokenSuccessWithoutRefreshToken(scopeOptions, params) { - return nock(authorizationServerUrl, scopeOptions) - .post('/oauth/token', params) - .reply(200, { ...accessToken, refresh_token: undefined }, { - 'Content-Type': 'application/json', - }); + return msw + .scope(authorizationServerUrl) + .post('/oauth/token') + .matchBody(params) + .matchHeaders(scopeOptions.reqheaders) + .handler(() => HttpResponse.json({ ...accessToken, refresh_token: undefined }, { + status: 200, + })); } return { diff --git a/test/access-token-refresh.js b/test/access-token-refresh.js index bf4f7ef9..ec17e401 100644 --- a/test/access-token-refresh.js +++ b/test/access-token-refresh.js @@ -1,6 +1,7 @@ 'use strict'; const test = require('ava'); +const { setupServer } = require('msw/node'); const Chance = require('./_chance'); const AccessToken = require('../lib/access-token'); @@ -10,6 +11,7 @@ const { createModuleConfigWithDefaults: createModuleConfig } = require('./_modul const { createAuthorizationServer, getHeaderCredentialsScopeOptions } = require('./_authorization-server-mock'); const chance = new Chance(); +const mockServer = setupServer(); const scopeOptions = { reqheaders: { @@ -18,7 +20,10 @@ const scopeOptions = { }, }; -test.serial('@refresh => creates a new access token with default params', async (t) => { +test.before(() => mockServer.listen()); +test.after(() => mockServer.close()); + +test('@refresh => creates a new access token with default params', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -34,14 +39,16 @@ test.serial('@refresh => creates a new access token with default params', async const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh(); scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with a custom grant type', async (t) => { +test('@refresh => creates a new access token with a custom grant type', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -57,6 +64,8 @@ test.serial('@refresh => creates a new access token with a custom grant type', a const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh({ grant_type: 'my_grant', @@ -64,9 +73,9 @@ test.serial('@refresh => creates a new access token with a custom grant type', a scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with multiple scopes', async (t) => { +test('@refresh => creates a new access token with multiple scopes', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -83,6 +92,8 @@ test.serial('@refresh => creates a new access token with multiple scopes', async const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh({ scope: ['scope-a', 'scope-b'], @@ -90,9 +101,9 @@ test.serial('@refresh => creates a new access token with multiple scopes', async scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with custom params', async (t) => { +test('@refresh => creates a new access token with custom params', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -109,6 +120,8 @@ test.serial('@refresh => creates a new access token with custom params', async ( const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh({ scope: 'TESTING_EXAMPLE_SCOPES', @@ -116,9 +129,9 @@ test.serial('@refresh => creates a new access token with custom params', async ( scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with custom module configuration (scope separator)', async (t) => { +test('@refresh => creates a new access token with custom module configuration (scope separator)', mockServer.boundary(async (t) => { const config = createModuleConfig({ options: { scopeSeparator: ',', @@ -140,6 +153,8 @@ test.serial('@refresh => creates a new access token with custom module configura const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh({ scope: ['scope-a', 'scope-b'], @@ -147,9 +162,9 @@ test.serial('@refresh => creates a new access token with custom module configura scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with a custom token path', async (t) => { +test('@refresh => creates a new access token with a custom token path', mockServer.boundary(async (t) => { const config = createModuleConfig({ auth: { tokenPath: '/the-custom/path', @@ -171,14 +186,16 @@ test.serial('@refresh => creates a new access token with a custom token path', a const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithCustomPath('/the-custom/path', scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh({ scope: 'TESTING_EXAMPLE_SCOPES' }); scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with a custom refresh path', async (t) => { +test('@refresh => creates a new access token with a custom refresh path', mockServer.boundary(async (t) => { const config = createModuleConfig({ auth: { refreshPath: '/the-custom/refresh-path', @@ -199,14 +216,16 @@ test.serial('@refresh => creates a new access token with a custom refresh path', const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithCustomPath('/the-custom/refresh-path', scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh(); scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with custom (inline) http options', async (t) => { +test('@refresh => creates a new access token with custom (inline) http options', mockServer.boundary(async (t) => { const config = createModuleConfig(); const accessTokenResponse = chance.accessToken({ @@ -229,6 +248,8 @@ test.serial('@refresh => creates a new access token with custom (inline) http op const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(customScopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const httpOptions = { headers: { 'X-REQUEST-ID': 123, @@ -240,9 +261,9 @@ test.serial('@refresh => creates a new access token with custom (inline) http op scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with custom (inline) http options without overriding (required) http options', async (t) => { +test('@refresh => creates a new access token with custom (inline) http options without overriding (required) http options', mockServer.boundary(async (t) => { const config = createModuleConfig(); const accessTokenResponse = chance.accessToken({ @@ -259,6 +280,8 @@ test.serial('@refresh => creates a new access token with custom (inline) http op const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const httpOptions = { headers: { Authorization: 'Basic credentials', @@ -270,9 +293,9 @@ test.serial('@refresh => creates a new access token with custom (inline) http op scope.done(); t.true(has(refreshAccessToken.token, 'access_token')); -}); +})); -test.serial('@refresh => creates a new access token with keeping the old refresh token if refresh did not provide a new refresh token', async (t) => { +test.serial('@refresh => creates a new access token with keeping the old refresh token if refresh did not provide a new refresh token', mockServer.boundary(async (t) => { const config = createModuleConfig(); const accessTokenResponse = chance.accessToken({ @@ -289,9 +312,11 @@ test.serial('@refresh => creates a new access token with keeping the old refresh const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithoutRefreshToken(scopeOptions, refreshParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const refreshAccessToken = await accessToken.refresh(); scope.done(); t.true(has(refreshAccessToken.token, 'refresh_token')); -}); +})); diff --git a/test/access-token-revoke.js b/test/access-token-revoke.js index 384b1c2f..a5cf3f82 100644 --- a/test/access-token-revoke.js +++ b/test/access-token-revoke.js @@ -1,6 +1,7 @@ 'use strict'; const test = require('ava'); +const { setupServer } = require('msw/node'); const Chance = require('./_chance'); const AccessToken = require('../lib/access-token'); @@ -9,6 +10,7 @@ const { createModuleConfigWithDefaults: createModuleConfig } = require('./_modul const { createAuthorizationServer, getHeaderCredentialsScopeOptions } = require('./_authorization-server-mock'); const chance = new Chance(); +const mockServer = setupServer(); const scopeOptions = { reqheaders: { @@ -17,7 +19,10 @@ const scopeOptions = { }, }; -test.serial('@revoke => performs the access token revoke', async (t) => { +test.before(() => mockServer.listen()); +test.after(() => mockServer.close()); + +test('@revoke => performs the access token revoke', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -33,14 +38,16 @@ test.serial('@revoke => performs the access token revoke', async (t) => { const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeSuccess(scopeOptions, revokeParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); await t.notThrowsAsync(() => accessToken.revoke('access_token')); scope.done(); -}); +})); -test.serial('@revoke => performs the refresh token revoke', async (t) => { +test('@revoke => performs the refresh token revoke', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -56,14 +63,16 @@ test.serial('@revoke => performs the refresh token revoke', async (t) => { const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeSuccess(scopeOptions, revokeParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); await t.notThrowsAsync(() => accessToken.revoke('refresh_token')); scope.done(); -}); +})); -test.serial('@revoke => performs a token revoke with a custom revoke path', async (t) => { +test('@revoke => performs a token revoke with a custom revoke path', mockServer.boundary(async (t) => { const config = createModuleConfig({ auth: { revokePath: '/the-custom/revoke-path', @@ -84,14 +93,16 @@ test.serial('@revoke => performs a token revoke with a custom revoke path', asyn const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeSuccessWithCustomPath('/the-custom/revoke-path', scopeOptions, revokeParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); await t.notThrowsAsync(() => accessToken.revoke('refresh_token')); scope.done(); -}); +})); -test.serial('@revoke => performs a token revoke with custom (inline) http options', async (t) => { +test('@revoke => performs a token revoke with custom (inline) http options', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -113,6 +124,8 @@ test.serial('@revoke => performs a token revoke with custom (inline) http option const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeSuccess(customScopeOptions, revokeParams); + mockServer.use(...scope.handlers); + const httpOptions = { headers: { 'X-REQUEST-ID': 123, @@ -124,9 +137,9 @@ test.serial('@revoke => performs a token revoke with custom (inline) http option await t.notThrowsAsync(() => accessToken.revoke('refresh_token', httpOptions)); scope.done(); -}); +})); -test.serial('@revoke => performs a token revoke with custom (inline) http options without overriding (required) http options', async (t) => { +test('@revoke => performs a token revoke with custom (inline) http options without overriding (required) http options', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -142,6 +155,8 @@ test.serial('@revoke => performs a token revoke with custom (inline) http option const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeSuccess(scopeOptions, revokeParams); + mockServer.use(...scope.handlers); + const httpOptions = { headers: { Authorization: 'Basic credentials', @@ -153,9 +168,9 @@ test.serial('@revoke => performs a token revoke with custom (inline) http option await t.notThrowsAsync(() => accessToken.revoke('refresh_token', httpOptions)); scope.done(); -}); +})); -test.serial('@revoke => throws an error with an invalid tokenType option', async (t) => { +test('@revoke => throws an error with an invalid tokenType option', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -165,9 +180,9 @@ test.serial('@revoke => throws an error with an invalid tokenType option', async await t.throwsAsync(() => accessToken.revoke('invalid_value'), { message: /Invalid token type. Only access_token or refresh_token are valid values/, }); -}); +})); -test.serial('@revokeAll => revokes both the access and refresh tokens', async (t) => { +test('@revokeAll => revokes both the access and refresh tokens', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -188,14 +203,16 @@ test.serial('@revokeAll => revokes both the access and refresh tokens', async (t const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeAllSuccess(scopeOptions, accessTokenRevokeParams, refreshTokenRevokeParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); await t.notThrowsAsync(() => accessToken.revokeAll()); scope.done(); -}); +})); -test.serial('@revokenAll => revokes both the access and refresh tokens with custom (inline) http options', async (t) => { +test('@revokenAll => revokes both the access and refresh tokens with custom (inline) http options', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -222,6 +239,8 @@ test.serial('@revokenAll => revokes both the access and refresh tokens with cust const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeAllSuccess(customScopeOptions, accessTokenRevokeParams, refreshTokenRevokeParams); + mockServer.use(...scope.handlers); + const httpOptions = { headers: { 'X-REQUEST-ID': 123, @@ -233,9 +252,9 @@ test.serial('@revokenAll => revokes both the access and refresh tokens with cust await t.notThrowsAsync(() => accessToken.revokeAll(httpOptions)); scope.done(); -}); +})); -test.serial('@revokeAll => revokes tboth the access and refresh tokens with custom (inline) http options without overriding (required) http options', async (t) => { +test('@revokeAll => revokes tboth the access and refresh tokens with custom (inline) http options without overriding (required) http options', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -256,6 +275,8 @@ test.serial('@revokeAll => revokes tboth the access and refresh tokens with cust const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeAllSuccess(scopeOptions, accessTokenRevokeParams, refreshTokenRevokeParams); + mockServer.use(...scope.handlers); + const httpOptions = { headers: { Authorization: 'Basic credentials', @@ -267,9 +288,9 @@ test.serial('@revokeAll => revokes tboth the access and refresh tokens with cust await t.notThrowsAsync(() => accessToken.revokeAll(httpOptions)); scope.done(); -}); +})); -test.serial('@revokeAll => revokes the refresh token only if the access token is successfully revoked', async (t) => { +test('@revokeAll => revokes the refresh token only if the access token is successfully revoked', mockServer.boundary(async (t) => { const config = createModuleConfig(); const client = new Client(config); @@ -285,6 +306,8 @@ test.serial('@revokeAll => revokes the refresh token only if the access token is const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenRevokeError(scopeOptions, accessTokenRevokeParams); + mockServer.use(...scope.handlers); + const accessToken = new AccessToken(config, client, accessTokenResponse); const error = await t.throwsAsync(() => accessToken.revokeAll(), { instanceOf: Error }); @@ -293,4 +316,4 @@ test.serial('@revokeAll => revokes the refresh token only if the access token is t.is(error.output.statusCode, 500); scope.done(); -}); +})); diff --git a/test/authorization-code-grant-type-get-token.js b/test/authorization-code-grant-type-get-token.js index 40540e0c..40afb3ba 100644 --- a/test/authorization-code-grant-type-get-token.js +++ b/test/authorization-code-grant-type-get-token.js @@ -1,6 +1,7 @@ 'use strict'; const test = require('ava'); +const { setupServer } = require('msw/node'); const { AuthorizationCode } = require('../index'); const AccessToken = require('../lib/access-token'); const { createModuleConfig } = require('./_module-config'); @@ -11,7 +12,12 @@ const { getHeaderCredentialsScopeOptions, } = require('./_authorization-server-mock'); -test.serial('@getToken => resolves to an access token (body credentials and JSON format)', async (t) => { +const mockServer = setupServer(); + +test.before(() => mockServer.listen()); +test.after(() => mockServer.close()); + +test('@getToken => resolves to an access token (body credentials and JSON format)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -24,6 +30,8 @@ test.serial('@getToken => resolves to an access token (body credentials and JSON const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'json', @@ -41,9 +49,9 @@ test.serial('@getToken => resolves to an access token (body credentials and JSON scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token (body credentials and form format)', async (t) => { +test('@getToken => resolves to an access token (body credentials and form format)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -56,6 +64,8 @@ test.serial('@getToken => resolves to an access token (body credentials and form const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'form', @@ -73,9 +83,9 @@ test.serial('@getToken => resolves to an access token (body credentials and form scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token (header credentials)', async (t) => { +test('@getToken => resolves to an access token (header credentials)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -86,6 +96,8 @@ test.serial('@getToken => resolves to an access token (header credentials)', asy const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { authorizationMethod: 'header', @@ -102,9 +114,9 @@ test.serial('@getToken => resolves to an access token (header credentials)', asy scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials + loose encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials + loose encoding)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -120,6 +132,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'the + client + id & symbols', @@ -141,9 +155,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials + strict encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials + strict encoding)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -159,6 +173,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'the + client + id & symbols', @@ -180,9 +196,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials with unescaped characters + strict encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials with unescaped characters + strict encoding)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -198,6 +214,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'I\'m the_client-id! & (symbols*)', @@ -219,9 +237,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (access token host and path)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (access token host and path)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -232,6 +250,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithCustomPath('/oauth/token', scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ auth: { tokenHost: 'https://authorization-server.org:443/root/', @@ -249,9 +269,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (http options)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (http options)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -268,6 +288,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ http: { headers: { @@ -287,9 +309,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (scope separator)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (scope separator)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -301,6 +323,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { scopeSeparator: ',', @@ -318,9 +342,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token while requesting multiple scopes', async (t) => { +test('@getToken => resolves to an access token while requesting multiple scopes', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -332,6 +356,8 @@ test.serial('@getToken => resolves to an access token while requesting multiple const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { code: 'code', redirect_uri: 'http://callback.com', @@ -345,9 +371,9 @@ test.serial('@getToken => resolves to an access token while requesting multiple scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with a custom grant type', async (t) => { +test('@getToken => resolves to an access token with a custom grant type', mockServer.boundary(async (t) => { const expectedRequestParams = { code: 'code', redirect_uri: 'http://callback.com', @@ -358,6 +384,8 @@ test.serial('@getToken => resolves to an access token with a custom grant type', const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { code: 'code', redirect_uri: 'http://callback.com', @@ -371,9 +399,9 @@ test.serial('@getToken => resolves to an access token with a custom grant type', scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with no params', async (t) => { +test('@getToken => resolves to an access token with no params', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', }; @@ -382,6 +410,8 @@ test.serial('@getToken => resolves to an access token with no params', async (t) const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new AuthorizationCode(config); @@ -389,9 +419,9 @@ test.serial('@getToken => resolves to an access token with no params', async (t) scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom (inline) http options', async (t) => { +test('@getToken => resolves to an access token with custom (inline) http options', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', }; @@ -405,6 +435,8 @@ test.serial('@getToken => resolves to an access token with custom (inline) http const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new AuthorizationCode(config); @@ -418,9 +450,9 @@ test.serial('@getToken => resolves to an access token with custom (inline) http scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom (inline) http options without overriding (required) http options', async (t) => { +test('@getToken => resolves to an access token with custom (inline) http options without overriding (required) http options', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', }; @@ -429,6 +461,8 @@ test.serial('@getToken => resolves to an access token with custom (inline) http const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new AuthorizationCode(config); @@ -442,9 +476,9 @@ test.serial('@getToken => resolves to an access token with custom (inline) http scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => rejects the operation when a non json response is received', async (t) => { +test('@getToken => rejects the operation when a non json response is received', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'authorization_code', code: 'code', @@ -457,6 +491,8 @@ test.serial('@getToken => rejects the operation when a non json response is rece const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithNonJSONContent(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'json', @@ -476,4 +512,4 @@ test.serial('@getToken => rejects the operation when a non json response is rece t.true(error.isBoom); t.is(error.output.statusCode, 406); -}); +})); diff --git a/test/client-credentials-grant-type-get-token.js b/test/client-credentials-grant-type-get-token.js index 856e3f37..916da434 100644 --- a/test/client-credentials-grant-type-get-token.js +++ b/test/client-credentials-grant-type-get-token.js @@ -1,6 +1,7 @@ 'use strict'; const test = require('ava'); +const { setupServer } = require('msw/node'); const { ClientCredentials } = require('../index'); const AccessToken = require('../lib/access-token'); const { createModuleConfig } = require('./_module-config'); @@ -11,7 +12,12 @@ const { getHeaderCredentialsScopeOptions, } = require('./_authorization-server-mock'); -test.serial('@getToken => resolves to an access token (body credentials and JSON format)', async (t) => { +const mockServer = setupServer(); + +test.before(() => mockServer.listen()); +test.after(() => mockServer.close()); + +test('@getToken => resolves to an access token (body credentials and JSON format)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', client_id: 'the client id', @@ -23,6 +29,8 @@ test.serial('@getToken => resolves to an access token (body credentials and JSON const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'json', @@ -39,9 +47,9 @@ test.serial('@getToken => resolves to an access token (body credentials and JSON scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token (body credentials and form format)', async (t) => { +test('@getToken => resolves to an access token (body credentials and form format)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', random_param: 'random value', @@ -53,6 +61,8 @@ test.serial('@getToken => resolves to an access token (body credentials and form const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'form', @@ -69,9 +79,9 @@ test.serial('@getToken => resolves to an access token (body credentials and form scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token (header credentials)', async (t) => { +test('@getToken => resolves to an access token (header credentials)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', random_param: 'random value', @@ -81,6 +91,8 @@ test.serial('@getToken => resolves to an access token (header credentials)', asy const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { authorizationMethod: 'header', @@ -96,9 +108,9 @@ test.serial('@getToken => resolves to an access token (header credentials)', asy scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials + loose encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials + loose encoding)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', random_param: 'random value', @@ -113,6 +125,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'the + client + id & symbols', @@ -133,9 +147,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials + strict encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials + strict encoding)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', random_param: 'random value', @@ -150,6 +164,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'the + client + id & symbols', @@ -170,9 +186,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials with unescaped characters + strict encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials with unescaped characters + strict encoding)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', random_param: 'random value', @@ -187,6 +203,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'I\'m the_client-id! & (symbols*)', @@ -207,9 +225,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (access token host and path)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (access token host and path)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', random_param: 'random value', @@ -219,6 +237,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithCustomPath('/oauth/token', scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ auth: { tokenHost: 'https://authorization-server.org:443/root/', @@ -235,9 +255,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (http options)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (http options)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', random_param: 'random value', @@ -253,6 +273,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ http: { headers: { @@ -271,9 +293,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken = resolves to an access token with custom module configuration (scope separator)', async (t) => { +test('@getToken = resolves to an access token with custom module configuration (scope separator)', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', scope: 'scope-a,scope-b', @@ -283,6 +305,8 @@ test.serial('@getToken = resolves to an access token with custom module configur const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { scope: ['scope-a', 'scope-b'], }; @@ -298,9 +322,9 @@ test.serial('@getToken = resolves to an access token with custom module configur scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token while requesting multiple scopes', async (t) => { +test('@getToken => resolves to an access token while requesting multiple scopes', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', scope: 'scope-a scope-b', @@ -310,6 +334,8 @@ test.serial('@getToken => resolves to an access token while requesting multiple const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { scope: ['scope-a', 'scope-b'], }; @@ -321,9 +347,9 @@ test.serial('@getToken => resolves to an access token while requesting multiple scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with a custom grant type', async (t) => { +test('@getToken => resolves to an access token with a custom grant type', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'my_grant', }; @@ -332,6 +358,8 @@ test.serial('@getToken => resolves to an access token with a custom grant type', const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { grant_type: 'my_grant', }; @@ -343,9 +371,9 @@ test.serial('@getToken => resolves to an access token with a custom grant type', scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with no params', async (t) => { +test('@getToken => resolves to an access token with no params', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', }; @@ -354,6 +382,8 @@ test.serial('@getToken => resolves to an access token with no params', async (t) const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new ClientCredentials(config); @@ -361,9 +391,9 @@ test.serial('@getToken => resolves to an access token with no params', async (t) scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom (inline) http options', async (t) => { +test('@getToken => resolves to an access token with custom (inline) http options', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', }; @@ -377,6 +407,8 @@ test.serial('@getToken => resolves to an access token with custom (inline) http const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new ClientCredentials(config); @@ -390,9 +422,9 @@ test.serial('@getToken => resolves to an access token with custom (inline) http scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom (inline) http options without overriding (required) http options', async (t) => { +test('@getToken => resolves to an access token with custom (inline) http options without overriding (required) http options', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', }; @@ -401,6 +433,8 @@ test.serial('@getToken => resolves to an access token with custom (inline) http const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new ClientCredentials(config); @@ -414,9 +448,9 @@ test.serial('@getToken => resolves to an access token with custom (inline) http scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => rejects the operation when a non json response is received', async (t) => { +test('@getToken => rejects the operation when a non json response is received', mockServer.boundary(async (t) => { const expectedRequestParams = { grant_type: 'client_credentials', client_id: 'the client id', @@ -428,6 +462,8 @@ test.serial('@getToken => rejects the operation when a non json response is rece const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithNonJSONContent(scopeOptions, expectedRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'json', @@ -446,4 +482,4 @@ test.serial('@getToken => rejects the operation when a non json response is rece t.true(error.isBoom); t.is(error.output.statusCode, 406); -}); +})); diff --git a/test/errors.js b/test/errors.js index 871958e0..bb83443b 100644 --- a/test/errors.js +++ b/test/errors.js @@ -1,6 +1,8 @@ 'use strict'; const test = require('ava'); +const { setupServer } = require('msw/node'); + const { AuthorizationCode } = require('../index'); const { createModuleConfig } = require('./_module-config'); const { createAuthorizationServer, getHeaderCredentialsScopeOptions } = require('./_authorization-server-mock'); @@ -16,11 +18,18 @@ const oauthParams = { redirect_uri: 'http://callback.com', }; -test.serial('@errors => rejects operations on http error (401)', async (t) => { +const mockServer = setupServer(); + +test.before(() => mockServer.listen()); +test.after(() => mockServer.close()); + +test('@errors => rejects operations on http error (401)', mockServer.boundary(async (t) => { const scopeOptions = getHeaderCredentialsScopeOptions(); const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenAuthorizationError(scopeOptions, oauthParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new AuthorizationCode(config); @@ -30,19 +39,21 @@ test.serial('@errors => rejects operations on http error (401)', async (t) => { const authorizationError = { error: 'Unauthorized', - message: 'Response Error: 401 null', + message: 'Response Error: 401 Unauthorized', statusCode: 401, }; t.true(error.isBoom); t.deepEqual(error.output.payload, authorizationError); -}); +})); -test.serial('@errors => rejects operations on http error (500)', async (t) => { +test('@errors => rejects operations on http error (500)', mockServer.boundary(async (t) => { const scopeOptions = getHeaderCredentialsScopeOptions(); const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenError(scopeOptions, oauthParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new AuthorizationCode(config); @@ -58,4 +69,4 @@ test.serial('@errors => rejects operations on http error (500)', async (t) => { t.true(error.isBoom); t.deepEqual(error.output.payload, internalServerError); -}); +})); diff --git a/test/msw-matcher/_compare.js b/test/msw-matcher/_compare.js new file mode 100644 index 00000000..a1f0d1d1 --- /dev/null +++ b/test/msw-matcher/_compare.js @@ -0,0 +1,33 @@ +'use strict'; + +const diff = require('diff'); + +const createSelector = (obj, matcher, selector = {}) => { + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of Object.entries(matcher)) { + if (Object.hasOwn(obj, key)) { + if (typeof value === 'object' && value !== null) { + // eslint-disable-next-line no-param-reassign + selector[key] ??= {}; + createSelector(obj[key], value, selector[key]); + } else { + // eslint-disable-next-line no-param-reassign + selector[key] = obj[key]; + } + } + } + + return selector; +}; + +const comparePartial = (actual, expected) => { + const selector = createSelector(actual, expected); + const result = diff.diffJson(selector, expected); + const changes = result.reduce((res, part) => res && !part.added && !part.removed, true); + + return { + pass: changes, + }; +}; + +module.exports = { comparePartial }; diff --git a/test/msw-matcher/_index.js b/test/msw-matcher/_index.js new file mode 100644 index 00000000..87f0e35a --- /dev/null +++ b/test/msw-matcher/_index.js @@ -0,0 +1,9 @@ +'use strict'; + +const { Scope } = require('./_scope'); + +function scope(baseURL) { + return new Scope(baseURL); +} + +module.exports = { scope }; diff --git a/test/msw-matcher/_matchers.js b/test/msw-matcher/_matchers.js new file mode 100644 index 00000000..4a866a41 --- /dev/null +++ b/test/msw-matcher/_matchers.js @@ -0,0 +1,71 @@ +'use strict'; + +const { readBody } = require('./_request-body'); +const { comparePartial } = require('./_compare'); + +const kExpected = Symbol('expected'); + +const normalize = (obj, options = {}) => { + const normalized = {}; + + // eslint-disable-next-line no-restricted-syntax + for (const [key, value] of Object.entries(obj)) { + const normalizedKey = options.normalizeKeys ? key.toLowerCase() : key; + + if (typeof value === 'object' && value !== null) { + normalized[normalizedKey] ??= {}; + normalize(normalized[normalizedKey]); + } else { + // coherse to string + normalized[normalizedKey] = options.normalizeValues ? `${value}` : value; + } + } + + return normalized; +}; + +class Matcher { + constructor(expected) { + this[kExpected] = expected; + } +} + +class BodyMatcher extends Matcher { + async match(context) { + const isJSON = context.request.headers.get('content-type') === 'application/json'; + const expected = normalize(this[kExpected], { normalizeKeys: false, normalizeValues: !isJSON }); + const actual = await readBody(context.request); + + return comparePartial(actual, expected); + } +} + +class HeadersMatcher extends Matcher { + constructor(expected) { + const expectedValue = normalize(expected, { normalizeKeys: true, normalizeValues: true }); + super(expectedValue); + } + + async match(context) { + const expected = this[kExpected]; + const actual = Object.fromEntries(context.request.headers); + + return comparePartial(actual, expected); + } +} + +class URLSearchParamsMatcher extends Matcher { + constructor(expected) { + const expectedValue = normalize(expected, { normalizeKeys: false, normalizeValues: true }); + super(expectedValue); + } + + async match(context) { + const expected = this[kExpected]; + const actual = Object.fromEntries(context.searchParams); + + return comparePartial(actual, expected); + } +} + +module.exports = { BodyMatcher, HeadersMatcher, URLSearchParamsMatcher }; diff --git a/test/msw-matcher/_request-body.js b/test/msw-matcher/_request-body.js new file mode 100644 index 00000000..465e9a9b --- /dev/null +++ b/test/msw-matcher/_request-body.js @@ -0,0 +1,38 @@ +'use strict'; + +const bodies = new WeakMap(); + +const CONTENT_TYPE = 'content-type'; + +const JSON_CONTENT_TYPE = 'application/json'; +const FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded'; + +function readJSON(request) { + return request.json(); +} + +async function readForm(request) { + return Object.fromEntries(await request.formData()); +} + +const readers = { + [JSON_CONTENT_TYPE]: readJSON, + [FORM_CONTENT_TYPE]: readForm, +}; + +const readBody = async (request) => { + const contentType = request.headers.get(CONTENT_TYPE); + + if (readers[contentType]) { + const read = readers[contentType]; + const body = bodies.get(request) ?? await read(request); + + bodies.set(request, body); + + return body; + } + + return null; +}; + +module.exports = { readBody }; diff --git a/test/msw-matcher/_request.js b/test/msw-matcher/_request.js new file mode 100644 index 00000000..f6a0c115 --- /dev/null +++ b/test/msw-matcher/_request.js @@ -0,0 +1,85 @@ +'use strict'; + +const { http } = require('msw'); + +const { HeadersMatcher, BodyMatcher, URLSearchParamsMatcher } = require('./_matchers'); + +class Request { + #scope; + #options; + #matchers; + #times; + #remaining; + + constructor(scope, options) { + this.#times = 1; + this.#remaining = 1; + this.#scope = scope; + this.#options = options; + this.#matchers = []; + } + + times(times) { + this.#times = times; + this.#remaining = times; + return this; + } + + matchSearchParams(expectedSearchParams) { + this.#matchers.push(new URLSearchParamsMatcher(expectedSearchParams)); + return this; + } + + matchBody(expectedBody) { + this.#matchers.push(new BodyMatcher(expectedBody)); + return this; + } + + matchHeaders(expectedHeaders) { + this.#matchers.push(new HeadersMatcher(expectedHeaders)); + return this; + } + + handler(fn) { + const resolver = async (context) => { + if (this.#remaining > 0) { + // eslint-disable-next-line no-restricted-syntax + for (const matcher of this.#matchers) { + // eslint-disable-next-line no-await-in-loop + const result = await matcher.match(context); + + if (!result.pass) { + return null; + } + } + + this.#remaining -= 1; + + if (this.#remaining === 0) { + this.#scope.pendingRequests.delete(this); + } + + return fn(context); + } + + return null; + }; + + const url = new URL(this.#options.path, this.#options.baseURL).toString(); + const handler = http[this.#options.method](url, resolver); + + this.#scope.handlers.push(handler); + + return this.#scope; + } + + describe() { + return { + url: new URL(this.#options.path, this.#options.baseURL).toString(), + method: this.#options.method, + remaining: this.#remaining, + }; + } +} + +module.exports = { Request }; diff --git a/test/msw-matcher/_scope.js b/test/msw-matcher/_scope.js new file mode 100644 index 00000000..14d7565a --- /dev/null +++ b/test/msw-matcher/_scope.js @@ -0,0 +1,52 @@ +'use strict'; + +const { Request } = require('./_request'); + +class Scope { + #baseURL; + + constructor(baseURL) { + this.#baseURL = baseURL; + this.handlers = []; + this.pendingRequests = new Set(); + } + + #request(method, path) { + const request = new Request(this, { + baseURL: this.#baseURL, + method, + path, + }); + + this.pendingRequests.add(request); + + return request; + } + + get(path) { + return this.#request('get', path); + } + + post(path) { + return this.#request('post', path); + } + + put(path) { + return this.#request('put', path); + } + + delete(path) { + return this.#request('delete', path); + } + + done() { + if (this.pendingRequests.size > 0) { + const descriptions = Array.from(this.pendingRequests, (request) => request.describe()); + throw new Error(`Pending request queue is not empty. The following requests were not consumed: + ${JSON.stringify(descriptions, null, 2)} + `); + } + } +} + +module.exports = { Scope }; diff --git a/test/resource-owner-password-grant-type-get-token.js b/test/resource-owner-password-grant-type-get-token.js index 5662d2b9..42d39d44 100644 --- a/test/resource-owner-password-grant-type-get-token.js +++ b/test/resource-owner-password-grant-type-get-token.js @@ -1,6 +1,7 @@ 'use strict'; const test = require('ava'); +const { setupServer } = require('msw/node'); const { ResourceOwnerPassword } = require('../index'); const AccessToken = require('../lib/access-token'); const { createModuleConfig } = require('./_module-config'); @@ -11,7 +12,17 @@ const { getHeaderCredentialsScopeOptions, } = require('./_authorization-server-mock'); -test.serial('@getToken => resolves to an access token (body credentials and JSON format)', async (t) => { +const mockServer = setupServer(); + +test.before(() => { + mockServer.listen(); +}); + +test.after(() => { + mockServer.close(); +}); + +test('@getToken => resolves to an access token (body credentials and JSON format)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -24,6 +35,8 @@ test.serial('@getToken => resolves to an access token (body credentials and JSON const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'json', @@ -41,9 +54,9 @@ test.serial('@getToken => resolves to an access token (body credentials and JSON scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token (body credentials and form format)', async (t) => { +test('@getToken => resolves to an access token (body credentials and form format)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -56,6 +69,8 @@ test.serial('@getToken => resolves to an access token (body credentials and form const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'form', @@ -73,9 +88,9 @@ test.serial('@getToken => resolves to an access token (body credentials and form scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token (header credentials)', async (t) => { +test('@getToken => resolves to an access token (header credentials)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -86,6 +101,8 @@ test.serial('@getToken => resolves to an access token (header credentials)', asy const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { authorizationMethod: 'header', @@ -102,9 +119,9 @@ test.serial('@getToken => resolves to an access token (header credentials)', asy scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials + loose encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials + loose encoding)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -120,6 +137,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'the + client + id & symbols', @@ -141,9 +160,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials + strict encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials + strict encoding)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -159,6 +178,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'the + client + id & symbols', @@ -180,9 +201,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (header credentials with unescaped characters + strict encoding)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (header credentials with unescaped characters + strict encoding)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -198,6 +219,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ client: { id: 'I\'m the_client-id! & (symbols*)', @@ -219,9 +242,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (access token host and path)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (access token host and path)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -232,6 +255,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithCustomPath('/oauth/token', scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ auth: { tokenHost: 'https://authorization-server.org:443/root/', @@ -249,9 +274,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (http options)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (http options)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -268,6 +293,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ http: { headers: { @@ -287,9 +314,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom module configuration (token separator)', async (t) => { +test('@getToken => resolves to an access token with custom module configuration (token separator)', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -301,6 +328,8 @@ test.serial('@getToken => resolves to an access token with custom module configu const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { username: 'alice', password: 'secret', @@ -319,9 +348,9 @@ test.serial('@getToken => resolves to an access token with custom module configu scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token while requesting multiple scopes', async (t) => { +test('@getToken => resolves to an access token while requesting multiple scopes', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -333,6 +362,8 @@ test.serial('@getToken => resolves to an access token while requesting multiple const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { username: 'alice', password: 'secret', @@ -346,9 +377,9 @@ test.serial('@getToken => resolves to an access token while requesting multiple scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with a custom grant type', async (t) => { +test('@getToken => resolves to an access token with a custom grant type', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'my_grant', username: 'alice', @@ -359,6 +390,8 @@ test.serial('@getToken => resolves to an access token with a custom grant type', const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const tokenParams = { grant_type: 'my_grant', username: 'alice', @@ -372,9 +405,9 @@ test.serial('@getToken => resolves to an access token with a custom grant type', scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with no params', async (t) => { +test('@getToken => resolves to an access token with no params', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', }; @@ -383,6 +416,8 @@ test.serial('@getToken => resolves to an access token with no params', async (t) const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new ResourceOwnerPassword(config); @@ -390,9 +425,9 @@ test.serial('@getToken => resolves to an access token with no params', async (t) scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom (inline) http options', async (t) => { +test('@getToken => resolves to an access token with custom (inline) http options', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', }; @@ -406,6 +441,8 @@ test.serial('@getToken => resolves to an access token with custom (inline) http const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new ResourceOwnerPassword(config); @@ -419,9 +456,9 @@ test.serial('@getToken => resolves to an access token with custom (inline) http scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => resolves to an access token with custom (inline) http options without overriding (required) http options', async (t) => { +test('@getToken => resolves to an access token with custom (inline) http options without overriding (required) http options', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', }; @@ -430,6 +467,8 @@ test.serial('@getToken => resolves to an access token with custom (inline) http const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccess(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig(); const oauth2 = new ResourceOwnerPassword(config); @@ -443,9 +482,9 @@ test.serial('@getToken => resolves to an access token with custom (inline) http scope.done(); t.true(accessToken instanceof AccessToken); -}); +})); -test.serial('@getToken => rejects the operation when a non json response is received', async (t) => { +test('@getToken => rejects the operation when a non json response is received', mockServer.boundary(async (t) => { const tokenRequestParams = { grant_type: 'password', username: 'alice', @@ -458,6 +497,8 @@ test.serial('@getToken => rejects the operation when a non json response is rece const server = createAuthorizationServer('https://authorization-server.org:443'); const scope = server.tokenSuccessWithNonJSONContent(scopeOptions, tokenRequestParams); + mockServer.use(...scope.handlers); + const config = createModuleConfig({ options: { bodyFormat: 'json', @@ -477,4 +518,4 @@ test.serial('@getToken => rejects the operation when a non json response is rece t.true(error.isBoom); t.is(error.output.statusCode, 406); -}); +}));