@@ -12,8 +12,11 @@ import de.codingchallenge.repositories.{ArticleRepository, ProductExportReposito | |||
import de.codingchallenge.services.ArticleExportService | |||
import io.prometheus.client.CollectorRegistry | |||
import scala.concurrent.ExecutionContext | |||
trait DependencyInjectionModule extends LoggingModule { | |||
implicit val ec: ExecutionContext = ExecutionContext.global | |||
lazy val environment = wire[Environment] | |||
lazy val routes = wire[Routes] |
@@ -12,7 +12,7 @@ import scala.util.{Success, Try} | |||
* @param price the articles price | |||
* @param stock the current stock | |||
*/ | |||
case class Article(id: String, productId: String, name: String, description: String, price: Float, stock: Int) | |||
case class Article(id: String, productId: String, name: String, description: Option[String], price: Float, stock: Int) | |||
object Article { | |||
@@ -20,7 +20,7 @@ object Article { | |||
* Reads an article from a sequence of columns. Returns an Option with article in success case | |||
*/ | |||
implicit val csvColumnReads: CsvColumnReads[Article] = (s: Seq[String]) => | |||
Try{ (s.head, s(1), s(2), s(3), s(4).toFloat, s(5).toInt) } match { | |||
Try{ (s.head, s(1), s(2), Try(s(3)).toOption, s(4).toFloat, s(5).toInt) } match { | |||
case Success(t) => Some((Article.apply _).tupled(t)) | |||
case _ => None | |||
} |
@@ -12,7 +12,7 @@ object ProductExport{ | |||
def apply(a: Article, stockSum: Int): ProductExport = new ProductExport( | |||
productId = a.productId, | |||
name = a.name, | |||
description = a.description, | |||
description = a.description.getOrElse(""), | |||
price = a.price, | |||
stockSum = stockSum | |||
) |
@@ -13,13 +13,13 @@ import de.codingchallenge.models.Article | |||
class ArticleRepository(actorSystem: ActorSystem) extends LazyLogging{ | |||
implicit val as = actorSystem | |||
private implicit val as = actorSystem | |||
// might be a good idea to make that value configurable | |||
val baseUrl: String = "http://localhost:8080" | |||
lazy val connection = Http().superPool[NotUsed]() | |||
def getArticles(limit: Int): Source[Article, _] = Source.fromFuture( | |||
def getArticles(limit: Int): Source[Article, NotUsed] = Source.fromFuture( | |||
Http() | |||
.singleRequest(HttpRequest(uri = s"$baseUrl/articles/$limit")) | |||
).flatMapConcat(res => | |||
@@ -34,7 +34,7 @@ class ArticleRepository(actorSystem: ActorSystem) extends LazyLogging{ | |||
val articleFlow: Flow[ByteString, Article, NotUsed] = Flow[ByteString] | |||
.map(_.utf8String) | |||
.drop(1) | |||
.map{e => logger.info(s"CSV string: $e"); e} | |||
.map{e => logger.info(s"Parsing CSV string: $e"); e} | |||
.map(_.csvToOptOf[Article]) | |||
.map{ | |||
case Some(a) => a |
@@ -1,5 +1,6 @@ | |||
package de.codingchallenge.repositories | |||
import akka.NotUsed | |||
import akka.actor.ActorSystem | |||
import akka.http.scaladsl.Http | |||
import akka.http.scaladsl.model.HttpEntity.Chunked | |||
@@ -10,32 +11,45 @@ import com.typesafe.scalalogging.LazyLogging | |||
import de.codingchallenge.csv.CsvOps._ | |||
import de.codingchallenge.models.ProductExport | |||
import scala.concurrent.Future | |||
import scala.concurrent.{ExecutionContext, Future} | |||
class ProductExportRepository(actorSystem: ActorSystem) extends LazyLogging { | |||
class ProductExportRepository(actorSystem: ActorSystem)(implicit ec: ExecutionContext) extends LazyLogging { | |||
val headerLine = "produktId|name|beschreibung|preis|summeBestand\n" | |||
private implicit val as: ActorSystem = actorSystem | |||
val headerLine = "produktId|name|beschreibung|preis|summeBestand" | |||
val baseUrl = "http://localhost:8080" | |||
def add(p: Source[ProductExport, _], articlesSize: Int): Future[HttpResponse] = { | |||
def add(p: Source[ProductExport, NotUsed], articlesSize: Int): Future[HttpResponse] = { | |||
val sourceWithHeader = p.via(csvFlow) | |||
.prepend(Source.single(headerLine)) | |||
.intersperse("\n") | |||
.map(ByteString.apply) | |||
// it did not work with charset information | |||
val entity = Chunked.fromData( | |||
ContentType.WithMissingCharset(MediaTypes.`text/csv`), | |||
p.via(csvFlow) | |||
.prepend(Source.single(ByteString(headerLine))) | |||
sourceWithHeader | |||
) | |||
Http()(actorSystem) | |||
Http() | |||
.singleRequest( | |||
HttpRequest( | |||
method = HttpMethods.PUT, | |||
uri = s"$baseUrl/products/$articlesSize", | |||
entity = entity)) | |||
.map{res => | |||
logger.info(s"Server responded with $res") | |||
res | |||
} | |||
} | |||
private val csvFlow: Flow[ProductExport, ByteString, Any] = | |||
Flow.fromFunction(p => ByteString(p.toCsvLine)) | |||
private val csvFlow: Flow[ProductExport, String, NotUsed] = | |||
Flow.fromFunction{p => | |||
logger.info(s"processing export record $p") | |||
p.toCsvLine | |||
} | |||
} |
@@ -31,7 +31,7 @@ class ArticleExportService( | |||
* | |||
* @return | |||
*/ | |||
def exportArticles(): Future[HttpResponse] = productExportRepository.add(Source.fromGraph( | |||
def exportArticles(): Future[HttpResponse] = productExportRepository.add(Source.fromGraph[ProductExport, NotUsed]( | |||
articleRepository | |||
.getArticles(productsSize) | |||
.filter(_.stock > 0) |
@@ -9,7 +9,6 @@ class CsvOpsSpec extends WordSpec with MustMatchers{ | |||
case class CsvTestData(s: String, d: Double) | |||
implicit val csvReads: CsvColumnReads[CsvTestData] = (columns: Seq[String]) => { | |||
println(columns) | |||
Try{(columns.head, columns(1).toDouble)} match{ | |||
case Success((s: String, d: Double)) => Some(CsvTestData(s,d)) | |||
case _ => None |
@@ -8,7 +8,7 @@ trait ArticleFixture { | |||
id = "id", | |||
productId = "productId", | |||
name = "name", | |||
description = "desc", | |||
description = Some("desc"), | |||
price = 1, | |||
stock = 0) | |||
@@ -16,9 +16,111 @@ trait ArticleFixture { | |||
id = "cheapest", | |||
productId = "productId", | |||
name = "cheapestArticle", | |||
description = "cheapestArticleDesc", | |||
description = Some("cheapestArticleDesc"), | |||
price = 0, | |||
stock = 1) | |||
val anotherArticle = articleUnavailable.copy(price = 1, stock = 5) | |||
val sampleData: Seq[String] = | |||
"""A-U0xzQacF|P-U0xzQacF|EELDPYL||72.17|2 | |||
A-CxzQacFC|P-U0xzQacF|LDPYLMEMU YH SZQJVDEAMG|mnlHhk|7.72|6 | |||
A-0xzQacFC|P-U0xzQacF|LDPYLMEMU YH SZQJVDEAMG|mnlHhk|7.72|6 | |||
A-6TAYX4Ac|P-U0xzQacF|QFBC ELDP||94.22|0 | |||
A-tAYX4AcF|P-U0xzQacF|EELDPYL||72.17|2 | |||
A-xzQacFC7|P-U0xzQacF|YLM|yuzjdAgfm wwfkgu|92.08|1 | |||
A-TAYX4AcF|P-U0xzQacF|EELDPYL||72.17|2 | |||
A-XzQacFC7|P-U0xzQacF|YLM|yuzjdAgfm wwfkgu|92.08|1 | |||
A-AYX4AcFC|P-U0xzQacF|LDPYLMEMU YH SZQJVDEAMG|mnlHhk|7.72|6 | |||
A-zQacFC7P|P-U0xzQacF|M|yuzjdAgfm wwfkgu|92.08|1 | |||
A-YX4AcFC7|P-U0xzQacF|YLM|yuzjdAgfm wwfkgu|92.08|1 | |||
A-qacFC7Pe|P-U0xzQacF|XYHUSZQJV|mtbswwf gusiukA wllbkxklo|21.62|4 | |||
A-X4AcFC7P|P-U0xzQacF|M|yuzjdAgfm wwfkgu|92.08|1 | |||
A-QacFC7Pe|P-U0xzQacF|XYHUSZQJV|mtbswwf gusiukA wllbkxklo|21.62|4 | |||
A-4AcFC7Pe|P-4AcFC7Pe|XYHUSZQJV|mtbswwf gusiukA wllbkxklo|21.62|4 | |||
A-acFC7Pey|P-4AcFC7Pe|SZQJVDEAMG FBMSNWLW FHKKG|siukAyzr bkxklohaa|66.59|3 | |||
A-aHqBLPey|P-4AcFC7Pe|SZQJVDEAMG FBMSNWLW FHKKG|siukAyzr bkxklohaa|66.59|3 | |||
A-AcFC7Pey|P-4AcFC7Pe|SZQJVDEAMG FBMSNWLW FHKKG|siukAyzr bkxklohaa|66.59|3 | |||
A-hqBLPeyU|P-hqBLPeyU|QJVDEAM TFBMSNWLWH HKKGQURSZ|awtwllbkx ebH|16.03|22 | |||
A-cFC7PeyU|P-cFC7PeyU|QJVDEAM TFBMSNWLWH HKKGQURSZ|awtwllbkx ebH|16.03|22 | |||
A-HqBLPeyU|P-cFC7PeyU|QJVDEAM TFBMSNWLWH HKKGQURSZ|awtwllbkx ebH|16.03|22 | |||
A-CFC7PeyU|P-cFC7PeyU|QJVDEAM TFBMSNWLWH HKKGQURSZ|awtwllbkx ebH|16.03|22 | |||
A-qBLPeyUj|P-cFC7PeyU|EAMGTF MSNWLW|kgusiukAy llbkxkloha cplqlqxyJw yqscty XcpvAlqswj|15.66|1 | |||
A-FC7PeyUj|P-FC7PeyUj|EAMGTF MSNWLW|kgusiukAy llbkxkloha cplqlqxyJw yqscty XcpvAlqswj|15.66|1 | |||
A-QBLPeyUj|P-FC7PeyUj|EAMGTF MSNWLW|kgusiukAy llbkxkloha cplqlqxyJw yqscty XcpvAlqswj|15.66|1 | |||
A-c7PeyUjD|P-FC7PeyUj|MGT|swwfkg rzDawtwl vvvjebHd aenb aXkcwJyqsc|77.34|17 | |||
A-BLPeyUjD|P-FC7PeyUj|MGT|swwfkg rzDawtwl vvvjebHd aenb aXkcwJyqsc|77.34|17 | |||
A-C7PeyUjD|P-C7PeyUjD|MGT|swwfkg rzDawtwl vvvjebHd aenb aXkcwJyqsc|77.34|17 | |||
A-LPeyUjDg|P-C7PeyUjD|BMSNWL HFH||45.85|0 | |||
A-7PeyUjDg|P-C7PeyUjD|BMSNWL HFH||45.85|0 | |||
A-peyUjDgF|P-C7PeyUjD|SN L|hkqrz u wtwllbkxkl Hdwcpl|87.72|1 | |||
A-DYMxSQVE|P-C7PeyUjD|GTFB|wwfkgu zDawtw|84.12|7 | |||
A-PeyUjDgF|P-C7PeyUjD|SN L|hkqrz u wtwllbkxkl Hdwcpl|87.72|1 | |||
A-YMxSQVEt|P-YMxSQVEt|MSNWLW FHKKG URSZIDUA|twllb v klohaa plqlqxyJw|8.03|37 | |||
A-eyUjDgFM|P-eyUjDgFM|W|fkg rzDawtwl vvvjebHd|19.56|9 | |||
A-MxSQVEtB|P-eyUjDgFM|NWLWHF KGQURSZ D||62.12|0 | |||
A-2hqVEtBw|P-2hqVEtBw|HFH||45.85|0 | |||
A-xSQVEtBw|P-2hqVEtBw|HFH||45.85|0 | |||
A-yUjDgFMl|P-yUjDgFMl|FHKKG URSZIDUA|twllb v klohaa plqlqxyJw|8.03|37 | |||
A-hqVEtBwW|P-hqVEtBwW|HKKGQURSZ D AKW||2.31|0 | |||
A-YUjDgFMl|P-hqVEtBwW|FHKKG URSZIDUA|twllb v klohaa plqlqxyJw|8.03|37 | |||
A-SQVEtBwW|P-hqVEtBwW|HKKGQURSZ D AKW||2.31|0 | |||
A-UjDgFMlh|P-hqVEtBwW|GQURSZIDU|Ay llbkxkloha|95.28|1 | |||
A-qVEtBwWF|P-hqVEtBwW|KGQURSZ D||62.12|0 | |||
A-jDgFMlhq|P-hqVEtBwW|SZIDUA WA YWZLRLVBV||29.92|50 | |||
A-QVEtBwWF|P-hqVEtBwW|KGQURSZ D||62.12|0 | |||
A-dgFMlhqz|P-hqVEtBwW|UAKWATYWZ LVBVKVXJ LBOHHDAWAC|lqlqxyJwv PUpr XcpvAlqswj nlnzjmkv|24.45|2 | |||
A-VEtBwWFK|P-hqVEtBwW|URSZIDUA WA YWZLRLVBV||29.92|50 | |||
A-DgFMlhqz|P-hqVEtBwW|UAKWATYWZ LVBVKVXJ LBOHHDAWAC|lqlqxyJwv PUpr XcpvAlqswj nlnzjmkv|24.45|2 | |||
A-EtBwWFKu|P-hqVEtBwW|ZIDU|Ay llbkxkloha|95.28|1 | |||
A-gFMlhqza|P-hqVEtBwW|ATYWZ LVBVKVXJ|bHdwcplqlq kcwJyqsc rdXcpv W|88.45|7 | |||
A-tBwWFKuS|P-hqVEtBwW|D||62.12|0 | |||
A-fMlhqza2|P-fMlhqza2|WZLRLV VKVXJKEL|haaenbiaX|27.79|0 | |||
A-bwWFKuSI|P-bwWFKuSI|AKW YWZLRLVBV||29.92|50 | |||
A-FMlhqza2|P-FMlhqza2|WZLRLV VKVXJKEL|haaenbiaX|27.79|0 | |||
A-BwWFKuSI|P-FMlhqza2|AKW YWZLRLVBV||29.92|50 | |||
A-Mlhqza2Y|P-FMlhqza2|LRLV VKVXJKEL OHHDAW|plqlqxyJw yqscty XcpvAlqswj nlnzjmkv|24.45|2 | |||
A-wWFKuSIU|P-wWFKuSIU|WA YWZLRLVBV VXJKELBOHH|aenb|10.51|10 | |||
A-lhqza2Yr|P-lhqza2Yr|BVKVXJKELB HHDAWACEP|ql|48.6|10 | |||
A-WWFKuSIU|P-lhqza2Yr|WA YWZLRLVBV VXJKELBOHH|aenb|10.51|10 | |||
A-HHqza2Yr|P-lhqza2Yr|BVKVXJKELB HHDAWACEP|ql|48.6|10 | |||
A-WFKuSIUA|P-lhqza2Yr|WZLRLV VKVXJKEL|haaenbiaX|27.79|0 | |||
A-hqza2YrV|P-lhqza2Yr|K X|bHdwcplqlq kcwJyqsc rdXcpv W TqFbn|55.46|0 | |||
A-FKuSIUAY|P-lhqza2Yr|LRLV VKVXJKEL OHHDAW|plqlqxyJw yqscty XcpvAlqswj nlnzjmkv|24.45|2 | |||
A-Hqza2YrV|P-lhqza2Yr|K X|bHdwcplqlq kcwJyqsc rdXcpv W TqFbn|55.46|0 | |||
A-KuSIUAYr|P-lhqza2Yr|BVKVXJKELB HHDAWACEP|ql|48.6|10 | |||
A-qza2YrVV|P-lhqza2Yr|X|bHdwcplqlq kcwJyqsc rdXcpv W TqFbn|55.46|0 | |||
A-uSIUAYrV|P-uSIUAYrV|K X|bHdwcplqlq kcwJyqsc rdXcpv W TqFbn|55.46|0 | |||
A-rDwwLLBK|P-uSIUAYrV|JKELBO HDAWACEPNL|aXkcwJyqsc rdXcpv W|88.45|7 | |||
A-sIUAYrVV|P-sIUAYrVV|X|bHdwcplqlq kcwJyqsc rdXcpv W TqFbn|55.46|0 | |||
A-za2YrVVj|P-za2YrVVj|LBOHHDAWAC P|ql|48.6|10 | |||
A-SIUAYrVV|P-za2YrVVj|X|bHdwcplqlq kcwJyqsc rdXcpv W TqFbn|55.46|0 | |||
A-DwwLLBKX|P-za2YrVVj|LBOHHDAWAC P LBQ||27.63|8 | |||
A-IUAYrVVj|P-za2YrVVj|LBOHHDAWAC P|ql|48.6|10 | |||
A-a2YrVVjb|P-za2YrVVj|HDAWACEPNL QILAQX|cwJy PUpr XcpvAlqswj|15.66|1 | |||
A-UAYrVVjb|P-za2YrVVj|HDAWACEPNL QILAQX|cwJy PUpr XcpvAlqswj|15.66|1 | |||
A-wwLLBKXK|P-wwLLBKXK|BOHHDAWA EPNLBQILA|kcwJyqsc rdXcpv|97.51|0 | |||
A-AYrVVjbd|P-AYrVVjbd|ACEPNLBQIL QXX||96.39|1 | |||
A-2YrVVjbd|P-AYrVVjbd|ACEPNLBQIL QXX||96.39|1 | |||
A-yrVVjbdc|P-AYrVVjbd|NLBQILAQX KYCJWWJVYS|UprdXcpvA yTqFbn wzijx vo|93.83|0 | |||
A-wLLBKXKo|P-wLLBKXKo|DAWA||73.55|31 | |||
A-YrVVjbdc|P-wLLBKXKo|NLBQILAQX KYCJWWJVYS|UprdXcpvA yTqFbn wzijx vo|93.83|0 | |||
A-lLBKXKoH|P-lLBKXKoH|WACE NLBQILAQX|cwJy PUpr XcpvAlqswj|15.66|1 | |||
A-rVVjbdcl|P-rVVjbdcl|LAQXXKYCJW||8.03|37 | |||
A-LLBKXKoH|P-rVVjbdcl|WACE NLBQILAQX|cwJy PUpr XcpvAlqswj|15.66|1 | |||
A-RVVjbdcl|P-rVVjbdcl|LAQXXKYCJW||8.03|37 | |||
A-LBKXKoHA|P-rVVjbdcl|C P|ql|48.6|10 | |||
A-VVjbdclQ|P-VVjbdclQ|A|kcwJyqsc rdXcpv|97.51|0 | |||
A-BKXKoHAe|P-VVjbdclQ|LBQ LAQXXKYCJW J|PUpr XcpvAlqswj nlnzjmkv|24.45|2 | |||
A-VjbdclQq|P-VVjbdclQ|KYCJWWJVYS|UprdXcpvA yTqFbn wzijx vo|93.83|0 | |||
A-KXKoHAeN|P-VVjbdclQ|QILAQX KYCJWWJVYS PSUCP|d|31.34|35 | |||
A-jbdclQqX|P-VVjbdclQ|CJ|v PUpr XcpvAlqswj nlnzjmkv|24.45|2 | |||
A-XKoHAeNB|P-XKoHAeNB|LAQXXKYCJW J YSQPS||22.28|49 | |||
A-JbdclQqX|P-XKoHAeNB|CJ|v PUpr XcpvAlqswj nlnzjmkv|24.45|2 | |||
A-KoHAeNBa|P-XKoHAeNB|XKYCJWWJ YSQPS C|yydqpgW TqFbn wzijx vo gTro|12.86|2 | |||
A-bdclQqXJ|P-XKoHAeNB|J YSQPS||22.28|49 | |||
A-oHAeNBak|P-XKoHAeNB|JWWJVYSQPS C TRYDYX|pvAlqsw bnln ijxmdog roqumthtp lijqubnzz|97.32|24 | |||
A-BdclQqXJ|P-BdclQqXJ|J YSQPS||22.28|49 | |||
A-OHAeNBak|P-BdclQqXJ|JWWJVYSQPS C TRYDYX|pvAlqsw bnln ijxmdog roqumthtp lijqubnzz|97.32|24 | |||
A-dclQqXJ7|P-BdclQqXJ|QPSU||77.34|17""".split("\n").map(_.trim) | |||
} |
@@ -2,19 +2,27 @@ package de.codingchallenge.models | |||
import de.codingchallenge.BaseSpec | |||
import de.codingchallenge.csv.CsvOps._ | |||
import de.codingchallenge.fixtures.ArticleFixture | |||
class ArticleSpec extends BaseSpec { | |||
"The Article" must { | |||
"parse from sample" in { | |||
"parse fields as expecte" in { | |||
val sampleCsv = "A-UhnpVjCE|P-NhImbQSB|CKVTFO LCCOR TFIAZTP|lxqjlivf dppzKc|79.54|0" | |||
sampleCsv.csvToOptOf[Article].get mustBe Article( | |||
"A-UhnpVjCE", | |||
"P-NhImbQSB", | |||
"CKVTFO LCCOR TFIAZTP", | |||
"lxqjlivf dppzKc", | |||
Some("lxqjlivf dppzKc"), | |||
79.54f, | |||
0) | |||
} | |||
"parse a sample data" in new ArticleFixture { | |||
sampleData | |||
.foreach(row => { | |||
row.csvToOptOf[Article].get mustBe a[Article] | |||
} | |||
) | |||
} | |||
} | |||
} |
@@ -2,9 +2,10 @@ package de.codingchallenge.repositories | |||
import akka.http.scaladsl.model.StatusCodes | |||
import akka.stream.scaladsl.{Sink, Source} | |||
import de.codingchallenge.fixtures.ProductExportFixture | |||
import de.codingchallenge.models.Article | |||
import de.codingchallenge.{AkkaSpec, BaseSpec} | |||
import de.codingchallenge.fixtures.{ArticleFixture, ProductExportFixture} | |||
import de.codingchallenge.models.{Article, ProductExport} | |||
import de.codingchallenge.csv.CsvOps._ | |||
import de.codingchallenge.{AkkaSpec, BaseSpec, models} | |||
import scala.concurrent.Await | |||
import scala.concurrent.duration._ | |||
@@ -12,13 +13,18 @@ import scala.concurrent.duration._ | |||
/** | |||
* Honestly this is kind of an integration tests running against the Demo Server | |||
*/ | |||
class ProductExportRepositorySpec extends BaseSpec with AkkaSpec with ProductExportFixture { | |||
class ProductExportRepositorySpec extends BaseSpec with AkkaSpec with ProductExportFixture with ArticleFixture { | |||
"The ProductExportRepository" must { | |||
"put product reports as expected" in { | |||
"put a single report" in { | |||
val repo = new ProductExportRepository(system) | |||
Await.result(repo.add(Source.single(productExport), 1), 2.second).status mustBe StatusCodes.OK | |||
} | |||
"put multiple reports" in { | |||
val repo = new ProductExportRepository(system) | |||
val articles = sampleData.map(_.csvToOptOf[Article].get).toList.map(e => models.ProductExport(e, 1)) | |||
Await.result(repo.add(Source(articles), 1), 2.second).status mustBe StatusCodes.OK | |||
} | |||
} | |||
} |
@@ -1,9 +1,10 @@ | |||
package de.codingchallenge.services | |||
import akka.NotUsed | |||
import akka.http.scaladsl.model.HttpResponse | |||
import akka.stream.scaladsl.{Sink, Source} | |||
import de.codingchallenge.fixtures.{ArticleFixture, ProductExportFixture} | |||
import de.codingchallenge.models.ProductExport | |||
import de.codingchallenge.models.{Article, ProductExport} | |||
import de.codingchallenge.repositories.{ArticleRepository, ProductExportRepository} | |||
import de.codingchallenge.{AkkaSpec, BaseSpec} | |||
import org.mockito.ArgumentCaptor | |||
@@ -13,6 +14,7 @@ import scala.concurrent.{Await, Future} | |||
import org.mockito.Mockito._ | |||
import org.mockito.ArgumentMatchers._ | |||
import org.mockito.internal.verification.argumentmatching.ArgumentMatchingTool | |||
import de.codingchallenge.csv.CsvOps._ | |||
class ArticleExportServiceSpec extends BaseSpec with AkkaSpec{ | |||
@@ -31,7 +33,7 @@ class ArticleExportServiceSpec extends BaseSpec with AkkaSpec{ | |||
.thenReturn(Future.successful(HttpResponse())) | |||
Await.result(service.exportArticles(), 5.second) | |||
val sourceCaptor: ArgumentCaptor[Source[ProductExport, _]] = ArgumentCaptor.forClass(classOf[Source[ProductExport, _]]) | |||
val sourceCaptor: ArgumentCaptor[Source[ProductExport, NotUsed]] = ArgumentCaptor.forClass(classOf[Source[ProductExport, NotUsed]]) | |||
verify(productExportRepositoryMock).add(sourceCaptor.capture(), any()) | |||
Await.result(sourceCaptor.getValue.runWith(Sink.headOption), 1.second) mustBe None | |||
} | |||
@@ -43,9 +45,25 @@ class ArticleExportServiceSpec extends BaseSpec with AkkaSpec{ | |||
.thenReturn(Future.successful(HttpResponse())) | |||
Await.result(service.exportArticles(), 5.second) | |||
val sourceCaptor: ArgumentCaptor[Source[ProductExport, _]] = ArgumentCaptor.forClass(classOf[Source[ProductExport, _]]) | |||
val sourceCaptor: ArgumentCaptor[Source[ProductExport, NotUsed]] = ArgumentCaptor.forClass(classOf[Source[ProductExport, NotUsed]]) | |||
verify(productExportRepositoryMock).add(sourceCaptor.capture(), any()) | |||
Await.result(sourceCaptor.getValue.runWith(Sink.head), 1.second) mustBe productExport | |||
} | |||
"pass sample data as expected" in new TestSetup with ArticleFixture { | |||
val articles = sampleData.map(_.csvToOptOf[Article].get).toList | |||
doReturn(Source(articles), Nil: _*) | |||
.when(articleRepositoryMock) | |||
.getArticles(100) | |||
when(productExportRepositoryMock.add(any(), any())) | |||
.thenReturn(Future.successful(HttpResponse())) | |||
Await.result(service.exportArticles(), 5.second) | |||
val sourceCaptor: ArgumentCaptor[Source[ProductExport, NotUsed]] = ArgumentCaptor.forClass(classOf[Source[ProductExport, NotUsed]]) | |||
verify(productExportRepositoryMock).add(sourceCaptor.capture(), any()) | |||
Await.result(sourceCaptor.getValue.runWith(Sink.seq), 1.second).foreach { e => | |||
e mustBe a[ProductExport] | |||
} | |||
} | |||
} | |||
} |