11import { exec } from 'node:child_process' ;
22import * as fs from 'node:fs/promises' ;
3+ import * as os from 'node:os' ;
34import * as path from 'node:path' ;
45import { promisify } from 'node:util' ;
56import { App , Stack } from 'aws-cdk-lib' ;
67import { Match , Template } from 'aws-cdk-lib/assertions' ;
78import { Architecture , Runtime } from 'aws-cdk-lib/aws-lambda' ;
9+ import * as cxapi from 'aws-cdk-lib/cx-api' ;
810import { PythonFunction } from '../src' ;
911const execAsync = promisify ( exec ) ;
1012
@@ -27,9 +29,41 @@ async function getDockerHostArch(): Promise<Architecture> {
2729 }
2830}
2931
30- test ( 'Create a function from basic_app' , async ( ) => {
32+ /**
33+ * Create a new CDK App and Stack with the given name and set the context to ensure
34+ * that the 'aws:asset:path' metadata is set.
35+ *
36+ * @returns The App and Stack
37+ */
38+ async function createStack ( name = 'test' ) : Promise < { app : App ; stack : Stack } > {
3139 const app = new App ( { } ) ;
32- const stack = new Stack ( app , 'test' ) ;
40+ const stack = new Stack ( app , name ) ;
41+
42+ // This ensures that the 'aws:asset:path' metadata is set
43+ stack . node . setContext ( cxapi . ASSET_RESOURCE_METADATA_ENABLED_CONTEXT , true ) ;
44+
45+ return { app, stack } ;
46+ }
47+
48+ // Need to have CDK_OUTDIR set to something sensible as it's used to create the codePath when aws:asset:path is set
49+ const OLD_ENV = process . env ;
50+
51+ beforeEach ( async ( ) => {
52+ jest . resetModules ( ) ;
53+ process . env = { ...OLD_ENV } ;
54+ process . env . CDK_OUTDIR = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'uv-python-lambda-test-' ) ) ;
55+ } ) ;
56+
57+ afterEach ( async ( ) => {
58+ if ( process . env . CDK_OUTDIR ) {
59+ await fs . rm ( process . env . CDK_OUTDIR , { recursive : true } ) ;
60+ }
61+ process . env = OLD_ENV ;
62+ } ) ;
63+
64+ test ( 'Create a function from basic_app' , async ( ) => {
65+ const { app, stack } = await createStack ( ) ;
66+
3367 new PythonFunction ( stack , 'basic_app' , {
3468 rootDir : path . join ( resourcesPath , 'basic_app' ) ,
3569 index : 'handler.py' ,
@@ -55,8 +89,8 @@ test('Create a function from basic_app', async () => {
5589} ) ;
5690
5791test ( 'Create a function from basic_app with no .py index extension' , async ( ) => {
58- const app = new App ( { } ) ;
59- const stack = new Stack ( app , 'test' ) ;
92+ const { stack } = await createStack ( ) ;
93+
6094 new PythonFunction ( stack , 'basic_app' , {
6195 rootDir : path . join ( resourcesPath , 'basic_app' ) ,
6296 index : 'handler' ,
@@ -77,9 +111,29 @@ test('Create a function from basic_app with no .py index extension', async () =>
77111 } ) ;
78112} ) ;
79113
114+ test ( 'Create a function from basic_app when skip is true' , async ( ) => {
115+ const { stack } = await createStack ( ) ;
116+
117+ const bundlingSpy = jest . spyOn ( stack , 'bundlingRequired' , 'get' ) . mockReturnValue ( false ) ;
118+ const architecture = await getDockerHostArch ( ) ;
119+
120+ // To see this fail, comment out the `if (skip) { return; } code in the PythonFunction constructor
121+ expect ( ( ) => {
122+ new PythonFunction ( stack , 'basic_app' , {
123+ rootDir : path . join ( resourcesPath , 'basic_app' ) ,
124+ index : 'handler' ,
125+ handler : 'lambda_handler' ,
126+ runtime : Runtime . PYTHON_3_12 ,
127+ architecture,
128+ } ) ;
129+ } ) . not . toThrow ( ) ;
130+
131+ bundlingSpy . mockRestore ( ) ;
132+ } ) ;
133+
80134test ( 'Create a function with workspaces_app' , async ( ) => {
81- const app = new App ( { } ) ;
82- const stack = new Stack ( app , 'wstest' ) ;
135+ const { app, stack } = await createStack ( 'wstest' ) ;
136+
83137 new PythonFunction ( stack , 'workspaces_app' , {
84138 rootDir : path . join ( resourcesPath , 'workspaces_app' ) ,
85139 workspacePackage : 'app' ,
@@ -122,4 +176,3 @@ async function getFunctionAssetContents(functionResource: any, app: App) {
122176 const contents = await fs . readdir ( assetPath ) ;
123177 return contents ;
124178}
125-
0 commit comments