@@ -3,6 +3,8 @@ import * as path from 'path';
33import * as cxschema from '@aws-cdk/cloud-assembly-schema' ;
44import * as cxapi from '@aws-cdk/cx-api' ;
55import { CloudAssemblyBuilder } from '@aws-cdk/cx-api' ;
6+ // eslint-disable-next-line import/no-extraneous-dependencies
7+ import { expect } from '@jest/globals' ;
68import { WorkGraph } from '../lib/util/work-graph' ;
79import { WorkGraphBuilder } from '../lib/util/work-graph-builder' ;
810import { AssetBuildNode , AssetPublishNode , StackNode , WorkNode } from '../lib/util/work-graph-types' ;
@@ -16,6 +18,25 @@ afterEach(() => {
1618 rootBuilder . delete ( ) ;
1719} ) ;
1820
21+ function superset < A > ( xs : A [ ] ) : Set < A > {
22+ const ret = new Set ( xs ) ;
23+ ( ret as any ) . isSuperset = true ;
24+ return ret ;
25+ }
26+
27+ expect . addEqualityTesters ( [
28+ function ( exp : unknown , act : unknown ) : boolean | undefined {
29+ if ( exp instanceof Set && isIterable ( act ) ) {
30+ if ( ( exp as any ) . isSuperset ) {
31+ const actSet = new Set ( act ) ;
32+ return Array . from ( exp as any ) . every ( ( x ) => actSet . has ( x ) ) ;
33+ }
34+ return this . equals ( Array . from ( exp ) . sort ( ) , Array . from ( act ) . sort ( ) ) ;
35+ }
36+ return undefined ;
37+ } ,
38+ ] ) ;
39+
1940describe ( 'with some stacks and assets' , ( ) => {
2041 let assembly : cxapi . CloudAssembly ;
2142 beforeEach ( ( ) => {
@@ -28,34 +49,34 @@ describe('with some stacks and assets', () => {
2849
2950 expect ( assertableNode ( graph . node ( 'stack2' ) ) ) . toEqual ( expect . objectContaining ( {
3051 type : 'stack' ,
31- dependencies : expect . arrayContaining ( [ 'F1:D1-publish ' ] ) ,
32- } as StackNode ) ) ;
52+ dependencies : superset ( [ 'publish-F1-add54bdbcb ' ] ) ,
53+ } as Partial < StackNode > ) ) ;
3354 } ) ;
3455
3556 test ( 'asset publishing step depends on asset building step' , ( ) => {
3657 const graph = new WorkGraphBuilder ( true ) . build ( assembly . artifacts ) ;
3758
38- expect ( graph . node ( 'F1:D1-publish ' ) ) . toEqual ( expect . objectContaining ( {
59+ expect ( graph . node ( 'publish-F1-add54bdbcb ' ) ) . toEqual ( expect . objectContaining ( {
3960 type : 'asset-publish' ,
40- dependencies : new Set ( [ 'F1-build ' ] ) ,
41- } as Partial < AssetPublishNode > ) ) ;
61+ dependencies : superset ( [ 'build- F1-a533139934 ' ] ) ,
62+ } satisfies Partial < AssetPublishNode > ) ) ;
4263 } ) ;
4364
4465 test ( 'with prebuild off, asset building inherits dependencies from their parent stack' , ( ) => {
4566 const graph = new WorkGraphBuilder ( false ) . build ( assembly . artifacts ) ;
4667
47- expect ( graph . node ( 'F1-build ' ) ) . toEqual ( expect . objectContaining ( {
68+ expect ( graph . node ( 'build- F1-a533139934 ' ) ) . toEqual ( expect . objectContaining ( {
4869 type : 'asset-build' ,
49- dependencies : new Set ( [ 'stack0' , 'stack1' ] ) ,
70+ dependencies : superset ( [ 'stack0' , 'stack1' ] ) ,
5071 } as Partial < AssetBuildNode > ) ) ;
5172 } ) ;
5273
5374 test ( 'with prebuild on, assets only have their own dependencies' , ( ) => {
5475 const graph = new WorkGraphBuilder ( true ) . build ( assembly . artifacts ) ;
5576
56- expect ( graph . node ( 'F1-build ' ) ) . toEqual ( expect . objectContaining ( {
77+ expect ( graph . node ( 'build- F1-a533139934 ' ) ) . toEqual ( expect . objectContaining ( {
5778 type : 'asset-build' ,
58- dependencies : new Set ( [ 'stack0' ] ) ,
79+ dependencies : superset ( [ 'stack0' ] ) ,
5980 } as Partial < AssetBuildNode > ) ) ;
6081 } ) ;
6182} ) ;
@@ -84,13 +105,16 @@ test('can handle nested assemblies', async () => {
84105
85106 let workDone = 0 ;
86107 const graph = new WorkGraphBuilder ( true ) . build ( assembly . artifacts ) ;
108+
87109 await graph . doParallel ( 10 , {
88110 deployStack : async ( ) => { workDone += 1 ; } ,
89111 buildAsset : async ( ) => { } ,
90112 publishAsset : async ( ) => { workDone += 1 ; } ,
91113 } ) ;
92114
93- expect ( workDone ) . toEqual ( 8 ) ;
115+ // The asset is shared between parent assembly and nested assembly, but the stacks will be deployed
116+ // 3 stacks + 1 asset + 3 stacks (1 reused asset)
117+ expect ( workDone ) . toEqual ( 7 ) ;
94118} ) ;
95119
96120test ( 'dependencies on unselected artifacts are silently ignored' , async ( ) => {
@@ -143,8 +167,8 @@ describe('tests that use assets', () => {
143167 const traversal = await traverseAndRecord ( graph ) ;
144168
145169 expect ( traversal ) . toEqual ( [
146- ' work-graph-builder.test.js-build' ,
147- ' work-graph-builder.test.js:D1-publish' ,
170+ expect . stringMatching ( / ^ b u i l d - w o r k - g r a p h - b u i l d e r .t e s t .j s - . * $ / ) ,
171+ expect . stringMatching ( / ^ p u b l i s h - w o r k - g r a p h - b u i l d e r .t e s t .j s - . * $ / ) ,
148172 'StackA' ,
149173 'StackB' ,
150174 ] ) ;
@@ -205,11 +229,56 @@ describe('tests that use assets', () => {
205229 const traversal = await traverseAndRecord ( graph ) ;
206230
207231 expect ( traversal ) . toEqual ( [
208- 'abcdef-build' ,
209- 'abcdef:D1-publish' ,
210- 'abcdef:D2-publish' ,
232+ expect . stringMatching ( / ^ b u i l d - a b c d e f - .* $ / ) ,
233+ expect . stringMatching ( / ^ p u b l i s h - a b c d e f - .* $ / ) ,
234+ expect . stringMatching ( / ^ p u b l i s h - a b c d e f - .* $ / ) ,
235+ 'StackA' ,
236+ expect . stringMatching ( / ^ p u b l i s h - a b c d e f - .* $ / ) ,
237+ 'StackB' ,
238+ ] ) ;
239+ } ) ;
240+
241+ test ( 'different parameters for the same named definition are both published' , async ( ) => {
242+ addStack ( rootBuilder , 'StackA' , {
243+ environment : 'aws://11111/us-east-1' ,
244+ dependencies : [ 'StackA.assets' ] ,
245+ } ) ;
246+ addAssets ( rootBuilder , 'StackA.assets' , {
247+ files : {
248+ abcdef : {
249+ source : { path : __dirname } ,
250+ destinations : {
251+ D : { bucketName : 'bucket1' , objectKey : 'key' } ,
252+ } ,
253+ } ,
254+ } ,
255+ } ) ;
256+
257+ addStack ( rootBuilder , 'StackB' , {
258+ environment : 'aws://11111/us-east-1' ,
259+ dependencies : [ 'StackB.assets' , 'StackA' ] ,
260+ } ) ;
261+ addAssets ( rootBuilder , 'StackB.assets' , {
262+ files : {
263+ abcdef : {
264+ source : { path : __dirname } ,
265+ destinations : {
266+ D : { bucketName : 'bucket2' , objectKey : 'key' } ,
267+ } ,
268+ } ,
269+ } ,
270+ } ) ;
271+
272+ const assembly = rootBuilder . buildAssembly ( ) ;
273+
274+ const graph = new WorkGraphBuilder ( true ) . build ( assembly . artifacts ) ;
275+ const traversal = await traverseAndRecord ( graph ) ;
276+
277+ expect ( traversal ) . toEqual ( [
278+ expect . stringMatching ( / ^ b u i l d - a b c d e f - .* $ / ) ,
279+ expect . stringMatching ( / ^ p u b l i s h - a b c d e f - .* $ / ) ,
211280 'StackA' ,
212- ' abcdef:D3-publish' ,
281+ expect . stringMatching ( / ^ p u b l i s h - a b c d e f - . * $ / ) ,
213282 'StackB' ,
214283 ] ) ;
215284 } ) ;
@@ -302,4 +371,8 @@ async function traverseAndRecord(graph: WorkGraph) {
302371 publishAsset : async ( node ) => { ret . push ( node . id ) ; } ,
303372 } ) ;
304373 return ret ;
374+ }
375+
376+ function isIterable ( x : unknown ) : x is Iterable < any > {
377+ return x && typeof x === 'object' && ( x as any ) [ Symbol . iterator ] ;
305378}
0 commit comments