@@ -17,6 +17,7 @@ import {
1717 mockLogFn ,
1818} from '@dd/tests/_jest/helpers/mocks' ;
1919import { runBundlers } from '@dd/tests/_jest/helpers/runBundlers' ;
20+ import fsp from 'fs/promises' ;
2021import nock from 'nock' ;
2122import path from 'path' ;
2223
@@ -149,10 +150,18 @@ describe('Apps Plugin - getPlugins', () => {
149150 ] ;
150151 jest . spyOn ( assets , 'collectAssets' ) . mockResolvedValue ( mockedAssets ) ;
151152 jest . spyOn ( fsHelpers , 'rm' ) . mockResolvedValue ( undefined ) ;
152- jest . spyOn ( archive , 'createArchive' ) . mockResolvedValue ( {
153- archivePath : '/tmp/dd-apps-123/datadog-apps-assets.zip' ,
154- assets : mockedAssets ,
155- size : 10 ,
153+ let manifest : unknown ;
154+ jest . spyOn ( archive , 'createArchive' ) . mockImplementation ( async ( archiveAssets ) => {
155+ const manifestAsset = archiveAssets . find (
156+ ( asset ) => asset . relativePath === 'manifest.json' ,
157+ ) ;
158+ expect ( manifestAsset ) . toBeDefined ( ) ;
159+ manifest = JSON . parse ( await fsp . readFile ( manifestAsset ! . absolutePath , 'utf8' ) ) ;
160+ return {
161+ archivePath : '/tmp/dd-apps-123/datadog-apps-assets.zip' ,
162+ assets : archiveAssets ,
163+ size : 10 ,
164+ } ;
156165 } ) ;
157166 jest . spyOn ( uploader , 'uploadArchive' ) . mockResolvedValue ( {
158167 errors : [ ] ,
@@ -163,12 +172,18 @@ describe('Apps Plugin - getPlugins', () => {
163172 await closeBundle ( ) ;
164173
165174 expect ( assets . collectAssets ) . toHaveBeenCalledWith ( [ 'dist/**/*' ] , buildRoot ) ;
166- expect ( archive . createArchive ) . toHaveBeenCalledWith ( [
167- {
168- absolutePath : '/project/dist/index.js' ,
169- relativePath : path . join ( 'frontend' , 'dist/index.js' ) ,
170- } ,
171- ] ) ;
175+ expect ( archive . createArchive ) . toHaveBeenCalledWith (
176+ expect . arrayContaining ( [
177+ {
178+ absolutePath : '/project/dist/index.js' ,
179+ relativePath : path . join ( 'frontend' , 'dist/index.js' ) ,
180+ } ,
181+ expect . objectContaining ( {
182+ relativePath : 'manifest.json' ,
183+ } ) ,
184+ ] ) ,
185+ ) ;
186+ expect ( manifest ) . toEqual ( { backend : { functions : { } } } ) ;
172187 expect ( uploader . uploadArchive ) . toHaveBeenCalledWith (
173188 expect . objectContaining ( { archivePath : '/tmp/dd-apps-123/datadog-apps-assets.zip' } ) ,
174189 {
@@ -188,6 +203,94 @@ describe('Apps Plugin - getPlugins', () => {
188203 'warn' ,
189204 ) ;
190205 expect ( fsHelpers . rm ) . toHaveBeenCalledWith ( path . resolve ( '/tmp/dd-apps-123' ) ) ;
206+ expect ( fsHelpers . rm ) . toHaveBeenCalledWith ( expect . stringContaining ( 'dd-apps-manifest-' ) ) ;
207+ } ) ;
208+
209+ test ( 'Should emit root manifest.json with backend function connection allowlists' , async ( ) => {
210+ jest . spyOn ( identifier , 'resolveIdentifier' ) . mockReturnValue ( {
211+ identifier : 'repo:app' ,
212+ name : 'test-app' ,
213+ } ) ;
214+ jest . spyOn ( assets , 'collectAssets' ) . mockResolvedValue ( [
215+ { absolutePath : '/project/dist/index.js' , relativePath : 'dist/index.js' } ,
216+ ] ) ;
217+ jest . spyOn ( fsHelpers , 'rm' ) . mockResolvedValue ( undefined ) ;
218+ jest . spyOn ( uploader , 'uploadArchive' ) . mockResolvedValue ( {
219+ errors : [ ] ,
220+ warnings : [ ] ,
221+ } ) ;
222+
223+ let manifest : unknown ;
224+ jest . spyOn ( archive , 'createArchive' ) . mockImplementation ( async ( archiveAssets ) => {
225+ const manifestAsset = archiveAssets . find (
226+ ( asset ) => asset . relativePath === 'manifest.json' ,
227+ ) ;
228+ expect ( manifestAsset ) . toBeDefined ( ) ;
229+ manifest = JSON . parse ( await fsp . readFile ( manifestAsset ! . absolutePath , 'utf8' ) ) ;
230+ return {
231+ archivePath : '/tmp/dd-apps-789/datadog-apps-assets.zip' ,
232+ assets : archiveAssets ,
233+ size : 30 ,
234+ } ;
235+ } ) ;
236+
237+ const viteBuild = jest . fn ( ) . mockResolvedValue ( {
238+ output : [
239+ {
240+ type : 'chunk' ,
241+ isEntry : true ,
242+ name : expect . any ( String ) ,
243+ fileName : 'unused.greet.js' ,
244+ } ,
245+ ] ,
246+ } ) ;
247+ const args = getArgs ( ) ;
248+ args . bundler = { build : viteBuild } ;
249+ const plugins = getPlugins ( args ) ;
250+ const transform = plugins [ 0 ] . transform as {
251+ handler : ( code : string , id : string ) => unknown ;
252+ } ;
253+ transform . handler . call (
254+ {
255+ parse : ( ) => ( {
256+ type : 'Program' ,
257+ body : [
258+ {
259+ type : 'ExportNamedDeclaration' ,
260+ declaration : {
261+ type : 'FunctionDeclaration' ,
262+ id : { type : 'Identifier' , name : 'greet' } ,
263+ } ,
264+ specifiers : [ ] ,
265+ } ,
266+ ] ,
267+ } ) ,
268+ } ,
269+ 'export function greet() {}' ,
270+ '/project/src/backend/greet.backend.js' ,
271+ ) ;
272+
273+ await extractCloseBundle ( plugins ) ( ) ;
274+
275+ expect ( archive . createArchive ) . toHaveBeenCalledWith (
276+ expect . arrayContaining ( [
277+ expect . objectContaining ( { relativePath : 'manifest.json' } ) ,
278+ expect . objectContaining ( {
279+ relativePath : expect . stringMatching ( / ^ b a c k e n d \/ .* \. g r e e t \. j s $ / ) ,
280+ } ) ,
281+ ] ) ,
282+ ) ;
283+ expect (
284+ Object . keys ( ( manifest as { backend : { functions : object } } ) . backend . functions ) ,
285+ ) . toEqual ( [ expect . stringMatching ( / ^ [ a - f 0 - 9 ] { 64 } \. g r e e t $ / ) ] ) ;
286+ expect ( manifest ) . toMatchObject ( {
287+ backend : { functions : expect . any ( Object ) } ,
288+ } ) ;
289+ expect (
290+ Object . values (
291+ ( manifest as { backend : { functions : Record < string , unknown > } } ) . backend . functions ,
292+ ) ,
293+ ) . toEqual ( [ { allowedConnectionIds : [ ] } ] ) ;
191294 } ) ;
192295
193296 test ( 'Should surface upload errors' , async ( ) => {
@@ -215,6 +318,7 @@ describe('Apps Plugin - getPlugins', () => {
215318
216319 expect ( mockLogFn ) . toHaveBeenCalledWith ( expect . stringContaining ( 'upload failed' ) , 'error' ) ;
217320 expect ( fsHelpers . rm ) . toHaveBeenCalledWith ( path . resolve ( '/tmp/dd-apps-456' ) ) ;
321+ expect ( fsHelpers . rm ) . toHaveBeenCalledWith ( expect . stringContaining ( 'dd-apps-manifest-' ) ) ;
218322 } ) ;
219323
220324 test ( 'Should upload assets with vite bundler' , async ( ) => {
0 commit comments