@@ -433,14 +433,72 @@ class ClientGeneratorTest {
433433 assertEquals(" kotlin.String" , baseUrl.type.toString())
434434 }
435435
436- // -- No security: constructor has only baseUrl --
436+ // -- No security: constructor has only baseUrl and httpClient --
437437
438438 @Test
439- fun `no security schemes generates constructor with only baseUrl` () {
439+ fun `no security schemes generates constructor with only baseUrl and httpClient ` () {
440440 val cls = clientClass(endpoint())
441441 val constructor = assertNotNull(cls.primaryConstructor)
442442 val paramNames = constructor .parameters.map { it.name }
443- assertEquals(listOf (" baseUrl" ), paramNames)
443+ assertEquals(listOf (" baseUrl" , " httpClient" ), paramNames)
444+ }
445+
446+ // -- Injectable HttpClient --
447+
448+ @Test
449+ fun `constructor takes a nullable httpClient defaulting to null` () {
450+ val cls = clientClass(endpoint())
451+ val httpClient = assertNotNull(cls.primaryConstructor).parameters.first { it.name == " httpClient" }
452+ assertEquals(" io.ktor.client.HttpClient?" , httpClient.type.toString())
453+ assertEquals(" null" , httpClient.defaultValue.toString())
454+ }
455+
456+ @Test
457+ fun `httpClient precedes auth params so a trailing token lambda still binds to the token` () {
458+ val bearer = clientClass(listOf (endpoint()), listOf (SecurityScheme .Bearer (" BearerAuth" )))
459+ assertEquals(
460+ listOf (" baseUrl" , " httpClient" , " token" ),
461+ assertNotNull(bearer.primaryConstructor).parameters.map { it.name },
462+ )
463+
464+ val basic = clientClass(listOf (endpoint()), listOf (SecurityScheme .Basic (" BasicAuth" )))
465+ assertEquals(
466+ listOf (" baseUrl" , " httpClient" , " basicAuthTestUsername" , " basicAuthTestPassword" ),
467+ assertNotNull(basic.primaryConstructor).parameters.map { it.name },
468+ )
469+ }
470+
471+ @Test
472+ fun `client has a single constructor` () {
473+ val cls = clientClass(listOf (endpoint()), listOf (SecurityScheme .Bearer (" BearerAuth" )))
474+ assertTrue(cls.funSpecs.none { it.isConstructor }, " Expected no secondary constructors" )
475+ }
476+
477+ @Test
478+ fun `client property is derived from the injected httpClient` () {
479+ val clientInitializer = clientClass(endpoint())
480+ .propertySpecs
481+ .first { it.name == " client" }
482+ .initializer
483+ .toString()
484+ assertEquals(" createHttpClient(httpClient)" , clientInitializer)
485+ }
486+
487+ @Test
488+ fun `httpClient constructor param has KDoc only when generateKdoc is enabled` () {
489+ fun httpClientKdoc (options : OutputOptions ): String = generate(spec(endpoint()), options = options)
490+ .first()
491+ .members
492+ .filterIsInstance<TypeSpec >()
493+ .first()
494+ .let { assertNotNull(it.primaryConstructor) }
495+ .parameters
496+ .first { it.name == " httpClient" }
497+ .kdoc
498+ .toString()
499+
500+ assertTrue(httpClientKdoc(OutputOptions (generateKdoc = true )).contains(" sharing its engine" ))
501+ assertTrue(httpClientKdoc(OutputOptions (generateKdoc = false )).isEmpty())
444502 }
445503
446504 // -- Pitfall 3: Untagged endpoints go to DefaultClient --
@@ -774,7 +832,11 @@ class ClientGeneratorTest {
774832 .first { it.name == " client" }
775833 .initializer
776834 .toString()
777- assertEquals(" createHttpClient()" , clientInitializer, " createHttpClient() no longer takes serializersModule" )
835+ assertEquals(
836+ " createHttpClient(httpClient)" ,
837+ clientInitializer,
838+ " createHttpClient() no longer takes serializersModule" ,
839+ )
778840 }
779841
780842 // -- CONT-01: Multipart form-data code generation --
@@ -1189,7 +1251,10 @@ class ClientGeneratorTest {
11891251 .propertySpecs
11901252 .first { it.name == " client" }
11911253 val clientInitializer = clientProperty.initializer.toString()
1192- assertTrue(clientInitializer.contains(" createHttpClient()" ), " Expected plain createHttpClient()" )
1254+ assertTrue(
1255+ clientInitializer.contains(" createHttpClient(httpClient)" ),
1256+ " Expected plain createHttpClient(httpClient)" ,
1257+ )
11931258 assertTrue(! clientInitializer.contains(" serializersModule" ), " Expected no serializersModule" )
11941259 }
11951260
@@ -1301,9 +1366,9 @@ class ClientGeneratorTest {
13011366 val constructor = assertNotNull(cls.primaryConstructor)
13021367 val paramNames = constructor .parameters.map { it.name }
13031368 assertEquals(
1304- listOf (" baseUrl" ),
1369+ listOf (" baseUrl" , " httpClient " ),
13051370 paramNames,
1306- " Expected only baseUrl param when security is explicitly empty" ,
1371+ " Expected only baseUrl and httpClient params when security is explicitly empty" ,
13071372 )
13081373 }
13091374
@@ -1391,7 +1456,7 @@ class ClientGeneratorTest {
13911456 )
13921457 val constructor = assertNotNull(cls.primaryConstructor)
13931458 val paramNames = constructor .parameters.map { it.name }
1394- assertEquals(listOf (" baseUrl" , " token" ), paramNames, " Single Bearer should use plain token param" )
1459+ assertEquals(listOf (" baseUrl" , " httpClient " , " token" ), paramNames, " Single Bearer should use plain token param" )
13951460 }
13961461
13971462 @Test
0 commit comments