Skip to content

Commit e3b73c4

Browse files
authored
test(lambda): fix integration test failure in appBuilder (aws#6733)
## Problem There is a side effect from `sandbox.spy(AppBuilderRootNode.instance)` between two integration tests for AppBuilder causing test failure due to **_`TypeError: Attempted to wrap onDidChangeChildren which is already wrapped`_**` ``` 1) "before each" hook for "creates an AppBuilderRootNode with correct label": TypeError: Attempted to wrap onDidChangeChildren which is already wrapped at checkWrappedMethod (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/wrap-method.js:64:21) at wrapMethod (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/wrap-method.js:135:13) at spy (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/spy.js:180:16) at /Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk-object.js:33:17 at /Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk.js:27:22 at Array.forEach (<anonymous>) at walkInternal (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk.js:19:5) at walk (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk.js:48:12) at walkObject (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk-object.js:18:5) at Function.spy (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/spy.js:170:16) at Sandbox.spy (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/sandbox.js:383:35) at Context.<anonymous> (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/packages/core/src/testInteg/appBuilder/serverlessLand/main.test.ts:34:28) -------------- Error: Stack Trace for original at extendObjectWithWrappedMethods (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/wrap-method.js:169:34) at wrapMethod (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/wrap-method.js:157:5) at spy (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/spy.js:180:16) at /Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk-object.js:33:17 at /Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk.js:27:22 at Array.forEach (<anonymous>) at walkInternal (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk.js:19:5) at walk (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk.js:48:12) at walkObject (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/util/core/walk-object.js:18:5) at Function.spy (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/spy.js:170:16) at Sandbox.spy (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/node_modules/sinon/lib/sinon/sandbox.js:383:35) at Context.<anonymous> (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/packages/core/src/testInteg/appBuilder/sidebar/appBuilderNode.test.ts:35:28) at Context.fn (/Volumes/workplace/Lambda Tooling/aws-toolkit-vscode/packages/core/src/test/setupUtil.ts:34:24) at processImmediate (node:internal/timers:483:21) at process.topLevelDomainCallback (node:domain:161:15) at process.callbackTrampoline (node:internal/async_hooks:128:24) ``` ## Solution Add additional layer of `describe()`. Test result: ``` Walkthrough pattern URL exists ✔ Walkthrough pattern URL exists for APIdotnet (623ms) ✔ Walkthrough pattern URL exists for APInode (510ms) ✔ Walkthrough pattern URL exists for APIpython (473ms) ✔ Walkthrough pattern URL exists for APIjava (559ms) ✔ Walkthrough pattern URL exists for S3dotnet (584ms) ✔ Walkthrough pattern URL exists for S3node (525ms) ✔ Walkthrough pattern URL exists for S3python (566ms) ✔ Walkthrough pattern URL exists for S3java (560ms) Application Builder root node ✔ creates an AppBuilderRootNode with correct label ✔ generates correct number of children nodes: walkthrough node + project nodes application nodes in workspace (Test in order) ✔ 1: contains application node for appbuilder-test-app ✔ 2: contains correct application node properties ✔ 3: contains correct resource node properties (4266ms) ✔ 4: has registered refresh command successfully ✔ 5: triggers auto refresh when there a file getting updated (509ms) Happy Path ✔ creates project from Serverless Land integration (1083ms) 16 passing (10s) globalSetup: after() deleteTestTempDirs: deleted 2 test temp dirs ``` --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 975f0d0 commit e3b73c4

File tree

1 file changed

+101
-95
lines changed
  • packages/core/src/testInteg/appBuilder/serverlessLand

1 file changed

+101
-95
lines changed

packages/core/src/testInteg/appBuilder/serverlessLand/main.test.ts

Lines changed: 101 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -25,114 +25,120 @@ describe('Serverless Land Integration', async () => {
2525
const parseMetadata = JSON.parse(metadataContent) as ProjectMetadata
2626
const workspaceFolder = vscode.workspace.workspaceFolders![0]
2727
const projectFolder = 'my-project-from-Serverless-Land'
28-
let rootNode: sinon.SinonSpiedInstance<AppBuilderRootNode>
29-
let sandbox: sinon.SinonSandbox
3028

31-
beforeEach(async () => {
32-
sandbox = sinon.createSandbox()
33-
await fs.delete(path.join(workspaceFolder.uri.fsPath, projectFolder), { recursive: true })
34-
rootNode = sandbox.spy(AppBuilderRootNode.instance)
35-
})
29+
// Additional layer of describe() needed here to prevent side effect from
30+
// `sandbox.spy(AppBuilderRootNode.instance)`
31+
describe('Happy Path', async () => {
32+
let rootNode: sinon.SinonSpiedInstance<AppBuilderRootNode>
33+
let sandbox: sinon.SinonSandbox
3634

37-
afterEach(async () => {
38-
await fs.delete(path.join(workspaceFolder.uri.fsPath, projectFolder), { recursive: true })
39-
sandbox.restore()
40-
})
35+
beforeEach(async () => {
36+
sandbox = sinon.createSandbox()
37+
rootNode = sandbox.spy(AppBuilderRootNode.instance)
38+
await fs.delete(path.join(workspaceFolder.uri.fsPath, projectFolder), { recursive: true })
39+
})
40+
41+
afterEach(async () => {
42+
await fs.delete(path.join(workspaceFolder.uri.fsPath, projectFolder), { recursive: true })
43+
sandbox.restore()
44+
})
4145

42-
it('creates project from Serverless Land integration', async () => {
43-
/**
44-
* Selection:
45-
* - pattern : [Select] 2 apigw-rest-api-lambda-sam
46-
* - runtime : [Select] 3 dotnet
47-
* - iac : [Select] 1 sam
48-
* - location : [Input] From TestFolder.uri
49-
* - name : [Input] "my-project-from-Serverless-Land"
50-
*/
46+
it('creates project from Serverless Land integration', async () => {
47+
/**
48+
* Selection:
49+
* - pattern : [Select] 2 apigw-rest-api-lambda-sam
50+
* - runtime : [Select] 3 dotnet
51+
* - iac : [Select] 1 sam
52+
* - location : [Input] From TestFolder.uri
53+
* - name : [Input] "my-project-from-Serverless-Land"
54+
*/
5155

52-
const testWindow = getTestWindow()
53-
const prompterTester = PrompterTester.init({ testWindow })
54-
.handleQuickPick('Select a Pattern for your application', async (quickPick) => {
55-
await quickPick.untilReady()
56-
const options = quickPick.items
57-
Object.entries(parseMetadata.patterns).map(([key, pattern]) => {
58-
options.find((option) => option.label === key && option.detail === pattern.description)
56+
const testWindow = getTestWindow()
57+
const prompterTester = PrompterTester.init({ testWindow })
58+
.handleQuickPick('Select a Pattern for your application', async (quickPick) => {
59+
await quickPick.untilReady()
60+
const options = quickPick.items
61+
Object.entries(parseMetadata.patterns).map(([key, pattern]) => {
62+
options.find((option) => option.label === key && option.detail === pattern.description)
63+
})
64+
quickPick.acceptItem(quickPick.items[1])
5965
})
60-
quickPick.acceptItem(quickPick.items[1])
61-
})
62-
.handleQuickPick('Select Runtime', async (quickPick) => {
63-
await quickPick.untilReady()
64-
const options = quickPick.items
65-
assert.strictEqual(options[0].label, 'python')
66-
assert.strictEqual(options[1].label, 'javascript')
67-
assert.strictEqual(options[2].label, 'java')
68-
assert.strictEqual(options[3].label, 'dotnet')
69-
quickPick.acceptItem(options[3])
70-
})
71-
.handleQuickPick('Select IaC', async (quickPick) => {
72-
await quickPick.untilReady()
73-
const options = quickPick.items
74-
assert.strictEqual(options[0].label, 'sam')
75-
quickPick.acceptItem(options[0])
76-
})
77-
.handleQuickPick('Select Project Location', async (quickPick) => {
78-
await quickPick.untilReady()
79-
const options = quickPick.items
80-
assert.strictEqual(options[0].label, '$(folder) workspaceFolder')
81-
assert.strictEqual(options[1].label, '$(folder-opened) Select a folder...')
82-
quickPick.acceptItem(options[0])
83-
})
84-
.handleInputBox('Enter Project Name', (inputBox) => {
85-
inputBox.acceptValue('my-project-from-Serverless-Land')
66+
.handleQuickPick('Select Runtime', async (quickPick) => {
67+
await quickPick.untilReady()
68+
const options = quickPick.items
69+
assert.strictEqual(options[0].label, 'python')
70+
assert.strictEqual(options[1].label, 'javascript')
71+
assert.strictEqual(options[2].label, 'java')
72+
assert.strictEqual(options[3].label, 'dotnet')
73+
quickPick.acceptItem(options[3])
74+
})
75+
.handleQuickPick('Select IaC', async (quickPick) => {
76+
await quickPick.untilReady()
77+
const options = quickPick.items
78+
assert.strictEqual(options[0].label, 'sam')
79+
quickPick.acceptItem(options[0])
80+
})
81+
.handleQuickPick('Select Project Location', async (quickPick) => {
82+
await quickPick.untilReady()
83+
const options = quickPick.items
84+
assert.strictEqual(options[0].label, '$(folder) workspaceFolder')
85+
assert.strictEqual(options[1].label, '$(folder-opened) Select a folder...')
86+
quickPick.acceptItem(options[0])
87+
})
88+
.handleInputBox('Enter Project Name', (inputBox) => {
89+
inputBox.acceptValue('my-project-from-Serverless-Land')
90+
})
91+
.build()
92+
93+
// Validate that the README.md is shown.
94+
testWindow.onDidChangeActiveTextEditor((editors) => {
95+
assert(editors)
96+
const readMe = path.join(workspaceFolder.uri.fsPath, projectFolder, 'README.md')
97+
assert.strictEqual(editors?.document.fileName, readMe)
8698
})
87-
.build()
8899

89-
// Validate that the README.md is shown.
90-
testWindow.onDidChangeActiveTextEditor((editors) => {
91-
assert(editors)
92-
const readMe = path.join(workspaceFolder.uri.fsPath, projectFolder, 'README.md')
93-
assert.strictEqual(editors?.document.fileName, readMe)
94-
})
100+
await vscode.commands.executeCommand('aws.toolkit.lambda.createServerlessLandProject')
95101

96-
await vscode.commands.executeCommand('aws.toolkit.lambda.createServerlessLandProject')
102+
// projectNodes set from previous step
97103

98-
// projectNodes set from previous step
104+
const projectNode = await rootNode
105+
.getChildren()
106+
.then(
107+
(children) =>
108+
children.find(
109+
(node) =>
110+
node instanceof AppNode &&
111+
node.label === 'workspaceFolder/my-project-from-Serverless-Land'
112+
) as AppNode | undefined
113+
)
99114

100-
const projectNode = await rootNode
101-
.getChildren()
102-
.then(
103-
(children) =>
104-
children.find(
105-
(node) =>
106-
node instanceof AppNode && node.label === 'workspaceFolder/my-project-from-Serverless-Land'
107-
) as AppNode | undefined
108-
)
115+
assert.ok(projectNode, 'Expect Serverless Land project node in Application Builder')
109116

110-
assert.ok(projectNode, 'Expect Serverless Land project node in Application Builder')
117+
// Check App Builder resources
118+
const resourceNodes = await projectNode.getChildren()
119+
assert.strictEqual(resourceNodes.length, 1)
120+
assert.ok(resourceNodes[0] instanceof ResourceNode)
111121

112-
// Check App Builder resources
113-
const resourceNodes = await projectNode.getChildren()
114-
assert.strictEqual(resourceNodes.length, 1)
115-
assert.ok(resourceNodes[0] instanceof ResourceNode)
122+
// Validate Lambda resource configuration
123+
const lambdaResource = resourceNodes[0] as ResourceNode
124+
assert.strictEqual(lambdaResource.resource.resource.Type, 'AWS::Serverless::Function')
125+
assert.strictEqual(lambdaResource.resource.resource.Runtime, 'dotnet8')
126+
assert.strictEqual(lambdaResource.resource.resource.Id, 'HelloWorldFunction')
127+
assert.deepStrictEqual(lambdaResource.resource.resource.Events, [
128+
{
129+
Id: 'HelloWorld',
130+
Type: 'Api',
131+
Path: '/hello',
132+
Method: 'get',
133+
},
134+
])
135+
assert.deepStrictEqual(lambdaResource.resource.resource.Environment, {
136+
Variables: {
137+
PARAM1: 'VALUE',
138+
},
139+
})
116140

117-
// Validate Lambda resource configuration
118-
const lambdaResource = resourceNodes[0] as ResourceNode
119-
assert.strictEqual(lambdaResource.resource.resource.Type, 'AWS::Serverless::Function')
120-
assert.strictEqual(lambdaResource.resource.resource.Runtime, 'dotnet8')
121-
assert.strictEqual(lambdaResource.resource.resource.Id, 'HelloWorldFunction')
122-
assert.deepStrictEqual(lambdaResource.resource.resource.Events, [
123-
{
124-
Id: 'HelloWorld',
125-
Type: 'Api',
126-
Path: '/hello',
127-
Method: 'get',
128-
},
129-
])
130-
assert.deepStrictEqual(lambdaResource.resource.resource.Environment, {
131-
Variables: {
132-
PARAM1: 'VALUE',
133-
},
141+
prompterTester.assertCallAll()
134142
})
135-
136-
prompterTester.assertCallAll()
137143
})
138144
})

0 commit comments

Comments
 (0)