From bccfa8f1ce37d8508745b3eefa710d68502ee375 Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Fri, 15 Dec 2023 15:38:04 +0000 Subject: [PATCH] #1052 adding test for loading basket constituent csv file and handling when it errors & sourcing test resources for test --- .../module/basket/csv/CsvStaticLoader.scala | 82 ++++++++++++------- .../test/resources/constituents/ftse100.csv | 4 + .../resources/constituents/ftsewitherror.csv | 4 + .../finos/vuu/csv/CsvStaticLoaderTests.scala | 47 +++++++++++ 4 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 example/basket/src/test/resources/constituents/ftse100.csv create mode 100644 example/basket/src/test/resources/constituents/ftsewitherror.csv create mode 100644 example/basket/src/test/scala/org/finos/vuu/csv/CsvStaticLoaderTests.scala diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/csv/CsvStaticLoader.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/csv/CsvStaticLoader.scala index 13023bffa..9fef0d572 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/csv/CsvStaticLoader.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/csv/CsvStaticLoader.scala @@ -2,44 +2,70 @@ package org.finos.vuu.core.module.basket.csv import com.typesafe.scalalogging.StrictLogging +import java.io.File import scala.io.Source +import scala.util.control.NonFatal object CsvStaticLoader extends StrictLogging { - def loadConstituent(basketId: String): Array[Map[String, Any]] = { + def loadConstituent(basketId: String, resourcePath: Option[String] = None): Array[Map[String, Any]] = { + try { - val staticDirPath = getClass.getResource("/static").getPath - val dir = new java.io.File(staticDirPath) - val csvFiles = dir.listFiles.filter(_.isFile) - .filter(_.getName.endsWith(basketId.replace(".","").toLowerCase + ".csv")) - val csvFile = csvFiles(0) - logger.info("Loading basket static:" + basketId + "(" + csvFile + ")") + val constituentsFilesDirectory = + if(resourcePath.isDefined) resourcePath.get + else getClass.getResource("/static").getPath + + val dir = new File(constituentsFilesDirectory) + val csvFiles = dir.listFiles.filter(_.isFile) + .filter(_.getName.endsWith(basketId.replace(".", "").toLowerCase + ".csv")) + + if (csvFiles.isEmpty) { + logger.error(s"Failed to find constituents file for $basketId") + Array.empty + } + else { + val csvFile = csvFiles(0) + logger.info("Loading basket static:" + basketId + "(" + csvFile + ")") + val rows = readFileContent(csvFile) + logger.info(s"Found ${rows.length} constituents for basket $basketId") + + val header = rows(0) + val symbolInd = header.indexOf("Symbol") + val nameInd = header.indexOf("Name") + val lastTradeInd = header.indexOf("Last Trade") + val volumeInd = header.indexOf("Volume") + val weightInd = header.indexOf("Weighting") + val changeInd = header.indexOf("Change") + + val constituents = rows.tail.map(e => { + val weighting = if (getValueFromIndex(weightInd, e) == null) 0.0D else getValueFromIndex(weightInd, e).toDouble + Map[String, Any]( + "Symbol" -> getValueFromIndex(symbolInd, e), + "Last Trade" -> getValueFromIndex(lastTradeInd, e), + "Name" -> getValueFromIndex(nameInd, e), + "Weighting" -> weighting, + "Volume" -> getValueFromIndex(volumeInd, e), + "Change" -> getValueFromIndex(changeInd, e) + ) + }) + constituents + + } + } + catch { + case NonFatal(t) => logger.error(s"Failed to parse constituents for $basketId", t) + Array.empty + } + } + + + private def readFileContent(csvFile: File): Array[Array[String]] = { val bufferedSource = Source.fromFile(csvFile) val csv = for (line <- bufferedSource.getLines) yield line.split(",").map(_.trim) val array = csv.toArray bufferedSource.close - var data: Map[String, String] = Map() - val header = array(0) - val symbolInd = header.indexOf("Symbol") - val nameInd = header.indexOf("Name") - val lastTradeInd = header.indexOf("Last Trade") - val volumeInd = header.indexOf("Volume") - val weightInd = header.indexOf("Weighting") - val changeInd = header.indexOf("Change") - val list = array.tail.map(e => { - val weighting = if(getValueFromIndex(weightInd, e) == null) 0.0D else getValueFromIndex(weightInd, e).toDouble - Map[String, Any]( - "Symbol" -> getValueFromIndex(symbolInd, e), - "Last Trade" -> getValueFromIndex(lastTradeInd, e), - "Name" -> getValueFromIndex(nameInd, e), - "Weighting" -> weighting, - "Volume" -> getValueFromIndex(volumeInd, e), - "Change" -> getValueFromIndex(changeInd, e) - ) - }) - list + array } - def load: Array[String] = { val staticDirPath = getClass.getResource("/static").getPath val dir = new java.io.File(staticDirPath) diff --git a/example/basket/src/test/resources/constituents/ftse100.csv b/example/basket/src/test/resources/constituents/ftse100.csv new file mode 100644 index 000000000..57cee0058 --- /dev/null +++ b/example/basket/src/test/resources/constituents/ftse100.csv @@ -0,0 +1,4 @@ +Symbol,Name,Last Trade,Change,Volume, Weighting +AAL.L,Anglo American PLC,436.35,5.35,5799089,0.0278736825813547 +ABF.L,Associated British Foods PLC,435.60,7.40,86808,0.000417248060431947 +ADM.L,Admiral Group PLC,1,627.00,,86808,0.000417248060431947 \ No newline at end of file diff --git a/example/basket/src/test/resources/constituents/ftsewitherror.csv b/example/basket/src/test/resources/constituents/ftsewitherror.csv new file mode 100644 index 000000000..b11163374 --- /dev/null +++ b/example/basket/src/test/resources/constituents/ftsewitherror.csv @@ -0,0 +1,4 @@ +Symbol,Name,Last Trade,Change,Volume, Weighting +AAL.L,Anglo American PLC,436.35,5.35,5799089,0.0278736825813547 +ABF.L,Associated British Foods PLC,435.60,7.40,86808,NotANumber +ADM.L,Admiral Group PLC,1,627.00,,86808,0.000417248060431947 \ No newline at end of file diff --git a/example/basket/src/test/scala/org/finos/vuu/csv/CsvStaticLoaderTests.scala b/example/basket/src/test/scala/org/finos/vuu/csv/CsvStaticLoaderTests.scala new file mode 100644 index 000000000..03609aae7 --- /dev/null +++ b/example/basket/src/test/scala/org/finos/vuu/csv/CsvStaticLoaderTests.scala @@ -0,0 +1,47 @@ +package org.finos.vuu.csv + +import org.finos.vuu.core.module.basket.csv.CsvStaticLoader +import org.scalatest.featurespec.AnyFeatureSpec + +import scala.io.Source + +class CsvStaticLoaderTests extends AnyFeatureSpec{ + + Feature("CSV loading Test Case") { + + Scenario("Can successfully load and parse basket constituents") { + var path = getClass.getResource("/constituents") + //his.class.getResourceAsStream("/myfile") + var x = ClassLoader.getSystemResource("") +// var x = Source.fromURL(path) +// var B = Source.fromResource("/static") + val testResourcePath = this.getClass().getResource("/constituents").getPath + val constituents = CsvStaticLoader.loadConstituent(".FTSE100", Some(testResourcePath)) + + assert(constituents.length == 3) + val firstRow = constituents.head + assert(firstRow("Symbol") == "AAL.L") + assert(firstRow("Last Trade") == "436.35") + assert(firstRow("Name") == "Anglo American PLC") + assert(firstRow("Weighting") == 0.0278736825813547) + assert(firstRow("Volume") == "5799089") + assert(firstRow("Change") == "5.35") + } + + Scenario("When parsing basket constituents fails return empty") { + + val constituents = CsvStaticLoader.loadConstituent(".FTSEWithError") + + assert(constituents.length == 0) + } + + + Scenario("When no matching basket constituents file return empty") { + + val constituents = CsvStaticLoader.loadConstituent(".NoSuchFile") + + assert(constituents.length == 0) + } + + } +}