diff --git a/.gitignore b/.gitignore index 0607605fd..5d86f6e71 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ yarn-error.log* .vscode electron/env.ts + +coverage diff --git a/package-lock.json b/package-lock.json index 2a79fd001..7ec3877fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17171,17 +17171,17 @@ } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-report/node_modules/has-flag": { @@ -17193,21 +17193,48 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-report/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "semver": "^6.0.0" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/istanbul-lib-report/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -17220,10 +17247,16 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-report/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -27789,8 +27822,9 @@ } }, "node_modules/yargs": { - "version": "17.6.2", - "license": "MIT", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -28222,11 +28256,69 @@ "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "@types/sinon": "^10.0.16", + "c8": "^8.0.1", "chai": "^4.3.7", "mocha": "^10.2.0", "sinon": "^15.2.0" } }, + "packages/synchronizer/node_modules/c8": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz", + "integrity": "sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=12" + } + }, + "packages/synchronizer/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/synchronizer/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/synchronizer/node_modules/mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", @@ -28258,6 +28350,36 @@ "url": "https://opencollective.com/node-fetch" } }, + "packages/synchronizer/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/synchronizer/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/synchronizer/node_modules/yaml": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", @@ -28266,6 +28388,15 @@ "node": ">= 14" } }, + "packages/synchronizer/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "packages/tree-navigator": { "name": "@monokle/tree-navigator", "version": "1.0.4", @@ -31692,6 +31823,7 @@ "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "@types/sinon": "^10.0.16", + "c8": "^8.0.1", "chai": "^4.3.7", "env-paths": "^3.0.0", "mkdirp": "^3.0.1", @@ -31704,6 +31836,45 @@ "yaml": "^2.3.1" }, "dependencies": { + "c8": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz", + "integrity": "sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, "mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", @@ -31719,10 +31890,34 @@ "formdata-polyfill": "^4.0.10" } }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, "yaml": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==" + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } }, @@ -42092,13 +42287,13 @@ } }, "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "dependencies": { @@ -42108,13 +42303,31 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "requires": { - "semver": "^6.0.0" + "semver": "^7.5.3" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" } }, "supports-color": { @@ -42125,13 +42338,19 @@ "requires": { "has-flag": "^4.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -49832,7 +50051,9 @@ } }, "yargs": { - "version": "17.6.2", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "requires": { "cliui": "^8.0.1", "escalade": "^3.1.1", diff --git a/packages/synchronizer/package.json b/packages/synchronizer/package.json index a1f98a530..c76138961 100644 --- a/packages/synchronizer/package.json +++ b/packages/synchronizer/package.json @@ -18,6 +18,7 @@ "scripts": { "typecheck": "tsc --noEmit", "test": "npm run build && mocha", + "test:cc": "npm run build && c8 mocha", "test:snapshot": "vitest --update", "build": "rimraf lib && tsc --build tsconfig.build.json", "format:all": "prettier --write \"{src,test}/**/*.{js,jsx,ts,tsx}\"" @@ -50,6 +51,7 @@ "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "@types/sinon": "^10.0.16", + "c8": "^8.0.1", "chai": "^4.3.7", "mocha": "^10.2.0", "sinon": "^15.2.0" diff --git a/packages/synchronizer/src/__tests__/authenticator.spec.ts b/packages/synchronizer/src/__tests__/authenticator.spec.ts index 523c78482..5c701c7d1 100644 --- a/packages/synchronizer/src/__tests__/authenticator.spec.ts +++ b/packages/synchronizer/src/__tests__/authenticator.spec.ts @@ -1,15 +1,19 @@ import {dirname, resolve} from 'path'; import {fileURLToPath} from 'url'; import {rm, mkdir, cp} from 'fs/promises'; +import sinon from 'sinon'; import {assert} from 'chai'; import {createDefaultMonokleAuthenticator} from '../createDefaultAuthenticator.js'; import {StorageHandler} from '../handlers/storageHandler.js'; +import { ApiHandler } from '../handlers/apiHandler.js'; +import { DeviceFlowHandler } from '../handlers/deviceFlowHandler.js'; import type {AuthenticatorLoginEvent} from '../utils/authenticator.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); describe('Authenticator Tests', () => { + before(async () => { await cleanupTmpConfigDir(); }); @@ -18,87 +22,202 @@ describe('Authenticator Tests', () => { await cleanupTmpConfigDir(); }); - it('should return empty user data on init when no auth file present', async () => { - const storagePath = await createTmpConfigDir(); - const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + describe('Initialization', () => { - const user = authenticator.user; + it('should return empty user data on init when no auth file present', async () => { + const storagePath = await createTmpConfigDir(); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); - assert.isFalse(user.isAuthenticated); - assert.notExists(user.email); - assert.notExists(user.token); - }); + const user = authenticator.user; - it('should return user data on init when auth file present (token method)', async () => { - const storagePath = await createTmpConfigDir('token'); - const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + assert.isFalse(user.isAuthenticated); + assert.notExists(user.email); + assert.notExists(user.token); + }); - const user = authenticator.user; + it('should return user data on init when auth file present (token method)', async () => { + const storagePath = await createTmpConfigDir('token'); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); - assert.isTrue(user.isAuthenticated); - assert.equal(user.email, 'user1@kubeshop.io'); - assert.equal(user.token, 'USER1_ACCESS_TOKEN'); - assert.equal(user.data?.auth?.token.token_type, 'access_token'); - }); + const user = authenticator.user; - it('should return user data on init when auth file present (device code method)', async () => { - const storagePath = await createTmpConfigDir('deviceflow'); - const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + assert.isTrue(user.isAuthenticated); + assert.equal(user.email, 'user1@kubeshop.io'); + assert.equal(user.token, 'USER1_ACCESS_TOKEN'); + assert.equal(user.data?.auth?.token.token_type, 'access_token'); + }); - const user = authenticator.user; + it('should return user data on init when auth file present (device code method)', async () => { + const storagePath = await createTmpConfigDir('deviceflow'); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); - assert.isTrue(user.isAuthenticated); - assert.equal(user.email, 'user2@kubeshop.io'); - assert.equal(user.token, 'USER2_ACCESS_TOKEN'); - assert.equal(user.data?.auth?.token.token_type, 'bearer'); - }); + const user = authenticator.user; + + assert.isTrue(user.isAuthenticated); + assert.equal(user.email, 'user2@kubeshop.io'); + assert.equal(user.token, 'USER2_ACCESS_TOKEN'); + assert.equal(user.data?.auth?.token.token_type, 'bearer'); + }); - it('should trigger login event on init when auth file present (token method)', async () => { - const storagePath = await createTmpConfigDir('token'); - const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + it('should trigger login event on init when auth file present (token method)', async () => { + const storagePath = await createTmpConfigDir('token'); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); - return new Promise(resolve => { - authenticator.on('login', (evt: AuthenticatorLoginEvent) => { - assert.equal(evt.method, 'token'); - assert.isTrue(evt.user.isAuthenticated); - assert.equal(evt.user.email, 'user1@kubeshop.io'); - assert.equal(evt.user.token, 'USER1_ACCESS_TOKEN'); + return new Promise(resolve => { + authenticator.on('login', (evt: AuthenticatorLoginEvent) => { + assert.equal(evt.method, 'token'); + assert.isTrue(evt.user.isAuthenticated); + assert.equal(evt.user.email, 'user1@kubeshop.io'); + assert.equal(evt.user.token, 'USER1_ACCESS_TOKEN'); - resolve(); + resolve(); + }); }); }); - }); - it('should trigger login event on init when auth file present (device code method)', async () => { - const storagePath = await createTmpConfigDir('deviceflow'); - const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + it('should trigger login event on init when auth file present (device code method)', async () => { + const storagePath = await createTmpConfigDir('deviceflow'); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); - return new Promise(resolve => { - authenticator.on('login', (evt: AuthenticatorLoginEvent) => { - assert.equal(evt.method, 'device code'); - assert.isTrue(evt.user.isAuthenticated); - assert.equal(evt.user.email, 'user2@kubeshop.io'); - assert.equal(evt.user.token, 'USER2_ACCESS_TOKEN'); + return new Promise(resolve => { + authenticator.on('login', (evt: AuthenticatorLoginEvent) => { + assert.equal(evt.method, 'device code'); + assert.isTrue(evt.user.isAuthenticated); + assert.equal(evt.user.email, 'user2@kubeshop.io'); + assert.equal(evt.user.token, 'USER2_ACCESS_TOKEN'); - resolve(); + resolve(); + }); }); }); }); - it('should trigger logout event', async () => { - const storagePath = await createTmpConfigDir('token'); - const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + describe('Login', () => { + const stubs: sinon.SinonStub[] = []; - return new Promise(resolve => { - authenticator.on('logout', () => { - assert.ok(true); + afterEach(async () => { + if (stubs.length) { + stubs.forEach(stub => stub.restore()); + } + }); + + it('should login with token', async () => { + const apiHandler = new ApiHandler(); + const getUserStub = sinon.stub(apiHandler as any, 'queryApi').resolves({ + data: { + me: { + id: 123, + email: 'user3@kubeshop.io', + projects: [{ + project: { + id: 1230, + slug: 'user3-proj', + name: 'User3 Project', + repositories: [] + } + }], + }, + }, + }); + stubs.push(getUserStub); + + const storagePath = await createTmpConfigDir(); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath), apiHandler); - resolve(); + const loginResponse = await authenticator.login('token', 'USER3_ACCESS_TOKEN'); + const user = await loginResponse.onDone; + + assert.isTrue(user.isAuthenticated); + assert.equal(user.email, 'user3@kubeshop.io'); + assert.equal(user.token, 'USER3_ACCESS_TOKEN'); + assert.equal(user.data?.auth?.token.token_type, 'access_token'); + }); + + it('should login with device code', async () => { + const apiHandler = new ApiHandler(); + const getUserStub = sinon.stub(apiHandler as any, 'queryApi').resolves({ + data: { + me: { + id: 124, + email: 'user4@kubeshop.io', + projects: [{ + project: { + id: 1240, + slug: 'user4-proj', + name: 'User4 Project', + repositories: [] + } + }], + }, + }, + }); + stubs.push(getUserStub); + + const deviceFlowHandler = new DeviceFlowHandler(); + const deviceFlowClientStub = sinon.stub(deviceFlowHandler as any, 'getClient').resolves({ + deviceAuthorization: async () => { + return { + user_code: 'FOO-BAR', + verification_uri: 'https://app.monokle.com/device', + verification_uri_complete: 'https://app.monokle.com/device?code=FOO-BAR', + poll: () => Promise.resolve({ + access_token: 'USER4_ACCESS_TOKEN', + token_type: 'bearer', + expires_at: 1691579288, + id_token: 'USER4_ID_TOKEN', + refresh_token: 'USER4_REFRESH_TOKEN', + }), + } + }, }); + stubs.push(deviceFlowClientStub); - authenticator.logout(); + const storagePath = await createTmpConfigDir(); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath), apiHandler, deviceFlowHandler); + + const loginResponse = await authenticator.login('device code'); + + assert.equal(loginResponse.handle!.user_code, 'FOO-BAR'); + + const user = await loginResponse.onDone; + + assert.isTrue(user.isAuthenticated); + assert.equal(user.email, 'user4@kubeshop.io'); + assert.equal(user.token, 'USER4_ACCESS_TOKEN'); + assert.equal(user.data?.auth?.token.token_type, 'bearer'); }); }); + + describe('Logout', () => { + it('should logout ', async () => { + const storagePath = await createTmpConfigDir('token'); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + + await authenticator.logout(); + + const user = authenticator.user; + + assert.isFalse(user.isAuthenticated); + assert.notExists(user.email); + assert.notExists(user.token); + }); + + it('should trigger logout event on logout', async () => { + const storagePath = await createTmpConfigDir('token'); + const authenticator = createDefaultMonokleAuthenticator(new StorageHandler(storagePath)); + + return new Promise(resolve => { + authenticator.on('logout', () => { + assert.ok(true); + + resolve(); + }); + + authenticator.logout(); + }); + }); + }); + }); async function createTmpConfigDir(copyAuthFixture = '') { diff --git a/packages/synchronizer/src/handlers/deviceFlowHandler.ts b/packages/synchronizer/src/handlers/deviceFlowHandler.ts index 5a37cc92a..90c69ead8 100644 --- a/packages/synchronizer/src/handlers/deviceFlowHandler.ts +++ b/packages/synchronizer/src/handlers/deviceFlowHandler.ts @@ -20,23 +20,14 @@ export class DeviceFlowHandler { async initializeAuthFlow(): Promise { const client = await this.getClient(); - const handle = await client.deviceAuthorization({ + + return client.deviceAuthorization({ scope: 'openid profile offline_access', }); - - console.log('DeviceFlow: User Code: ', handle.user_code); - console.log('DeviceFlow: Verification URI: ', handle.verification_uri); - console.log('DeviceFlow: Verification URI (complete): ', handle.verification_uri_complete); - - return handle; } async pollAuthFlow(handle: DeviceFlowHandle) { - const token = await handle.poll(); - - console.log('DeviceFlow: Token', token); - - return token; + return handle.poll(); } async refreshAuthFlow(refreshToken: string) { diff --git a/packages/synchronizer/src/utils/authenticator.ts b/packages/synchronizer/src/utils/authenticator.ts index 6472443ff..cf75c0b57 100644 --- a/packages/synchronizer/src/utils/authenticator.ts +++ b/packages/synchronizer/src/utils/authenticator.ts @@ -36,8 +36,10 @@ export class Authenticator extends EventEmitter { } get user() { - // we want to have synchronous file reading/checking here (recheck auth file on every request?) + // @TODO we want to have synchronous file reading/checking here (recheck auth file on every request?) // above will be useful only when auth.yaml file gets added/deleted by external action + // + // @TODO what about token refresh logic? may be a part of synchronizer too? return this._user; }