Skip to content

Commit

Permalink
chore(s3-assets): use jest for tests (#8411)
Browse files Browse the repository at this point in the history
No new tests or expectations added.


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jogold authored Jun 7, 2020
1 parent f44ae60 commit aa920af
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 365 deletions.
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-s3-assets/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ nyc.config.js
.LAST_PACKAGE
*.snk
!.eslintrc.js
!jest.config.js
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-s3-assets/.npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ dist

tsconfig.json
.eslintrc.js
jest.config.js
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-s3-assets/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const baseConfig = require('../../../tools/cdk-build-tools/config/jest.config');
module.exports = baseConfig;
14 changes: 4 additions & 10 deletions packages/@aws-cdk/aws-s3-assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
"build+test": "npm run build && npm test",
"compat": "cdk-compat"
},
"cdk-build": {
"jest": true
},
"keywords": [
"aws",
"cdk",
Expand All @@ -60,16 +63,10 @@
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/assert": "0.0.0",
"@types/nodeunit": "^0.0.31",
"@types/sinon": "^9.0.4",
"aws-cdk": "0.0.0",
"cdk-build-tools": "0.0.0",
"cdk-integ-tools": "0.0.0",
"nodeunit": "^0.11.3",
"pkglint": "0.0.0",
"sinon": "^9.0.2",
"@aws-cdk/cloud-assembly-schema": "0.0.0",
"ts-mock-imports": "^1.3.0"
"@aws-cdk/cloud-assembly-schema": "0.0.0"
},
"dependencies": {
"@aws-cdk/assets": "0.0.0",
Expand All @@ -93,9 +90,6 @@
},
"stability": "experimental",
"maturity": "experimental",
"nyc": {
"statements": 75
},
"awslint": {
"exclude": [
"docs-public-apis:@aws-cdk/aws-s3-assets.AssetOptions",
Expand Down
328 changes: 328 additions & 0 deletions packages/@aws-cdk/aws-s3-assets/test/asset.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
import { ResourcePart, SynthUtils } from '@aws-cdk/assert';
import '@aws-cdk/assert/jest';
import * as iam from '@aws-cdk/aws-iam';
import * as cxschema from '@aws-cdk/cloud-assembly-schema';
import * as cdk from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { Asset } from '../lib/asset';

const SAMPLE_ASSET_DIR = path.join(__dirname, 'sample-asset-directory');

test('simple use case', () => {
const app = new cdk.App({
context: {
[cxapi.DISABLE_ASSET_STAGING_CONTEXT]: 'true',
},
});
const stack = new cdk.Stack(app, 'MyStack');
new Asset(stack, 'MyAsset', {
path: SAMPLE_ASSET_DIR,
});

// verify that metadata contains an "aws:cdk:asset" entry with
// the correct information
const entry = stack.node.metadata.find(m => m.type === 'aws:cdk:asset');
expect(entry).toBeTruthy();

// verify that now the template contains parameters for this asset
const session = app.synth();

expect(stack.resolve(entry!.data)).toEqual({
path: SAMPLE_ASSET_DIR,
id: '6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2',
packaging: 'zip',
sourceHash: '6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2',
s3BucketParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B',
s3KeyParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9',
artifactHashParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2ArtifactHash220DE9BD',
});

const template = JSON.parse(fs.readFileSync(path.join(session.directory, 'MyStack.template.json'), 'utf-8'));

expect(template.Parameters.AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B.Type).toBe('String');
expect(template.Parameters.AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9.Type).toBe('String');
});

test('verify that the app resolves tokens in metadata', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app, 'my-stack');
const dirPath = path.resolve(__dirname, 'sample-asset-directory');

new Asset(stack, 'MyAsset', {
path: dirPath,
});

const synth = app.synth().getStackByName(stack.stackName);
const meta = synth.manifest.metadata || {};
expect(meta['/my-stack']).toBeTruthy();
expect(meta['/my-stack'][0]).toBeTruthy();
expect(meta['/my-stack'][0].data).toEqual({
path: 'asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2',
id: '6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2',
packaging: 'zip',
sourceHash: '6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2',
s3BucketParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B',
s3KeyParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3VersionKey1F7D75F9',
artifactHashParameter: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2ArtifactHash220DE9BD',
});
});

