Files
vercel/packages/cli/test/unit/commands/env.test.ts
Dan Stowell 1b0d72aba5 [cli] Change vc env pull default output file to .env.local (#9892)
`vc deploy` ignores `.env.local`. To make sure we don't inadvertently
push people's secrets to source control, have all environment pulls
default to writing to `.env.local`.
2023-05-22 15:24:38 -07:00

389 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import fs from 'fs-extra';
import path from 'path';
import { parse } from 'dotenv';
import env from '../../../src/commands/env';
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
import { client } from '../../mocks/client';
import { defaultProject, envs, useProject } from '../../mocks/project';
import { useTeams } from '../../mocks/team';
import { useUser } from '../../mocks/user';
describe('env', () => {
describe('pull', () => {
it('should handle pulling', async () => {
const cwd = setupUnitFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'));
// check for development env value
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
expect(devFileHasDevEnv).toBeTruthy();
});
it('should handle pulling from Preview env vars', async () => {
const cwd = setupUnitFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv(
'env',
'pull',
'--yes',
'--cwd',
cwd,
'--environment',
'preview'
);
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'), 'utf8');
expect(rawDevEnv).toContain(
'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"'
);
expect(rawDevEnv).not.toContain(
'BRANCH_ENV_VAR="env var for a specific branch"'
);
});
it('should handle pulling from specific Git branch', async () => {
const cwd = setupUnitFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv(
'env',
'pull',
'--yes',
'--cwd',
cwd,
'--environment',
'preview',
'--git-branch',
'feat/awesome-thing'
);
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'), 'utf8');
expect(rawDevEnv).toContain(
'REDIS_CONNECTION_STRING="redis://abc123@redis.example.com:6379"'
);
expect(rawDevEnv).toContain(
'BRANCH_ENV_VAR="env var for a specific branch"'
);
const parsed = parse(rawDevEnv);
const keys = Object.keys(parsed);
expect(keys).toHaveLength(3);
expect(keys[0]).toEqual('ANOTHER');
expect(keys[1]).toEqual('BRANCH_ENV_VAR');
expect(keys[2]).toEqual('REDIS_CONNECTION_STRING');
});
it('should handle alternate filename', async () => {
const cwd = setupUnitFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created other.env file');
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
// check for development env value
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
expect(devFileHasDevEnv).toBeTruthy();
});
it('should use given environment', async () => {
const cwd = setupUnitFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv(
'env',
'pull',
'--environment',
'production',
'--cwd',
cwd
);
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
`Downloading \`production\` Environment Variables for Project vercel-env-pull`
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(exitCodePromise).resolves.toEqual(0);
const rawProdEnv = await fs.readFile(path.join(cwd, '.env.local'));
// check for development env value
const envFileHasEnv = rawProdEnv
.toString()
.includes('REDIS_CONNECTION_STRING');
expect(envFileHasEnv).toBeTruthy();
});
it('should throw an error when it does not recognize given environment', async () => {
const cwd = setupUnitFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv(
'env',
'pull',
'.env.production',
'--environment',
'something-invalid',
'--cwd',
cwd
);
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
`Invalid environment \`something-invalid\`. Valid options: <production | preview | development>`
);
await expect(exitCodePromise).resolves.toEqual(1);
});
it('should expose production system env variables', async () => {
const cwd = setupUnitFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
autoExposeSystemEnvs: true,
});
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created other.env file');
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
const productionFileHasVercelEnv = rawDevEnv
.toString()
.includes('VERCEL_ENV="development"');
expect(productionFileHasVercelEnv).toBeTruthy();
});
it('should show a delta string', async () => {
const cwd = setupUnitFixture('vercel-env-pull-delta');
try {
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'env-pull-delta',
name: 'env-pull-delta',
});
client.setArgv('env', 'add', 'NEW_VAR', '--cwd', cwd);
const addPromise = env(client);
await expect(client.stderr).toOutput('Whats the value of NEW_VAR?');
client.stdin.write('testvalue\n');
await expect(client.stderr).toOutput(
'Add NEW_VAR to which Environments (select multiple)?'
);
client.stdin.write('\x1B[B'); // Down arrow
client.stdin.write('\x1B[B');
client.stdin.write(' ');
client.stdin.write('\r');
await expect(addPromise).resolves.toEqual(0);
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const pullPromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project env-pull-delta'
);
await expect(client.stderr).toOutput(
'+ SPECIAL_FLAG (Updated)\n+ NEW_VAR\n- TEST\n'
);
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0);
} finally {
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
await env(client);
}
});
it('should not show a delta string when it fails to read a file', async () => {
const cwd = setupUnitFixture('vercel-env-pull-delta-corrupt');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'env-pull-delta-corrupt',
name: 'env-pull-delta-corrupt',
});
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const pullPromise = env(client);
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0);
});
it('should show that no changes were found', async () => {
const cwd = setupUnitFixture('vercel-env-pull-delta-no-changes');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'env-pull-delta-no-changes',
name: 'env-pull-delta-no-changes',
});
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const pullPromise = env(client);
await expect(client.stderr).toOutput('> No changes found.');
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0);
});
it('should correctly render delta string when env variable has quotes', async () => {
const cwd = setupUnitFixture('vercel-env-pull-delta-quotes');
try {
useUser();
useTeams('team_dummy');
useProject(
{
...defaultProject,
id: 'env-pull-delta-quotes',
name: 'env-pull-delta-quotes',
},
[
...envs,
{
type: 'encrypted',
id: '781dt89g8r2h789g',
key: 'NEW_VAR',
value: '"testvalue"',
target: ['development'],
configurationId: null,
updatedAt: 1557241361455,
createdAt: 1557241361455,
},
]
);
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const pullPromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project env-pull-delta'
);
await expect(client.stderr).toOutput('No changes found.\n');
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(pullPromise).resolves.toEqual(0);
} finally {
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
await env(client);
}
});
it('should correctly render delta string when local env variable has quotes', async () => {
const cwd = setupUnitFixture('vercel-env-pull-delta-quotes');
try {
useUser();
useTeams('team_dummy');
useProject(
{
...defaultProject,
id: 'env-pull-delta-quotes',
name: 'env-pull-delta-quotes',
},
[
...envs,
{
type: 'encrypted',
id: '781dt89g8r2h789g',
key: 'NEW_VAR',
value: 'testvalue',
target: ['development'],
configurationId: null,
updatedAt: 1557241361455,
createdAt: 1557241361455,
},
]
);
client.setArgv('env', 'pull', '.env.testquotes', '--yes', '--cwd', cwd);
const pullPromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project env-pull-delta'
);
await expect(client.stderr).toOutput('No changes found.\n');
await expect(client.stderr).toOutput('Updated .env.testquotes file');
await expect(pullPromise).resolves.toEqual(0);
} finally {
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
await env(client);
}
});
});
});