From 8fef53d44a57008dea411b882b12bc3d5d1ca2e0 Mon Sep 17 00:00:00 2001 From: Sergey Nastich Date: Wed, 12 Sep 2018 16:10:17 -0400 Subject: Add `Trimmed` tag and its logic (revisited) (#215) * Add option and iterable converters for transparent `@@ Trimmed` creation. * Move tagging stuff to a separate package - relieve `core.scala` from some extra code. * Add Tagging stuff and publishing section to README.md --- .../scala/xyz/driver/core/BlobStorageTest.scala | 1 + src/test/scala/xyz/driver/core/CoreTest.scala | 12 +++-- src/test/scala/xyz/driver/core/FileTest.scala | 48 ++++++++--------- src/test/scala/xyz/driver/core/JsonTest.scala | 11 ++-- .../scala/xyz/driver/core/rest/DriverAppTest.scala | 1 - .../xyz/driver/core/tagging/TaggingTest.scala | 63 ++++++++++++++++++++++ 6 files changed, 103 insertions(+), 33 deletions(-) create mode 100644 src/test/scala/xyz/driver/core/tagging/TaggingTest.scala (limited to 'src/test/scala') diff --git a/src/test/scala/xyz/driver/core/BlobStorageTest.scala b/src/test/scala/xyz/driver/core/BlobStorageTest.scala index 637f9e0..811cc60 100644 --- a/src/test/scala/xyz/driver/core/BlobStorageTest.scala +++ b/src/test/scala/xyz/driver/core/BlobStorageTest.scala @@ -12,6 +12,7 @@ import xyz.driver.core.storage.{BlobStorage, FileSystemBlobStorage} import scala.concurrent.Future import scala.concurrent.duration._ +import scala.language.postfixOps class BlobStorageTest extends FlatSpec with ScalaFutures { diff --git a/src/test/scala/xyz/driver/core/CoreTest.scala b/src/test/scala/xyz/driver/core/CoreTest.scala index d280d73..f448d24 100644 --- a/src/test/scala/xyz/driver/core/CoreTest.scala +++ b/src/test/scala/xyz/driver/core/CoreTest.scala @@ -34,8 +34,10 @@ class CoreTest extends FlatSpec with Matchers with MockitoSugar { (Id[String]("1234213") === Id[String]("213414")) should be(false) (Id[String]("213414") === Id[String]("1234213")) should be(false) - Seq(Id[String]("4"), Id[String]("3"), Id[String]("2"), Id[String]("1")).sorted should contain - theSameElementsInOrderAs(Seq(Id[String]("1"), Id[String]("2"), Id[String]("3"), Id[String]("4"))) + val ids = Seq(Id[String]("4"), Id[String]("3"), Id[String]("2"), Id[String]("1")) + val sorted = Seq(Id[String]("1"), Id[String]("2"), Id[String]("3"), Id[String]("4")) + + ids.sorted should contain theSameElementsInOrderAs sorted } it should "have type-safe conversions" in { @@ -68,8 +70,9 @@ class CoreTest extends FlatSpec with Matchers with MockitoSugar { (Name[String]("foo") === Name[String]("bar")) should be(false) (Name[String]("bar") === Name[String]("foo")) should be(false) - Seq(Name[String]("d"), Name[String]("cc"), Name[String]("a"), Name[String]("bbb")).sorted should contain - theSameElementsInOrderAs(Seq(Name[String]("a"), Name[String]("bbb"), Name[String]("cc"), Name[String]("d"))) + val names = Seq(Name[String]("d"), Name[String]("cc"), Name[String]("a"), Name[String]("bbb")) + val sorted = Seq(Name[String]("a"), Name[String]("bbb"), Name[String]("cc"), Name[String]("d")) + names.sorted should contain theSameElementsInOrderAs sorted } "Revision" should "have equality working correctly" in { @@ -81,4 +84,5 @@ class CoreTest extends FlatSpec with Matchers with MockitoSugar { (bla === foo) should be(false) (foo === bla) should be(false) } + } diff --git a/src/test/scala/xyz/driver/core/FileTest.scala b/src/test/scala/xyz/driver/core/FileTest.scala index 8728089..554a991 100644 --- a/src/test/scala/xyz/driver/core/FileTest.scala +++ b/src/test/scala/xyz/driver/core/FileTest.scala @@ -62,17 +62,17 @@ class FileTest extends FlatSpec with Matchers with MockitoSugar { val s3Storage = new S3Storage(amazonS3Mock, testBucket, scala.concurrent.ExecutionContext.global) - val filesBefore = Await.result(s3Storage.list(testDirPath).run, 10 seconds) + val filesBefore = Await.result(s3Storage.list(testDirPath).run, 10.seconds) filesBefore shouldBe empty - val fileExistsBeforeUpload = Await.result(s3Storage.exists(testFilePath), 10 seconds) + val fileExistsBeforeUpload = Await.result(s3Storage.exists(testFilePath), 10.seconds) fileExistsBeforeUpload should be(false) - Await.result(s3Storage.upload(sourceTestFile, testFilePath), 10 seconds) + Await.result(s3Storage.upload(sourceTestFile, testFilePath), 10.seconds) - val filesAfterUpload = Await.result(s3Storage.list(testDirPath).run, 10 seconds) + val filesAfterUpload = Await.result(s3Storage.list(testDirPath).run, 10.seconds) filesAfterUpload.size should be(1) - val fileExistsAfterUpload = Await.result(s3Storage.exists(testFilePath), 10 seconds) + val fileExistsAfterUpload = Await.result(s3Storage.exists(testFilePath), 10.seconds) fileExistsAfterUpload should be(true) val uploadedFileLine = filesAfterUpload.head uploadedFileLine.name should be(Name[File](testFileName)) @@ -80,15 +80,15 @@ class FileTest extends FlatSpec with Matchers with MockitoSugar { uploadedFileLine.revision.id.length should be > 0 uploadedFileLine.lastModificationDate.millis should be > 0L - val downloadedFile = Await.result(s3Storage.download(testFilePath).run, 10 seconds) + val downloadedFile = Await.result(s3Storage.download(testFilePath).run, 10.seconds) downloadedFile shouldBe defined downloadedFile.foreach { _.getAbsolutePath.endsWith(testFilePath.toString) should be(true) } - Await.result(s3Storage.delete(testFilePath), 10 seconds) + Await.result(s3Storage.delete(testFilePath), 10.seconds) - val filesAfterRemoval = Await.result(s3Storage.list(testDirPath).run, 10 seconds) + val filesAfterRemoval = Await.result(s3Storage.list(testDirPath).run, 10.seconds) filesAfterRemoval shouldBe empty } @@ -103,18 +103,18 @@ class FileTest extends FlatSpec with Matchers with MockitoSugar { val fileStorage = new FileSystemStorage(scala.concurrent.ExecutionContext.global) - val filesBefore = Await.result(fileStorage.list(testDirPath).run, 10 seconds) + val filesBefore = Await.result(fileStorage.list(testDirPath).run, 10.seconds) filesBefore shouldBe empty - val fileExistsBeforeUpload = Await.result(fileStorage.exists(testFilePath), 10 seconds) + val fileExistsBeforeUpload = Await.result(fileStorage.exists(testFilePath), 10.seconds) fileExistsBeforeUpload should be(false) - Await.result(fileStorage.upload(sourceTestFile, testFilePath), 10 seconds) + Await.result(fileStorage.upload(sourceTestFile, testFilePath), 10.seconds) - val filesAfterUpload = Await.result(fileStorage.list(testDirPath).run, 10 seconds) + val filesAfterUpload = Await.result(fileStorage.list(testDirPath).run, 10.seconds) filesAfterUpload.size should be(1) - val fileExistsAfterUpload = Await.result(fileStorage.exists(testFilePath), 10 seconds) + val fileExistsAfterUpload = Await.result(fileStorage.exists(testFilePath), 10.seconds) fileExistsAfterUpload should be(true) val uploadedFileLine = filesAfterUpload.head @@ -123,13 +123,13 @@ class FileTest extends FlatSpec with Matchers with MockitoSugar { uploadedFileLine.revision.id.length should be > 0 uploadedFileLine.lastModificationDate.millis should be > 0L - val downloadedFile = Await.result(fileStorage.download(testFilePath).run, 10 seconds) + val downloadedFile = Await.result(fileStorage.download(testFilePath).run, 10.seconds) downloadedFile shouldBe defined downloadedFile.map(_.getAbsolutePath) should be(Some(testFilePath.toString)) - Await.result(fileStorage.delete(testFilePath), 10 seconds) + Await.result(fileStorage.delete(testFilePath), 10.seconds) - val filesAfterRemoval = Await.result(fileStorage.list(testDirPath).run, 10 seconds) + val filesAfterRemoval = Await.result(fileStorage.list(testDirPath).run, 10.seconds) filesAfterRemoval shouldBe empty } @@ -174,10 +174,10 @@ class FileTest extends FlatSpec with Matchers with MockitoSugar { blobMock // after file is uploaded ) - val filesBefore = Await.result(gcsStorage.list(testDirPath).run, 10 seconds) + val filesBefore = Await.result(gcsStorage.list(testDirPath).run, 10.seconds) filesBefore shouldBe empty - val fileExistsBeforeUpload = Await.result(gcsStorage.exists(testFilePath), 10 seconds) + val fileExistsBeforeUpload = Await.result(gcsStorage.exists(testFilePath), 10.seconds) fileExistsBeforeUpload should be(false) when(gcsMock.get(testBucket.value)).thenReturn(bucketMock) @@ -185,23 +185,23 @@ class FileTest extends FlatSpec with Matchers with MockitoSugar { when(bucketMock.create(org.mockito.Matchers.eq(testFileName), any[FileInputStream], any[BlobWriteOption])) .thenReturn(blobMock) - Await.result(gcsStorage.upload(sourceTestFile, testFilePath), 10 seconds) + Await.result(gcsStorage.upload(sourceTestFile, testFilePath), 10.seconds) - val filesAfterUpload = Await.result(gcsStorage.list(testDirPath).run, 10 seconds) + val filesAfterUpload = Await.result(gcsStorage.list(testDirPath).run, 10.seconds) filesAfterUpload.size should be(1) - val fileExistsAfterUpload = Await.result(gcsStorage.exists(testFilePath), 10 seconds) + val fileExistsAfterUpload = Await.result(gcsStorage.exists(testFilePath), 10.seconds) fileExistsAfterUpload should be(true) - val downloadedFile = Await.result(gcsStorage.download(testFilePath).run, 10 seconds) + val downloadedFile = Await.result(gcsStorage.download(testFilePath).run, 10.seconds) downloadedFile shouldBe defined downloadedFile.foreach { _.getAbsolutePath should endWith(testFilePath.toString) } - Await.result(gcsStorage.delete(testFilePath), 10 seconds) + Await.result(gcsStorage.delete(testFilePath), 10.seconds) - val filesAfterRemoval = Await.result(gcsStorage.list(testDirPath).run, 10 seconds) + val filesAfterRemoval = Await.result(gcsStorage.list(testDirPath).run, 10.seconds) filesAfterRemoval shouldBe empty } diff --git a/src/test/scala/xyz/driver/core/JsonTest.scala b/src/test/scala/xyz/driver/core/JsonTest.scala index 9a079b2..2aa3572 100644 --- a/src/test/scala/xyz/driver/core/JsonTest.scala +++ b/src/test/scala/xyz/driver/core/JsonTest.scala @@ -18,11 +18,12 @@ import xyz.driver.core.auth.AuthCredentials import xyz.driver.core.domain.{Email, PhoneNumber} import xyz.driver.core.json._ import xyz.driver.core.json.enumeratum.HasJsonFormat -import xyz.driver.core.tagging.Taggable +import xyz.driver.core.tagging._ import xyz.driver.core.time.provider.SystemTimeProvider import xyz.driver.core.time.{Time, TimeOfDay} import scala.collection.immutable.IndexedSeq +import scala.language.postfixOps class JsonTest extends WordSpec with Matchers with Inspectors { import DefaultJsonProtocol._ @@ -55,10 +56,12 @@ class JsonTest extends WordSpec with Matchers with Inspectors { } "read and write correct JSON when there's an implicit conversion defined" in { - JsString(" some string ").convertTo[String @@ Trimmed] shouldBe "some string" + val input = " some string " - val trimmed: String @@ Trimmed = " some string " - trimmed.toJson shouldBe JsString("some string") + JsString(input).convertTo[String @@ Trimmed] shouldBe input.trim() + + val trimmed: String @@ Trimmed = input + trimmed.toJson shouldBe JsString(trimmed) } } diff --git a/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala b/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala index 8f552db..118024a 100644 --- a/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala +++ b/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala @@ -1,6 +1,5 @@ package xyz.driver.core.rest -import akka.http.scaladsl.model.headers.CacheDirectives.`no-cache` import akka.http.scaladsl.model.headers._ import akka.http.scaladsl.model.{HttpMethod, StatusCodes} import akka.http.scaladsl.server.{Directives, Route} diff --git a/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala b/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala new file mode 100644 index 0000000..14dfaf9 --- /dev/null +++ b/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala @@ -0,0 +1,63 @@ +package xyz.driver.core.tagging + +import org.scalatest.{Matchers, WordSpec} +import xyz.driver.core.{@@, Name} + +/** + * @author sergey + * @since 9/11/18 + */ +class TaggingTest extends WordSpec with Matchers { + + "@@ Trimmed" should { + "produce values transparently from Strings and Names (by default)" in { + val s: String @@ Trimmed = " trimmed " + val n: Name[Int] @@ Trimmed = Name(" trimmed ") + + s shouldBe "trimmed" + n shouldBe Name[Int]("trimmed") + } + + "produce values transparently from values that have an implicit conversion defined" in { + import scala.language.implicitConversions + implicit def stringSeq2Trimmed(stringSeq: Seq[String]): Seq[String] @@ Trimmed = + stringSeq.map(_.trim()).tagged[Trimmed] + + val strings: Seq[String] @@ Trimmed = Seq(" trimmed1 ", " trimmed2 ") + strings shouldBe Seq("trimmed1", "trimmed2") + } + + "produce values transparently from Options of values that have Trimmed implicits" in { + val maybeStringDirect: Option[String @@ Trimmed] = Some(" trimmed ") + val maybeStringFromMap: Option[String @@ Trimmed] = Map("s" -> " trimmed ").get("s") + + val maybeNameDirect: Option[Name[Int] @@ Trimmed] = Some(Name(" trimmed ")) + val maybeNameFromMap: Option[Name[Int] @@ Trimmed] = Map("s" -> Name[Int](" trimmed ")).get("s") + + maybeStringDirect shouldBe Some("trimmed") + maybeStringFromMap shouldBe Some("trimmed") + maybeNameDirect shouldBe Some(Name[Int]("trimmed")) + maybeNameFromMap shouldBe Some(Name[Int]("trimmed")) + } + + "produce values transparently from collections of values that have Trimmed implicits" in { + val strings = Seq("s" -> " trimmed1 ", "s" -> " trimmed2 ") + val names = strings.map { + case (k, v) => k -> Name[Int](v) + } + + val trimmedStrings: Seq[String @@ Trimmed] = strings.groupBy(_._1)("s").map(_._2) + val trimmedNames: Seq[Name[Int] @@ Trimmed] = names.groupBy(_._1)("s").map(_._2) + + trimmedStrings shouldBe Seq("trimmed1", "trimmed2") + trimmedNames shouldBe Seq("trimmed1", "trimmed2").map(Name[Int]) + } + + "have Ordering" in { + val names: Seq[Name[Int] @@ Trimmed] = Seq(" 2 ", " 1 ", "3").map(Name[Int]) + + names.sorted should contain inOrderOnly (Name("1"), Name("2"), Name("3")) + } + } + +} -- cgit v1.2.3