1+ import { TryCatchFinallyHooksBuilder } from "../TryCatchFinallyHooks"
2+ import { callStack } from './callStack'
3+ import { AsyncResource , AsyncLocalStorage } from 'node:async_hooks'
4+
5+ function createTrack ( log :any ) {
6+ return new TryCatchFinallyHooksBuilder ( )
7+ . add ( callStack )
8+ . add ( {
9+ onTry ( ctx ) {
10+ log ( "onTry" , ctx . args . name )
11+ return {
12+ onFinally ( ) {
13+ log ( "onFinally" , ctx . callstack . map ( c => c . args . name ) . join ( "/" ) )
14+ } ,
15+ onCatch ( ) {
16+ log ( "onCatch" , ctx . callstack . map ( c => c . args . name ) . join ( "/" ) )
17+ }
18+ }
19+ }
20+ } )
21+ }
22+
23+
24+ test ( "callstack" , ( ) => {
25+ const log = jest . fn ( ( ...args :any [ ] ) => console . log ( ...args ) )
26+ const track = createTrack ( log )
27+
28+ const myChildFunc = jest . fn ( track . asFunctionWrapper ( { name :"MyChildFunc" } ) ( ( a :number , b :number ) => {
29+ return a + b
30+ } ) )
31+
32+
33+ const myParentFunc = jest . fn ( track . asFunctionWrapper ( { name : 'MyParentFunc' } ) ( ( ) => {
34+ ( new Array ( 10 ) . fill ( 0 ) . map ( ( _ , i ) => {
35+ return myChildFunc ( i , i * 2 )
36+ } ) )
37+ } ) )
38+
39+
40+ myParentFunc ( )
41+
42+ expect ( myChildFunc ) . toHaveBeenCalledTimes ( 10 )
43+
44+ expect ( log ) . toHaveBeenCalledTimes ( 22 )
45+ expect ( log ) . toHaveBeenCalledWith ( "onTry" , "MyParentFunc" )
46+ expect ( log ) . toHaveBeenCalledWith ( "onTry" , "MyParentFunc/MyChildFunc" )
47+ } )
48+
49+ test ( "callstack async" , async ( ) => {
50+ const log = jest . fn ( ( ...args :any [ ] ) => console . log ( "async" , ...args ) )
51+ const track = createTrack ( log )
52+
53+ const asyncStr = new AsyncLocalStorage < any > ( )
54+ asyncStr . enterWith ( [ "root" ] )
55+
56+ const myChildFunc = jest . fn ( track . asFunctionWrapper ( { name :"MyAsyncChildFunc" } ) ( async ( a :number , b :number ) => {
57+ const path = [ ...asyncStr . getStore ( ) || [ ] ]
58+ asyncStr . enterWith ( [ ...path , "child" ] )
59+ await delay ( Math . random ( ) * 1000 )
60+ asyncStr . enterWith ( path )
61+
62+ return a + b
63+ } ) )
64+
65+
66+ const myParentFunc = jest . fn ( track . asFunctionWrapper ( { name : 'MyAsyncParentFunc' } ) ( ( ) => {
67+ const path = [ ...asyncStr . getStore ( ) || [ ] ]
68+ asyncStr . enterWith ( [ ...path , "parent" ] )
69+ return Promise . allSettled ( new Array ( 2 ) . fill ( 0 ) . map ( async ( _ , i ) => {
70+ await myChildFunc ( i , i * 2 )
71+ } ) )
72+ } ) )
73+
74+
75+ await myParentFunc ( )
76+
77+ expect ( myChildFunc ) . toHaveBeenCalledTimes ( 10 )
78+
79+ expect ( log ) . toHaveBeenCalledTimes ( 22 )
80+ expect ( log ) . toHaveBeenCalledWith ( "onTry" , "MyAsyncParentFunc" )
81+ expect ( log ) . toHaveBeenCalledWith ( "onTry" , "MyAsyncParentFunc/MyAsyncChildFunc" )
82+ } )
83+
84+ function delay ( ms :number ) { return new Promise ( r => setTimeout ( r , ms ) ) }
85+
86+
87+ test ( 'async hooks' , async ( ) => {
88+ const callStack = new AsyncLocalStorage < { name :string } [ ] > ( )
89+ callStack . enterWith ( [ { name :"parent" } ] )
90+ await delay ( 100 )
91+ expect ( callStack . getStore ( ) ) . toEqual ( [ { name :"parent" } ] )
92+ callStack . enterWith ( [ { name :"parent" } , { name :'child' } ] )
93+ await delay ( 100 )
94+ expect ( callStack . getStore ( ) ) . toEqual ( [ { name :"parent" } , { name :"child" } ] )
95+ await delay ( 100 )
96+
97+
98+ async function myAsyncFunc ( ) {
99+ await delay ( 500 )
100+ expect ( callStack . getStore ( ) ) . toEqual ( [ { name :"parent" } , { name :"child" } ] )
101+ callStack . enterWith ( [ ...callStack . getStore ( ) ! , { name :"myAsyncFunc" } ] )
102+ await delay ( 1000 )
103+ expect ( callStack . getStore ( ) ) . toEqual ( [ { name :"parent" } , { name :"child" } , { name :"myAsyncFunc" } ] )
104+ }
105+
106+ async function myAsyncFunc2 ( ) {
107+ await delay ( 1000 )
108+ expect ( callStack . getStore ( ) ) . toEqual ( [ { name :"parent" } , { name :"child" } ] )
109+ callStack . enterWith ( [ ...callStack . getStore ( ) ! , { name :"myAsyncFunc2" } ] )
110+ await delay ( 1000 )
111+ expect ( callStack . getStore ( ) ) . toEqual ( [ { name :"parent" } , { name :"child" } , { name :"myAsyncFunc2" } ] )
112+ }
113+
114+
115+ await Promise . allSettled ( [ myAsyncFunc ( ) , myAsyncFunc2 ( ) ] )
116+
117+ expect ( callStack . getStore ( ) ) . toEqual ( [ { name :"parent" } , { name :"child" } ] )
118+
119+
120+ } )
121+
122+ test . only ( 'async hooks array' , async ( ) => {
123+ const callStack = new AsyncLocalStorage < string [ ] > ( )
124+ callStack . enterWith ( [ "parent" ] )
125+ await delay ( 100 )
126+ expect ( callStack . getStore ( ) ) . toEqual ( [ "parent" ] )
127+ await delay ( 100 )
128+
129+
130+ const stack = { } as any
131+ Error . captureStackTrace ( stack )
132+ console . log ( "stack" , stack . stack )
133+
134+
135+ async function myAsyncFunc ( n : number ) {
136+ const actionName = "myAsyncFunc" + n
137+ //await delay(0)
138+ const storeBeforeAwait = callStack . getStore ( ) !
139+ //expect(storeBeforeAwait).toEqual(["parent"])
140+ callStack . enterWith ( [ ...storeBeforeAwait , actionName ] )
141+
142+ await delay ( Math . random ( ) * 1000 )
143+
144+ const storeAfterAwait = callStack . getStore ( ) !
145+ expect ( storeAfterAwait ) . toEqual ( [ ...storeBeforeAwait , actionName ] )
146+ const newStack = [ ...storeAfterAwait ]
147+ newStack . pop ( )
148+ callStack . enterWith ( newStack )
149+ }
150+
151+
152+ await Promise . all ( new Array ( 2 ) . fill ( 0 ) . map ( ( _ , i ) => myAsyncFunc ( i ) ) )
153+
154+ expect ( callStack . getStore ( ) ) . toEqual ( [ "parent" ] )
155+
156+
157+ } )
0 commit comments