forked from aws/jsii
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds @jsii/integ-test private module for defining new integration tests. Adds a new integration test that downloads the latest CDK release source code and builds it with the local version of jsii and jsii-pacmak. This unit test requires a github access token defined in the environment to get the latest release version and download the asset. Fixes: aws#1209
- Loading branch information
1 parent
2652043
commit 5296985
Showing
8 changed files
with
274 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
GITHUB_TOKEN=personal_access_token |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.env | ||
*.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# JSII Integration Tests | ||
|
||
A suite of integration tests for JSII and related modules. | ||
|
||
## Running | ||
|
||
Running the integration tests locally requires a github access token. Copy the | ||
.env.example file and replace the dummy value with a personal access token. | ||
|
||
then run `yarn run test:integ` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "integ-test", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"private": true, | ||
"scripts": { | ||
"build": "tsc", | ||
"test:integ": "jest" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"@octokit/rest": "^16.36.0", | ||
"dotenv": "^8.2.0", | ||
"jest": "^25.1.0", | ||
"jsii": "^0.21.2", | ||
"jsii-pacmak": "^0.21.2", | ||
"typescript": "^3.7.5" | ||
}, | ||
"jest": { | ||
"errorOnDeprecated": true, | ||
"testEnvironment": "node", | ||
"testMatch": [ | ||
"**/?(*.)+(spec|test).js" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// import { IncomingMessage } from 'http'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import * as Octokit from '@octokit/rest'; | ||
import { downloadReleaseAsset, minutes, ProcessManager, rmdirRecursive } from '../utils'; | ||
import * as dotenv from 'dotenv'; | ||
|
||
const { mkdtemp } = fs.promises; | ||
|
||
dotenv.config(); | ||
const JSII_DIR = path.resolve(require.resolve('jsii'), '..', '..'); | ||
const JSII_PACMAK_DIR = path.resolve(require.resolve('jsii-pacmak'), '..', '..'); | ||
|
||
const octokit = new Octokit({ | ||
auth: process.env.GITHUB_TOKEN | ||
}); | ||
|
||
describe('Build CDK', () => { | ||
let buildDir: string; | ||
let processes: ProcessManager; | ||
|
||
beforeAll(async () => { | ||
processes = new ProcessManager(); | ||
buildDir = await mkdtemp(path.join(__dirname, 'build')); | ||
}); | ||
|
||
afterAll(async () => { | ||
await processes.killAll(); | ||
await rmdirRecursive(buildDir); | ||
}); | ||
|
||
test('can build latest cdk release', async (done) => { | ||
// download latest release info | ||
console.time('cdkbuild'); | ||
const release = await octokit.repos.getLatestRelease({ | ||
owner: 'aws', | ||
repo: 'aws-cdk' | ||
}); | ||
|
||
// save code to tmp dir | ||
const fileName = 'cdk.tar.gz'; | ||
const tarFile = path.join(buildDir, fileName); | ||
const code = await downloadReleaseAsset(`https://api.github.com/repos/aws/aws-cdk/tarball/${release.data.tag_name}`); | ||
const codeStream = fs.createWriteStream(tarFile); | ||
|
||
// save to file and wait to finish | ||
code.pipe(codeStream); | ||
await new Promise(resolve => codeStream.on('close', () => { | ||
resolve(); | ||
})); | ||
|
||
// unzip tar archive | ||
await processes.spawn('tar', ['-xzvf', fileName], { | ||
cwd: buildDir | ||
}); | ||
|
||
// root dir of extracted src | ||
// `${buildDir}/${owner}-${repo}-${first 7 chars of commit hash} | ||
const srcDir = path.join(buildDir, `aws-aws-cdk-${release.data.target_commitish.substring(0, 7)}`); | ||
|
||
// install cdk dependencies | ||
await processes.spawn('yarn', ['install'], { | ||
cwd: srcDir | ||
}); | ||
|
||
// link local jsii/jsii-pacmak builds | ||
await processes.spawn('rm', ['-rf', './node_modules/jsii'], { cwd: srcDir }); | ||
await processes.spawn('rm', ['-rf', './node_modules/jsii-pacmak'], { cwd: srcDir }); | ||
await processes.spawn('ln', ['-s', JSII_DIR, './node_modules'], { cwd: srcDir }); | ||
await processes.spawn('ln', ['-s', JSII_PACMAK_DIR, './node_modules'], { cwd: srcDir }); | ||
|
||
// build cdk | ||
await processes.spawn('./node_modules/.bin/lerna', ['run', 'build', '--stream'], { cwd: srcDir }); | ||
|
||
// package modules | ||
await processes.spawn('yarn', ['run', 'pack'], { cwd: srcDir }); | ||
console.timeEnd('cdkbuild'); | ||
done(); | ||
|
||
}, minutes(60)); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import * as cp from 'child_process'; | ||
import * as https from 'https'; | ||
import * as path from 'path'; | ||
import { PathLike, promises as fs } from 'fs'; | ||
|
||
export const minutes = (num: number) => num * 1000 * 60 | ||
|
||
/** | ||
* rmdirRecursive | ||
* | ||
* recursive directory removal for cleanup after build test. Node10 fs module | ||
* doesn't support the `recursive` option | ||
*/ | ||
export const rmdirRecursive = async (dir: PathLike) => { | ||
const contents = await fs.readdir(dir); | ||
await Promise.all(contents.map(async (file) => { | ||
const currentPath = path.join(dir.toString(), file); | ||
if ((await fs.lstat(currentPath)).isDirectory()) { | ||
await rmdirRecursive(currentPath); | ||
} else { | ||
await fs.unlink(currentPath); | ||
} | ||
})); | ||
|
||
await fs.rmdir(dir); | ||
}; | ||
|
||
/* | ||
* ProcessManager | ||
* | ||
* Used to track and clean up processes if tests fail or timeout | ||
*/ | ||
export class ProcessManager { | ||
processes: { | ||
[pid: string]: { | ||
proc: cp.ChildProcess, | ||
promise: Promise<void> | ||
} | ||
}; | ||
|
||
constructor() { | ||
this.processes = {}; | ||
} | ||
|
||
async killAll() { | ||
const values = Object.values(this.processes); | ||
values.forEach(procObj => procObj.proc.kill()); | ||
await Promise.all(values.map(proc => proc.promise)); | ||
this.processes = {}; | ||
} | ||
|
||
private add(proc: cp.ChildProcess, promise: Promise<void>) { | ||
const { pid } = proc; | ||
this.processes[pid] = { proc, promise }; | ||
} | ||
|
||
private remove(proc: cp.ChildProcess) { | ||
delete this.processes[proc.pid]; | ||
} | ||
|
||
spawn(cmd: string, args: string[], opts: any) { | ||
const proc = cp.spawn(cmd, args, opts); | ||
proc.stdout.pipe(process.stdout); | ||
proc.stderr.pipe(process.stderr); | ||
|
||
const promise: Promise<void> = new Promise((resolve, reject) => { | ||
proc.on('exit', code => { | ||
const message = `child process exited with code: ${code}`; | ||
if (code !== 0) { | ||
process.stderr.write(message); | ||
reject(new Error(message)); | ||
} else { | ||
process.stdout.write(message); | ||
resolve(); | ||
} | ||
|
||
this.remove(proc); | ||
}); | ||
|
||
proc.on('error', error => { | ||
process.stderr.write(`Process ${proc.pid} error: ${error}`); | ||
}); | ||
}); | ||
|
||
this.add(proc, promise); | ||
return promise; | ||
} | ||
} | ||
|
||
/** | ||
* downloadReleaseAsset | ||
* | ||
* Wrap http calls to download release asset in a promise. Github responds with | ||
* a 302 sometimes which is required to be handled. Returns the buffer to be | ||
* streamed to destination fs stream. | ||
*/ | ||
export const downloadReleaseAsset = (url: string): Promise<any> => new Promise((resolve, reject) => { | ||
const config = { | ||
headers: { | ||
'User-Agent': 'aws-cdk', | ||
Authorization: `token ${process.env.GITHUB_TOKEN}`, | ||
Accept: 'application/json' | ||
} | ||
}; | ||
|
||
https.get(url, config, response => { | ||
if (response.statusCode! < 200 && response.statusCode !== 302) { | ||
reject(new Error(`Status Code: ${response.statusCode}`)); | ||
} | ||
if (response.statusCode === 302 && response.headers.location) { | ||
return https.get(response.headers.location, config, response => { | ||
return resolve(response); | ||
}); | ||
} | ||
|
||
return resolve(response); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters