Skip to content

Commit

Permalink
Merge pull request #17 from 47deg/rafa-15-unit-test
Browse files Browse the repository at this point in the history
Rafa 15 unit test
  • Loading branch information
Rafa Paradela committed May 25, 2016
2 parents 33fa70b + d4ca29c commit 63674ae
Show file tree
Hide file tree
Showing 17 changed files with 634 additions and 36 deletions.
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ lazy val dependencies = libraryDependencies ++= Seq(
"io.circe" %% "circe-core" % "0.3.0",
"io.circe" %% "circe-generic" % "0.3.0",
"io.circe" %% "circe-parser" % "0.3.0",
"com.typesafe" % "config" % "1.3.0",
"org.scalatest" %% "scalatest" % "2.2.6" % "test",
"com.ironcorelabs" %% "cats-scalatest" % "1.1.2" % "test"
"com.ironcorelabs" %% "cats-scalatest" % "1.1.2" % "test",
"org.mock-server" % "mockserver-netty" % "3.10.4" % "test"
)

lazy val scalariformSettings = SbtScalariform.scalariformSettings ++ Seq(
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
github.baseUrl = "https://api.github.com/"
github.authorizeUrl = "https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&scope=%s&state=%s"
github.accessTokenUrl = "https://github.com/login/oauth/access_token"
15 changes: 15 additions & 0 deletions src/main/scala/github4s/GithubApiConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package github4s

import com.typesafe.config.ConfigFactory

class GithubApiConfig(hocon: Option[String] = None) {

val config = hocon.fold(ConfigFactory.load)(ConfigFactory.parseString)

def getString(key: String) = sys.props.getOrElse(key, config.getString(key))
}

object GithubApiConfig {

implicit val defaultConfig: GithubApiConfig = new GithubApiConfig
}
5 changes: 3 additions & 2 deletions src/main/scala/github4s/HttpClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import github4s.free.domain.Pagination
import io.circe.Decoder
import scalaj.http._

class HttpClient {
class HttpClient(implicit config: GithubApiConfig) {

val defaultPagination = Pagination(1, 1000)

Expand Down Expand Up @@ -132,6 +132,7 @@ class HttpClient {
data: String
)(implicit D: Decoder[A]): GHResponse[A] =
GithubResponses.toEntity(HttpRequestBuilder(url)
.postMethod
.withHeaders(Map("Accept" "application/json"))
.withData(data)
.run)
Expand All @@ -147,6 +148,6 @@ class HttpClient {
val defaultPage: Int = 1
val defaultPerPage: Int = 30

private def buildURL(method: String) = s"https://api.github.com/$method"
private def buildURL(method: String) = config.getString("github.baseUrl") + method

}
11 changes: 5 additions & 6 deletions src/main/scala/github4s/api/Auth.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package github4s.api

import java.util.UUID

import cats.data.Xor
import github4s.GithubResponses.{ GHResult, GHResponse }
import github4s.free.domain._
import github4s.HttpClient
import github4s.{ GithubApiConfig, HttpClient }
import io.circe.generic.auto._
import io.circe.syntax._
import scalaj.http.HttpConstants._

/** Factory to encapsulate calls related to Auth operations */
object Auth {
class Auth(implicit config: GithubApiConfig) {

protected val httpClient = new HttpClient()
val httpClient = new HttpClient

val authorizeUrl = "https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&scope=%s&state=%s"
val accessTokenUrl = "https://github.com/login/oauth/access_token"
val authorizeUrl = config.getString("github.authorizeUrl")
val accessTokenUrl = config.getString("github.accessTokenUrl")

/**
* Call to request a new authorization given a basic authentication, the returned object Authorization includes an
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/github4s/api/Repos.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package github4s.api

import github4s.GithubResponses.GHResponse
import github4s.free.domain.{ Pagination, Commit, Repository, User }
import github4s.{ Decoders, HttpClient }
import github4s.{ GithubApiConfig, Decoders, HttpClient }
import io.circe.generic.auto._

/** Factory to encapsulate calls related to Repositories operations */
object Repos {
class Repos(implicit config: GithubApiConfig) {

import Decoders._

protected val httpClient = new HttpClient()
val httpClient = new HttpClient

/**
* Get information of a particular repository
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/github4s/api/Users.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package github4s.api

import github4s.GithubResponses.GHResponse
import github4s.HttpClient
import github4s.{ GithubApiConfig, HttpClient }
import github4s.free.domain.{ Pagination, User }
import io.circe.generic.auto._

/** Factory to encapsulate calls related to Users operations */
object Users {
class Users(implicit config: GithubApiConfig) {

protected val httpClient = new HttpClient()
val httpClient = new HttpClient

/**
* Get information for a particular user
Expand Down
29 changes: 16 additions & 13 deletions src/main/scala/github4s/free/interpreters/Interpreters.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package github4s.free.interpreters

import cats.{ MonadError, ApplicativeError, ~>, Eval }
import github4s.HttpClient
import github4s.api.{ Auth, Repos }
import github4s.api.{ Users, Auth, Repos }
import github4s.app.{ COGH01, GitHub4s }
import github4s.free.algebra._
import io.circe.Decoder
Expand All @@ -18,15 +17,16 @@ trait Interpreters[M[_]] {
all
}

protected val httpClient = new HttpClient()

/**
* Lifts Repository Ops to an effect capturing Monad such as Task via natural transformations
*/
def repositoryOpsInterpreter(implicit A: ApplicativeError[M, Throwable]): RepositoryOp ~> M = new (RepositoryOp ~> M) {

val repos = new Repos()

def apply[A](fa: RepositoryOp[A]): M[A] = fa match {
case GetRepo(owner, repo, accessToken) A.pureEval(Eval.later(Repos.get(accessToken, owner, repo)))
case ListCommits(owner, repo, sha, path, author, since, until, pagination, accessToken) A.pureEval(Eval.later(Repos.listCommits(accessToken, owner, repo, sha, path, author, since, until, pagination)))
case GetRepo(owner, repo, accessToken) A.pureEval(Eval.later(repos.get(accessToken, owner, repo)))
case ListCommits(owner, repo, sha, path, author, since, until, pagination, accessToken) A.pureEval(Eval.later(repos.listCommits(accessToken, owner, repo, sha, path, author, since, until, pagination)))
}
}

Expand All @@ -35,23 +35,26 @@ trait Interpreters[M[_]] {
*/
def userOpsInterpreter(implicit A: ApplicativeError[M, Throwable]): UserOp ~> M = new (UserOp ~> M) {

import github4s.api.Users
val users = new Users()

def apply[A](fa: UserOp[A]): M[A] = fa match {
case GetUser(username, accessToken) A.pureEval(Eval.later(Users.get(accessToken, username)))
case GetAuthUser(accessToken) A.pureEval(Eval.later(Users.getAuth(accessToken)))
case GetUsers(since, pagination, accessToken) A.pureEval(Eval.later(Users.getUsers(accessToken, since, pagination)))
case GetUser(username, accessToken) A.pureEval(Eval.later(users.get(accessToken, username)))
case GetAuthUser(accessToken) A.pureEval(Eval.later(users.getAuth(accessToken)))
case GetUsers(since, pagination, accessToken) A.pureEval(Eval.later(users.getUsers(accessToken, since, pagination)))
}
}

/**
* Lifts Auth Ops to an effect capturing Monad such as Task via natural transformations
*/
def authOpsInterpreter(implicit A: ApplicativeError[M, Throwable]): AuthOp ~> M = new (AuthOp ~> M) {

val auth = new Auth()

def apply[A](fa: AuthOp[A]): M[A] = fa match {
case NewAuth(username, password, scopes, note, client_id, client_secret) A.pureEval(Eval.later(Auth.newAuth(username, password, scopes, note, client_id, client_secret)))
case AuthorizeUrl(client_id, redirect_uri, scopes) A.pureEval(Eval.later(Auth.authorizeUrl(client_id, redirect_uri, scopes)))
case GetAccessToken(client_id, client_secret, code, redirect_uri, state) A.pureEval(Eval.later(Auth.getAccessToken(client_id, client_secret, code, redirect_uri, state)))
case NewAuth(username, password, scopes, note, client_id, client_secret) A.pureEval(Eval.later(auth.newAuth(username, password, scopes, note, client_id, client_secret)))
case AuthorizeUrl(client_id, redirect_uri, scopes) A.pureEval(Eval.later(auth.authorizeUrl(client_id, redirect_uri, scopes)))
case GetAccessToken(client_id, client_secret, code, redirect_uri, state) A.pureEval(Eval.later(auth.getAccessToken(client_id, client_secret, code, redirect_uri, state)))
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/test/scala/github4s/integration/GHAuthSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import cats.Id
import cats.scalatest.{ XorMatchers, XorValues }
import github4s.Github._
import github4s.free.interpreters.IdInterpreters._
import github4s.{ Github, TestUtils }
import github4s.Github
import github4s.utils.TestUtils
import org.scalatest._

class GHAuthSpec extends FlatSpec with Matchers with XorMatchers with XorValues with TestUtils {
Expand Down
3 changes: 2 additions & 1 deletion src/test/scala/github4s/integration/GHReposSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import cats.scalatest.{ XorMatchers, XorValues }
import github4s.Github._
import github4s.GithubResponses._
import github4s.free.interpreters.IdInterpreters._
import github4s.{ Github, TestUtils }
import github4s.Github
import github4s.utils.TestUtils
import org.scalatest.{ Matchers, FlatSpec }

class GHReposSpec extends FlatSpec with Matchers with XorMatchers with XorValues with TestUtils {
Expand Down
8 changes: 4 additions & 4 deletions src/test/scala/github4s/integration/GHUsersSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package github4s.integration
import cats.Id
import cats.scalatest.{ XorMatchers, XorValues }
import github4s.Github._
import github4s.GithubResponses._
import github4s.free.interpreters.IdInterpreters._
import github4s.{ Github, TestUtils }
import github4s.Github
import github4s.utils.TestUtils
import org.scalatest._

class GHUsersSpec extends FlatSpec with Matchers with XorMatchers with XorValues with TestUtils {
Expand Down Expand Up @@ -34,10 +34,10 @@ class GHUsersSpec extends FlatSpec with Matchers with XorMatchers with XorValues
response.value.statusCode shouldBe okStatusCode
}

it should "return error on Left when a invalid since value is provided" in {
it should "return an empty list when a invalid since value is provided" in {
val response = Github(accessToken).users.getUsers(invalidSinceInt).exec[Id]
response shouldBe right
response.value.value.nonEmpty shouldBe true
response.value.value.isEmpty shouldBe true
response.value.statusCode shouldBe okStatusCode
}

Expand Down
136 changes: 136 additions & 0 deletions src/test/scala/github4s/unit/ApiSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package github4s.unit

import cats.scalatest.{ XorMatchers, XorValues }
import github4s.api.{ Users, Repos, Auth }
import github4s.free.domain.Pagination
import github4s.utils.{ DummyGithubConfig, MockGithubApiServer, TestUtils }
import org.scalatest._

class ApiSpec
extends FlatSpec
with Matchers
with XorMatchers
with XorValues
with TestUtils
with MockGithubApiServer
with DummyGithubConfig {

val auth = new Auth
val repos = new Repos
val users = new Users

"Auth >> NewAuth" should "return a valid token when valid credential is provided" in {
val response = auth.newAuth(validUsername, "", validScopes, validNote, validClientId, "")
response shouldBe right
response.value.value.token.nonEmpty shouldBe true
response.value.statusCode shouldBe okStatusCode
}

it should "return error on Left when invalid credential is provided" in {
val response = auth.newAuth(validUsername, invalidPassword, validScopes, validNote, validClientId, "")
response shouldBe left
}

"Auth >> AuthorizeUrl" should "return the expected URL for valid username" in {
val response = auth.authorizeUrl(validClientId, validRedirectUri, validScopes)
response shouldBe right
response.value.value.url.contains(validRedirectUri) shouldBe true
response.value.statusCode shouldBe okStatusCode
}

"Auth >> GetAccessToken" should "return a valid access_token when a valid code is provided" in {
val response = auth.getAccessToken("", "", validCode, "", "")
response shouldBe right
response.value.value.access_token.nonEmpty shouldBe true
response.value.statusCode shouldBe okStatusCode
}

it should "return error on Left when invalid code is provided" in {
val response = auth.getAccessToken("", "", invalidCode, "", "")
response shouldBe left
}

"Repos >> Get" should "return the expected name when valid repo is provided" in {

val response = repos.get(accessToken, validRepoOwner, validRepoName)
response shouldBe right
response.value.value.name shouldBe validRepoName
response.value.statusCode shouldBe okStatusCode
}

it should "return error when an invalid repo name is passed" in {
val response = repos.get(accessToken, validRepoOwner, invalidRepoName)
response shouldBe left
}

"Repos >> ListCommits" should "return the expected list of commits for valid data" in {
val response = repos.listCommits(
accessToken = accessToken,
owner = validRepoOwner,
repo = validRepoName,
pagination = Option(Pagination(validPage, validPerPage))
)
response shouldBe right
response.value.value.nonEmpty shouldBe true
response.value.statusCode shouldBe okStatusCode
}

it should "return an empty list of commits for invalid page parameter" in {
val response = repos.listCommits(
accessToken = accessToken,
owner = validRepoOwner,
repo = validRepoName,
pagination = Option(Pagination(invalidPage, validPerPage))
)

response shouldBe right
response.value.value.isEmpty shouldBe true
response.value.statusCode shouldBe okStatusCode
}

it should "return error for invalid repo name" in {
val response = repos.listCommits(accessToken, validRepoOwner, invalidRepoName)
response shouldBe left
}

"Users >> Get" should "return the expected login for a valid username" in {

val response = users.get(accessToken, validUsername)

response shouldBe right
response.value.value.login shouldBe validUsername
response.value.statusCode shouldBe okStatusCode
}

it should "return error on Left for invalid username" in {
val response = users.get(accessToken, invalidUsername)
response shouldBe left
}

"Users >> GetAuth" should "return the expected login for a valid accessToken" in {
val response = users.getAuth(accessToken)
response shouldBe right
response.value.value.login shouldBe validUsername
response.value.statusCode shouldBe okStatusCode
}

it should "return error on Left when no accessToken is provided" in {
val response = users.getAuth(None)
response shouldBe left
}

"Users >> GetUsers" should "return users for a valid since value" in {
val response = users.getUsers(accessToken, validSinceInt)
response shouldBe right
response.value.value.nonEmpty shouldBe true
response.value.statusCode shouldBe okStatusCode
}

it should "return an empty list when a invalid since value is provided" in {
val response = users.getUsers(accessToken, invalidSinceInt)
response shouldBe right
response.value.value.isEmpty shouldBe true
response.value.statusCode shouldBe okStatusCode
}

}
15 changes: 15 additions & 0 deletions src/test/scala/github4s/utils/DummyGithubConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package github4s.utils

import github4s.GithubApiConfig

trait DummyGithubConfig {

val dummyConfigHocon =
"""
|github.baseUrl = "http://127.0.0.1:9999/"
|github.authorizeUrl = "http://127.0.0.1:9999/authorize?client_id=%s&redirect_uri=%s&scope=%s&state=%s"
|github.accessTokenUrl = "http://127.0.0.1:9999/login/oauth/access_token"
|""".stripMargin

implicit val dummyConfig = new GithubApiConfig(Option(dummyConfigHocon))
}

0 comments on commit 63674ae

Please sign in to comment.