11import List from '../../../src/subCommands/list' ;
22import FC from '../../../src/resources/fc' ;
33import { IInputs } from '../../../src/interface' ;
4- import { tableShow } from '../../../src/utils' ;
4+ import { isAppCenter , tableShow } from '../../../src/utils' ;
5+ import logger from '../../../src/logger' ;
56
67// Mock dependencies
78jest . mock ( '../../../src/resources/fc' ) ;
@@ -12,17 +13,24 @@ jest.mock('../../../src/logger', () => ({
1213 error : jest . fn ( ) ,
1314 warn : jest . fn ( ) ,
1415 log : jest . fn ( ) ,
16+ write : jest . fn ( ) ,
1517} ) ) ;
16- jest . mock ( '../../../src/utils' , ( ) => ( {
17- tableShow : jest . fn ( ) ,
18- isAppCenter : jest . fn ( ) ,
19- getUserAgent : jest . fn ( ( userAgent , command ) => {
20- return (
21- userAgent ||
22- `Component:fc3;Nodejs:${ process . version } ;OS:${ process . platform } -${ process . arch } ;command:${ command } `
23- ) ;
24- } ) ,
25- } ) ) ;
18+ jest . mock ( '../../../src/utils' , ( ) => {
19+ const actual = jest . requireActual ( '../../../src/utils' ) ;
20+ return {
21+ tableShow : jest . fn ( ) ,
22+ isAppCenter : jest . fn ( ) ,
23+ getUserAgent : jest . fn ( ( userAgent , command ) => {
24+ return (
25+ userAgent ||
26+ `Component:fc3;Nodejs:${ process . version } ;OS:${ process . platform } -${ process . arch } ;command:${ command } `
27+ ) ;
28+ } ) ,
29+ MAX_DEFAULT_RENDER_LINES : actual . MAX_DEFAULT_RENDER_LINES ,
30+ estimateRenderLines : actual . estimateRenderLines ,
31+ isDefaultRenderOutput : actual . isDefaultRenderOutput ,
32+ } ;
33+ } ) ;
2634
2735describe ( 'List' , ( ) => {
2836 let list : List ;
@@ -237,6 +245,91 @@ describe('List', () => {
237245 } ) ;
238246 } ) ;
239247
248+ describe ( 'run - output too large for the default renderer' , ( ) => {
249+ // 每个函数约 6 个字段,2 万个函数 > MAX_DEFAULT_RENDER_LINES(5w) 行
250+ const hugeFunctionsArray = Array . from ( { length : 20000 } , ( _v , i ) => ( {
251+ functionName : `test-func-${ i } ` ,
252+ runtime : 'nodejs18' ,
253+ handler : 'index.handler' ,
254+ memorySize : 128 ,
255+ state : 'Active' ,
256+ lastModifiedTime : '2024-01-01T00:00:00Z' ,
257+ } ) ) ;
258+
259+ let originalArgv : string [ ] ;
260+
261+ beforeEach ( ( ) => {
262+ originalArgv = process . argv ;
263+ // clearAllMocks 不会清掉 mockReturnValue,这里显式回到默认值
264+ ( isAppCenter as jest . Mock ) . mockReturnValue ( false ) ;
265+ } ) ;
266+
267+ afterEach ( ( ) => {
268+ process . argv = originalArgv ;
269+ } ) ;
270+
271+ it ( 'should print raw JSON instead of returning it when the default output format is used' , async ( ) => {
272+ process . argv = [ 'node' , 's' , 'cli' , 'fc3' , 'list' ] ;
273+ mockInputs . args = [ ] ;
274+ list = new List ( mockInputs ) ;
275+ mockFcSdk . listFunctions = jest . fn ( ) . mockResolvedValue ( hugeFunctionsArray ) ;
276+
277+ const result = await list . run ( ) ;
278+ expect ( result ) . toBeUndefined ( ) ;
279+ expect ( logger . warn ) . toHaveBeenCalledWith ( expect . stringContaining ( '20000 functions' ) ) ;
280+ expect ( logger . write ) . toHaveBeenCalledWith (
281+ JSON . stringify ( { functions : hugeFunctionsArray } , null , 2 ) ,
282+ ) ;
283+ } ) ;
284+
285+ it ( 'should return the result untouched when an output format is specified' , async ( ) => {
286+ process . argv = [ 'node' , 's' , 'cli' , 'fc3' , 'list' , '-o' , 'json' ] ;
287+ mockInputs . args = [ ] ;
288+ list = new List ( mockInputs ) ;
289+ mockFcSdk . listFunctions = jest . fn ( ) . mockResolvedValue ( hugeFunctionsArray ) ;
290+
291+ const result = await list . run ( ) ;
292+ expect ( result ) . toEqual ( { functions : hugeFunctionsArray } ) ;
293+ expect ( logger . write ) . not . toHaveBeenCalled ( ) ;
294+ } ) ;
295+
296+ it ( 'should return the result untouched when it is small enough to render' , async ( ) => {
297+ process . argv = [ 'node' , 's' , 'cli' , 'fc3' , 'list' ] ;
298+ mockInputs . args = [ ] ;
299+ list = new List ( mockInputs ) ;
300+ mockFcSdk . listFunctions = jest . fn ( ) . mockResolvedValue ( mockFunctionsArray ) ;
301+
302+ const result = await list . run ( ) ;
303+ expect ( result ) . toEqual ( { functions : mockFunctionsArray } ) ;
304+ expect ( logger . write ) . not . toHaveBeenCalled ( ) ;
305+ } ) ;
306+
307+ it ( 'should return the result untouched for programmatic app center callers' , async ( ) => {
308+ process . argv = [ 'node' , 's' , 'cli' , 'fc3' , 'list' ] ;
309+ ( isAppCenter as jest . Mock ) . mockReturnValue ( true ) ;
310+ mockInputs . args = [ ] ;
311+ list = new List ( mockInputs ) ;
312+ mockFcSdk . listFunctions = jest . fn ( ) . mockResolvedValue ( hugeFunctionsArray ) ;
313+
314+ const result = await list . run ( ) ;
315+ expect ( result ) . toEqual ( { functions : hugeFunctionsArray } ) ;
316+ expect ( logger . write ) . not . toHaveBeenCalled ( ) ;
317+ } ) ;
318+
319+ it ( 'should also guard the single page path' , async ( ) => {
320+ process . argv = [ 'node' , 's' , 'cli' , 'fc3' , 'list' ] ;
321+ mockInputs . args = [ '--limit' , '20000' ] ;
322+ list = new List ( mockInputs ) ;
323+ mockFcSdk . listFunctionsPage = jest
324+ . fn ( )
325+ . mockResolvedValue ( { functions : hugeFunctionsArray , nextToken : 'next' } ) ;
326+
327+ const result = await list . run ( ) ;
328+ expect ( result ) . toBeUndefined ( ) ;
329+ expect ( logger . write ) . toHaveBeenCalled ( ) ;
330+ } ) ;
331+ } ) ;
332+
240333 describe ( 'run - error handling' , ( ) => {
241334 it ( 'should propagate auto-pagination API errors' , async ( ) => {
242335 mockInputs . args = [ ] ;
0 commit comments