From a997aa6539d1f0af4ab4fc395ff2033335da312a Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 30 Jun 2017 12:29:54 -0700 Subject: Latest PDS UI utils --- .../scala/xyz/driver/pdsuicommon/BaseSuite.scala | 23 +-- src/test/scala/xyz/driver/pdsuicommon/Mocks.scala | 37 ++-- .../BridgeUploadQueueRepositoryAdapterSuite.scala | 207 +++++++++++++-------- 3 files changed, 154 insertions(+), 113 deletions(-) (limited to 'src/test/scala/xyz/driver') diff --git a/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala b/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala index 656ee24..71f8ebf 100644 --- a/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala +++ b/src/test/scala/xyz/driver/pdsuicommon/BaseSuite.scala @@ -5,18 +5,15 @@ import java.time.{LocalDateTime, ZoneId} import org.scalatest.FreeSpecLike import org.scalatest.concurrent.ScalaFutures import org.scalatest.time.{Millis, Span} -import xyz.driver.pdsuicommon.db.{MysqlQueryBuilder, SearchFilterExpr, SqlContext, Transactions} +import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.domain.{Email, LongId, PasswordHash, User} import xyz.driver.pdsuicommon.error.UnexpectedFilterException import xyz.driver.pdsuicommon.utils.DiffUtils -import scala.concurrent.ExecutionContext.Implicits._ -import scala.concurrent.Future - trait BaseSuite extends FreeSpecLike with DiffUtils with ScalaFutures { implicit val defaultPatience = PatienceConfig(timeout = Span(1000, Millis), interval = Span(20, Millis)) - implicit val sqlContext = new MockSqlContext(global) + implicit val sqlContext = new MockMySqlContext() def sampleUser(role: User.Role, email: String = "test@example.com", password: String = "123") = User( id = LongId(2001), @@ -30,20 +27,10 @@ trait BaseSuite extends FreeSpecLike with DiffUtils with ScalaFutures { def createMockQueryBuilder[T](isExpectedFilter: SearchFilterExpr => Boolean): MysqlQueryBuilder[T] = { MockQueryBuilder[T] { - case (filter, _, _) if isExpectedFilter(filter) => - Future.successful(Seq.empty) - case (filter, _, _) => - Future.failed(new UnexpectedFilterException(s"Filter is unexpected: $filter")) + case (filter, _, _) if isExpectedFilter(filter) => Seq.empty + case (filter, _, _) => throw new UnexpectedFilterException(s"Filter is unexpected: $filter") } { - case _ => - Future.successful((0, Option.empty[LocalDateTime])) + case _ => (0, Option.empty[LocalDateTime]) } } - - def transactions = new Transactions { - override def run[T](f: (SqlContext) => T): Future[T] = { - Future(f(sqlContext)) - } - } - } diff --git a/src/test/scala/xyz/driver/pdsuicommon/Mocks.scala b/src/test/scala/xyz/driver/pdsuicommon/Mocks.scala index d4b4d3c..1c01483 100644 --- a/src/test/scala/xyz/driver/pdsuicommon/Mocks.scala +++ b/src/test/scala/xyz/driver/pdsuicommon/Mocks.scala @@ -10,7 +10,7 @@ import com.typesafe.config.ConfigFactory import xyz.driver.pdsuicommon.db._ import xyz.driver.pdsuicommon.http.HttpFetcher -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.Future class MockDataSource extends DataSource with Closeable { override def getConnection: Connection = throw new NotImplementedError("MockDataSource.getConnection") @@ -27,10 +27,10 @@ class MockDataSource extends DataSource with Closeable { override def isWrapperFor(iface: Class[_]): Boolean = throw new NotImplementedError("MockDataSource.isWrapperFor") } -object MockSqlContext { +object MockMySqlContext { - val Settings = SqlContext.Settings( - credentials = SqlContext.DbCredentials( + val Settings = MySqlContext.Settings( + credentials = MySqlContext.DbCredentials( user = "test", password = "test", host = "localhost", @@ -45,18 +45,16 @@ object MockSqlContext { connectionAttemptsOnStartup = 1, threadPoolSize = 10 ) - } -class MockSqlContext(ec: ExecutionContext) extends SqlContext(new MockDataSource, MockSqlContext.Settings) { - override implicit val executionContext = ec - override protected def withConnection[T](f: Connection => T) = { +class MockMySqlContext() extends MySqlContext(new MockDataSource, MockMySqlContext.Settings) { + override protected def withConnection[T](f: Connection => T): Nothing = { throw new NotImplementedError("MockSqlContext.withConnection") } } -class MockFactory()(implicit val sqlContext: SqlContext) { - val MockHttpFetcher: HttpFetcher = (url: URL) => { +class MockFactory()(implicit val sqlContext: MySqlContext) { + val MockHttpFetcher: HttpFetcher = { (url: URL) => Future.successful(Array.empty[Byte]) } } @@ -64,25 +62,28 @@ class MockFactory()(implicit val sqlContext: SqlContext) { object MockQueryBuilder { type MockRunnerIn = (SearchFilterExpr, Sorting, Option[Pagination]) - type MockRunnerOut[T] = Future[Seq[T]] - type MockCountRunnerOut = Future[QueryBuilder.CountResult] + type MockRunnerOut[T] = Seq[T] + type MockCountRunnerOut = QueryBuilder.CountResult def apply[T](matcher: PartialFunction[MockRunnerIn, MockRunnerOut[T]])( countMatcher: PartialFunction[MockRunnerIn, MockCountRunnerOut])( - implicit context: SqlContext): MysqlQueryBuilder[T] = { - def runner(parameters: QueryBuilderParameters): MockRunnerOut[T] = { + implicit context: MySqlContext): MysqlQueryBuilder[T] = { + + val runner: QueryBuilder.Runner[T] = { parameters => matcher((parameters.filter, parameters.sorting, parameters.pagination)) } - def countRunner(parameters: QueryBuilderParameters): MockCountRunnerOut = { + + val countRunner: QueryBuilder.CountRunner = { parameters => countMatcher((parameters.filter, parameters.sorting, parameters.pagination)) } + MysqlQueryBuilder[T]( tableName = "", lastUpdateFieldName = Option.empty[String], nullableFields = Set.empty[String], links = Set.empty[TableLink], - runner = runner _, - countRunner = countRunner _ - )(context.executionContext) + runner = runner, + countRunner = countRunner + ) } } diff --git a/src/test/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapterSuite.scala b/src/test/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapterSuite.scala index d0dbb04..8b38316 100644 --- a/src/test/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapterSuite.scala +++ b/src/test/scala/xyz/driver/pdsuicommon/concurrent/BridgeUploadQueueRepositoryAdapterSuite.scala @@ -3,11 +3,10 @@ package xyz.driver.pdsuicommon.concurrent import java.util.concurrent.ThreadLocalRandom import xyz.driver.pdsuicommon.BaseSuite -import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueue.Item import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueueRepositoryAdapter.Strategy import xyz.driver.pdsuicommon.concurrent.BridgeUploadQueueRepositoryAdapter.Strategy.{OnAttempt, OnComplete} +import xyz.driver.pdsuicommon.db.{FakeDbIo, MysqlQueryBuilder} import xyz.driver.pdsuicommon.db.repositories.BridgeUploadQueueRepository -import xyz.driver.pdsuicommon.domain.LongId import scala.concurrent.Future import scala.concurrent.duration.DurationInt @@ -48,6 +47,71 @@ class BridgeUploadQueueRepositoryAdapterSuite extends BaseSuite { } } + "complete" - { + "onComplete == mark" - { + "should update the item" in { + var done = false + val item = defaultItem + + val repository = new BridgeUploadQueueRepository { + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + + override def delete(kind: String, tag: String): Unit = throw new IllegalStateException("Impossible call") + + override def update(entity: EntityT): EntityT = { + assert(entity.kind == item.kind, "repository.delete, kind") + assert(entity.tag == item.tag, "repository.delete, tag") + done = true + entity + } + + override def getById(kind: String, tag: String): Option[EntityT] = Some(item) + } + + val adapter = new BridgeUploadQueueRepositoryAdapter( + strategy = Strategy.Stop(OnComplete.Mark), + repository = repository, + dbIo = FakeDbIo + ) + + assert(adapter.complete(item.kind, item.tag).isReadyWithin(100.millis)) + assert(done) + } + } + + "onComplete == delete" - { + "should delete the item" in { + var done = false + val item = defaultItem + + val repository = new BridgeUploadQueueRepository { + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + override def getById(kind: String, tag: String): Option[EntityT] = fail("getById should not be used!") + + override def delete(kind: String, tag: String): Unit = { + assert(kind == item.kind, "repository.delete, kind") + assert(tag == item.tag, "repository.delete, tag") + done = true + } + override def update(entity: EntityT): EntityT = throw new IllegalStateException("Impossible call") + } + + val adapter = new BridgeUploadQueueRepositoryAdapter( + strategy = Strategy.Stop(OnComplete.Delete), + repository = repository, + dbIo = FakeDbIo + ) + + assert(adapter.complete(item.kind, item.tag).isReadyWithin(100.millis)) + assert(done) + } + } + } + "tryRetry" - { "when all attempts are not out" - { @@ -56,19 +120,19 @@ class BridgeUploadQueueRepositoryAdapterSuite extends BaseSuite { "should return an updated item" in { val repository = new BridgeUploadQueueRepository { - override def update(draft: EntityT): EntityT = draft - override def delete(id: IdT): Unit = {} - override def add(draft: EntityT): EntityT = fail("add should not be used!") - override def getById(id: LongId[EntityT]): Option[EntityT] = fail("getById should not be used!") - override def isCompleted(kind: String, tag: String): Future[Boolean] = - fail("isCompleted should not be used!") - override def getOne(kind: String): Future[Option[Item]] = fail("getOne should not be used!") + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + override def getById(kind: String, tag: String): Option[EntityT] = fail("getById should not be used!") + + override def update(draft: EntityT): EntityT = draft + override def delete(kind: String, tag: String): Unit = throw new IllegalAccessError(s"kind=$kind, tag=$tag") } val adapter = new BridgeUploadQueueRepositoryAdapter( strategy = defaultStrategy, repository = repository, - transactions = transactions + dbIo = FakeDbIo ) val item = defaultItem @@ -77,147 +141,136 @@ class BridgeUploadQueueRepositoryAdapterSuite extends BaseSuite { assert(!r.contains(item)) } - "should add an item with increased attempts" in { + "should update an item with increased attempts" in { val item = defaultItem val repository = new BridgeUploadQueueRepository { + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + override def getById(kind: String, tag: String): Option[EntityT] = fail("getById should not be used!") + override def update(draft: EntityT): EntityT = { assert(draft.attempts === (item.attempts + 1), "repository.add") draft } - override def delete(id: IdT): Unit = {} - override def add(draft: EntityT): EntityT = fail("add should not be used!") - override def getById(id: LongId[EntityT]): Option[EntityT] = fail("getById should not be used!") - override def isCompleted(kind: String, tag: String): Future[Boolean] = - fail("isCompleted should not be used!") - override def getOne(kind: String): Future[Option[Item]] = fail("getOne should not be used!") + override def delete(kind: String, tag: String): Unit = throw new IllegalAccessError(s"kind=$kind, tag=$tag") } val adapter = new BridgeUploadQueueRepositoryAdapter( strategy = defaultStrategy, repository = repository, - transactions = transactions + dbIo = FakeDbIo ) - adapter.tryRetry(item).isReadyWithin(100.millis) + assert(adapter.tryRetry(item).isReadyWithin(100.millis)) } "should remove an old item" in { val item = defaultItem val repository = new BridgeUploadQueueRepository { - override def update(draft: EntityT): EntityT = draft - override def delete(id: IdT): Unit = { - assert(id == item.id, "repository.delete") + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + override def getById(kind: String, tag: String): Option[EntityT] = fail("getById should not be used!") + override def update(draft: EntityT): EntityT = draft + override def delete(kind: String, tag: String): Unit = { + assert(kind == item.kind, "repository.delete, kind") + assert(tag == item.tag, "repository.delete, kind") } - override def add(draft: EntityT): EntityT = fail("add should not be used!") - override def getById(id: LongId[EntityT]): Option[EntityT] = fail("getById should not be used!") - override def isCompleted(kind: String, tag: String): Future[Boolean] = - fail("isCompleted should not be used!") - override def getOne(kind: String): Future[Option[Item]] = fail("getOne should not be used!") } val adapter = new BridgeUploadQueueRepositoryAdapter( strategy = defaultStrategy, repository = repository, - transactions = transactions + dbIo = FakeDbIo ) - adapter.tryRetry(item).isReadyWithin(100.millis) + assert(adapter.tryRetry(item).isReadyWithin(100.millis)) } "should update time of the next attempt" in { val item = defaultItem val repository = new BridgeUploadQueueRepository { + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + override def getById(kind: String, tag: String): Option[EntityT] = fail("getById should not be used!") + override def update(draft: EntityT): EntityT = { assert(draft.nextAttempt.isAfter(item.nextAttempt), "repository.add") draft } - override def delete(id: IdT): Unit = {} - override def add(draft: EntityT): EntityT = fail("add should not be used!") - override def getById(id: LongId[EntityT]): Option[EntityT] = fail("getById should not be used!") - override def isCompleted(kind: String, tag: String): Future[Boolean] = - fail("isCompleted should not be used!") - override def getOne(kind: String): Future[Option[Item]] = fail("getOne should not be used!") + override def delete(kind: String, tag: String): Unit = throw new IllegalAccessError(s"kind=$kind, tag=$tag") } val adapter = new BridgeUploadQueueRepositoryAdapter( strategy = defaultStrategy, repository = repository, - transactions = transactions + dbIo = FakeDbIo ) - adapter.tryRetry(item).isReadyWithin(100.millis) + assert(adapter.tryRetry(item).isReadyWithin(100.millis)) } } "when all attempts are out" - { - val defaultStrategy = Strategy.Ignore + val defaultStrategy = Strategy.Stop() "should not return an item" in { val repository = new BridgeUploadQueueRepository { - override def delete(id: IdT): Unit = {} - override def update(entity: EntityT): EntityT = fail("update should not be used!") - override def add(draft: EntityT): EntityT = fail("add should not be used!") - override def getById(id: LongId[EntityT]): Option[EntityT] = fail("getById should not be used!") - override def isCompleted(kind: String, tag: String): Future[Boolean] = - fail("isCompleted should not be used!") - override def getOne(kind: String): Future[Option[Item]] = fail("getOne should not be used!") + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + override def getById(kind: String, tag: String): Option[EntityT] = fail("getById should not be used!") + override def update(entity: EntityT): EntityT = fail("update should not be used!") + + override def delete(kind: String, tag: String): Unit = {} } val adapter = new BridgeUploadQueueRepositoryAdapter( strategy = defaultStrategy, repository = repository, - transactions = transactions + dbIo = FakeDbIo ) val r = adapter.tryRetry(defaultItem).futureValue assert(r.isEmpty) } - "should not add any item to the queue" in { + "should complete the item" in { + var taskWasCompleted = false + val item = defaultItem + val repository = new BridgeUploadQueueRepository { - override def update(draft: EntityT): EntityT = throw new IllegalAccessException("add should not be called") - override def delete(id: IdT): Unit = {} - override def add(draft: EntityT): EntityT = fail("add should not be used!") - override def getById(id: LongId[EntityT]): Option[EntityT] = fail("getById should not be used!") - override def isCompleted(kind: String, tag: String): Future[Boolean] = - fail("isCompleted should not be used!") - override def getOne(kind: String): Future[Option[Item]] = fail("getOne should not be used!") + override def add(draft: EntityT): EntityT = draft + override def getOne(kind: String): Option[EntityT] = fail("getOne should not be used!") + override def buildQuery: MysqlQueryBuilder[EntityT] = fail("buildQuery should not be used!") + override def getById(kind: String, tag: String): Option[EntityT] = fail("getById should not be used!") + override def update(entity: EntityT): EntityT = fail("update should not be used!") + + override def delete(kind: String, tag: String): Unit = {} } val adapter = new BridgeUploadQueueRepositoryAdapter( strategy = defaultStrategy, repository = repository, - transactions = transactions - ) - - adapter.tryRetry(defaultItem).isReadyWithin(100.millis) - } - - "should remove the item from the queue" in { - val repository = new BridgeUploadQueueRepository { - override def delete(id: IdT): Unit = { - assert(id == defaultItem.id, "repository.delete") + dbIo = FakeDbIo + ) { + override def complete(kind: String, tag: String): Future[Unit] = Future { + assert(kind == item.kind, "adapter.complete, kind") + assert(tag == item.tag, "adapter.complete, tag") + taskWasCompleted = true } - override def update(entity: EntityT): EntityT = fail("update should not be used!") - override def add(draft: EntityT): EntityT = fail("add should not be used!") - override def getById(id: LongId[EntityT]): Option[EntityT] = fail("getById should not be used!") - override def isCompleted(kind: String, tag: String): Future[Boolean] = - fail("isCompleted should not be used!") - override def getOne(kind: String): Future[Option[Item]] = fail("getOne should not be used!") } - val adapter = new BridgeUploadQueueRepositoryAdapter( - strategy = defaultStrategy, - repository = repository, - transactions = transactions - ) - - adapter.tryRetry(defaultItem).isReadyWithin(100.millis) + val r = adapter.tryRetry(item).futureValue + assert(r.isEmpty) + assert(taskWasCompleted) } } -- cgit v1.2.3