generated from amazon-archives/__template_Apache-2.0
-
Notifications
You must be signed in to change notification settings - Fork 83
Expand file tree
/
Copy pathexec.test.ts
More file actions
295 lines (239 loc) · 9.45 KB
/
exec.test.ts
File metadata and controls
295 lines (239 loc) · 9.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
jest.mock('child_process');
import bockfs from '../../_helpers/bockfs';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import * as cdk from 'aws-cdk-lib';
import * as semver from 'semver';
import * as sinon from 'sinon';
import { ImportMock } from 'ts-mock-imports';
import { execProgram } from '../../../lib/api/cxapp/exec';
import { Configuration } from '../../../lib/cli/user-configuration';
import { testAssembly } from '../../_helpers/assembly';
import { mockSpawn } from '../../util/mock-child_process';
import { MockSdkProvider } from '../../_helpers/mock-sdk';
import { RWLock } from '../../../lib/api/rwlock';
import { rewriteManifestMinimumCliVersion, rewriteManifestVersion } from './assembly-versions';
import { TestIoHost } from '../../_helpers/io-host';
import { ToolkitError } from '../../../../@aws-cdk/tmp-toolkit-helpers/src/api';
let sdkProvider: MockSdkProvider;
let config: Configuration;
const ioHost = new TestIoHost();
const ioHelper = ioHost.asHelper('synth');
beforeEach(() => {
ioHost.notifySpy.mockClear();
ioHost.requestSpy.mockClear();
sdkProvider = new MockSdkProvider();
config = new Configuration();
config.settings.set(['output'], 'cdk.out');
// insert contents in fake filesystem
bockfs({
'/home/project/cloud-executable': 'ARBITRARY',
'/home/project/windows.js': 'ARBITRARY',
'home/project/executable-app.js': 'ARBITRARY',
});
bockfs.workingDirectory('/home/project');
bockfs.executable('/home/project/cloud-executable');
bockfs.executable('/home/project/executable-app.js');
});
afterEach(() => {
sinon.restore();
bockfs.restore();
});
// We need to increase the default 5s jest
// timeout for async tests because the 'execProgram' invocation
// might take a while :\
const TEN_SECOND_TIMEOUT = 10000;
function createApp(): cdk.App {
const app = new cdk.App({ outdir: 'cdk.out' });
const stack = new cdk.Stack(app, 'Stack');
new cdk.CfnResource(stack, 'Role', {
type: 'AWS::IAM::Role',
properties: {
RoleName: 'Role',
},
});
return app;
}
test('cli throws when manifest version > schema version', async () => {
const app = createApp();
const currentSchemaVersion = cxschema.Manifest.version();
const mockManifestVersion = semver.inc(currentSchemaVersion, 'major');
const mockMinimumCliVersion = mockManifestVersion;
// this mock will cause the framework to use a greater schema version than the real one,
// and should cause the CLI to fail.
const mockVersionNumber = ImportMock.mockFunction(cxschema.Manifest, 'version', mockManifestVersion);
try {
app.synth();
} finally {
mockVersionNumber.restore();
}
rewriteManifestVersion('cdk.out', `${mockManifestVersion}`);
rewriteManifestMinimumCliVersion('cdk.out', `${mockMinimumCliVersion}`);
const expectedError = 'This CDK CLI is not compatible with the CDK library used by your application. Please upgrade the CLI to the latest version.'
+ `\n(Cloud assembly schema version mismatch: Maximum schema version supported is ${semver.major(currentSchemaVersion)}.x.x, but found ${mockManifestVersion}. You need at least CLI version ${mockMinimumCliVersion} to read this manifest.)`;
config.settings.set(['app'], 'cdk.out');
await expect(execProgram(sdkProvider, ioHelper, config)).rejects.toEqual(new ToolkitError(expectedError));
}, TEN_SECOND_TIMEOUT);
test('cli does not throw when manifest version = schema version', async () => {
const app = createApp();
app.synth();
rewriteManifestVersionToOurs();
config.settings.set(['app'], 'cdk.out');
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
}, TEN_SECOND_TIMEOUT);
// Why do we have to do something here at all? Because `aws-cdk-lib` has its own version of `cloud-assembly-schema`,
// which will have real version `38.0.0`, different from the `0.0.0` version of `cloud-assembly-schema` that the CLI
// uses.
//
// Since our Cloud Assembly Schema version will be `0.0.0` and there is no such thing as `-1.0.0`, this test doesn't
// make any sense anymore.
// eslint-disable-next-line jest/no-disabled-tests
test.skip('cli does not throw when manifest version < schema version', async () => {
const app = createApp();
const currentSchemaVersion = cxschema.Manifest.version();
app.synth();
rewriteManifestVersionToOurs();
config.settings.set(['app'], 'cdk.out');
// this mock will cause the cli to think its exepcted schema version is
// greater that the version created in the manifest, which is what we are testing for.
const mockVersionNumber = ImportMock.mockFunction(cxschema.Manifest, 'version', semver.inc(currentSchemaVersion, 'major'));
try {
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
} finally {
mockVersionNumber.restore();
}
}, TEN_SECOND_TIMEOUT);
test('validates --app key is present', async () => {
// GIVEN no config key for `app`
await expect(execProgram(sdkProvider, ioHelper, config)).rejects.toThrow(
'--app is required either in command-line, in cdk.json or in ~/.cdk.json',
);
});
test('bypasses synth when app points to a cloud assembly', async () => {
// GIVEN
config.settings.set(['app'], 'cdk.out');
writeOutputAssembly();
rewriteManifestVersionToOurs();
// WHEN
const { assembly: cloudAssembly, lock } = await execProgram(sdkProvider, ioHelper, config);
expect(cloudAssembly.artifacts).toEqual([]);
expect(cloudAssembly.directory).toEqual('cdk.out');
await lock.release();
});
test('the application set in --app is executed', async () => {
// GIVEN
config.settings.set(['app'], 'cloud-executable');
mockSpawn({
commandLine: 'cloud-executable',
sideEffect: () => writeOutputAssembly(),
});
// WHEN
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
});
test('the application set in --app is executed as-is if it contains a filename that does not exist', async () => {
// GIVEN
config.settings.set(['app'], 'does-not-exist');
mockSpawn({
commandLine: 'does-not-exist',
sideEffect: () => writeOutputAssembly(),
});
// WHEN
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
});
test('the application set in --app is executed with arguments', async () => {
// GIVEN
config.settings.set(['app'], 'cloud-executable an-arg');
mockSpawn({
commandLine: 'cloud-executable an-arg',
sideEffect: () => writeOutputAssembly(),
});
// WHEN
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
});
test('application set in --app as `*.js` always uses handler on windows', async () => {
// GIVEN
sinon.stub(process, 'platform').value('win32');
config.settings.set(['app'], 'windows.js');
mockSpawn({
commandLine: process.execPath + ' windows.js',
sideEffect: () => writeOutputAssembly(),
});
// WHEN
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
});
test('application set in --app is `*.js` and executable', async () => {
// GIVEN
config.settings.set(['app'], 'executable-app.js');
mockSpawn({
commandLine: 'executable-app.js',
sideEffect: () => writeOutputAssembly(),
});
// WHEN
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
});
test('cli throws when the `build` script fails', async () => {
// GIVEN
config.settings.set(['build'], 'fake-command');
mockSpawn({
commandLine: 'fake-command',
exitCode: 127,
});
// WHEN
await expect(execProgram(sdkProvider, ioHelper, config)).rejects.toEqual(new Error('Subprocess exited with error 127'));
}, TEN_SECOND_TIMEOUT);
test('cli does not throw when the `build` script succeeds', async () => {
// GIVEN
config.settings.set(['build'], 'real command');
config.settings.set(['app'], 'executable-app.js');
mockSpawn({
commandLine: 'real command', // `build` key is not split on whitespace
exitCode: 0,
},
{
commandLine: 'executable-app.js',
sideEffect: () => writeOutputAssembly(),
});
// WHEN
const { lock } = await execProgram(sdkProvider, ioHelper, config);
await lock.release();
}, TEN_SECOND_TIMEOUT);
test('cli releases the outdir lock when execProgram throws', async () => {
// GIVEN
config.settings.set(['app'], 'cloud-executable');
mockSpawn({
commandLine: 'fake-command',
exitCode: 127,
});
// WHEN
await expect(execProgram(sdkProvider, ioHelper, config)).rejects.toThrow();
const output = config.settings.get(['output']);
expect(output).toBeDefined();
// check that the lock is released
const lock = await new RWLock(output).acquireWrite();
await lock.release();
});
function writeOutputAssembly() {
const asm = testAssembly({
stacks: [],
});
bockfs.write('/home/project/cdk.out/manifest.json', JSON.stringify(asm.manifest));
rewriteManifestVersionToOurs(bockfs.path('/home/project/cdk.out'));
}
/**
* Rewrite the manifest schema version in the given directory to match the version number we expect (probably `0.0.0`).
*
* Why do we have to do this? Because `aws-cdk-lib` has its own version of `cloud-assembly-schema`,
* which will have real version `38.0.0`, different from the `0.0.0` version of `cloud-assembly-schema` that the CLI
* uses.
*
* If we don't do this, every time we load a Cloud Assembly the code will say "Maximum schema version supported is 0.x.x, but found 30.0.0".0
*/
function rewriteManifestVersionToOurs(dir: string = 'cdk.out') {
rewriteManifestVersion(dir, cxschema.Manifest.version());
}