Skip to content

Commit

Permalink
server: The /health endpoint fails if there are more than 10 missing …
Browse files Browse the repository at this point in the history
…blocks
  • Loading branch information
AlexITC committed Jun 9, 2019
1 parent ab8ae6f commit f1b0f32
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.xsn.explorer.models

case class SynchronizationProgress(total: Int, synced: Int) {
def missing: Int = Math.max(0, total - synced)
}
14 changes: 13 additions & 1 deletion server/app/com/xsn/explorer/services/StatisticsService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.xsn.explorer.services
import com.alexitc.playsonify.core.FutureApplicationResult
import com.alexitc.playsonify.core.FutureOr.Implicits.FutureOps
import com.xsn.explorer.data.async.StatisticsFutureDataHandler
import com.xsn.explorer.models.StatisticsDetails
import com.xsn.explorer.models.{StatisticsDetails, SynchronizationProgress}
import javax.inject.Inject
import org.scalactic.{Bad, Good}

Expand All @@ -27,6 +27,18 @@ class StatisticsService @Inject()(xsnService: XSNService, statisticsFutureDataHa
result.toFuture
}

def getSynchronizationProgress: FutureApplicationResult[SynchronizationProgress] = {
val dbStats = statisticsFutureDataHandler.getStatistics()
val rpcBlock = xsnService.getLatestBlock()

val result = for {
stats <- dbStats.toFutureOr
latestBlock <- rpcBlock.toFutureOr
} yield SynchronizationProgress(total = latestBlock.height.int, synced = stats.blocks)

result.toFuture
}

private def discardErrors[T](value: FutureApplicationResult[T]): FutureApplicationResult[Option[T]] = {
value
.map {
Expand Down
20 changes: 15 additions & 5 deletions server/app/controllers/HealthController.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
package controllers

import javax.inject.Inject

import com.xsn.explorer.services.StatisticsService
import controllers.common.{MyJsonController, MyJsonControllerComponents}
import javax.inject.Inject
import org.scalactic.Good

class HealthController @Inject()(cc: MyJsonControllerComponents) extends MyJsonController(cc) {
class HealthController @Inject()(cc: MyJsonControllerComponents, statsService: StatisticsService)
extends MyJsonController(cc) {

def check() = Action {
Ok
def check() = Action.async { _ =>
statsService.getSynchronizationProgress
.map {
case Good(progress) if progress.missing < 10 => Ok
case Good(progress) => InternalServerError(s"There are ${progress.missing} missing blocks")
case _ => InternalServerError("Failed to check sync progress")
}
.recover {
case _: Throwable => InternalServerError("Failed to check sync progress")
}
}
}
32 changes: 31 additions & 1 deletion server/test/controllers/HealthControllerSpec.scala
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
package controllers

import com.xsn.explorer.data.StatisticsBlockingDataHandler
import com.xsn.explorer.helpers.DataGenerator
import com.xsn.explorer.models._
import com.xsn.explorer.models.values._
import com.xsn.explorer.services.XSNService
import controllers.common.MyAPISpec
import org.mockito.MockitoSugar._
import org.scalactic.Good
import play.api.Application
import play.api.inject.bind
import play.api.test.Helpers._

import scala.concurrent.Future

class HealthControllerSpec extends MyAPISpec {

val application: Application = guiceApplicationBuilder.build()
private val xsnServiceMock = mock[XSNService]
private val statisticsDataHandlerMock = mock[StatisticsBlockingDataHandler]

val application: Application = guiceApplicationBuilder
.overrides(bind[XSNService].to(xsnServiceMock))
.overrides(bind[StatisticsBlockingDataHandler].to(statisticsDataHandlerMock))
.build()

"GET /health" should {
"return OK" in {
val latestXSNBlock = DataGenerator.randomBlock().copy(height = Height(19))
val stats = Statistics(10, 0, None, None)
when(xsnServiceMock.getLatestBlock()).thenReturn(Future.successful(Good(latestXSNBlock)))
when(statisticsDataHandlerMock.getStatistics()).thenReturn(Good(stats))
val response = GET("/health")

status(response) mustEqual OK
}

"return Internal Server Error if there are 10 missing blocks" in {
val latestXSNBlock = DataGenerator.randomBlock().copy(height = Height(20))
val stats = Statistics(10, 0, None, None)
when(xsnServiceMock.getLatestBlock()).thenReturn(Future.successful(Good(latestXSNBlock)))
when(statisticsDataHandlerMock.getStatistics()).thenReturn(Good(stats))
val response = GET("/health")

status(response) mustEqual INTERNAL_SERVER_ERROR
}
}
}

0 comments on commit f1b0f32

Please sign in to comment.