Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 20 additions & 52 deletions __tests__/config/utils/credentials.test.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
jest.mock('../../../src/config/utils/env');

import { getCredentialsFromFlags } from '../../../src/config/utils/credentials';

const baseFlags = {
config: '.twilio-function',
cwd: process.cwd(),
logLevel: 'info' as 'info',
loadSystemEnv: false,
};

describe('getCredentialsFromFlags', () => {
test('should return empty if nothing is passed', async () => {
require('../../../src/config/utils/env').__setVariables({}, '');

const credentials = await getCredentialsFromFlags(baseFlags, undefined);
const credentials = await getCredentialsFromFlags(baseFlags, {}, undefined);
expect(credentials).toEqual({
accountSid: '',
authToken: '',
});
});

test('should return flag values if passed', async () => {
require('../../../src/config/utils/env').__setVariables({}, '');

const credentials = await getCredentialsFromFlags(
{ ...baseFlags, accountSid: 'ACxxxxx', authToken: 'some-token' },
{},
undefined
);
expect(credentials).toEqual({
Expand All @@ -33,16 +29,12 @@ describe('getCredentialsFromFlags', () => {
});

test('should read from env file', async () => {
require('../../../src/config/utils/env').__setVariables(
const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
''
);

const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
undefined
);
expect(credentials).toEqual({
Expand All @@ -52,10 +44,9 @@ describe('getCredentialsFromFlags', () => {
});

test('should take external default options if nothing is passed', async () => {
require('../../../src/config/utils/env').__setVariables({}, '');

const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{},
{ username: 'ACzzzzzzz', password: 'api-secret', profile: undefined }
);
expect(credentials).toEqual({
Expand All @@ -65,16 +56,12 @@ describe('getCredentialsFromFlags', () => {
});

test('env variables should override external default options', async () => {
require('../../../src/config/utils/env').__setVariables(
const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
''
);

const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{ username: 'ACzzzzzzz', password: 'api-secret', profile: undefined }
);
expect(credentials).toEqual({
Expand All @@ -84,16 +71,12 @@ describe('getCredentialsFromFlags', () => {
});

test('external options with profile should override env variables', async () => {
require('../../../src/config/utils/env').__setVariables(
const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
''
);

const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{ username: 'ACzzzzzzz', password: 'api-secret', profile: 'demo' }
);
expect(credentials).toEqual({
Expand All @@ -105,18 +88,15 @@ describe('getCredentialsFromFlags', () => {
test('external options with project should override env variables', async () => {
// project flag is deprecated and removed in v3 @twilio/cli-core but
// included here just to make sure
require('../../../src/config/utils/env').__setVariables(
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
''
);

const credentials = await getCredentialsFromFlags(
{
...baseFlags,
},
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
{
username: 'ACzzzzzzz',
password: 'api-secret',
Expand All @@ -130,16 +110,12 @@ describe('getCredentialsFromFlags', () => {
});

test('should prefer external CLI if profile is passed', async () => {
require('../../../src/config/utils/env').__setVariables(
const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
''
);

const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{ username: 'ACzzzzzzz', password: 'api-secret', profile: 'demo' }
);
expect(credentials).toEqual({
Expand All @@ -151,16 +127,12 @@ describe('getCredentialsFromFlags', () => {
test('should prefer external CLI if project is passed', async () => {
// project flag is deprecated and removed in v3 @twilio/cli-core but
// included here just to make sure
require('../../../src/config/utils/env').__setVariables(
const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
''
);

const credentials = await getCredentialsFromFlags(
{ ...baseFlags },
{ username: 'ACzzzzzzz', password: 'api-secret', project: 'demo' }
);
expect(credentials).toEqual({
Expand All @@ -170,16 +142,12 @@ describe('getCredentialsFromFlags', () => {
});

test('should prefer flag over everything', async () => {
require('../../../src/config/utils/env').__setVariables(
const credentials = await getCredentialsFromFlags(
{ ...baseFlags, accountSid: 'ACxxxxx', authToken: 'some-token' },
{
ACCOUNT_SID: 'ACyyyyyyyyy',
AUTH_TOKEN: 'example-token',
},
''
);

const credentials = await getCredentialsFromFlags(
{ ...baseFlags, accountSid: 'ACxxxxx', authToken: 'some-token' },
{
username: 'ACzzzzzzz',
password: 'api-secret',
Expand Down
150 changes: 149 additions & 1 deletion __tests__/config/utils/env.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { filterEnvVariablesForDeploy } from '../../../src/config/utils/env';
import { stripIndent } from 'common-tags';
import mockFs from 'mock-fs';
import path from 'path';
import {
filterEnvVariablesForDeploy,
readLocalEnvFile,
} from '../../../src/config/utils/env';
import { EnvironmentVariablesWithAuth } from '../../../src/types/generic';

function normalize(unixPath: string) {
return path.resolve('/', ...unixPath.split('/'));
}

describe('filterEnvVariablesForDeploy', () => {
const testVars: EnvironmentVariablesWithAuth = {
ACCOUNT_SID: 'ACCOUNT_SID',
Expand All @@ -25,3 +35,141 @@ describe('filterEnvVariablesForDeploy', () => {
expect(deployVars['hello']).toEqual('world');
});
});

describe('readLocalEnvFile', () => {
let backupSystemEnv = {};

const baseFlags = {
cwd: '/tmp/project',
env: undefined,
loadSystemEnv: false,
};

beforeEach(() => {
mockFs({
'/tmp/project': {
'.env': stripIndent`
ACCOUNT_SID=ACxxxxxxx
AUTH_TOKEN=123456789f
MY_PHONE_NUMBER=+12345
SECRET_API_KEY=abc
`,
'.env.prod': stripIndent`
ACCOUNT_SID=
AUTH_TOKEN=
MY_PHONE_NUMBER=+3333333
SECRET_API_KEY=
`,
},
'/tmp/project-two': {
'.env': stripIndent`
ACCOUNT_SID=ACyyyyyyy
AUTH_TOKEN=123456789a
TWILIO=https://www.twilio.com
`,
'.env.prod': stripIndent`
ACCOUNT_SID=
AUTH_TOKEN=
TWILIO=https://www.twilio.com
`,
},
});
backupSystemEnv = { ...process.env };
});

afterEach(() => {
mockFs.restore();
process.env = { ...backupSystemEnv };
});

it('should throw an error if you use --load-system-env without --env', async () => {
const errorMessage = stripIndent`
If you are using --load-system-env you'll also have to supply a --env flag.

The .env file you are pointing at will be used to primarily load environment variables.
Any empty entries in the .env file will fall back to the system's environment variables.
`;

expect(
readLocalEnvFile({ ...baseFlags, loadSystemEnv: true })
).rejects.toEqual(new Error(errorMessage));
});

it('should load the default env variables', async () => {
expect(await readLocalEnvFile(baseFlags)).toEqual({
localEnv: {
ACCOUNT_SID: 'ACxxxxxxx',
AUTH_TOKEN: '123456789f',
MY_PHONE_NUMBER: '+12345',
SECRET_API_KEY: 'abc',
},
envPath: normalize('/tmp/project/.env'),
});
});

it('should load env variables from a different filename', async () => {
expect(await readLocalEnvFile({ ...baseFlags, env: '.env.prod' })).toEqual({
localEnv: {
ACCOUNT_SID: '',
AUTH_TOKEN: '',
MY_PHONE_NUMBER: '+3333333',
SECRET_API_KEY: '',
},
envPath: normalize('/tmp/project/.env.prod'),
});
});

it('should load the default env variables with different cwd', async () => {
expect(
await readLocalEnvFile({ ...baseFlags, cwd: '/tmp/project-two' })
).toEqual({
localEnv: {
ACCOUNT_SID: 'ACyyyyyyy',
AUTH_TOKEN: '123456789a',
TWILIO: 'https://www.twilio.com',
},
envPath: normalize('/tmp/project-two/.env'),
});
});

it('should load env variables from a different filename & cwd', async () => {
expect(
await readLocalEnvFile({
...baseFlags,
cwd: '/tmp/project-two',
env: '.env.prod',
})
).toEqual({
localEnv: {
ACCOUNT_SID: '',
AUTH_TOKEN: '',
TWILIO: 'https://www.twilio.com',
},
envPath: normalize('/tmp/project-two/.env.prod'),
});
});

it('should fallback to system env variables for empty variables with loadSystemEnv', async () => {
process.env = {
TWILIO: 'https://www.twilio.com/blog',
ACCOUNT_SID: 'ACzzzzzzz',
SECRET_API_KEY: 'psst',
};

expect(
await readLocalEnvFile({
...baseFlags,
env: '.env.prod',
loadSystemEnv: true,
})
).toEqual({
localEnv: {
ACCOUNT_SID: 'ACzzzzzzz',
AUTH_TOKEN: '',
MY_PHONE_NUMBER: '+3333333',
SECRET_API_KEY: 'psst',
},
envPath: normalize('/tmp/project/.env.prod'),
});
});
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"@types/lodash.flatten": "^4.4.6",
"@types/lodash.kebabcase": "^4.1.6",
"@types/lodash.startcase": "^4.4.6",
"@types/mock-fs": "^4.10.0",
"@types/prompts": "^2.0.1",
"@types/supertest": "^2.0.8",
"@types/title": "^1.0.5",
Expand All @@ -107,6 +108,7 @@
"jest-express": "^1.10.1",
"lint-staged": "^8.2.1",
"listr-silent-renderer": "^1.1.1",
"mock-fs": "^4.12.0",
"nock": "^12.0.2",
"npm-run-all": "^4.1.5",
"prettier": "^1.18.2",
Expand Down
7 changes: 7 additions & 0 deletions src/commands/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type SharedFlagsWithCredentials = SharedFlags & {
env?: string;
region?: string;
edge?: string;
loadSystemEnv: boolean;
};

export type ExternalCliOptions = {
Expand Down Expand Up @@ -60,6 +61,12 @@ export const sharedApiRelatedCliOptions: { [key: string]: Options } = {
describe:
'Use a specific auth token for deployment. Uses fields from .env otherwise',
},
'load-system-env': {
default: false,
type: 'boolean',
describe:
'Uses system environment variables as fallback for variables specified in your .env file. Needs to be used with --env explicitly specified.',
},
};

export const sharedCliOptions: { [key: string]: Options } = {
Expand Down
Loading