diff --git a/README.md b/README.md index 9cb7b11..dcca511 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -install the specs-lib using maven, +install the conifers-specs using maven, ``` mvn clean install @@ -10,8 +10,8 @@ Then use as a dependency ``` com.specs - specs-lib - 1.0-SNAPSHOT + conifers-specs + 1.0 ``` @@ -67,15 +67,15 @@ class CustomerOrderComponentSpecs extends ComponentSpecs { //beforeAll start a database server //afterAll stop the database server - scenario("when customer order is put") { - Given("a order") + scenario("successful customer order") { + Given("an order") val order = new CustomerOrder(List(Item("Skateboard"))) When("order is checked out") order.checkout() - Then("should be ready") - assert(order.state == "READY") + Then("should be ready to be shipped") + assert(order.state == "READY TO BE SHIPPED") //maybe I want to test its state in database as well //more granular assertions than FlowSpecs } @@ -87,7 +87,10 @@ class CustomerOrderComponentSpecs extends ComponentSpecs { ---------- ``` -System testing is testing conducted on a complete, integrated system to evaluate the system's compliance with its specified requirements. System testing falls within the scope of black-box testing, and as such, should require no knowledge of the inner design of the code or logic. +System testing is testing conducted on a complete, integrated system to +evaluate the system's compliance with its specified requirements. +System testing falls within the scope of black-box testing, and as such, +should require no knowledge of the inner design of the code or logic. ``` ``` diff --git a/pom.xml b/pom.xml index 56ebdc7..343b9e7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,8 +3,8 @@ 4.0.0 com.specs - specs-lib - 1.0-SNAPSHOT + conifers-specs + 1.0 jar UCF-specs-lib @@ -23,6 +23,13 @@ compile + + org.json + json + 20160810 + compile + + org.scala-lang scala-library @@ -72,6 +79,26 @@ compile + + io.gatling.highcharts + gatling-charts-highcharts + 2.0.0 + compile + + + ch.qos.logback + logback-classic + + + + + + commons-dbcp + commons-dbcp + 1.4 + compile + + @@ -122,6 +149,22 @@ + + io.gatling + gatling-maven-plugin + 2.0.0 + + + -Durl=http://localhost:9000 + -DnumberOfUsers=10 + -DnumberOfRepetitions=1 + -DdurationMinutes=1 + -DpauseBetweenRequestsMs=3000 + -Ddebug=true + + + + diff --git a/src/main/scala/com/specs/AsyncFlowSpecs.scala b/src/main/scala/com/specs/AsyncFlowSpecs.scala index 2ed9abd..30fea34 100644 --- a/src/main/scala/com/specs/AsyncFlowSpecs.scala +++ b/src/main/scala/com/specs/AsyncFlowSpecs.scala @@ -1,12 +1,12 @@ package com.specs -import org.scalatest.{AsyncFeatureSpec, BeforeAndAfterAll, GivenWhenThen} +import org.scalatest.{AsyncFeatureSpec, BeforeAndAfterAll, GivenWhenThen, Matchers} /** * Created by prayagupd * on 2/15/17. */ -class AsyncFlowSpecs extends AsyncFeatureSpec with GivenWhenThen with BeforeAndAfterAll { +class AsyncFlowSpecs extends AsyncFeatureSpec with GivenWhenThen with BeforeAndAfterAll with Matchers { } diff --git a/src/main/scala/com/specs/ComponentSpecs.scala b/src/main/scala/com/specs/ComponentSpecs.scala index ea23391..067ea59 100644 --- a/src/main/scala/com/specs/ComponentSpecs.scala +++ b/src/main/scala/com/specs/ComponentSpecs.scala @@ -1,12 +1,12 @@ package com.specs -import org.scalatest.{BeforeAndAfterEach, FeatureSpec, GivenWhenThen} +import org.scalatest._ /** * Created by prayagupd * on 2/15/17. */ -class ComponentSpecs extends FeatureSpec with GivenWhenThen with BeforeAndAfterEach { +trait ComponentSpecs extends FeatureSpec with GivenWhenThen with BeforeAndAfterEach with BeforeAndAfterAll with Matchers { -} \ No newline at end of file +} diff --git a/src/main/scala/com/specs/FlowSpecs.scala b/src/main/scala/com/specs/FlowSpecs.scala index 4df6898..be510bb 100644 --- a/src/main/scala/com/specs/FlowSpecs.scala +++ b/src/main/scala/com/specs/FlowSpecs.scala @@ -1,12 +1,12 @@ package com.specs -import org.scalatest.{AsyncFeatureSpec, BeforeAndAfterAll, FeatureSpec, GivenWhenThen} +import org.scalatest.{BeforeAndAfterAll, FeatureSpec, GivenWhenThen, Matchers} /** * Created by prayagupd * on 2/15/17. */ -class FlowSpecs extends AsyncFeatureSpec with GivenWhenThen with BeforeAndAfterAll { +trait FlowSpecs extends FeatureSpec with GivenWhenThen with BeforeAndAfterAll with Matchers { } diff --git a/src/main/scala/com/specs/UnitSpecs.scala b/src/main/scala/com/specs/UnitSpecs.scala index b364d6a..62d2a8f 100644 --- a/src/main/scala/com/specs/UnitSpecs.scala +++ b/src/main/scala/com/specs/UnitSpecs.scala @@ -1,13 +1,13 @@ package com.specs import org.scalamock.scalatest.MockFactory -import org.scalatest.FunSpec +import org.scalatest.{FunSpec, Matchers} /** * Created by prayagupd * on 2/15/17. */ -abstract class UnitSpecs extends FunSpec with MockFactory { +abstract class UnitSpecs extends FunSpec with MockFactory with Matchers { } diff --git a/src/main/scala/com/specs/config/ConfigLoader.scala b/src/main/scala/com/specs/config/ConfigLoader.scala new file mode 100644 index 0000000..b473886 --- /dev/null +++ b/src/main/scala/com/specs/config/ConfigLoader.scala @@ -0,0 +1,41 @@ +package com.specs.config + +import java.util.Properties + +import sys.process._ +import scala.language.postfixOps + +/** + * Created by prayagupd + * on 4/4/17. + */ +class ConfigLoader { + + var appConfig: Properties = new Properties() { + load(ConfigLoader.this.getClass.getClassLoader.getResourceAsStream(config)) + } + + def environment: String = { + + Option(System.getProperty("CI_APPLICATION_ENVIRONMENT")) match { + case Some(env) => + println("-DCI_APPLICATION_ENVIRONMENT") + println(s"application CI environment is set to $env") + env + case None => + println("============ infra environment ============") + println("env"!!) + println("============ infra environment ============") + println(s"application environment is set to ${System.getenv("APPLICATION_ENVIRONMENT")}") + System.getenv("APPLICATION_ENVIRONMENT") + } + } + + def config: String = { + if (environment != null && configExists(environment)) return "application-" + environment + ".properties" + "application.properties" + } + + private def configExists(env: String): Boolean = + classOf[ConfigLoader].getClassLoader.getResource("application-" + env + ".properties") != null +} diff --git a/src/main/scala/com/specs/database/DatabaseComponentSpecs.scala b/src/main/scala/com/specs/database/DatabaseComponentSpecs.scala new file mode 100644 index 0000000..7271137 --- /dev/null +++ b/src/main/scala/com/specs/database/DatabaseComponentSpecs.scala @@ -0,0 +1,163 @@ +package com.specs.database + +import java.sql._ + +import com.specs.ComponentSpecs +import com.specs.config.ConfigLoader +import org.apache.commons.dbcp.BasicDataSource +import org.json.JSONObject + +/** + * Created by prayagupd + * on 3/20/17. + */ + +trait DatabaseComponentSpecs extends ComponentSpecs { + + val appConfig = new ConfigLoader().appConfig + + val url: String = appConfig.getProperty("state.url") + val database: String = appConfig.getProperty("state.database.name") + val user: String = appConfig.getProperty("state.username") + val password: String = appConfig.getProperty("state.password") + + val establishedConnections = connectionPool() + + def queryState(query: String, numberOfColumns: Int): java.util.List[JSONObject] = { + var statement: Statement = null + var resultSet: ResultSet = null + val states: java.util.List[JSONObject] = new java.util.ArrayList[JSONObject] + try { + val connection: Connection = getEstablishedDatabaseConnection + connection.setAutoCommit(false) + statement = connection.createStatement + val start: Long = System.currentTimeMillis + resultSet = statement.executeQuery(query) + val columns: ResultSetMetaData = resultSet.getMetaData + while (resultSet.next) { + val jsonObject: JSONObject = new JSONObject + Range(1, numberOfColumns).foreach(col => { + try { + jsonObject.put(columns.getColumnName(col), resultSet.getString(col)); + } catch { + case e: SQLException => e.printStackTrace() + } + }) + states.add(jsonObject) + } + System.out.println("read :: timeTaken to read = " + (System.currentTimeMillis - start) + "ms") + resultSet.close() + statement.close() + connection.commit() + connection.close() + return states + + } catch { + case ex: SQLException => { + System.err.println("SQLException information") + while (ex != null) { + System.err.println("Error msg: " + ex.getMessage) + val ex1 = ex.getNextException + } + } + } + states + } + + def connectionPool() : BasicDataSource = { + println(s"setting up connection pool for $url") + val driver: String = appConfig.getProperty("state.database.driver") + val autoCommit: Boolean = appConfig.getOrDefault("state.database.auto.commit", "true").toString.toBoolean + + val establishedConnections = new BasicDataSource() + establishedConnections.setUrl(url) + establishedConnections.setUsername(user) + establishedConnections.setPassword(password) + establishedConnections.setDriverClassName(driver) + + establishedConnections.setMinIdle(5) + establishedConnections.setMaxIdle(10) + establishedConnections.setMaxOpenPreparedStatements(50) + establishedConnections.setInitialSize(5) + establishedConnections.setPoolPreparedStatements(true) + establishedConnections.setDefaultAutoCommit(autoCommit); //adds ~30ms, on connection creation delay + establishedConnections.getConnection.close() + + println("Physical connection pool is setup for " + establishedConnections.getValidationQuery) + establishedConnections + } + + def readState(databaseName: String, tableName: String): java.util.List[String] = { + var statement: Statement = null + var resultSet: ResultSet = null + val states: java.util.List[String] = new java.util.ArrayList[String] + try { + val connection: Connection = getEstablishedDatabaseConnection + connection.setAutoCommit(false) + statement = connection.createStatement + val start: Long = System.currentTimeMillis + resultSet = statement.executeQuery("SELECT * FROM " + databaseName + "." + tableName) + System.out.println("read :: timeTaken to read = " + (System.currentTimeMillis - start) + "ms") + while (resultSet.next) { + Range(1, 16).foreach(x => { + try { + System.out.print(resultSet.getString(x) + ","); + } catch { + case e: SQLException => e.printStackTrace() + } + }) + System.out.println("\n") + states.add(resultSet.getString(1)) + } + resultSet.close() + statement.close() + connection.commit() + + println("readState :: closing connection") + connection.close() + + } catch { + case ex: SQLException => { + System.err.println("readState :: SQLException error") + while (ex != null) { + System.err.println("Error msg: " + ex.getMessage) + val ex1 = ex.getNextException + } + } + } + states + } + + def dropRecords(table: String) { + var statement: Statement = null + try { + val connection: Connection = getEstablishedDatabaseConnection + connection.setAutoCommit(true) + val compiledQuery: String = "DELETE FROM " + table + " where 1=1" + statement = connection.createStatement + val start: Long = System.currentTimeMillis + statement.executeUpdate(compiledQuery) + val end: Long = System.currentTimeMillis + System.out.println("drop :: total time taken = " + (end - start) + " ms") + statement.close() + + println("dropRecords :: closing connection") + connection.close() + + } catch { + case ex: SQLException => { + System.err.println("dropRecords :: SQLException information") + while (ex != null) { + System.err.println("Error msg: " + ex.getMessage) + val ex1 = ex.getNextException + System.exit(0) + } + } + } + } + + @throws[SQLException] + protected def getEstablishedDatabaseConnection: Connection = { + establishedConnections.getConnection + } +} diff --git a/src/main/scala/com/specs/end2end/SiahlSpecs.scala b/src/main/scala/com/specs/end2end/SiahlSpecs.scala new file mode 100644 index 0000000..ea63112 --- /dev/null +++ b/src/main/scala/com/specs/end2end/SiahlSpecs.scala @@ -0,0 +1,13 @@ +package com.specs.end2end + +import com.specs.database.DatabaseComponentSpecs +import com.specs.http.HttpFlowSpecs + +/** + * Created by prayagupd + * on 3/24/17. + */ + +trait SiahlSpecs extends HttpFlowSpecs with DatabaseComponentSpecs { + +} diff --git a/src/main/scala/com/specs/http/HttpFlowSpecs.scala b/src/main/scala/com/specs/http/HttpFlowSpecs.scala index 03b6ade..e76e9d9 100644 --- a/src/main/scala/com/specs/http/HttpFlowSpecs.scala +++ b/src/main/scala/com/specs/http/HttpFlowSpecs.scala @@ -13,10 +13,11 @@ import org.apache.http.impl.client.DefaultHttpClient * on 3/4/17. */ -class HttpFlowSpecs extends FlowSpecs { +trait HttpFlowSpecs extends FlowSpecs { def doHttpGet(endpoint: String): CloseableHttpResponse = { val httpRequest = new HttpGet(endpoint) + println(s"curl -XGET ${endpoint}") val response = (new DefaultHttpClient).execute(httpRequest) response } @@ -26,7 +27,11 @@ class HttpFlowSpecs extends FlowSpecs { httpRequest.setHeader("Content-type", contentType) httpRequest.setEntity(new StringEntity(content)) + val contentTypo = s"Content-Type: $contentType" + println(s"curl -H $contentTypo -XPOST -d '${content}' ${endpoint}") + val response = (new DefaultHttpClient).execute(httpRequest) + println("http response :: " + response ) response } @@ -34,7 +39,7 @@ class HttpFlowSpecs extends FlowSpecs { val responseBody: String = new BufferedReader(new InputStreamReader(response.getEntity.getContent)) .lines().collect(Collectors.joining("\n")) - println("Response body = " + responseBody) + println("Http Response body = " + responseBody) responseBody } } diff --git a/src/main/scala/com/specs/tags/E2ETagged.scala b/src/main/scala/com/specs/tags/E2ETagged.scala new file mode 100644 index 0000000..a6c3dc2 --- /dev/null +++ b/src/main/scala/com/specs/tags/E2ETagged.scala @@ -0,0 +1,12 @@ +package com.specs.tags + +import org.scalatest.Tag + +/** + * Created by prayagupd + * on 3/27/17. + */ + +object E2ETagged extends Tag(name = "e2e") { + +} diff --git a/src/main/scala/com/specs/tags/PerfTagged.scala b/src/main/scala/com/specs/tags/PerfTagged.scala new file mode 100644 index 0000000..79cf14d --- /dev/null +++ b/src/main/scala/com/specs/tags/PerfTagged.scala @@ -0,0 +1,12 @@ +package com.specs.tags + +import org.scalatest.Tag + +/** + * Created by prayagupd + * on 3/27/17. + */ + +object PerfTagged extends Tag(name = "perf") { + +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/src/test/scala/com/specs/CustomerOrder.scala b/src/test/scala/com/specs/CustomerOrder.scala index 7e3e598..8a07f60 100644 --- a/src/test/scala/com/specs/CustomerOrder.scala +++ b/src/test/scala/com/specs/CustomerOrder.scala @@ -14,4 +14,3 @@ class CustomerOrder(items: List[Item]) { } case class Item(name: String) - diff --git a/src/test/scala/com/specs/CustomerOrderComponentSpecs.scala b/src/test/scala/com/specs/CustomerOrderComponentSpecs.scala index 5d02f9a..901c353 100644 --- a/src/test/scala/com/specs/CustomerOrderComponentSpecs.scala +++ b/src/test/scala/com/specs/CustomerOrderComponentSpecs.scala @@ -17,7 +17,7 @@ class CustomerOrderComponentSpecs extends ComponentSpecs { order.checkout() Then("should be ready") - assert(order.state == "READY") + order.state shouldBe "READY" //maybe I want to test its state in database as well //more granular assertions than FlowSpecs } diff --git a/src/test/scala/com/specs/CustomerOrderFlowSpecs.scala b/src/test/scala/com/specs/CustomerOrderFlowSpecs.scala index 6a2389b..5cc632d 100644 --- a/src/test/scala/com/specs/CustomerOrderFlowSpecs.scala +++ b/src/test/scala/com/specs/CustomerOrderFlowSpecs.scala @@ -18,7 +18,7 @@ class CustomerOrderFlowSpecs extends FlowSpecs { customerOrder.checkout() Then("My order should be ready") - assert(customerOrder.state == "READY") + customerOrder.state shouldBe "READY" } } diff --git a/src/test/scala/com/specs/SomeUnitSpecs.scala b/src/test/scala/com/specs/SomeUnitSpecs.scala index 328fa06..c3f6fe6 100644 --- a/src/test/scala/com/specs/SomeUnitSpecs.scala +++ b/src/test/scala/com/specs/SomeUnitSpecs.scala @@ -12,7 +12,7 @@ class SomeUnitSpecs extends UnitSpecs { val subject = new Subject() val response = subject.doSomething("event") - assert(response == "Processing event") + response shouldBe "Processing event" } } } diff --git a/src/test/scala/com/specs/config/ConfigLoaderUnitSpecs.scala b/src/test/scala/com/specs/config/ConfigLoaderUnitSpecs.scala new file mode 100644 index 0000000..d0a80a5 --- /dev/null +++ b/src/test/scala/com/specs/config/ConfigLoaderUnitSpecs.scala @@ -0,0 +1,22 @@ +package com.specs.config + +import java.util.Properties + +import org.scalatest.{FunSuite, Matchers} + +/** + * Created by prayagupd + * on 6/9/17. + */ + +class ConfigLoaderUnitSpecs extends FunSuite with Matchers { + + test("returns environment if theres APPLICATION_ENVIRONMENT set as argLine") { + + val configLoader = new ConfigLoader + configLoader.appConfig = new Properties() + + configLoader.environment should not be empty + + } +} diff --git a/src/test/scala/com/specs/flow/http/AsyncSomeHttpFlowSpecs.scala b/src/test/scala/com/specs/flow/http/AsyncSomeHttpFlowSpecs.scala index 4b2eea5..c237d16 100644 --- a/src/test/scala/com/specs/flow/http/AsyncSomeHttpFlowSpecs.scala +++ b/src/test/scala/com/specs/flow/http/AsyncSomeHttpFlowSpecs.scala @@ -42,7 +42,7 @@ class AsyncSomeHttpFlowSpecs extends AsyncHttpFlowSpecs with Informing { println(s"After assertion ${json}") } } - assert(response.status == StatusCode.int2StatusCode(200)) + response.status shouldBe StatusCode.int2StatusCode(200) } } } diff --git a/src/test/scala/com/specs/flow/http/SomeHttpFlowSpecs.scala b/src/test/scala/com/specs/flow/http/SomeHttpFlowSpecs.scala index 548bb1c..4df96aa 100644 --- a/src/test/scala/com/specs/flow/http/SomeHttpFlowSpecs.scala +++ b/src/test/scala/com/specs/flow/http/SomeHttpFlowSpecs.scala @@ -13,7 +13,7 @@ class SomeHttpFlowSpecs extends HttpFlowSpecs { feature("Getting user posts on my API server") { - scenario("As a software engineer, I want to receive the user posts when I call the endpoint") { + ignore("As a software engineer, I want to receive the user posts when I call the endpoint") { When("I send a GET request to the http endpoint") val response = doHttpGet("https://jsonplaceholder.typicode.com/posts/1") @@ -31,7 +31,7 @@ class SomeHttpFlowSpecs extends HttpFlowSpecs { |} """.stripMargin - assert(JSON.parseRaw(responseContent(response)) == JSON.parseRaw(expectedJson)) + JSON.parseRaw(responseContent(response)) shouldBe JSON.parseRaw(expectedJson) } } }