diff --git a/README.md b/README.md index ef817fa..613580d 100644 --- a/README.md +++ b/README.md @@ -89,12 +89,14 @@ You can also set the following environment variables - Bearer Auth - `PACT_BROKER_TOKEN` +Note: command line options will take precedence over environment variables. + Options: -V, --version output the version number -p, --provider [string] The name of the provider in the pact broker -t, --tag [string] The tag to filter pacts retrieved from the pact broker - -u, --user [USERNAME:PASSWORD] The basic auth username and password to access the pact broker - -u, --token [string] The bearer token to access the pact broker + -u, --user [USERNAME:PASSWORD] The basic auth username and password to access the pact broker (env - PACT_BROKER_USERNAME:PACT_BROKER_PASSWORD) + -u, --token [string] The bearer token to access the pact broker (env - PACT_BROKER_TOKEN) -a, --analyticsUrl [string] The url to send analytics events to as a http post -o, --outputDepth [integer] Specifies the number of times to recurse while formatting the output objects. This is useful in case of large complicated objects or schemas. (default: 4) -A, --additionalPropertiesInResponse [boolean] allow additional properties in response bodies, default false diff --git a/lib/cli.ts b/lib/cli.ts index c68178a..bb846b7 100644 --- a/lib/cli.ts +++ b/lib/cli.ts @@ -47,8 +47,8 @@ program .arguments(' ') .option('-p, --provider [string]', 'The name of the provider in the pact broker') .option('-t, --tag [string]', 'The tag to filter pacts retrieved from the pact broker') - .option('-u, --user [USERNAME:PASSWORD]', 'The basic auth username and password to access the pact broker') - .option('-b, --token [string]', 'The bearer token to access the pact broker') + .option('-u, --user [USERNAME:PASSWORD]', 'The basic auth username and password to access the pact broker (env - PACT_BROKER_USERNAME:PACT_BROKER_PASSWORD)') + .option('-b, --token [string]', 'The bearer token to access the pact broker (env - PACT_BROKER_TOKEN)') .option('-a, --analyticsUrl [string]', 'The url to send analytics events to as a http post') .option('-o, --outputDepth [integer]', 'Specifies the number of times to recurse ' + 'while formatting the output objects. ' + @@ -73,7 +73,20 @@ json file. Optionally, pass a --tag option alongside a --provider option to filt pacts from the broker by Pact Broker version tags. If the pact broker has basic auth enabled, pass a --user option with username and password joined by a colon -(i.e. THE_USERNAME:THE_PASSWORD) to access the pact broker resources.` +(i.e. THE_USERNAME:THE_PASSWORD) to access the pact broker resources. + +If the pact broker has bearer token auth enabled, pass a --token option along with the token to access the pact broker resources. + +You can also set the following environment variables + +- Basic Auth + - PACT_BROKER_USERNAME + - PACT_BROKER_PASSWORD +- Bearer Auth + - PACT_BROKER_TOKEN + +Note: command line options will take precedence over environment variables. +` ) .action(async (swagger, mock, options) => { try { diff --git a/test/e2e/cli.spec.ts b/test/e2e/cli.spec.ts index 387d263..4b93c82 100644 --- a/test/e2e/cli.spec.ts +++ b/test/e2e/cli.spec.ts @@ -8,11 +8,13 @@ import {expectToFail} from '../helpers/expect-to-fail'; interface InvokeCommandOptions { analyticsUrl?: string; auth?: string; + token?: string; mock: string; providerName?: string; swagger: string; tag?: string; outputDepth?: string; + envVars?: string; } const execute = (command: string): Promise => { @@ -28,7 +30,9 @@ const execute = (command: string): Promise => { }; const invokeCommand = (options: InvokeCommandOptions): Promise => { - let command = `./bin/swagger-mock-validator.mjs ${options.swagger} ${options.mock}`; + let command = `${options.envVars ? `${options.envVars} ` : ''}./bin/swagger-mock-validator.mjs ${options.swagger} ${ + options.mock + }`; if (options.providerName) { command += ` --provider ${options.providerName}`; @@ -46,6 +50,10 @@ const invokeCommand = (options: InvokeCommandOptions): Promise => { command += ` --user ${options.auth}`; } + if (options.token) { + command += ` --token ${options.token}`; + } + if (options.outputDepth) { command += ` --outputDepth ${options.outputDepth}`; } @@ -388,10 +396,41 @@ describe('swagger-mock-validator/cli', () => { ); }, 30000); it('should make an bearer token authenticated request to the provided pact broker url when asked to do so', async () => { - const auth = 'token'; + const token = 'token'; await invokeCommand({ - auth, + token, + mock: urlTo('test/e2e/fixtures/pact-broker.json'), + providerName: 'provider-1', + swagger: urlTo('test/e2e/fixtures/swagger-provider.json') + }); + + expect(mockPactBroker.get).toHaveBeenCalledWith( + jasmine.objectContaining({authorization: 'Bearer token'}), + jasmine.stringMatching('test/e2e/fixtures/pact-broker.json') + ); + }, 30000); + it('should make an authenticated request with a user/pass combo read from PACT_BROKER_USERNAME/PACT_BROKER_PASSWORD variable', async () => { + const user = 'user'; + const pass = 'pass'; + + await invokeCommand({ + envVars: `PACT_BROKER_USERNAME=${user} PACT_BROKER_PASSWORD=${pass}`, + mock: urlTo('test/e2e/fixtures/pact-broker.json'), + providerName: 'provider-1', + swagger: urlTo('test/e2e/fixtures/swagger-provider.json') + }); + + expect(mockPactBroker.get).toHaveBeenCalledWith( + jasmine.objectContaining({authorization: 'Basic dXNlcjpwYXNz'}), + jasmine.stringMatching('test/e2e/fixtures/pact-broker.json') + ); + }, 30000); + it('should make an authenticated request with a bearer token read from PACT_BROKER_TOKEN variable', async () => { + const token = 'token'; + + await invokeCommand({ + envVars: `PACT_BROKER_TOKEN=${token}`, mock: urlTo('test/e2e/fixtures/pact-broker.json'), providerName: 'provider-1', swagger: urlTo('test/e2e/fixtures/swagger-provider.json') @@ -402,7 +441,39 @@ describe('swagger-mock-validator/cli', () => { jasmine.stringMatching('test/e2e/fixtures/pact-broker.json') ); }, 30000); + it('should prefer user variable, even if PACT_BROKER_USERNAME/PACT_BROKER_PASSWORD variable are set', async () => { + const user = 'user'; + const pass = 'pass'; + await invokeCommand({ + auth: `${user}:${pass}`, + envVars: `PACT_BROKER_USERNAME=${user}_env PACT_BROKER_PASSWORD=${pass}_env`, + mock: urlTo('test/e2e/fixtures/pact-broker.json'), + providerName: 'provider-1', + swagger: urlTo('test/e2e/fixtures/swagger-provider.json') + }); + + expect(mockPactBroker.get).toHaveBeenCalledWith( + jasmine.objectContaining({authorization: 'Basic dXNlcjpwYXNz'}), + jasmine.stringMatching('test/e2e/fixtures/pact-broker.json') + ); + }, 30000); + it('should prefer user variable, even if PACT_BROKER_TOKEN variable are set', async () => { + const token = 'token'; + + await invokeCommand({ + token, + envVars: `PACT_BROKER_TOKEN=${token}_env`, + mock: urlTo('test/e2e/fixtures/pact-broker.json'), + providerName: 'provider-1', + swagger: urlTo('test/e2e/fixtures/swagger-provider.json') + }); + + expect(mockPactBroker.get).toHaveBeenCalledWith( + jasmine.objectContaining({authorization: 'Bearer token'}), + jasmine.stringMatching('test/e2e/fixtures/pact-broker.json') + ); + }, 30000); it('should format output objects to depth 0', async () => { const result = await invokeCommand({ mock: 'test/e2e/fixtures/pact-working-consumer.json',