Test for FileFetchScheduler
This commit is contained in:
@@ -4,4 +4,8 @@ cd web
|
|||||||
npm run build
|
npm run build
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
sbt assembly && fly deploy
|
if sbt test; then
|
||||||
|
sbt assembly && fly deploy
|
||||||
|
else
|
||||||
|
echo "Tests failed, skipping deploy!"
|
||||||
|
fi
|
||||||
@@ -22,11 +22,11 @@ final case class WeatherServerConfig(
|
|||||||
|
|
||||||
object FetchService {
|
object FetchService {
|
||||||
def of: IO[FetchService] = {
|
def of: IO[FetchService] = {
|
||||||
Slf4jLogger.create[IO].map(logger => new FetchService(logger))
|
Slf4jLogger.create[IO].map(logger => new FetchService(new FileNameService, logger))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FetchService(log: Logger[IO]) {
|
class FetchService(fileNameService: FileNameService, log: Logger[IO]) {
|
||||||
private val weatherServerConfig: WeatherServerConfig = ConfigSource.default.load[WeatherServerConfig] match {
|
private val weatherServerConfig: WeatherServerConfig = ConfigSource.default.load[WeatherServerConfig] match {
|
||||||
case Right(config) => config
|
case Right(config) => config
|
||||||
case Left(errors) => throw new RuntimeException(s"Unable to load config: $errors")
|
case Left(errors) => throw new RuntimeException(s"Unable to load config: $errors")
|
||||||
@@ -71,12 +71,12 @@ class FetchService(log: Logger[IO]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def fetchInRange(from: LocalDateTime, to: LocalDateTime): IO[List[Either[Throwable, (String, String)]]] = {
|
def fetchInRange(from: LocalDateTime, to: LocalDateTime): IO[List[Either[Throwable, (String, String)]]] = {
|
||||||
val fileNames = FileNameService.generate(from, to)
|
val fileNames = fileNameService.generate(from, to)
|
||||||
fetchFiles(fileNames)
|
fetchFiles(fileNames)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fetchFromDate(date: LocalDate): IO[List[Either[Throwable, (String, String)]]] = {
|
def fetchFromDate(date: LocalDate): IO[List[Either[Throwable, (String, String)]]] = {
|
||||||
val fileNames = FileNameService.generateFromDate(date)
|
val fileNames = fileNameService.generateFromDate(date)
|
||||||
fetchFiles(fileNames)
|
fetchFiles(fileNames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,16 +11,15 @@ object FileFetchScheduler {
|
|||||||
def of(dbService: DBService, fetch: FetchService): IO[FileFetchScheduler] = {
|
def of(dbService: DBService, fetch: FetchService): IO[FileFetchScheduler] = {
|
||||||
Scheduler.of.flatMap { scheduler =>
|
Scheduler.of.flatMap { scheduler =>
|
||||||
Slf4jLogger.create[IO].map {
|
Slf4jLogger.create[IO].map {
|
||||||
new FileFetchScheduler(dbService, fetch, scheduler, _)
|
new FileFetchScheduler(dbService, fetch, new FileNameService(), scheduler, _)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FileFetchScheduler(dbService: DBService, fetch: FetchService, scheduler: Scheduler, log: Logger[IO]) {
|
class FileFetchScheduler(dbService: DBService, fetch: FetchService, fileNameService: FileNameService, scheduler: Scheduler, log: Logger[IO]) {
|
||||||
def run: Stream[IO, Unit] = {
|
def run: Stream[IO, Unit] = {
|
||||||
val fetchTask = FileNameService.generateCurrentHour.flatMap(fetch.fetchSingleFile)
|
val fetchTask = fileNameService.generateCurrentHour.flatMap(fetch.fetchSingleFile)
|
||||||
|
|
||||||
scheduler.scheduleTask(fetchTask)
|
scheduler.scheduleTask(fetchTask)
|
||||||
.evalMap {
|
.evalMap {
|
||||||
case Left(fetchErr) =>
|
case Left(fetchErr) =>
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import cats.effect._
|
|||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.{Duration, LocalDate, LocalDateTime, ZoneId, ZonedDateTime}
|
import java.time.{Duration, LocalDate, LocalDateTime, ZoneId, ZonedDateTime}
|
||||||
|
|
||||||
object FileNameService {
|
trait FileNameServiceTrait {
|
||||||
|
def generateCurrentHour(implicit clock: Clock[IO]): IO[String]
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileNameService extends FileNameServiceTrait {
|
||||||
private val interval = Duration.ofMinutes(30)
|
private val interval = Duration.ofMinutes(30)
|
||||||
private val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm")
|
private val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm")
|
||||||
|
|
||||||
@@ -23,7 +27,7 @@ object FileNameService {
|
|||||||
time.plusMinutes(adjustment)
|
time.plusMinutes(adjustment)
|
||||||
}
|
}
|
||||||
|
|
||||||
def generateCurrentHour(implicit clock: Clock[IO]): IO[String] = {
|
override def generateCurrentHour(implicit clock: Clock[IO]): IO[String] = {
|
||||||
clock.realTime.map { duration =>
|
clock.realTime.map { duration =>
|
||||||
val instant = java.time.Instant.ofEpochMilli(duration.toMillis)
|
val instant = java.time.Instant.ofEpochMilli(duration.toMillis)
|
||||||
val zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of("Europe/Riga"))
|
val zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of("Europe/Riga"))
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import cats.implicits.catsSyntaxApply
|
|||||||
import fs2.Stream
|
import fs2.Stream
|
||||||
import org.typelevel.log4cats.Logger
|
import org.typelevel.log4cats.Logger
|
||||||
import org.typelevel.log4cats.slf4j.Slf4jLogger
|
import org.typelevel.log4cats.slf4j.Slf4jLogger
|
||||||
import server.Server
|
|
||||||
|
|
||||||
import java.time.{Duration, LocalTime}
|
import java.time.{Duration, LocalTime}
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
@@ -57,7 +56,7 @@ class Scheduler(log: Logger[IO]) {
|
|||||||
// run.compile.drain.unsafeRunSync()
|
// run.compile.drain.unsafeRunSync()
|
||||||
for {
|
for {
|
||||||
fetch <- FetchService.of
|
fetch <- FetchService.of
|
||||||
fetchTask = FileNameService.generateCurrentHour.flatMap(fetch.fetchSingleFile)
|
fetchTask = new FileNameService().generateCurrentHour.flatMap(fetch.fetchSingleFile)
|
||||||
} yield scheduleTask(fetchTask).compile.drain.unsafeRunSync()
|
} yield scheduleTask(fetchTask).compile.drain.unsafeRunSync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package fetch
|
||||||
|
|
||||||
|
import base.IOSuite
|
||||||
|
import cats.effect.{Clock, IO}
|
||||||
|
import cats.effect.kernel.Ref
|
||||||
|
import cats.implicits.catsSyntaxTuple3Semigroupal
|
||||||
|
import db.DBService
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AsyncWordSpec
|
||||||
|
import org.typelevel.log4cats.slf4j.Slf4jLogger
|
||||||
|
import fs2.Stream
|
||||||
|
|
||||||
|
|
||||||
|
class FileFetchSchedulerSpec extends AsyncWordSpec with Matchers with IOSuite {
|
||||||
|
"FileFetchScheduler" should {
|
||||||
|
"save into db" in runIO {
|
||||||
|
for {
|
||||||
|
refDb <- Ref.of[IO, Option[String]](None)
|
||||||
|
refFetch <- Ref.of[IO, Option[(String, String)]](None)
|
||||||
|
refScheduler <- Ref.of[IO, Option[Either[Throwable, (String, String)]]](None)
|
||||||
|
services = Slf4jLogger.create[IO].flatMap { log =>
|
||||||
|
val dbService = new DBService(log) {
|
||||||
|
override def save(fileName: String, content: String): IO[Either[Throwable, String]] = {
|
||||||
|
refDb.set(Some(fileName)).as(Right(fileName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val fileNameService = new FileNameService {
|
||||||
|
override def generateCurrentHour(implicit clock: Clock[IO]): IO[String] = IO.pure("file_test")
|
||||||
|
}
|
||||||
|
val fetchService = new FetchService(fileNameService, log) {
|
||||||
|
override def fetchSingleFile(fileName: String): IO[Either[Throwable, (String, String)]] = {
|
||||||
|
refFetch.set(Some((fileName, "content"))).as(Right((fileName, "content")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val scheduler = new Scheduler(log) {
|
||||||
|
override def scheduleTask(task: IO[Either[Throwable, (String, String)]]): Stream[IO, Either[Throwable, (String, String)]] = {
|
||||||
|
Stream.eval(task).flatMap { result =>
|
||||||
|
Stream.eval(refScheduler.set(Some(result))).as(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IO((dbService, fetchService, fileNameService, scheduler, log))
|
||||||
|
}
|
||||||
|
res <- services.flatMap { case (dbService, fetchService, fileNameService, scheduler, log) => {
|
||||||
|
new FileFetchScheduler(dbService, fetchService, fileNameService, scheduler, log)
|
||||||
|
.run.compile.drain *>
|
||||||
|
(refDb.get, refFetch.get, refScheduler.get).tupled.map { case (db, fetch, scheduler) =>
|
||||||
|
db shouldBe Some("file_test")
|
||||||
|
fetch shouldBe Some(("file_test", "content"))
|
||||||
|
scheduler shouldBe Some(Right(("file_test", "content")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} yield res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ class FileNameServiceSpec extends AnyFunSuite {
|
|||||||
"20230517_0330.csv",
|
"20230517_0330.csv",
|
||||||
)
|
)
|
||||||
|
|
||||||
val actualFileNames = FileNameService.generate(startTime, endTime)
|
val actualFileNames = new FileNameService().generate(startTime, endTime)
|
||||||
|
|
||||||
assert(actualFileNames == expectedFileNames)
|
assert(actualFileNames == expectedFileNames)
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ class FileNameServiceSpec extends AnyFunSuite {
|
|||||||
.map(hour => if(hour < 10) "0"+hour else ""+hour)
|
.map(hour => if(hour < 10) "0"+hour else ""+hour)
|
||||||
.map(str => s"20230517_${str}30.csv")
|
.map(str => s"20230517_${str}30.csv")
|
||||||
|
|
||||||
val actualFileNames = FileNameService.generateFromDate(date)
|
val actualFileNames = new FileNameService().generateFromDate(date)
|
||||||
|
|
||||||
assert(actualFileNames == expectedFileNames)
|
assert(actualFileNames == expectedFileNames)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user