Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing primary data from SQL #98

Merged
merged 19 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Implementing test for SQL primary data with postgresql testcontainer
  • Loading branch information
sebastian-peter committed Jan 20, 2022
commit 1212567f636a5e5ef9be65267d93b4dc358eca8e
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ ext {
akkaVersion = '2.6.18'
tscfgVersion = '0.9.996'

testContainerVersion = '0.39.12'

scriptsLocation = 'gradle' + File.separator + 'scripts' + File.separator // location of script plugins
}

Expand Down Expand Up @@ -100,6 +102,10 @@ dependencies {
testImplementation group: 'org.pegdown', name: 'pegdown', version: '1.6.0'
testImplementation "com.typesafe.akka:akka-testkit_${scalaVersion}:${akkaVersion}" // akka testkit

// testcontainers
testImplementation "com.dimafeng:testcontainers-scala-scalatest_${scalaVersion}:${testContainerVersion}"
testImplementation "com.dimafeng:testcontainers-scala-postgresql_${scalaVersion}:${testContainerVersion}"

/* --- Scala libs --- */
/* CORE Scala */
implementation "org.scala-lang:scala-library:${scalaBinaryVersion}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ final case class PrimaryServiceWorker[V <: Value](
Option[Seq[SchedulerMessage.ScheduleTriggerMessage]]
)
] = {
val trySource = initServiceData match {
(initServiceData match {
case PrimaryServiceWorker.CsvInitPrimaryServiceStateData(
timeSeriesUuid,
simulationStart,
Expand Down Expand Up @@ -95,7 +95,7 @@ final case class PrimaryServiceWorker[V <: Value](
simulationStart: ZonedDateTime
) =>
Try {
val valueFactory =
val factory =
new TimeBasedSimpleValueFactory(valueClass, sqlParams.timePattern)

val sqlConnector = new SqlConnector(
Expand All @@ -110,7 +110,7 @@ final case class PrimaryServiceWorker[V <: Value](
sqlParams.tableName,
timeSeriesUuid,
valueClass,
valueFactory
factory
)

(source, simulationStart)
Expand All @@ -122,8 +122,7 @@ final case class PrimaryServiceWorker[V <: Value](
s"Provided init data '${unsupported.getClass.getSimpleName}' for primary service are invalid!"
)
)
}
trySource.map { case (source, simulationStart) =>
}).map { case (source, simulationStart) =>
val (maybeNextTick, furtherActivationTicks) = SortedDistinctSeq(
source.getTimeSeries.getEntries.asScala
.filter { timeBasedValue =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE TABLE public."its_p_9185b8c1-86ba-4a16-8dea-5ac898e8caa5"
(
time timestamp with time zone,
p double precision,
uuid uuid,
CONSTRAINT its_p_pkey PRIMARY KEY (uuid)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;

INSERT INTO
public."its_p_9185b8c1-86ba-4a16-8dea-5ac898e8caa5" (uuid, time, p)
VALUES
('0245d599-9a5c-4c32-9613-5b755fac8ca0', '2020-01-01 00:00:00+0', 1000.0),
('a5e27652-9024-4a93-9d2a-590fbc3ab5a1', '2020-01-01 00:15:00+0', 1250.0);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
CREATE TABLE public."its_pqh_46be1e57-e4ed-4ef7-95f1-b2b321cb2047"
(
time timestamp with time zone,
p double precision,
q double precision,
heat_demand double precision,
uuid uuid,
CONSTRAINT its_pqh_pkey PRIMARY KEY (uuid)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;

INSERT INTO
public."its_pqh_46be1e57-e4ed-4ef7-95f1-b2b321cb2047" (uuid, time, p, q, heat_demand)
VALUES
('661ac594-47f0-4442-8d82-bbeede5661f7', '2020-01-01 00:00:00+0', 1000.0, 329.0, 8.0),
('5adcd6c5-a903-433f-b7b5-5fe669a3ed30', '2020-01-01 00:15:00+0', 1250.0, 411.0, 12.0);
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* © 2022. TU Dortmund University,
* Institute of Energy Systems, Energy Efficiency and Energy Economics,
* Research group Distribution grid planning and operation
*/

package edu.ie3.simona.service.primary

import akka.actor.{ActorRef, ActorSystem}
import akka.testkit.{TestActorRef, TestProbe}
import com.dimafeng.testcontainers.{ForAllTestContainer, PostgreSQLContainer}
import com.typesafe.config.ConfigFactory
import edu.ie3.datamodel.models.value.{HeatAndSValue, PValue, Value}
import edu.ie3.simona.agent.participant.data.Data.PrimaryData.{
ActivePower,
ApparentPowerAndHeat
}
import edu.ie3.simona.config.SimonaConfig.Simona.Input.Primary.SqlParams
import edu.ie3.simona.ontology.messages.SchedulerMessage.{
CompletionMessage,
ScheduleTriggerMessage,
TriggerWithIdMessage
}
import edu.ie3.simona.ontology.messages.services.ServiceMessage.RegistrationResponseMessage.RegistrationSuccessfulMessage
import edu.ie3.simona.ontology.messages.services.ServiceMessage.WorkerRegistrationMessage
import edu.ie3.simona.ontology.trigger.Trigger.{
ActivityStartTrigger,
InitializeServiceTrigger
}
import edu.ie3.simona.service.primary.PrimaryServiceWorker.{
ProvidePrimaryDataMessage,
SqlInitPrimaryServiceStateData
}
import edu.ie3.simona.test.common.AgentSpec
import edu.ie3.util.TimeUtil
import org.scalatest.BeforeAndAfterAll
import org.scalatest.prop.TableDrivenPropertyChecks
import org.testcontainers.utility.MountableFile

import java.nio.file.Paths
import java.util.UUID
import scala.language.postfixOps
import scala.reflect.ClassTag

class PrimaryServiceWorkerSqlIT
extends AgentSpec(
ActorSystem(
"PrimaryServiceWorkerSqlIT",
ConfigFactory
.parseString("""
|akka.loglevel="OFF"
""".stripMargin)
)
)
with ForAllTestContainer
with BeforeAndAfterAll
with TableDrivenPropertyChecks {

override val container: PostgreSQLContainer = PostgreSQLContainer(
"postgres:11.14"
)

private val simulationStart =
TimeUtil.withDefaults.toZonedDateTime("2020-01-01 00:00:00")

private val schemaName = "public"

private val uuidP = UUID.fromString("9185b8c1-86ba-4a16-8dea-5ac898e8caa5")
private val uuidPhq = UUID.fromString("46be1e57-e4ed-4ef7-95f1-b2b321cb2047")

private val tableNameP = s"its_p_$uuidP"
private val tableNamePhq = s"its_pqh_$uuidPhq"

override protected def beforeAll(): Unit = {
val url = getClass.getResource("timeseries/")
url shouldNot be(null)
val path = Paths.get(url.toURI)

// Copy sql import scripts into docker
val sqlImportFile = MountableFile.forHostPath(path)
container.copyFileToContainer(sqlImportFile, "/home/")

Iterable(s"$tableNameP.sql", s"$tableNamePhq.sql")
.foreach { file =>
val res = container.execInContainer("psql", "-Utest", "-f/home/" + file)
res.getStderr shouldBe empty
}
}

override protected def afterAll(): Unit = {
container.stop()
container.close()
}

private def getServiceActor[T <: Value](
scheduler: ActorRef
)(implicit tag: ClassTag[T]): PrimaryServiceWorker[T] = {
new PrimaryServiceWorker[T](
scheduler,
tag.runtimeClass.asInstanceOf[Class[T]],
simulationStart
)
}

"A primary service actor with SQL source" should {
"initialize and send out data when activated" in {

val cases = Table(
(
"getService",
"uuid",
"tableName",
"firstTick",
"dataValueClass",
"maybeNextTick"
),
(
getServiceActor[HeatAndSValue](_),
uuidPhq,
tableNamePhq,
0L,
classOf[ApparentPowerAndHeat],
Some(900L)
),
(
getServiceActor[PValue](_),
uuidP,
tableNameP,
0L,
classOf[ActivePower],
Some(900L)
)
)

forAll(cases) {
(
getService,
uuid,
tableName,
firstTick,
dataValueClass,
maybeNextTick
) =>
val scheduler = TestProbe("scheduler")

val serviceRef =
TestActorRef(
getService(scheduler.ref)
)

val initData = SqlInitPrimaryServiceStateData(
SqlParams(
jdbcUrl = container.jdbcUrl,
userName = container.username,
password = container.password,
schemaName = schemaName,
tableName = tableName,
timePattern = "yyyy-MM-dd HH:mm:ss"
),
uuid,
simulationStart
)

val triggerId1 = 1L

scheduler.send(
serviceRef,
TriggerWithIdMessage(
InitializeServiceTrigger(initData),
triggerId1,
serviceRef
)
)

scheduler.expectMsg(
CompletionMessage(
triggerId1,
Some(
List(
ScheduleTriggerMessage(
ActivityStartTrigger(firstTick),
serviceRef
)
)
)
)
)

val participant = TestProbe()

participant.send(
serviceRef,
WorkerRegistrationMessage(participant.ref)
)
participant.expectMsg(RegistrationSuccessfulMessage(Some(firstTick)))

val triggerId2 = 2L

scheduler.send(
serviceRef,
TriggerWithIdMessage(
ActivityStartTrigger(firstTick),
triggerId2,
serviceRef
)
)

val dataMsg = participant.expectMsgType[ProvidePrimaryDataMessage]
dataMsg.tick shouldBe firstTick
dataMsg.data.getClass shouldBe dataValueClass
dataMsg.nextDataTick shouldBe maybeNextTick
}
}
}
}