test('"file" assets', () => {
const stack = new cdk.Stack();
const filePath = path.join(__dirname, 'file-asset.txt');
new Asset(stack, 'MyAsset', { path: filePath });
const entry = stack.node.metadata.find(m => m.type === 'aws:cdk:asset');
expect(entry).toBeTruthy();

// synthesize first so "prepare" is called
const template = SynthUtils.synthesize(stack).template;

expect(stack.resolve(entry!.data)).toEqual({
path: 'asset.78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197.txt',
packaging: 'file',
id: '78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197',
sourceHash: '78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197',
s3BucketParameter: 'AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A',
s3KeyParameter: 'AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35',
artifactHashParameter: 'AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197ArtifactHash22BFFA67',
});

// verify that now the template contains parameters for this asset
expect(template.Parameters.AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3Bucket2C60F94A.Type).toBe('String');
expect(template.Parameters.AssetParameters78add9eaf468dfa2191da44a7da92a21baba4c686cf6053d772556768ef21197S3VersionKey9482DC35.Type).toBe('String');
});

test('"readers" or "grantRead" can be used to grant read permissions on the asset to a principal', () => {
const stack = new cdk.Stack();
const user = new iam.User(stack, 'MyUser');
const group = new iam.Group(stack, 'MyGroup');

const asset = new Asset(stack, 'MyAsset', {
path: path.join(__dirname, 'sample-asset-directory'),
readers: [ user ],
});

asset.grantRead(group);

expect(stack).toHaveResource('AWS::IAM::Policy', {
PolicyDocument: {
Version: '2012-10-17',
Statement: [
{
Action: ['s3:GetObject*', 's3:GetBucket*', 's3:List*'],
Effect: 'Allow',
Resource: [
{ 'Fn::Join': ['', ['arn:', {Ref: 'AWS::Partition'}, ':s3:::', {Ref: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B'} ] ] },
{ 'Fn::Join': ['', [ 'arn:', {Ref: 'AWS::Partition'}, ':s3:::', {Ref: 'AssetParameters6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2S3Bucket50B5A10B'}, '/*' ] ] },
],
},
],
},
});
});

test('fails if directory not found', () => {
const stack = new cdk.Stack();
expect(() => new Asset(stack, 'MyDirectory', {
path: '/path/not/found/' + Math.random() * 999999,
})).toThrow();
});

test('multiple assets under the same parent', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
expect(() => new Asset(stack, 'MyDirectory1', { path: path.join(__dirname, 'sample-asset-directory') })).not.toThrow();
expect(() => new Asset(stack, 'MyDirectory2', { path: path.join(__dirname, 'sample-asset-directory') })).not.toThrow();
});

test('isZipArchive indicates if the asset represents a .zip file (either explicitly or via ZipDirectory packaging)', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
const nonZipAsset = new Asset(stack, 'NonZipAsset', {
path: path.join(__dirname, 'sample-asset-directory', 'sample-asset-file.txt'),
});

const zipDirectoryAsset = new Asset(stack, 'ZipDirectoryAsset', {
path: path.join(__dirname, 'sample-asset-directory'),
});

const zipFileAsset = new Asset(stack, 'ZipFileAsset', {
path: path.join(__dirname, 'sample-asset-directory', 'sample-zip-asset.zip'),
});

const jarFileAsset = new Asset(stack, 'JarFileAsset', {
path: path.join(__dirname, 'sample-asset-directory', 'sample-jar-asset.jar'),
});

// THEN
expect(nonZipAsset.isZipArchive).toBe(false);
expect(zipDirectoryAsset.isZipArchive).toBe(true);
expect(zipFileAsset.isZipArchive).toBe(true);
expect(jarFileAsset.isZipArchive).toBe(true);
});

test('addResourceMetadata can be used to add CFN metadata to resources', () => {
// GIVEN
const stack = new cdk.Stack();
stack.node.setContext(cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT, true);

const location = path.join(__dirname, 'sample-asset-directory');
const resource = new cdk.CfnResource(stack, 'MyResource', { type: 'My::Resource::Type' });
const asset = new Asset(stack, 'MyAsset', { path: location });

// WHEN
asset.addResourceMetadata(resource, 'PropName');

// THEN
expect(stack).toHaveResource('My::Resource::Type', {
Metadata: {
'aws:asset:path': 'asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2',
'aws:asset:property': 'PropName',
},
}, ResourcePart.CompleteDefinition);
});

test('asset metadata is only emitted if ASSET_RESOURCE_METADATA_ENABLED_CONTEXT is defined', () => {
// GIVEN
const stack = new cdk.Stack();

const resource = new cdk.CfnResource(stack, 'MyResource', { type: 'My::Resource::Type' });
const asset = new Asset(stack, 'MyAsset', { path: SAMPLE_ASSET_DIR });

// WHEN
asset.addResourceMetadata(resource, 'PropName');

// THEN
expect(stack).not.toHaveResource('My::Resource::Type', {
Metadata: {
'aws:asset:path': SAMPLE_ASSET_DIR,
'aws:asset:property': 'PropName',
},
}, ResourcePart.CompleteDefinition);
});

describe('staging', () => {
test('copy file assets under <outdir>/${fingerprint}.ext', () => {
const tempdir = mkdtempSync();
process.chdir(tempdir); // change current directory to somewhere in /tmp

// GIVEN
const app = new cdk.App({ outdir: tempdir });
const stack = new cdk.Stack(app, 'stack');

// WHEN
new Asset(stack, 'ZipFile', {
path: path.join(SAMPLE_ASSET_DIR, 'sample-zip-asset.zip'),
});

new Asset(stack, 'TextFile', {
path: path.join(SAMPLE_ASSET_DIR, 'sample-asset-file.txt'),
});

// THEN
app.synth();
expect(fs.existsSync(tempdir)).toBe(true);
expect(fs.existsSync(path.join(tempdir, 'asset.a7a79cdf84b802ea8b198059ff899cffc095a1b9606e919f98e05bf80779756b.zip'))).toBe(true);
});

test('copy directory under .assets/fingerprint/**', () => {
const tempdir = mkdtempSync();
process.chdir(tempdir); // change current directory to somewhere in /tmp

// GIVEN
const app = new cdk.App({ outdir: tempdir });
const stack = new cdk.Stack(app, 'stack');

// WHEN
new Asset(stack, 'ZipDirectory', {
path: SAMPLE_ASSET_DIR,
});

// THEN
app.synth();
expect(fs.existsSync(tempdir)).toBe(true);
const hash = 'asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2';
expect(fs.existsSync(path.join(tempdir, hash, 'sample-asset-file.txt'))).toBe(true);
expect(fs.existsSync(path.join(tempdir, hash, 'sample-jar-asset.jar'))).toBe(true);
expect(() => fs.readdirSync(tempdir)).not.toThrow();
});

test('staging path is relative if the dir is below the working directory', () => {
// GIVEN
const tempdir = mkdtempSync();
process.chdir(tempdir); // change current directory to somewhere in /tmp

const staging = '.my-awesome-staging-directory';
const app = new cdk.App({
outdir: staging,
context: {
[cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT]: 'true',
},
});

const stack = new cdk.Stack(app, 'stack');

const resource = new cdk.CfnResource(stack, 'MyResource', { type: 'My::Resource::Type' });
const asset = new Asset(stack, 'MyAsset', { path: SAMPLE_ASSET_DIR });

// WHEN
asset.addResourceMetadata(resource, 'PropName');

const template = SynthUtils.synthesize(stack).template;
expect(template.Resources.MyResource.Metadata).toEqual({
'aws:asset:path': 'asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2',
'aws:asset:property': 'PropName',
});
});

test('if staging is disabled, asset path is absolute', () => {
// GIVEN
const staging = path.resolve(mkdtempSync());
const app = new cdk.App({
outdir: staging,
context: {
[cxapi.DISABLE_ASSET_STAGING_CONTEXT]: 'true',
[cxapi.ASSET_RESOURCE_METADATA_ENABLED_CONTEXT]: 'true',
},
});

const stack = new cdk.Stack(app, 'stack');

const resource = new cdk.CfnResource(stack, 'MyResource', { type: 'My::Resource::Type' });
const asset = new Asset(stack, 'MyAsset', { path: SAMPLE_ASSET_DIR });

// WHEN
asset.addResourceMetadata(resource, 'PropName');

const template = SynthUtils.synthesize(stack).template;
expect(template.Resources.MyResource.Metadata).toEqual({
'aws:asset:path': SAMPLE_ASSET_DIR,
'aws:asset:property': 'PropName',
});
});

test('cdk metadata points to staged asset', () => {
// GIVEN
const app = new cdk.App();
const stack = new cdk.Stack(app, 'stack');
new Asset(stack, 'MyAsset', { path: SAMPLE_ASSET_DIR });

// WHEN
const session = app.synth();
const artifact = session.getStackByName(stack.stackName);
const metadata = artifact.manifest.metadata || {};
const md = Object.values(metadata)[0]![0]!.data as cxschema.AssetMetadataEntry;
expect(md.path).toBe('asset.6b84b87243a4a01c592d78e1fd3855c4bfef39328cd0a450cc97e81717fea2a2');
});
});

function mkdtempSync() {
return fs.mkdtempSync(path.join(os.tmpdir(), 'assets.test'));
}
Loading

0 comments on commit aa920af

Please sign in to comment.