/misk-service/src/test/kotlin/misk/ServiceManagerModuleTest.kt

https://github.com/cashapp/misk · Kotlin · 236 lines · 206 code · 26 blank · 4 comment · 0 complexity · cc4aa05440951c73bc0b7923b9768716 MD5 · raw file

  1. package misk
  2. import com.google.common.util.concurrent.AbstractIdleService
  3. import com.google.common.util.concurrent.AbstractService
  4. import com.google.common.util.concurrent.Service
  5. import com.google.common.util.concurrent.ServiceManager
  6. import com.google.inject.Guice
  7. import com.google.inject.Provides
  8. import com.google.inject.Scopes
  9. import com.google.inject.Singleton
  10. import javax.inject.Inject
  11. import kotlin.test.assertFailsWith
  12. import misk.inject.KAbstractModule
  13. import misk.inject.getInstance
  14. import misk.inject.keyOf
  15. import org.assertj.core.api.Assertions.assertThat
  16. import org.junit.jupiter.api.Test
  17. internal class ServiceManagerModuleTest {
  18. @com.google.inject.Singleton
  19. class SingletonService1 : AbstractIdleService() {
  20. override fun startUp() {}
  21. override fun shutDown() {}
  22. }
  23. @javax.inject.Singleton
  24. class SingletonService2 : AbstractIdleService() {
  25. override fun startUp() {}
  26. override fun shutDown() {}
  27. }
  28. class NonSingletonService1 : AbstractIdleService() {
  29. override fun startUp() {}
  30. override fun shutDown() {}
  31. }
  32. class NonSingletonService2 : AbstractIdleService() {
  33. override fun startUp() {}
  34. override fun shutDown() {}
  35. }
  36. class ExplicitEagerSingletonService : AbstractIdleService() {
  37. override fun startUp() {}
  38. override fun shutDown() {}
  39. }
  40. class SingletonScopeService : AbstractIdleService() {
  41. override fun startUp() {}
  42. override fun shutDown() {}
  43. }
  44. class SingletonAnnotationService : AbstractIdleService() {
  45. override fun startUp() {}
  46. override fun shutDown() {}
  47. }
  48. class GoogleSingletonAnnotationService : AbstractIdleService() {
  49. override fun startUp() {}
  50. override fun shutDown() {}
  51. }
  52. class InstanceService : AbstractIdleService() {
  53. override fun startUp() {}
  54. override fun shutDown() {}
  55. }
  56. class ProvidesMethodService : AbstractIdleService() {
  57. override fun startUp() {}
  58. override fun shutDown() {}
  59. }
  60. @Test fun multibindingServicesThrows() {
  61. assertThat(
  62. assertFailsWith<com.google.inject.ProvisionException> {
  63. val injector = Guice.createInjector(
  64. MiskTestingServiceModule(),
  65. object : KAbstractModule() {
  66. override fun configure() {
  67. // Multibinding services is not supported. Expect that we throw for the first service
  68. // that was bound.
  69. multibind<Service>().to<SingletonService1>()
  70. multibind<Service>().to<SingletonService2>()
  71. }
  72. }
  73. )
  74. injector.getInstance<ServiceManager>()
  75. }.message
  76. ).contains(
  77. "This doesn't work anymore! " +
  78. "Instead of using `multibind<Service>().to(SingletonService1)`, " +
  79. "use `install(ServiceModule<SingletonService1>())`."
  80. )
  81. }
  82. @Test fun detectsNonSingletonServiceEntries() {
  83. assertThat(
  84. assertFailsWith<com.google.inject.ProvisionException> {
  85. val injector = Guice.createInjector(
  86. MiskTestingServiceModule(),
  87. object : KAbstractModule() {
  88. override fun configure() {
  89. // Should be recognized as singletons
  90. install(ServiceModule<SingletonService1>())
  91. install(ServiceModule<SingletonService2>())
  92. install(ServiceModule<ProvidesMethodService>())
  93. install(ServiceModule<InstanceService>())
  94. bind(keyOf<InstanceService>()).toInstance(
  95. InstanceService()
  96. )
  97. install(
  98. ServiceModule<ExplicitEagerSingletonService>()
  99. )
  100. bind(keyOf<ExplicitEagerSingletonService>()).asEagerSingleton()
  101. install(ServiceModule<SingletonScopeService>())
  102. bind(keyOf<SingletonScopeService>())
  103. .`in`(Scopes.SINGLETON)
  104. install(ServiceModule<SingletonAnnotationService>())
  105. bind(keyOf<SingletonAnnotationService>())
  106. .`in`(com.google.inject.Singleton::class.java)
  107. install(
  108. ServiceModule<GoogleSingletonAnnotationService>()
  109. )
  110. bind(keyOf<GoogleSingletonAnnotationService>())
  111. .`in`(com.google.inject.Singleton::class.java)
  112. // Should be recognized as non-singletons
  113. install(ServiceModule<NonSingletonService1>())
  114. install(ServiceModule<NonSingletonService2>())
  115. }
  116. @Provides @Singleton
  117. fun providesSingletonService(): ProvidesMethodService = ProvidesMethodService()
  118. }
  119. )
  120. injector.getInstance<ServiceManager>()
  121. }.message
  122. ).contains(
  123. "the following services are not marked as @Singleton: " +
  124. "misk.ServiceManagerModuleTest\$NonSingletonService1, " +
  125. "misk.ServiceManagerModuleTest\$NonSingletonService2"
  126. )
  127. }
  128. @Singleton
  129. class ProducerService @Inject constructor(
  130. private val log: StringBuilder
  131. ) : AbstractService() {
  132. init {
  133. log.append("ProducerService.init\n")
  134. }
  135. override fun doStart() {
  136. log.append("ProducerService.startUp\n")
  137. notifyStarted()
  138. }
  139. override fun doStop() {
  140. log.append("ProducerService.shutDown\n")
  141. notifyStopped()
  142. }
  143. }
  144. @Singleton
  145. class ConsumerService @Inject constructor(
  146. private val log: StringBuilder
  147. ) : AbstractService() {
  148. init {
  149. log.append("ConsumerService.init\n")
  150. }
  151. override fun doStart() {
  152. log.append("ConsumerService.startUp\n")
  153. notifyStarted()
  154. }
  155. override fun doStop() {
  156. log.append("ConsumerService.shutDown\n")
  157. notifyStopped()
  158. }
  159. }
  160. @Singleton
  161. class AnotherUpstreamService @Inject constructor(
  162. private val log: StringBuilder
  163. ) : AbstractService() {
  164. init {
  165. log.append("AnotherUpstreamService.init\n")
  166. }
  167. override fun doStart() {
  168. log.append("AnotherUpstreamService.startUp\n")
  169. notifyStarted()
  170. }
  171. override fun doStop() {
  172. log.append("AnotherUpstreamService.shutDown\n")
  173. notifyStopped()
  174. }
  175. }
  176. @Test fun serviceNotProvidedUntilAllDependenciesCreated() {
  177. val log = StringBuilder()
  178. val injector = Guice.createInjector(
  179. MiskTestingServiceModule(),
  180. object : KAbstractModule() {
  181. override fun configure() {
  182. bind<StringBuilder>().toInstance(log)
  183. install(ServiceModule<ProducerService>())
  184. install(ServiceModule<ConsumerService>()
  185. .dependsOn<ProducerService>()
  186. .dependsOn<AnotherUpstreamService>())
  187. install(ServiceModule<AnotherUpstreamService>())
  188. }
  189. }
  190. )
  191. val serviceManager = injector.getInstance<ServiceManager>()
  192. serviceManager.startAsync()
  193. serviceManager.awaitHealthy()
  194. log.append("healthy\n")
  195. serviceManager.stopAsync()
  196. serviceManager.awaitStopped()
  197. assertThat(log.toString()).isEqualTo("""
  198. |ProducerService.init
  199. |ProducerService.startUp
  200. |AnotherUpstreamService.init
  201. |AnotherUpstreamService.startUp
  202. |ConsumerService.init
  203. |ConsumerService.startUp
  204. |healthy
  205. |ConsumerService.shutDown
  206. |ProducerService.shutDown
  207. |AnotherUpstreamService.shutDown
  208. |""".trimMargin())
  209. }
  210. }