@@ -41,15 +41,32 @@ const serviceMethodGenSpec = {
4141 parameters : [ { name : 'limit' , in : 'query' , type : 'integer' } ] // No 'schema' key
4242 }
4343 } ,
44- '/post-no-req-schema' : {
44+ // NEW paths for coverage
45+ '/post-infer-return' : {
46+ post : {
47+ tags : [ 'ResponseType' ] ,
48+ operationId : 'postInferReturn' ,
49+ requestBody : { content : { 'application/json' : { schema : { $ref : '#/components/schemas/BodyModel' } } } } ,
50+ responses : { '400' : { description : 'Bad Request' } } // No 2xx response
51+ }
52+ } ,
53+ '/body-no-schema' : {
4554 post : {
46- operationId : 'postNoReqSchema' ,
4755 tags : [ 'ResponseType' ] ,
56+ operationId : 'postBodyNoSchema' ,
4857 requestBody : { content : { 'application/json' : { } } } , // Body exists, but no schema
4958 responses : { '204' : { } }
5059 }
5160 } ,
52- // NEW paths for coverage
61+ '/multipart-no-params' : {
62+ post : {
63+ tags : [ 'FormData' ] ,
64+ operationId : 'postMultipartNoParams' ,
65+ consumes : [ 'multipart/form-data' ] ,
66+ // No `parameters` array with `in: 'formData'`
67+ responses : { '200' : { } }
68+ }
69+ } ,
5370 '/all-required/{id}' : {
5471 post : {
5572 tags : [ 'RequiredParams' ] ,
@@ -172,9 +189,8 @@ describe('Emitter: ServiceMethodGenerator', () => {
172189 } ) ;
173190
174191 describe ( 'Parameter and Body Generation' , ( ) => {
175- const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
176-
177192 it ( 'should handle multipart/form-data' , ( ) => {
193+ const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
178194 const op = parser . operations . find ( o => o . operationId === 'postMultipart' ) ! ;
179195 op . methodName = 'postMultipart' ;
180196 methodGen . addServiceMethod ( serviceClass , op ) ;
@@ -184,7 +200,19 @@ describe('Emitter: ServiceMethodGenerator', () => {
184200 expect ( body ) . toContain ( "return this.http.post(url, formData, requestOptions);" ) ;
185201 } ) ;
186202
203+ it ( 'should handle multipart/form-data with no formData params' , ( ) => {
204+ const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
205+ const op = parser . operations . find ( o => o . operationId === 'postMultipartNoParams' ) ! ;
206+ op . methodName = 'postMultipartNoParams' ;
207+ methodGen . addServiceMethod ( serviceClass , op ) ;
208+ const body = serviceClass . getMethodOrThrow ( 'postMultipartNoParams' ) . getBodyText ( ) ! ;
209+ // It should not generate FormData logic and fall back to a null body.
210+ expect ( body ) . not . toContain ( 'new FormData()' ) ;
211+ expect ( body ) . toContain ( 'return this.http.post(url, null, requestOptions);' ) ;
212+ } ) ;
213+
187214 it ( 'should handle application/x-www-form-urlencoded' , ( ) => {
215+ const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
188216 const op = parser . operations . find ( o => o . operationId === 'postUrlencoded' ) ! ;
189217 op . methodName = 'postUrlencoded' ;
190218 methodGen . addServiceMethod ( serviceClass , op ) ;
@@ -195,6 +223,7 @@ describe('Emitter: ServiceMethodGenerator', () => {
195223 } ) ;
196224
197225 it ( 'should handle Swagger 2.0 style parameters (without schema wrapper)' , ( ) => {
226+ const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
198227 const op = parser . operations . find ( o => o . operationId === 'getWithSwagger2Param' ) ! ;
199228 op . methodName = 'getWithSwagger2Param' ;
200229 methodGen . addServiceMethod ( serviceClass , op ) ;
@@ -204,6 +233,7 @@ describe('Emitter: ServiceMethodGenerator', () => {
204233 } ) ;
205234
206235 it ( 'should name the body parameter after the model type if it is an interface' , ( ) => {
236+ const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
207237 const op = parser . operations . find ( o => o . operationId === 'postAndReturn' ) ! ;
208238 op . methodName = 'postAndReturn' ;
209239 methodGen . addServiceMethod ( serviceClass , op ) ;
@@ -212,23 +242,13 @@ describe('Emitter: ServiceMethodGenerator', () => {
212242 expect ( param ?. getType ( ) . getText ( ) ) . toBe ( 'BodyModel' ) ;
213243 } ) ;
214244
215- it ( 'should name the body parameter `body` for primitive types' , ( ) => {
216- const operation = parser . operations . find ( op => op . operationId === 'primitiveBody' ) ! ;
217- operation . methodName = 'primitiveBody' ;
218- methodGen . addServiceMethod ( serviceClass , operation ) ;
219- const method = serviceClass . getMethodOrThrow ( 'primitiveBody' ) ;
220- const impl = method . getImplementation ( ) ! ;
221- const param = impl . getParameters ( ) . find ( p => p . getType ( ) . getText ( ) === 'string' ) ;
222- expect ( param ?. getName ( ) ) . toBe ( 'body' ) ;
223- } ) ;
224-
225- it ( 'should handle request body without a schema' , ( ) => {
245+ it ( 'should handle request body without a schema by creating an "unknown" body param' , ( ) => {
226246 const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
227- const op = parser . operations . find ( o => o . operationId === 'postNoReqSchema ' ) ! ;
228- op . methodName = 'postNoReqSchema ' ;
247+ const op = parser . operations . find ( o => o . operationId === 'postBodyNoSchema ' ) ! ;
248+ op . methodName = 'postBodyNoSchema ' ;
229249 methodGen . addServiceMethod ( serviceClass , op ) ;
230250
231- const method = serviceClass . getMethodOrThrow ( 'postNoReqSchema ' ) ;
251+ const method = serviceClass . getMethodOrThrow ( 'postBodyNoSchema ' ) ;
232252 const bodyParam = method . getParameters ( ) . find ( p => p . getName ( ) === 'body' ) ! ;
233253 expect ( bodyParam ) . toBeDefined ( ) ;
234254 expect ( bodyParam . getType ( ) . getText ( ) ) . toBe ( 'unknown' ) ;
@@ -248,60 +268,37 @@ describe('Emitter: ServiceMethodGenerator', () => {
248268 } ) ;
249269
250270 describe ( 'Response Type Resolution' , ( ) => {
251- const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
252-
253- it ( 'should fall back to "any" when a POST has a request body but no schema' , ( ) => {
254- const op = parser . operations . find ( o => o . operationId === 'postNoReqSchema' ) ! ;
255- op . methodName = 'postNoReqSchema' ;
256- // It has a 204 response, which means `getResponseType` will return `void`.
257- // Let's modify it to have no responses so it falls through.
258- op . responses = { } ;
271+ it ( 'should infer response type from request body on POST when no success response is defined' , ( ) => {
272+ const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
273+ const op = parser . operations . find ( o => o . operationId === 'postInferReturn' ) ! ;
274+ op . methodName = 'postInferReturn' ;
259275 methodGen . addServiceMethod ( serviceClass , op ) ;
260- const overload = serviceClass . getMethodOrThrow ( 'postNoReqSchema' ) . getOverloads ( ) [ 0 ] ;
261- expect ( overload . getReturnType ( ) . getText ( ) ) . toBe ( 'Observable<any>' ) ;
262- } ) ;
263-
264- it ( 'should correctly determine response type from requestBody for POST/PUT' , ( ) => {
265- const operation = parser . operations . find ( op => op . operationId === 'postAndReturn' ) ! ;
266- operation . methodName = 'postAndReturn' ;
267- methodGen . addServiceMethod ( serviceClass , operation ) ;
268- const overload = serviceClass . getMethodOrThrow ( 'postAndReturn' ) . getOverloads ( ) [ 0 ] ;
276+ const overload = serviceClass . getMethodOrThrow ( 'postInferReturn' ) . getOverloads ( ) [ 0 ] ;
269277 expect ( overload . getReturnType ( ) . getText ( ) ) . toBe ( 'Observable<BodyModel>' ) ;
270278 } ) ;
271279
272- it ( "should fall back to 'any' for responseType when no success response is defined" , ( ) => {
273- const operation = parser . operations . find ( op => op . operationId === 'getOAS2NoSchema' ) ! ;
280+ it ( "should fall back to 'any' for responseType when no success response or request body schema is defined" , ( ) => {
281+ const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
282+ const operation = parser . operations . find ( o => o . operationId === 'getOAS2NoSchema' ) ! ;
274283 operation . methodName = 'getOAS2NoSchema' ;
275284 methodGen . addServiceMethod ( serviceClass , operation ) ;
276285 const overload = serviceClass . getMethodOrThrow ( 'getOAS2NoSchema' ) . getOverloads ( ) [ 0 ] ;
277286 expect ( overload . getReturnType ( ) . getText ( ) ) . toBe ( 'Observable<any>' ) ;
278287 } ) ;
279288 } ) ;
280289
281- it ( 'should fall back to a generic `body: unknown` parameter for non-json content' , ( ) => {
282- const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
283- const operation = parser . operations . find ( op => op . operationId === 'allParams' ) ! ;
284- operation . methodName = 'allParams' ;
285- methodGen . addServiceMethod ( serviceClass , operation ) ;
286- const method = serviceClass . getMethodOrThrow ( 'allParams' ) ;
287- const impl = method . getImplementation ( ) ! ;
288- const param = impl . getParameters ( ) . find ( p => p . getName ( ) === 'body' ) ;
289- expect ( param ?. getType ( ) . getText ( ) ) . toBe ( 'unknown' ) ;
290- } ) ;
291-
292- it ( 'should generate query param logic when query params are present' , ( ) => {
290+ it ( 'should generate query param logic with correct nullish coalescing for options.params' , ( ) => {
293291 const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
294292 const operation = parser . operations . find ( op => op . operationId === 'withQuery' ) ! ;
295293 operation . methodName = 'withQuery' ;
296294 methodGen . addServiceMethod ( serviceClass , operation ) ;
297- const method = serviceClass . getMethodOrThrow ( 'withQuery' ) ;
298- const body = method . getImplementation ( ) ?. getBodyText ( ) ?? '' ;
295+ const body = serviceClass . getMethodOrThrow ( 'withQuery' ) . getImplementation ( ) ?. getBodyText ( ) ?? '' ;
299296 expect ( body ) . toContain ( `let params = new HttpParams({ fromObject: options?.params ?? {} });` ) ;
300297 expect ( body ) . toContain ( `if (search != null) { params = HttpParamsBuilder.addToHttpParams(params, search, 'search'); }` ) ;
301298 expect ( body ) . toContain ( `params,` ) ;
302299 } ) ;
303300
304- it ( 'should generate header param logic when header params are present ' , ( ) => {
301+ it ( 'should generate header param logic correctly ' , ( ) => {
305302 const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
306303 const operation = parser . operations . find ( op => op . operationId === 'withHeader' ) ! ;
307304 operation . methodName = 'withHeader' ;
@@ -312,13 +309,4 @@ describe('Emitter: ServiceMethodGenerator', () => {
312309 expect ( body ) . toContain ( `if (xCustomHeader != null) { headers = headers.set('X-Custom-Header', String(xCustomHeader)); }` ) ;
313310 expect ( body ) . toContain ( `headers,` ) ;
314311 } ) ;
315-
316- it ( "should handle OAS2 `type: 'file'` by creating an 'any' type parameter" , ( ) => {
317- const { methodGen, serviceClass, parser } = createTestEnvironment ( ) ;
318- const operation = parser . operations . find ( op => op . operationId === 'uploadFile' ) ! ;
319- operation . methodName = 'uploadFile' ;
320- methodGen . addServiceMethod ( serviceClass , operation ) ;
321- const param = serviceClass . getMethodOrThrow ( 'uploadFile' ) . getParameters ( ) . find ( p => p . getName ( ) === 'file' ) ;
322- expect ( param ?. getType ( ) . getText ( ) ) . toBe ( 'any' ) ;
323- } ) ;
324312} ) ;
0 commit comments