Skip to content

Commit

Permalink
test(lambda): fix integration test failure in appBuilder (#6733)
Browse files Browse the repository at this point in the history
## 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.
  • Loading branch information
vicheey authored Mar 6, 2025
1 parent 975f0d0 commit e3b73c4
Showing 1 changed file with 101 additions and 95 deletions.
196 changes: 101 additions & 95 deletions packages/core/src/testInteg/appBuilder/serverlessLand/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,114 +25,120 @@ describe('Serverless Land Integration', async () => {
const parseMetadata = JSON.parse(metadataContent) as ProjectMetadata
const workspaceFolder = vscode.workspace.workspaceFolders![0]
const projectFolder = 'my-project-from-Serverless-Land'
let rootNode: sinon.SinonSpiedInstance<AppBuilderRootNode>
let sandbox: sinon.SinonSandbox

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

afterEach(async () => {
await fs.delete(path.join(workspaceFolder.uri.fsPath, projectFolder), { recursive: true })
sandbox.restore()
})
beforeEach(async () => {
sandbox = sinon.createSandbox()
rootNode = sandbox.spy(AppBuilderRootNode.instance)
await fs.delete(path.join(workspaceFolder.uri.fsPath, projectFolder), { recursive: true })
})

afterEach(async () => {
await fs.delete(path.join(workspaceFolder.uri.fsPath, projectFolder), { recursive: true })
sandbox.restore()
})

it('creates project from Serverless Land integration', async () => {
/**
* Selection:
* - pattern : [Select] 2 apigw-rest-api-lambda-sam
* - runtime : [Select] 3 dotnet
* - iac : [Select] 1 sam
* - location : [Input] From TestFolder.uri
* - name : [Input] "my-project-from-Serverless-Land"
*/
it('creates project from Serverless Land integration', async () => {
/**
* Selection:
* - pattern : [Select] 2 apigw-rest-api-lambda-sam
* - runtime : [Select] 3 dotnet
* - iac : [Select] 1 sam
* - location : [Input] From TestFolder.uri
* - name : [Input] "my-project-from-Serverless-Land"
*/

const testWindow = getTestWindow()
const prompterTester = PrompterTester.init({ testWindow })
.handleQuickPick('Select a Pattern for your application', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
Object.entries(parseMetadata.patterns).map(([key, pattern]) => {
options.find((option) => option.label === key && option.detail === pattern.description)
const testWindow = getTestWindow()
const prompterTester = PrompterTester.init({ testWindow })
.handleQuickPick('Select a Pattern for your application', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
Object.entries(parseMetadata.patterns).map(([key, pattern]) => {
options.find((option) => option.label === key && option.detail === pattern.description)
})
quickPick.acceptItem(quickPick.items[1])
})
quickPick.acceptItem(quickPick.items[1])
})
.handleQuickPick('Select Runtime', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
assert.strictEqual(options[0].label, 'python')
assert.strictEqual(options[1].label, 'javascript')
assert.strictEqual(options[2].label, 'java')
assert.strictEqual(options[3].label, 'dotnet')
quickPick.acceptItem(options[3])
})
.handleQuickPick('Select IaC', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
assert.strictEqual(options[0].label, 'sam')
quickPick.acceptItem(options[0])
})
.handleQuickPick('Select Project Location', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
assert.strictEqual(options[0].label, '$(folder) workspaceFolder')
assert.strictEqual(options[1].label, '$(folder-opened) Select a folder...')
quickPick.acceptItem(options[0])
})
.handleInputBox('Enter Project Name', (inputBox) => {
inputBox.acceptValue('my-project-from-Serverless-Land')
.handleQuickPick('Select Runtime', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
assert.strictEqual(options[0].label, 'python')
assert.strictEqual(options[1].label, 'javascript')
assert.strictEqual(options[2].label, 'java')
assert.strictEqual(options[3].label, 'dotnet')
quickPick.acceptItem(options[3])
})
.handleQuickPick('Select IaC', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
assert.strictEqual(options[0].label, 'sam')
quickPick.acceptItem(options[0])
})
.handleQuickPick('Select Project Location', async (quickPick) => {
await quickPick.untilReady()
const options = quickPick.items
assert.strictEqual(options[0].label, '$(folder) workspaceFolder')
assert.strictEqual(options[1].label, '$(folder-opened) Select a folder...')
quickPick.acceptItem(options[0])
})
.handleInputBox('Enter Project Name', (inputBox) => {
inputBox.acceptValue('my-project-from-Serverless-Land')
})
.build()

// Validate that the README.md is shown.
testWindow.onDidChangeActiveTextEditor((editors) => {
assert(editors)
const readMe = path.join(workspaceFolder.uri.fsPath, projectFolder, 'README.md')
assert.strictEqual(editors?.document.fileName, readMe)
})
.build()

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

await vscode.commands.executeCommand('aws.toolkit.lambda.createServerlessLandProject')
// projectNodes set from previous step

// projectNodes set from previous step
const projectNode = await rootNode
.getChildren()
.then(
(children) =>
children.find(
(node) =>
node instanceof AppNode &&
node.label === 'workspaceFolder/my-project-from-Serverless-Land'
) as AppNode | undefined
)

const projectNode = await rootNode
.getChildren()
.then(
(children) =>
children.find(
(node) =>
node instanceof AppNode && node.label === 'workspaceFolder/my-project-from-Serverless-Land'
) as AppNode | undefined
)
assert.ok(projectNode, 'Expect Serverless Land project node in Application Builder')

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

// Check App Builder resources
const resourceNodes = await projectNode.getChildren()
assert.strictEqual(resourceNodes.length, 1)
assert.ok(resourceNodes[0] instanceof ResourceNode)
// Validate Lambda resource configuration
const lambdaResource = resourceNodes[0] as ResourceNode
assert.strictEqual(lambdaResource.resource.resource.Type, 'AWS::Serverless::Function')
assert.strictEqual(lambdaResource.resource.resource.Runtime, 'dotnet8')
assert.strictEqual(lambdaResource.resource.resource.Id, 'HelloWorldFunction')
assert.deepStrictEqual(lambdaResource.resource.resource.Events, [
{
Id: 'HelloWorld',
Type: 'Api',
Path: '/hello',
Method: 'get',
},
])
assert.deepStrictEqual(lambdaResource.resource.resource.Environment, {
Variables: {
PARAM1: 'VALUE',
},
})

// Validate Lambda resource configuration
const lambdaResource = resourceNodes[0] as ResourceNode
assert.strictEqual(lambdaResource.resource.resource.Type, 'AWS::Serverless::Function')
assert.strictEqual(lambdaResource.resource.resource.Runtime, 'dotnet8')
assert.strictEqual(lambdaResource.resource.resource.Id, 'HelloWorldFunction')
assert.deepStrictEqual(lambdaResource.resource.resource.Events, [
{
Id: 'HelloWorld',
Type: 'Api',
Path: '/hello',
Method: 'get',
},
])
assert.deepStrictEqual(lambdaResource.resource.resource.Environment, {
Variables: {
PARAM1: 'VALUE',
},
prompterTester.assertCallAll()
})

prompterTester.assertCallAll()
})
})

0 comments on commit e3b73c4

Please sign in to comment.