1+ import { describe , it , expect , jest } from '@jest/globals' ;
2+ import z from 'zod' ;
3+ import { endpoint , router , CtxRouter , type Endpoint } from '../core' ;
4+
5+ describe ( 'core' , ( ) => {
6+ describe ( 'endpoint' , ( ) => {
7+ describe ( 'input method' , ( ) => {
8+ it ( 'creates endpoint with input schema and action' , ( ) => {
9+ const schema = z . object ( { name : z . string ( ) } ) ;
10+ const actionFn = jest . fn ( ) . mockReturnValue ( { success : true } ) ;
11+
12+ const result = endpoint . input ( schema ) . action ( actionFn ) ;
13+
14+ expect ( result . input ) . toBe ( schema ) ;
15+ expect ( result . action ) . toBe ( actionFn ) ;
16+ } ) ;
17+
18+ it ( 'handles typed input and context' , ( ) => {
19+ const schema = z . object ( { id : z . number ( ) } ) ;
20+ type Context = { userId : string } ;
21+
22+ const actionFn = jest . fn ( ( ) => ( { data : 'test' } ) ) ;
23+ const result = endpoint . input ( schema ) . action < { data : string } , Context > ( actionFn ) ;
24+
25+ expect ( result . input ) . toBe ( schema ) ;
26+ expect ( result . action ) . toBe ( actionFn ) ;
27+ } ) ;
28+ } ) ;
29+
30+ describe ( 'action method' , ( ) => {
31+ it ( 'creates endpoint without input schema' , ( ) => {
32+ const actionFn = jest . fn ( ) . mockReturnValue ( { message : 'hello' } ) ;
33+
34+ const result = endpoint . action ( actionFn ) ;
35+
36+ expect ( result . input ) . toBeUndefined ( ) ;
37+ expect ( result . action ) . toBe ( actionFn ) ;
38+ } ) ;
39+
40+ it ( 'handles typed output and context' , ( ) => {
41+ type Context = { role : string } ;
42+ const actionFn = jest . fn ( ( ) => ( { status : 'ok' } ) ) ;
43+
44+ const result = endpoint . action < { status : string } , Context > ( actionFn ) ;
45+
46+ expect ( result . input ) . toBeUndefined ( ) ;
47+ expect ( result . action ) . toBe ( actionFn ) ;
48+ } ) ;
49+ } ) ;
50+ } ) ;
51+
52+ describe ( 'router' , ( ) => {
53+ it ( 'returns the router object as-is' , ( ) => {
54+ const testRouter = {
55+ getUser : endpoint . input ( z . object ( { id : z . string ( ) } ) ) . action ( ( ) => ( { name : 'test' } ) ) ,
56+ getAllUsers : endpoint . action ( ( ) => [ { name : 'user1' } , { name : 'user2' } ] )
57+ } ;
58+
59+ const result = router ( testRouter ) ;
60+
61+ expect ( result ) . toBe ( testRouter ) ;
62+ expect ( result . getUser ) . toBeDefined ( ) ;
63+ expect ( result . getAllUsers ) . toBeDefined ( ) ;
64+ } ) ;
65+
66+ it ( 'handles empty router' , ( ) => {
67+ const emptyRouter = { } ;
68+ const result = router ( emptyRouter ) ;
69+
70+ expect ( result ) . toBe ( emptyRouter ) ;
71+ expect ( Object . keys ( result ) ) . toHaveLength ( 0 ) ;
72+ } ) ;
73+
74+ it ( 'preserves router structure with context type' , ( ) => {
75+ type Context = { user : { id : string } } ;
76+ const contextRouter = {
77+ protectedEndpoint : endpoint . input ( z . object ( { data : z . string ( ) } ) ) . action < string , Context > ( ( ) => 'protected' )
78+ } ;
79+
80+ const result = router ( contextRouter as any ) ;
81+
82+ expect ( result ) . toBe ( contextRouter ) ;
83+ expect ( result . protectedEndpoint ) . toBeDefined ( ) ;
84+ } ) ;
85+ } ) ;
86+
87+ describe ( 'CtxRouter' , ( ) => {
88+ type TestContext = { userId : string ; role : string } ;
89+
90+ it ( 'creates instance with context type' , ( ) => {
91+ const ctxRouter = new CtxRouter < TestContext > ( ) ;
92+
93+ expect ( ctxRouter ) . toBeInstanceOf ( CtxRouter ) ;
94+ expect ( ctxRouter . endpoint ) . toBeDefined ( ) ;
95+ expect ( ctxRouter . router ) . toBeDefined ( ) ;
96+ } ) ;
97+
98+ describe ( 'endpoint.input' , ( ) => {
99+ it ( 'creates endpoint with input schema and typed action' , ( ) => {
100+ const ctxRouter = new CtxRouter < TestContext > ( ) ;
101+ const schema = z . object ( { name : z . string ( ) } ) ;
102+ const actionFn = jest . fn ( ) . mockReturnValue ( { created : true } ) ;
103+
104+ const result = ctxRouter . endpoint . input ( schema ) . action ( actionFn ) ;
105+
106+ expect ( result . input ) . toBe ( schema ) ;
107+ expect ( result . action ) . toBe ( actionFn ) ;
108+ } ) ;
109+
110+ it ( 'maintains context type in action function' , ( ) => {
111+ const ctxRouter = new CtxRouter < TestContext > ( ) ;
112+ const schema = z . object ( { data : z . string ( ) } ) ;
113+
114+ const actionFn = jest . fn ( ( input : z . infer < typeof schema > , context : { userId : string ; role : string ; request : any } ) => {
115+ return { result : `${ input . data } -${ context . userId } ` } ;
116+ } ) ;
117+
118+ const result = ctxRouter . endpoint . input ( schema ) . action < { result : string } > ( actionFn ) ;
119+
120+ expect ( result . input ) . toBe ( schema ) ;
121+ expect ( result . action ) . toBe ( actionFn ) ;
122+ } ) ;
123+ } ) ;
124+
125+ describe ( 'endpoint.action' , ( ) => {
126+ it ( 'creates endpoint without input schema' , ( ) => {
127+ const ctxRouter = new CtxRouter < TestContext > ( ) ;
128+ const actionFn = jest . fn ( ) . mockReturnValue ( { status : 'ready' } ) ;
129+
130+ const result = ctxRouter . endpoint . action ( actionFn ) ;
131+
132+ expect ( result . input ) . toBeUndefined ( ) ;
133+ expect ( result . action ) . toBe ( actionFn ) ;
134+ } ) ;
135+
136+ it ( 'maintains context type in action function' , ( ) => {
137+ const ctxRouter = new CtxRouter < TestContext > ( ) ;
138+
139+ const actionFn = jest . fn ( ( context : { userId : string ; role : string ; request : any } ) => {
140+ return { userInfo : context . userId } ;
141+ } ) ;
142+
143+ const result = ctxRouter . endpoint . action < { userInfo : string } > ( actionFn ) ;
144+
145+ expect ( result . input ) . toBeUndefined ( ) ;
146+ expect ( result . action ) . toBe ( actionFn ) ;
147+ } ) ;
148+ } ) ;
149+
150+ describe ( 'router method' , ( ) => {
151+ it ( 'returns router object as-is' , ( ) => {
152+ const ctxRouter = new CtxRouter < TestContext > ( ) ;
153+ const testRouterObj = {
154+ endpoint1 : ctxRouter . endpoint . action ( ( ) => ( { data : 'test1' } ) ) ,
155+ endpoint2 : ctxRouter . endpoint . input ( z . object ( { id : z . number ( ) } ) ) . action ( ( ) => ( { data : 'test2' } ) )
156+ } ;
157+
158+ const result = ctxRouter . router ( testRouterObj ) ;
159+
160+ expect ( result ) . toBe ( testRouterObj ) ;
161+ expect ( result . endpoint1 ) . toBeDefined ( ) ;
162+ expect ( result . endpoint2 ) . toBeDefined ( ) ;
163+ } ) ;
164+
165+ it ( 'handles empty router object' , ( ) => {
166+ const ctxRouter = new CtxRouter < TestContext > ( ) ;
167+ const emptyRouterObj = { } ;
168+
169+ const result = ctxRouter . router ( emptyRouterObj ) ;
170+
171+ expect ( result ) . toBe ( emptyRouterObj ) ;
172+ expect ( Object . keys ( result ) ) . toHaveLength ( 0 ) ;
173+ } ) ;
174+ } ) ;
175+ } ) ;
176+
177+ describe ( 'type definitions' , ( ) => {
178+ it ( 'Endpoint type works with different configurations' , ( ) => {
179+ const schema = z . object ( { test : z . string ( ) } ) ;
180+
181+ // Test endpoint without input
182+ const noInputEndpoint : Endpoint < string > = {
183+ action : ( ) => 'result'
184+ } ;
185+ expect ( noInputEndpoint . input ) . toBeUndefined ( ) ;
186+
187+ // Test endpoint with input
188+ const withInputEndpoint : Endpoint < number , typeof schema > = {
189+ input : schema ,
190+ action : ( input ) => input . test . length
191+ } ;
192+ expect ( withInputEndpoint . input ) . toBe ( schema ) ;
193+
194+ // Test endpoint with context
195+ type Context = { admin : boolean } ;
196+ const withContextEndpoint : Endpoint < boolean , undefined , Context > = {
197+ action : ( context ) => context . admin
198+ } ;
199+ expect ( withContextEndpoint . input ) . toBeUndefined ( ) ;
200+ } ) ;
201+ } ) ;
202+ } ) ;
0 commit comments