From eabba6c636e837264d975aa44678f57307a845f4 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Fri, 22 May 2015 21:00:30 +0900 Subject: [PATCH 1/7] [WIP] #3 - issueAPI Added the last missing method listRepositoryIssue. Implemented tests for create, get, and edit issue, but not done tests for listing and filtering issues. listRepositoryIssue method works but untested. --- .../scala/codecheck/github/models/Issue.scala | 66 ++++++- .../codecheck/github/operations/IssueOp.scala | 15 +- src/test/scala/IssueOpSpec.scala | 170 +++++++++++++++++- src/test/scala/MilestoneOpSpec.scala | 24 ++- 4 files changed, 248 insertions(+), 27 deletions(-) diff --git a/src/main/scala/codecheck/github/models/Issue.scala b/src/main/scala/codecheck/github/models/Issue.scala index 325ed32..98b8139 100644 --- a/src/main/scala/codecheck/github/models/Issue.scala +++ b/src/main/scala/codecheck/github/models/Issue.scala @@ -4,6 +4,7 @@ import org.json4s.JValue import org.json4s.JString import org.json4s.JNothing import org.json4s.JNull +import org.json4s.JInt import org.json4s.JArray import org.json4s.JsonDSL._ import org.joda.time.DateTime @@ -54,6 +55,26 @@ object IssueSort { def fromString(str: String) = values.filter(_.name == str).head } +sealed abstract class MilestoneSearchOption { + +} + +object MilestoneSearchOption { + case object all extends MilestoneSearchOption { + val value = JString("*") + } + + case object none extends MilestoneSearchOption { + val value = JString("none") + } + + case class Specified(number: Int) extends MilestoneSearchOption { + val value = JInt(number) + } + + def apply(number: Int) = Specified(number) +} + case class IssueListOption( filter: IssueFilter = IssueFilter.assigned, state: IssueState = IssueState.open, @@ -67,7 +88,27 @@ case class IssueListOption( since.map("&since=" + _.toString("yyyy-MM-dd'T'HH:mm:ssZ")) } -/*case*/ class IssueListOption4Repository extends ToDo +case class IssueListOption4Repository( + milestone: Option[MilestoneSearchOption] = None, + state: IssueState = IssueState.open, + assignee: Option[String] = None, + creator: Option[String] = None, + mentioned: Option[String] = None, + labels: Seq[String] = Nil, + sort: IssueSort = IssueSort.created, + direction: SortDirection = SortDirection.desc, + since: Option[DateTime] = None +) { + def q: String = "?" + (if (!milestone.isEmpty) milestone map (t => s"milestone=$t&")) + + s"state=$state" + + (if (!assignee.isEmpty) assignee map (t => s"&assignee=$t")) + + (if (!creator.isEmpty) creator map (t => s"&creator=$t")) + + (if (!mentioned.isEmpty) mentioned map (t => s"&mentioned=$t")) + + (if (!labels.isEmpty) "&labels=" + labels.mkString(",")) + + s"&sort=$sort" + + s"&direction=$direction" + + (if (!since.isEmpty) since map ("&since=" + _.toString("yyyy-MM-dd'T'HH:mm:ssZ"))) + } case class IssueInput( title: Option[String] = None, @@ -96,23 +137,36 @@ object IssueInput { def apply(title: String, body: Option[String], assignee: Option[String], milestone: Option[Int], labels: Seq[String]): IssueInput = IssueInput(Some(title), body, assignee, milestone, labels, None) } + case class Issue(value: JValue) extends AbstractJson(value) { + def url = get("url") + def labels_url = get("labels_url") + def comments_url = get("comments_url") + def events_url = get("events_url") + def html_url = get("html_url") + def id = get("id").toLong def number = get("number").toLong def title = get("title") - def body = opt("body") - - lazy val assignee = objectOpt("assignee")(v => User(v)) - lazy val milestone = objectOpt("milestone")(v => Milestone(v)) lazy val user = new User(value \ "user") lazy val labels = (value \ "labels") match { case JArray(arr) => arr.map(new Label(_)) case _ => Nil } - lazy val repository = new Repository(value \ "repository") + + def state = get("state") + def locked = boolean("locked") + + lazy val assignee = objectOpt("assignee")(v => User(v)) + lazy val milestone = objectOpt("milestone")(v => Milestone(v)) def comments = get("comments").toInt def created_at = getDate("created_at") def updated_at = getDate("updated_at") def closed_at = dateOpt("closed_at") + def body = opt("body") + + lazy val closed_by = objectOpt("closed_by")(v => User(v)) + + lazy val repository = new Repository(value \ "repository") } diff --git a/src/main/scala/codecheck/github/operations/IssueOp.scala b/src/main/scala/codecheck/github/operations/IssueOp.scala index 7cb15dd..d16aec2 100644 --- a/src/main/scala/codecheck/github/operations/IssueOp.scala +++ b/src/main/scala/codecheck/github/operations/IssueOp.scala @@ -14,13 +14,11 @@ import codecheck.github.models.Issue import codecheck.github.models.IssueListOption import codecheck.github.models.IssueListOption4Repository -import codecheck.github.utils.ToDo - trait IssueOp { self: GitHubAPI => private def doList(path: String): Future[List[Issue]] = { - exec("GET", path).map( + exec("GET", path).map( _.body match { case JArray(arr) => arr.map(v => Issue(v)) case _ => throw new IllegalStateException() @@ -28,19 +26,20 @@ trait IssueOp { ) } - def listAllIssues(option: IssueListOption = IssueListOption()): Future[List[Issue]] = + def listAllIssues(option: IssueListOption = IssueListOption()): Future[List[Issue]] = doList("/issues" + option.q) - def listUserIssues(option: IssueListOption = IssueListOption()): Future[List[Issue]] = + def listUserIssues(option: IssueListOption = IssueListOption()): Future[List[Issue]] = doList("/user/issues" + option.q) def listOrgIssues(org: String, option: IssueListOption = IssueListOption()): Future[List[Issue]] = doList(s"/orgs/$org/issues" + option.q) - def listRepositoryIssues(owner: String, repo: String, option: IssueListOption4Repository): Future[List[Issue]] = ToDo[Future[List[Issue]]] + def listRepositoryIssues(owner: String, repo: String, option: IssueListOption4Repository = IssueListOption4Repository()): Future[List[Issue]] = + doList(s"/repos/$owner/$repo/issues" + option.q) - def getIssue(owner: String, repo: String, number: Long): Future[Option[Issue]] = - exec("GET", s"/repos/$owner/$repo/issues/$number", fail404=false).map(res => + def getIssue(owner: String, repo: String, number: Long): Future[Option[Issue]] = + exec("GET", s"/repos/$owner/$repo/issues/$number", fail404=false).map(res => res.statusCode match { case 404 => None case 200 => Some(Issue(res.body)) diff --git a/src/test/scala/IssueOpSpec.scala b/src/test/scala/IssueOpSpec.scala index f384574..cfd4271 100644 --- a/src/test/scala/IssueOpSpec.scala +++ b/src/test/scala/IssueOpSpec.scala @@ -1,20 +1,176 @@ + import org.scalatest.FunSpec import scala.concurrent.Await +import org.joda.time.DateTime +import org.joda.time.DateTimeZone + +import codecheck.github.models.IssueListOption +import codecheck.github.models.IssueFilter +import codecheck.github.models.IssueListOption4Repository +import codecheck.github.models.IssueState +import codecheck.github.models.Issue +import codecheck.github.models.IssueInput class IssueOpSpec extends FunSpec with Constants { val number = 1 + var nUser: Long = 0 + var nOrg: Long = 0 + var createdUser: DateTime = DateTime.now + var createdOrg: DateTime = DateTime.now - describe("assign operations") { - it("assign should succeed") { - val result = Await.result(api.assign(organization, repo, number, user), TIMEOUT) - showResponse(result) - assert(result.get("assignee.login") == user) + describe("createIssue(owner, repo, input)") { + val input = IssueInput(Some("test issue"), Some("testing"), Some(user), Some(1), Seq("question")) + + it("should create issue for user's own repo.") { + val result = Await.result(api.createIssue(user, userRepo, input), TIMEOUT) + //showResponse(result) + nUser = result.number + assert(result.url == "https://api.github.com/repos/" + user + "/" + userRepo + "/issues/" + nUser) + assert(result.labels_url == "https://api.github.com/repos/" + user + "/" + userRepo + "/issues/" + nUser + "/labels{/name}") + assert(result.comments_url == "https://api.github.com/repos/" + user + "/" + userRepo + "/issues/" + nUser + "/comments") + assert(result.events_url == "https://api.github.com/repos/" + user + "/" + userRepo + "/issues/" + nUser + "/events") + assert(result.html_url == "https://github.com/" + user + "/" + userRepo + "/issues/" + nUser) + assert(result.title == "test issue") + assert(result.user.login == user) + assert(result.labels.head.name == "question") + assert(result.state == "open") + assert(result.locked == false) + assert(result.assignee.get.login == user) + assert(result.milestone.get.number == 1) + assert(result.comments == 0) + assert(result.created_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) + assert(result.updated_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) + createdUser = result.created_at + assert(result.closed_at.isEmpty) + assert(result.body.get == "testing") + assert(result.closed_by.isEmpty) + } + + it("should create issue for organization's repo.") { + val result = Await.result(api.createIssue(organization, repo, input), TIMEOUT) + nOrg = result.number + assert(result.url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg) + assert(result.labels_url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg + "/labels{/name}") + assert(result.comments_url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg + "/comments") + assert(result.events_url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg + "/events") + assert(result.html_url == "https://github.com/" + organization + "/" + repo + "/issues/" + nOrg) + assert(result.title == "test issue") + assert(result.user.login == user) + assert(result.labels.head.name == "question") + assert(result.state == "open") + assert(result.locked == false) + assert(result.assignee.get.login == user) + assert(result.milestone.get.number == 1) + assert(result.comments == 0) + assert(result.created_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) + assert(result.updated_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) + createdOrg = result.created_at + assert(result.closed_at.isEmpty) + assert(result.body.get == "testing") + assert(result.closed_by.isEmpty) } + } + + describe("getIssue(owner, repo, number)") { + it("should return issue from user's own repo.") { + val result = Await.result(api.getIssue(user, userRepo, nUser), TIMEOUT) + assert(result.get.title == "test issue") + } + + it("should return issue from organization's repo.") { + val result = Await.result(api.getIssue(organization, repo, nOrg), TIMEOUT) + assert(result.get.title == "test issue") + } + } - it("unassign should succeed") { - val result = Await.result(api.unassign(organization, repo, number), TIMEOUT) + describe("unassign(owner, repo, number)") { + it("should succeed with valid inputs on issues in user's own repo.") { + val result = Await.result(api.unassign(user, userRepo, nUser), TIMEOUT) assert(result.opt("assignee").isEmpty) } + + it("should succeed with valid inputs on issues in organization's repo.") { + val result = Await.result(api.unassign(organization, repo, nOrg), TIMEOUT) + assert(result.opt("assignee").isEmpty) + } + } + + describe("assign(owner, repo, number, assignee)") { + it("should succeed with valid inputs on issues in user's own repo.") { + val result = Await.result(api.assign(user, userRepo, nUser, user), TIMEOUT) + assert(result.get("assignee.login") == user) + } + + it("should succeed with valid inputs on issues in organization's repo.") { + val result = Await.result(api.assign(organization, repo, nOrg, user), TIMEOUT) + assert(result.get("assignee.login") == user) + } + } + + describe("listAllIssues(option)") { + it("shold return at least one issue.") { + val result = Await.result(api.listAllIssues(), TIMEOUT) + assert(result.length > 0) + } + + it("shold return only one issue.") { + val option = IssueListOption(IssueFilter.all, IssueState.open, since=Some(createdUser)) + val result = Await.result(api.listAllIssues(option), TIMEOUT) + assert(result.length == 1) + assert(result.head.title == "test issue") + } + } + + describe("listUserIssues(option)") { + it("shold return at least one issue.") { + val result = Await.result(api.listUserIssues(), TIMEOUT) + assert(result.length > 0) + } + } + + describe("listOrgIssues(org, option)") { + it("should return at least one issue.") { + val result = Await.result(api.listOrgIssues(organization), TIMEOUT) + assert(result.length > 0) + } + } + + describe("listRepositoryIssues(owner, repo, option)") { + it("should return at least one issue.") { + val result = Await.result(api.listRepositoryIssues(organization, repo), TIMEOUT) + assert(result.length > 0) + } + } + + describe("listRepositoryIssues") { + it("is just testing.") { + val input = new IssueListOption4Repository(state=IssueState.all) + val result = Await.result(api.listRepositoryIssues(organization, repo, input), TIMEOUT) + } + } + + describe("editIssue(owner, repo, number, input)") { + val input = IssueInput(Some("test issue edited"), Some("testing again"), Some(user), Some(2), Seq("question", "bug"), Some(IssueState.closed)) + + it("should edit the issue in user's own repo.") { + val result = Await.result(api.editIssue(user, userRepo, nUser, input), TIMEOUT) + assert(result.title == "test issue edited") + assert(result.body.get == "testing again") + assert(result.milestone.get.number == 2) + assert(result.labels.head.name == "bug") + assert(result.state == "closed") + assert(result.updated_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) + } + + it("should edit the issue in organization's repo.") { + val result = Await.result(api.editIssue(organization, repo, nOrg, input), TIMEOUT) + assert(result.title == "test issue edited") + assert(result.body.get == "testing again") + assert(result.milestone.get.number == 2) + assert(result.labels.head.name == "bug") + assert(result.state == "closed") + assert(result.updated_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) + } } } diff --git a/src/test/scala/MilestoneOpSpec.scala b/src/test/scala/MilestoneOpSpec.scala index 41ab208..960ef45 100644 --- a/src/test/scala/MilestoneOpSpec.scala +++ b/src/test/scala/MilestoneOpSpec.scala @@ -1,4 +1,5 @@ import org.scalatest.path.FunSpec +import org.scalatest.BeforeAndAfter import codecheck.github.exceptions.NotFoundException import codecheck.github.models.Milestone import codecheck.github.models.MilestoneInput @@ -11,10 +12,21 @@ import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import org.joda.time.DateTime -class MilestoneOpSpec extends FunSpec - with Constants -{ +class MilestoneOpSpec extends FunSpec with Constants { + /* + before { } + after { + val input = new MilestoneInput(Some("test milestone")) + val input2 = new MilestoneInput(Some("test milestone 2")) + + Await.result(api.createMilestone(user, userRepo, input), TIMEOUT) + Await.result(api.createMilestone(user, userRepo, input2), TIMEOUT) + + Await.result(api.createMilestone(organization, repo, input), TIMEOUT) + Await.result(api.createMilestone(organization, repo, input2), TIMEOUT) + } +*/ private def removeAll = { val list = Await.result(api.listMilestones(organization, repo, MilestoneListOption(state=MilestoneState.all)), TIMEOUT) list.foreach { m => @@ -67,7 +79,7 @@ class MilestoneOpSpec extends FunSpec val input = MilestoneInput(gName, gDescription, d1) val ex = Await.result(api.createMilestone(organization, repoInvalid, input).failed, TIMEOUT) ex match { - case e: NotFoundException => + case e: NotFoundException => case _ => fail } } @@ -168,7 +180,7 @@ class MilestoneOpSpec extends FunSpec it("with wrong reponame should fail") { val ex = Await.result(api.listMilestones(organization, repoInvalid).failed, TIMEOUT) ex match { - case e: NotFoundException => + case e: NotFoundException => case _ => fail } } @@ -189,7 +201,7 @@ class MilestoneOpSpec extends FunSpec val ex = Await.result(api.removeMilestone(organization, repo, m1.number).failed, TIMEOUT) ex match { - case e: NotFoundException => + case e: NotFoundException => case _ => fail } } From 064b1490d4a9116fcf856f7b5252f83951952dc4 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Mon, 25 May 2015 13:28:27 +0900 Subject: [PATCH 2/7] [WIP] #3 - Fixed query string generator. Created more tests for issue listing methods. --- .../scala/codecheck/github/models/Issue.scala | 34 +++++------- src/test/scala/IssueOpSpec.scala | 53 +++++++++++++------ 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/main/scala/codecheck/github/models/Issue.scala b/src/main/scala/codecheck/github/models/Issue.scala index 98b8139..df27317 100644 --- a/src/main/scala/codecheck/github/models/Issue.scala +++ b/src/main/scala/codecheck/github/models/Issue.scala @@ -55,22 +55,14 @@ object IssueSort { def fromString(str: String) = values.filter(_.name == str).head } -sealed abstract class MilestoneSearchOption { - +sealed abstract class MilestoneSearchOption(val name: String) { + override def toString = name } object MilestoneSearchOption { - case object all extends MilestoneSearchOption { - val value = JString("*") - } - - case object none extends MilestoneSearchOption { - val value = JString("none") - } - - case class Specified(number: Int) extends MilestoneSearchOption { - val value = JInt(number) - } + case object all extends MilestoneSearchOption("*") + case object none extends MilestoneSearchOption("none") + case class Specified(number: Int) extends MilestoneSearchOption(number.toString()) def apply(number: Int) = Specified(number) } @@ -84,8 +76,8 @@ case class IssueListOption( since: Option[DateTime] = None ) { def q = s"?filter=$filter&state=$state&sort=$sort&direction=$direction" + - (if (labels.length == 0) "" else "&labels=" + labels.mkString(",")) + - since.map("&since=" + _.toString("yyyy-MM-dd'T'HH:mm:ssZ")) + (if (!labels.isEmpty) "&labels=" + labels.mkString(",") else "") + + (if (!since.isEmpty) (since map ("&since=" + _.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))).get else "") } case class IssueListOption4Repository( @@ -99,15 +91,15 @@ case class IssueListOption4Repository( direction: SortDirection = SortDirection.desc, since: Option[DateTime] = None ) { - def q: String = "?" + (if (!milestone.isEmpty) milestone map (t => s"milestone=$t&")) + + def q: String = "?" + (if (!milestone.isEmpty) (milestone map (t => s"milestone=$t&")).get else "") + s"state=$state" + - (if (!assignee.isEmpty) assignee map (t => s"&assignee=$t")) + - (if (!creator.isEmpty) creator map (t => s"&creator=$t")) + - (if (!mentioned.isEmpty) mentioned map (t => s"&mentioned=$t")) + - (if (!labels.isEmpty) "&labels=" + labels.mkString(",")) + + (if (!assignee.isEmpty) (assignee map (t => s"&assignee=$t")).get else "") + + (if (!creator.isEmpty) (creator map (t => s"&creator=$t")).get else "") + + (if (!mentioned.isEmpty) (mentioned map (t => s"&mentioned=$t")).get else "") + + (if (!labels.isEmpty) "&labels=" + labels.mkString(",") else "") + s"&sort=$sort" + s"&direction=$direction" + - (if (!since.isEmpty) since map ("&since=" + _.toString("yyyy-MM-dd'T'HH:mm:ssZ"))) + (if (!since.isEmpty) (since map ("&since=" + _.toString("yyyy-MM-dd'T'HH:mm:ss'Z'"))).get else "") } case class IssueInput( diff --git a/src/test/scala/IssueOpSpec.scala b/src/test/scala/IssueOpSpec.scala index cfd4271..e730a64 100644 --- a/src/test/scala/IssueOpSpec.scala +++ b/src/test/scala/IssueOpSpec.scala @@ -10,21 +10,20 @@ import codecheck.github.models.IssueListOption4Repository import codecheck.github.models.IssueState import codecheck.github.models.Issue import codecheck.github.models.IssueInput +import codecheck.github.models.MilestoneSearchOption class IssueOpSpec extends FunSpec with Constants { val number = 1 var nUser: Long = 0 var nOrg: Long = 0 - var createdUser: DateTime = DateTime.now - var createdOrg: DateTime = DateTime.now + var nTime: DateTime = DateTime.now() describe("createIssue(owner, repo, input)") { val input = IssueInput(Some("test issue"), Some("testing"), Some(user), Some(1), Seq("question")) it("should create issue for user's own repo.") { val result = Await.result(api.createIssue(user, userRepo, input), TIMEOUT) - //showResponse(result) nUser = result.number assert(result.url == "https://api.github.com/repos/" + user + "/" + userRepo + "/issues/" + nUser) assert(result.labels_url == "https://api.github.com/repos/" + user + "/" + userRepo + "/issues/" + nUser + "/labels{/name}") @@ -41,7 +40,6 @@ class IssueOpSpec extends FunSpec with Constants { assert(result.comments == 0) assert(result.created_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) assert(result.updated_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) - createdUser = result.created_at assert(result.closed_at.isEmpty) assert(result.body.get == "testing") assert(result.closed_by.isEmpty) @@ -65,8 +63,6 @@ class IssueOpSpec extends FunSpec with Constants { assert(result.comments == 0) assert(result.created_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) assert(result.updated_at.toDateTime(DateTimeZone.UTC).getMillis() - DateTime.now(DateTimeZone.UTC).getMillis() <= 5000) - createdOrg = result.created_at - assert(result.closed_at.isEmpty) assert(result.body.get == "testing") assert(result.closed_by.isEmpty) } @@ -114,10 +110,10 @@ class IssueOpSpec extends FunSpec with Constants { assert(result.length > 0) } - it("shold return only one issue.") { - val option = IssueListOption(IssueFilter.all, IssueState.open, since=Some(createdUser)) + it("shold return only two issues when using options.") { + val option = IssueListOption(IssueFilter.created, IssueState.open, Seq("question"), since=Some(nTime)) val result = Await.result(api.listAllIssues(option), TIMEOUT) - assert(result.length == 1) + assert(result.length == 2) assert(result.head.title == "test issue") } } @@ -127,6 +123,13 @@ class IssueOpSpec extends FunSpec with Constants { val result = Await.result(api.listUserIssues(), TIMEOUT) assert(result.length > 0) } + + it("shold return only one issues when using options.") { + val option = IssueListOption(IssueFilter.created, IssueState.open, Seq("question"), since=Some(nTime)) + val result = Await.result(api.listUserIssues(option), TIMEOUT) + assert(result.length == 1) + assert(result.head.title == "test issue") + } } describe("listOrgIssues(org, option)") { @@ -134,19 +137,39 @@ class IssueOpSpec extends FunSpec with Constants { val result = Await.result(api.listOrgIssues(organization), TIMEOUT) assert(result.length > 0) } + + it("shold return only one issues when using options.") { + val option = IssueListOption(IssueFilter.created, IssueState.open, Seq("question"), since=Some(nTime)) + val result = Await.result(api.listOrgIssues(organization, option), TIMEOUT) + assert(result.length == 1) + assert(result.head.title == "test issue") + } } describe("listRepositoryIssues(owner, repo, option)") { - it("should return at least one issue.") { + it("should return at least one issue from user's own repo.") { val result = Await.result(api.listRepositoryIssues(organization, repo), TIMEOUT) assert(result.length > 0) } - } - describe("listRepositoryIssues") { - it("is just testing.") { - val input = new IssueListOption4Repository(state=IssueState.all) - val result = Await.result(api.listRepositoryIssues(organization, repo, input), TIMEOUT) + it("should return at least one issue from organization's repo.") { + val result = Await.result(api.listRepositoryIssues(organization, repo), TIMEOUT) + assert(result.length > 0) + } + + it("should return only one issue from user's own repo when using options.") { + val option = new IssueListOption4Repository(Some(MilestoneSearchOption(1)), IssueState.open, Some(user), Some(user), labels=Seq("question"), since=Some(nTime)) + val result = Await.result(api.listRepositoryIssues(user, userRepo), TIMEOUT) + //showResponse(result) + assert(result.length == 1) + } + + it("should return only one issue from organization's repo when using options.") { + val option = IssueListOption4Repository(Some(MilestoneSearchOption.all), IssueState.open, Some(user), Some(user), labels=Seq("question"), since=Some(nTime)) + val result = Await.result(api.listRepositoryIssues(organization, repo, option), TIMEOUT) + showResponse(option.q) + showResponse(result) + //assert(result.length == 1) } } From 134f0897caa208e7fbb369ef3b90cdc2e5a2aac9 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Mon, 25 May 2015 17:41:19 +0900 Subject: [PATCH 3/7] [WIP] #3 - Refactored tests and minor changes to code --- .../scala/codecheck/github/models/Issue.scala | 2 +- .../codecheck/github/operations/IssueOp.scala | 1 + src/test/scala/IssueOpSpec.scala | 19 ++++++++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/scala/codecheck/github/models/Issue.scala b/src/main/scala/codecheck/github/models/Issue.scala index df27317..ab9e0f5 100644 --- a/src/main/scala/codecheck/github/models/Issue.scala +++ b/src/main/scala/codecheck/github/models/Issue.scala @@ -91,7 +91,7 @@ case class IssueListOption4Repository( direction: SortDirection = SortDirection.desc, since: Option[DateTime] = None ) { - def q: String = "?" + (if (!milestone.isEmpty) (milestone map (t => s"milestone=$t&")).get else "") + + def q = "?" + (if (!milestone.isEmpty) (milestone map (t => s"milestone=$t&")).get else "") + s"state=$state" + (if (!assignee.isEmpty) (assignee map (t => s"&assignee=$t")).get else "") + (if (!creator.isEmpty) (creator map (t => s"&creator=$t")).get else "") + diff --git a/src/main/scala/codecheck/github/operations/IssueOp.scala b/src/main/scala/codecheck/github/operations/IssueOp.scala index d16aec2..d347299 100644 --- a/src/main/scala/codecheck/github/operations/IssueOp.scala +++ b/src/main/scala/codecheck/github/operations/IssueOp.scala @@ -26,6 +26,7 @@ trait IssueOp { ) } + //Only listAll/User/OrgIssues return Repository object def listAllIssues(option: IssueListOption = IssueListOption()): Future[List[Issue]] = doList("/issues" + option.q) diff --git a/src/test/scala/IssueOpSpec.scala b/src/test/scala/IssueOpSpec.scala index e730a64..be14444 100644 --- a/src/test/scala/IssueOpSpec.scala +++ b/src/test/scala/IssueOpSpec.scala @@ -1,5 +1,6 @@ import org.scalatest.FunSpec +import org.scalatest.BeforeAndAfter import scala.concurrent.Await import org.joda.time.DateTime import org.joda.time.DateTimeZone @@ -12,6 +13,8 @@ import codecheck.github.models.Issue import codecheck.github.models.IssueInput import codecheck.github.models.MilestoneSearchOption +import codecheck.github.models.MilestoneInput + class IssueOpSpec extends FunSpec with Constants { val number = 1 @@ -108,6 +111,7 @@ class IssueOpSpec extends FunSpec with Constants { it("shold return at least one issue.") { val result = Await.result(api.listAllIssues(), TIMEOUT) assert(result.length > 0) + assert(result.head.repository.name == "test-repo") } it("shold return only two issues when using options.") { @@ -122,6 +126,7 @@ class IssueOpSpec extends FunSpec with Constants { it("shold return at least one issue.") { val result = Await.result(api.listUserIssues(), TIMEOUT) assert(result.length > 0) + assert(result.head.repository.name == "test-repo") } it("shold return only one issues when using options.") { @@ -148,7 +153,7 @@ class IssueOpSpec extends FunSpec with Constants { describe("listRepositoryIssues(owner, repo, option)") { it("should return at least one issue from user's own repo.") { - val result = Await.result(api.listRepositoryIssues(organization, repo), TIMEOUT) + val result = Await.result(api.listRepositoryIssues(user, userRepo), TIMEOUT) assert(result.length > 0) } @@ -159,17 +164,17 @@ class IssueOpSpec extends FunSpec with Constants { it("should return only one issue from user's own repo when using options.") { val option = new IssueListOption4Repository(Some(MilestoneSearchOption(1)), IssueState.open, Some(user), Some(user), labels=Seq("question"), since=Some(nTime)) - val result = Await.result(api.listRepositoryIssues(user, userRepo), TIMEOUT) - //showResponse(result) + val result = Await.result(api.listRepositoryIssues(user, userRepo, option), TIMEOUT) + showResponse(option.q) assert(result.length == 1) + assert(result.head.title == "test issue") } it("should return only one issue from organization's repo when using options.") { - val option = IssueListOption4Repository(Some(MilestoneSearchOption.all), IssueState.open, Some(user), Some(user), labels=Seq("question"), since=Some(nTime)) + val option = new IssueListOption4Repository(Some(MilestoneSearchOption(1)), IssueState.open, Some(user), Some(user), labels=Seq("question"), since=Some(nTime)) val result = Await.result(api.listRepositoryIssues(organization, repo, option), TIMEOUT) - showResponse(option.q) - showResponse(result) - //assert(result.length == 1) + assert(result.length == 1) + assert(result.head.title == "test issue") } } From 7abe80cfb8c21240ed1a98e0dc523437dbcbfcbe Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Mon, 25 May 2015 17:49:32 +0900 Subject: [PATCH 4/7] [WIP] #3 - manually added the new Constants.scala --- src/test/scala/Constants.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/scala/Constants.scala b/src/test/scala/Constants.scala index 5711024..297d3e2 100644 --- a/src/test/scala/Constants.scala +++ b/src/test/scala/Constants.scala @@ -1,4 +1,3 @@ - import com.ning.http.client.AsyncHttpClient import codecheck.github.api.GitHubAPI import scala.concurrent.duration._ From 1cf5899038e0fca46ef7cd8588cbebbb9b22c6dc Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Mon, 25 May 2015 18:35:05 +0900 Subject: [PATCH 5/7] [WIP] #3 - Added in BeforeAndAfter into tests. --- src/test/scala/IssueOpSpec.scala | 30 +++++++++++++++++++++++++++- src/test/scala/MilestoneOpSpec.scala | 18 +++-------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/test/scala/IssueOpSpec.scala b/src/test/scala/IssueOpSpec.scala index be14444..0f3e708 100644 --- a/src/test/scala/IssueOpSpec.scala +++ b/src/test/scala/IssueOpSpec.scala @@ -14,14 +14,42 @@ import codecheck.github.models.IssueInput import codecheck.github.models.MilestoneSearchOption import codecheck.github.models.MilestoneInput +import codecheck.github.models.MilestoneListOption +import codecheck.github.models.MilestoneState +import codecheck.github.models.Milestone -class IssueOpSpec extends FunSpec with Constants { +class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { val number = 1 var nUser: Long = 0 var nOrg: Long = 0 var nTime: DateTime = DateTime.now() + private def removeAll = { + val userMilestones = Await.result(api.listMilestones(user, userRepo, MilestoneListOption(state=MilestoneState.all)), TIMEOUT) + userMilestones.foreach { m => + Await.result(api.removeMilestone(user, userRepo, m.number), TIMEOUT) + } + + val orgMilestones = Await.result(api.listMilestones(organization, repo, MilestoneListOption(state=MilestoneState.all)), TIMEOUT) + orgMilestones.foreach { m => + Await.result(api.removeMilestone(organization, repo, m.number), TIMEOUT) + } + } + + before { + removeAll + + val nInput = new MilestoneInput(Some("test milestone")) + val nInput2 = new MilestoneInput(Some("test milestone 2")) + + Await.result(api.createMilestone(user, userRepo, nInput), TIMEOUT) + Await.result(api.createMilestone(organization, repo, nInput), TIMEOUT) + + Await.result(api.createMilestone(user, userRepo, nInput2), TIMEOUT) + Await.result(api.createMilestone(organization, repo, nInput2), TIMEOUT) + } + describe("createIssue(owner, repo, input)") { val input = IssueInput(Some("test issue"), Some("testing"), Some(user), Some(1), Seq("question")) diff --git a/src/test/scala/MilestoneOpSpec.scala b/src/test/scala/MilestoneOpSpec.scala index 960ef45..6da7c93 100644 --- a/src/test/scala/MilestoneOpSpec.scala +++ b/src/test/scala/MilestoneOpSpec.scala @@ -1,5 +1,4 @@ import org.scalatest.path.FunSpec -import org.scalatest.BeforeAndAfter import codecheck.github.exceptions.NotFoundException import codecheck.github.models.Milestone import codecheck.github.models.MilestoneInput @@ -12,21 +11,10 @@ import scala.concurrent.Await import scala.concurrent.ExecutionContext.Implicits.global import org.joda.time.DateTime -class MilestoneOpSpec extends FunSpec with Constants { - /* - before { } +class MilestoneOpSpec extends FunSpec + with Constants +{ - after { - val input = new MilestoneInput(Some("test milestone")) - val input2 = new MilestoneInput(Some("test milestone 2")) - - Await.result(api.createMilestone(user, userRepo, input), TIMEOUT) - Await.result(api.createMilestone(user, userRepo, input2), TIMEOUT) - - Await.result(api.createMilestone(organization, repo, input), TIMEOUT) - Await.result(api.createMilestone(organization, repo, input2), TIMEOUT) - } -*/ private def removeAll = { val list = Await.result(api.listMilestones(organization, repo, MilestoneListOption(state=MilestoneState.all)), TIMEOUT) list.foreach { m => From 40c7358052860839294b60fa185aa78a747d91ae Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 26 May 2015 11:54:44 +0900 Subject: [PATCH 6/7] [WIP] #3 - Added a second test-repo to not conflict with repo used by MilestoneOpSpec. --- src/test/scala/IssueOpSpec.scala | 50 +++++++++++------------- src/test/scala/OraganizationOpSpec.scala | 2 +- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/test/scala/IssueOpSpec.scala b/src/test/scala/IssueOpSpec.scala index 0f3e708..e34d755 100644 --- a/src/test/scala/IssueOpSpec.scala +++ b/src/test/scala/IssueOpSpec.scala @@ -1,6 +1,5 @@ - import org.scalatest.FunSpec -import org.scalatest.BeforeAndAfter +import org.scalatest.BeforeAndAfterAll import scala.concurrent.Await import org.joda.time.DateTime import org.joda.time.DateTimeZone @@ -18,36 +17,33 @@ import codecheck.github.models.MilestoneListOption import codecheck.github.models.MilestoneState import codecheck.github.models.Milestone -class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { +class IssueOpSpec extends FunSpec with Constants with BeforeAndAfterAll { val number = 1 var nUser: Long = 0 var nOrg: Long = 0 var nTime: DateTime = DateTime.now() + val tRepo = repo + "2" - private def removeAll = { + override def beforeAll() { val userMilestones = Await.result(api.listMilestones(user, userRepo, MilestoneListOption(state=MilestoneState.all)), TIMEOUT) userMilestones.foreach { m => Await.result(api.removeMilestone(user, userRepo, m.number), TIMEOUT) } - val orgMilestones = Await.result(api.listMilestones(organization, repo, MilestoneListOption(state=MilestoneState.all)), TIMEOUT) + val orgMilestones = Await.result(api.listMilestones(organization, tRepo, MilestoneListOption(state=MilestoneState.all)), TIMEOUT) orgMilestones.foreach { m => - Await.result(api.removeMilestone(organization, repo, m.number), TIMEOUT) + Await.result(api.removeMilestone(organization, tRepo, m.number), TIMEOUT) } - } - - before { - removeAll val nInput = new MilestoneInput(Some("test milestone")) val nInput2 = new MilestoneInput(Some("test milestone 2")) Await.result(api.createMilestone(user, userRepo, nInput), TIMEOUT) - Await.result(api.createMilestone(organization, repo, nInput), TIMEOUT) - Await.result(api.createMilestone(user, userRepo, nInput2), TIMEOUT) - Await.result(api.createMilestone(organization, repo, nInput2), TIMEOUT) + + Await.result(api.createMilestone(organization, tRepo, nInput), TIMEOUT) + Await.result(api.createMilestone(organization, tRepo, nInput2), TIMEOUT) } describe("createIssue(owner, repo, input)") { @@ -77,13 +73,13 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { } it("should create issue for organization's repo.") { - val result = Await.result(api.createIssue(organization, repo, input), TIMEOUT) + val result = Await.result(api.createIssue(organization, tRepo, input), TIMEOUT) nOrg = result.number - assert(result.url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg) - assert(result.labels_url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg + "/labels{/name}") - assert(result.comments_url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg + "/comments") - assert(result.events_url == "https://api.github.com/repos/" + organization + "/" + repo + "/issues/" + nOrg + "/events") - assert(result.html_url == "https://github.com/" + organization + "/" + repo + "/issues/" + nOrg) + assert(result.url == "https://api.github.com/repos/" + organization + "/" + tRepo + "/issues/" + nOrg) + assert(result.labels_url == "https://api.github.com/repos/" + organization + "/" + tRepo + "/issues/" + nOrg + "/labels{/name}") + assert(result.comments_url == "https://api.github.com/repos/" + organization + "/" + tRepo+ "/issues/" + nOrg + "/comments") + assert(result.events_url == "https://api.github.com/repos/" + organization + "/" + tRepo + "/issues/" + nOrg + "/events") + assert(result.html_url == "https://github.com/" + organization + "/" + tRepo + "/issues/" + nOrg) assert(result.title == "test issue") assert(result.user.login == user) assert(result.labels.head.name == "question") @@ -106,7 +102,7 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { } it("should return issue from organization's repo.") { - val result = Await.result(api.getIssue(organization, repo, nOrg), TIMEOUT) + val result = Await.result(api.getIssue(organization, tRepo, nOrg), TIMEOUT) assert(result.get.title == "test issue") } } @@ -118,7 +114,7 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { } it("should succeed with valid inputs on issues in organization's repo.") { - val result = Await.result(api.unassign(organization, repo, nOrg), TIMEOUT) + val result = Await.result(api.unassign(organization, tRepo, nOrg), TIMEOUT) assert(result.opt("assignee").isEmpty) } } @@ -130,7 +126,7 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { } it("should succeed with valid inputs on issues in organization's repo.") { - val result = Await.result(api.assign(organization, repo, nOrg, user), TIMEOUT) + val result = Await.result(api.assign(organization, tRepo, nOrg, user), TIMEOUT) assert(result.get("assignee.login") == user) } } @@ -139,7 +135,6 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { it("shold return at least one issue.") { val result = Await.result(api.listAllIssues(), TIMEOUT) assert(result.length > 0) - assert(result.head.repository.name == "test-repo") } it("shold return only two issues when using options.") { @@ -154,7 +149,6 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { it("shold return at least one issue.") { val result = Await.result(api.listUserIssues(), TIMEOUT) assert(result.length > 0) - assert(result.head.repository.name == "test-repo") } it("shold return only one issues when using options.") { @@ -186,21 +180,21 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { } it("should return at least one issue from organization's repo.") { - val result = Await.result(api.listRepositoryIssues(organization, repo), TIMEOUT) + val result = Await.result(api.listRepositoryIssues(organization, tRepo), TIMEOUT) assert(result.length > 0) } it("should return only one issue from user's own repo when using options.") { val option = new IssueListOption4Repository(Some(MilestoneSearchOption(1)), IssueState.open, Some(user), Some(user), labels=Seq("question"), since=Some(nTime)) val result = Await.result(api.listRepositoryIssues(user, userRepo, option), TIMEOUT) - showResponse(option.q) + //showResponse(option.q) assert(result.length == 1) assert(result.head.title == "test issue") } it("should return only one issue from organization's repo when using options.") { val option = new IssueListOption4Repository(Some(MilestoneSearchOption(1)), IssueState.open, Some(user), Some(user), labels=Seq("question"), since=Some(nTime)) - val result = Await.result(api.listRepositoryIssues(organization, repo, option), TIMEOUT) + val result = Await.result(api.listRepositoryIssues(organization, tRepo, option), TIMEOUT) assert(result.length == 1) assert(result.head.title == "test issue") } @@ -220,7 +214,7 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfter { } it("should edit the issue in organization's repo.") { - val result = Await.result(api.editIssue(organization, repo, nOrg, input), TIMEOUT) + val result = Await.result(api.editIssue(organization, tRepo, nOrg, input), TIMEOUT) assert(result.title == "test issue edited") assert(result.body.get == "testing again") assert(result.milestone.get.number == 2) diff --git a/src/test/scala/OraganizationOpSpec.scala b/src/test/scala/OraganizationOpSpec.scala index 0dcee7b..f8a4899 100644 --- a/src/test/scala/OraganizationOpSpec.scala +++ b/src/test/scala/OraganizationOpSpec.scala @@ -80,7 +80,7 @@ class OrganizationOpSpec extends FunSpec with Constants with BeforeAndAfter { assert(org.blog == "") assert(org.location == "Tokyo") assert(org.email == "") - assert(org.public_repos == 1) + assert(org.public_repos == 2) assert(org.public_gists == 0) assert(org.followers == 0) assert(org.following == 0) From 9f33cc8f84538351a937632b10aa1f382b96d8e1 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Tue, 26 May 2015 13:46:16 +0900 Subject: [PATCH 7/7] [WIP] #3 - Added conversion to UTC timezone. --- src/test/scala/IssueOpSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/IssueOpSpec.scala b/src/test/scala/IssueOpSpec.scala index e34d755..533c1c0 100644 --- a/src/test/scala/IssueOpSpec.scala +++ b/src/test/scala/IssueOpSpec.scala @@ -22,7 +22,7 @@ class IssueOpSpec extends FunSpec with Constants with BeforeAndAfterAll { val number = 1 var nUser: Long = 0 var nOrg: Long = 0 - var nTime: DateTime = DateTime.now() + var nTime: DateTime = DateTime.now().toDateTime(DateTimeZone.UTC) val tRepo = repo + "2" override def beforeAll() {