@@ -10,6 +10,8 @@ const {
1010 mockQueryRows,
1111 mockGetOrCreateTableSnapshot,
1212 mockDownloadFile,
13+ mockGeneratePresignedDownloadUrl,
14+ mockHasCloudStorage,
1315 mockExecuteTool,
1416} = vi . hoisted ( ( ) => ( {
1517 mockIsFeatureEnabled : vi . fn ( ) ,
@@ -18,6 +20,8 @@ const {
1820 mockQueryRows : vi . fn ( ) ,
1921 mockGetOrCreateTableSnapshot : vi . fn ( ) ,
2022 mockDownloadFile : vi . fn ( ) ,
23+ mockGeneratePresignedDownloadUrl : vi . fn ( ) ,
24+ mockHasCloudStorage : vi . fn ( ) ,
2125 mockExecuteTool : vi . fn ( ) ,
2226} ) )
2327
@@ -29,8 +33,13 @@ vi.mock('@/lib/table/service', () => ({
2933vi . mock ( '@/lib/table/rows/service' , ( ) => ( { queryRows : mockQueryRows } ) )
3034vi . mock ( '@/lib/table/snapshot-cache' , ( ) => ( {
3135 getOrCreateTableSnapshot : mockGetOrCreateTableSnapshot ,
36+ SNAPSHOT_MAX_BYTES : 500 * 1024 * 1024 ,
37+ } ) )
38+ vi . mock ( '@/lib/uploads/core/storage-service' , ( ) => ( {
39+ downloadFile : mockDownloadFile ,
40+ generatePresignedDownloadUrl : mockGeneratePresignedDownloadUrl ,
41+ hasCloudStorage : mockHasCloudStorage ,
3242} ) )
33- vi . mock ( '@/lib/uploads/core/storage-service' , ( ) => ( { downloadFile : mockDownloadFile } ) )
3443vi . mock ( '@/tools' , ( ) => ( { executeTool : mockExecuteTool } ) )
3544// Workspace-file + VFS surfaces are unused on the tables-only path; stub to avoid heavy loads.
3645vi . mock ( '@/lib/uploads/contexts/workspace/workspace-file-manager' , ( ) => ( {
@@ -67,18 +76,22 @@ const context = { workspaceId: 'ws_1', userId: 'u1' }
6776
6877function mountedFiles ( ) {
6978 const params = mockExecuteTool . mock . calls [ 0 ] [ 1 ] as {
70- _sandboxFiles ?: Array < { path : string ; content : string } >
79+ _sandboxFiles ?: Array < { path : string ; type ?: string ; content ?: string ; url ? : string } >
7180 }
7281 return params . _sandboxFiles ?? [ ]
7382}
7483
84+ const snapshotCacheOn = ( flag : string ) => Promise . resolve ( flag === 'table-snapshot-cache' )
85+
7586describe ( 'executeFunctionExecute table mounts' , ( ) => {
7687 beforeEach ( ( ) => {
7788 vi . clearAllMocks ( )
7889 mockExecuteTool . mockResolvedValue ( { success : true } )
7990 mockGetTableById . mockResolvedValue ( table )
8091 mockIsFeatureEnabled . mockResolvedValue ( false )
8192 mockQueryRows . mockResolvedValue ( { rows : [ { data : { name : 'Ada' } } ] } )
93+ mockHasCloudStorage . mockReturnValue ( true )
94+ mockGeneratePresignedDownloadUrl . mockResolvedValue ( 'https://s3.example/presigned?sig=abc' )
8295 } )
8396
8497 it ( 'flag OFF: drains the table inline via queryRows (existing path)' , async ( ) => {
@@ -91,33 +104,55 @@ describe('executeFunctionExecute table mounts', () => {
91104 expect ( files [ 0 ] . content ) . toBe ( 'name\nAda' )
92105 } )
93106
94- it ( 'flag ON + large table: mounts by reference from the snapshot, no row drain' , async ( ) => {
95- mockIsFeatureEnabled . mockImplementation ( ( flag : string ) =>
96- Promise . resolve ( flag === 'table-snapshot-cache' )
97- )
107+ it ( 'flag ON + cloud storage: mounts by presigned URL, no bytes through web' , async ( ) => {
108+ mockIsFeatureEnabled . mockImplementation ( snapshotCacheOn )
98109 mockGetOrCreateTableSnapshot . mockResolvedValue ( {
99110 key : 'table-snapshots/ws_1/tbl_1/v5.csv' ,
100111 size : 9 ,
101112 version : 5 ,
102113 } )
103- mockDownloadFile . mockResolvedValue ( Buffer . from ( 'name\nAda\n' ) )
104114
105115 await executeFunctionExecute ( { inputTables : [ 'tbl_1' ] } , context as never )
106116
107117 expect ( mockGetOrCreateTableSnapshot ) . toHaveBeenCalledTimes ( 1 )
108118 expect ( mockQueryRows ) . not . toHaveBeenCalled ( )
119+ expect ( mockDownloadFile ) . not . toHaveBeenCalled ( )
120+ expect ( mockGeneratePresignedDownloadUrl ) . toHaveBeenCalledWith (
121+ 'table-snapshots/ws_1/tbl_1/v5.csv' ,
122+ 'execution' ,
123+ expect . any ( Number )
124+ )
125+ expect ( mountedFiles ( ) [ 0 ] ) . toEqual ( {
126+ type : 'url' ,
127+ path : '/home/user/tables/tbl_1.csv' ,
128+ url : 'https://s3.example/presigned?sig=abc' ,
129+ } )
130+ } )
131+
132+ it ( 'flag ON + local storage: falls back to a buffered content mount' , async ( ) => {
133+ mockIsFeatureEnabled . mockImplementation ( snapshotCacheOn )
134+ mockHasCloudStorage . mockReturnValue ( false )
135+ mockGetOrCreateTableSnapshot . mockResolvedValue ( {
136+ key : 'table-snapshots/ws_1/tbl_1/v5.csv' ,
137+ size : 9 ,
138+ version : 5 ,
139+ } )
140+ mockDownloadFile . mockResolvedValue ( Buffer . from ( 'name\nAda\n' ) )
141+
142+ await executeFunctionExecute ( { inputTables : [ 'tbl_1' ] } , context as never )
143+
144+ expect ( mockGeneratePresignedDownloadUrl ) . not . toHaveBeenCalled ( )
109145 expect ( mockDownloadFile ) . toHaveBeenCalledWith (
110146 expect . objectContaining ( { key : 'table-snapshots/ws_1/tbl_1/v5.csv' , context : 'execution' } )
111147 )
112- const files = mountedFiles ( )
113- expect ( files [ 0 ] . path ) . toBe ( '/home/user/tables/tbl_1.csv' )
114- expect ( files [ 0 ] . content ) . toBe ( 'name\nAda\n' )
148+ const file = mountedFiles ( ) [ 0 ]
149+ expect ( file . path ) . toBe ( '/home/user/tables/tbl_1.csv' )
150+ expect ( file . content ) . toBe ( 'name\nAda\n' )
151+ expect ( file . type ) . toBeUndefined ( )
115152 } )
116153
117154 it ( 'flag ON but small table stays on the inline path' , async ( ) => {
118- mockIsFeatureEnabled . mockImplementation ( ( flag : string ) =>
119- Promise . resolve ( flag === 'table-snapshot-cache' )
120- )
155+ mockIsFeatureEnabled . mockImplementation ( snapshotCacheOn )
121156 mockGetTableById . mockResolvedValue ( { ...table , rowCount : 10 } )
122157
123158 await executeFunctionExecute ( { inputTables : [ 'tbl_1' ] } , context as never )
@@ -126,10 +161,23 @@ describe('executeFunctionExecute table mounts', () => {
126161 expect ( mockQueryRows ) . toHaveBeenCalledTimes ( 1 )
127162 } )
128163
129- it ( 'flag ON: throws when the snapshot exceeds the per-file mount limit' , async ( ) => {
130- mockIsFeatureEnabled . mockImplementation ( ( flag : string ) =>
131- Promise . resolve ( flag === 'table-snapshot-cache' )
132- )
164+ it ( 'flag ON + cloud: throws when the snapshot exceeds the table mount limit' , async ( ) => {
165+ mockIsFeatureEnabled . mockImplementation ( snapshotCacheOn )
166+ mockGetOrCreateTableSnapshot . mockResolvedValue ( {
167+ key : 'table-snapshots/ws_1/tbl_1/v5.csv' ,
168+ size : 600 * 1024 * 1024 ,
169+ version : 5 ,
170+ } )
171+
172+ await expect (
173+ executeFunctionExecute ( { inputTables : [ 'tbl_1' ] } , context as never )
174+ ) . rejects . toThrow ( / t a b l e m o u n t l i m i t / )
175+ expect ( mockGeneratePresignedDownloadUrl ) . not . toHaveBeenCalled ( )
176+ } )
177+
178+ it ( 'flag ON + local: throws when the snapshot exceeds the per-file mount limit' , async ( ) => {
179+ mockIsFeatureEnabled . mockImplementation ( snapshotCacheOn )
180+ mockHasCloudStorage . mockReturnValue ( false )
133181 mockGetOrCreateTableSnapshot . mockResolvedValue ( {
134182 key : 'table-snapshots/ws_1/tbl_1/v5.csv' ,
135183 size : 20 * 1024 * 1024 ,
0 commit comments