diff --git a/lib/code_corps/github/adapters/github_app_installation.ex b/lib/code_corps/github/adapters/app_installation.ex similarity index 93% rename from lib/code_corps/github/adapters/github_app_installation.ex rename to lib/code_corps/github/adapters/app_installation.ex index e0b5550bb..571055f93 100644 --- a/lib/code_corps/github/adapters/github_app_installation.ex +++ b/lib/code_corps/github/adapters/app_installation.ex @@ -1,4 +1,4 @@ -defmodule CodeCorps.GitHub.Adapters.GithubAppInstallation do +defmodule CodeCorps.GitHub.Adapters.AppInstallation do @moduledoc """ Module used to convert GitHub payloads into attributes for a `GithubAppInstallation`. diff --git a/lib/code_corps/github/adapters/pull_request.ex b/lib/code_corps/github/adapters/pull_request.ex new file mode 100644 index 000000000..993c85e52 --- /dev/null +++ b/lib/code_corps/github/adapters/pull_request.ex @@ -0,0 +1,39 @@ +defmodule CodeCorps.GitHub.Adapters.PullRequest do + + @mapping [ + {:additions, ["additions"]}, + {:body, ["body"]}, + {:changed_files, ["changed_files"]}, + {:closed_at, ["closed_at"]}, + {:comments, ["comments"]}, + {:comments_url, ["comments_url"]}, + {:commits, ["commits"]}, + {:commits_url, ["commits_url"]}, + {:deletions, ["deletions"]}, + {:diff_url, ["diff_url"]}, + {:github_created_at, ["created_at"]}, + {:github_id, ["id"]}, + {:github_updated_at, ["updated_at"]}, + {:html_url, ["html_url"]}, + {:issue_url, ["issue_url"]}, + {:locked, ["locked"]}, + {:merge_commit_sha, ["merge_commit_sha"]}, + {:mergeable_state, ["mergeable_state"]}, + {:merged, ["merged"]}, + {:merged_at, ["merged_at"]}, + {:number, ["number"]}, + {:patch_url, ["patch_url"]}, + {:review_comment_url, ["review_comment_url"]}, + {:review_comments, ["review_comments"]}, + {:review_comments_url, ["review_comments_url"]}, + {:state, ["state"]}, + {:statuses_url, ["statuses_url"]}, + {:title, ["title"]}, + {:url, ["url"]} + ] + + @spec from_api(map) :: map + def from_api(%{} = payload) do + payload |> CodeCorps.Adapter.MapTransformer.transform(@mapping) + end +end diff --git a/lib/code_corps/github/adapters/github_repo.ex b/lib/code_corps/github/adapters/repo.ex similarity index 89% rename from lib/code_corps/github/adapters/github_repo.ex rename to lib/code_corps/github/adapters/repo.ex index b383bf35a..b22de18fe 100644 --- a/lib/code_corps/github/adapters/github_repo.ex +++ b/lib/code_corps/github/adapters/repo.ex @@ -1,4 +1,4 @@ -defmodule CodeCorps.GitHub.Adapters.GithubRepo do +defmodule CodeCorps.GitHub.Adapters.Repo do @mapping [ {:github_account_avatar_url, ["owner", "avatar_url"]}, diff --git a/lib/code_corps/github/event/installation/changeset_builder.ex b/lib/code_corps/github/event/installation/changeset_builder.ex index c2574ac12..2f1b4a4be 100644 --- a/lib/code_corps/github/event/installation/changeset_builder.ex +++ b/lib/code_corps/github/event/installation/changeset_builder.ex @@ -8,7 +8,7 @@ defmodule CodeCorps.GitHub.Event.Installation.ChangesetBuilder do GithubAppInstallation, User } - alias CodeCorps.GitHub.Adapters.GithubAppInstallation, as: GithubAppInstallationAdapter + alias CodeCorps.GitHub.Adapters.AppInstallation, as: AppInstallationAdapter alias Ecto.Changeset @doc """ @@ -20,7 +20,7 @@ defmodule CodeCorps.GitHub.Event.Installation.ChangesetBuilder do %GithubAppInstallation{} = github_app_installation, %{} = payload) do - attrs = GithubAppInstallationAdapter.from_installation_event(payload) + attrs = AppInstallationAdapter.from_installation_event(payload) github_app_installation |> Changeset.change(attrs) diff --git a/lib/code_corps/github/event/installation/repos.ex b/lib/code_corps/github/event/installation/repos.ex index 6608ac173..1028eecaa 100644 --- a/lib/code_corps/github/event/installation/repos.ex +++ b/lib/code_corps/github/event/installation/repos.ex @@ -12,7 +12,7 @@ defmodule CodeCorps.GitHub.Event.Installation.Repos do Repo } - alias CodeCorps.GitHub.Adapters.GithubRepo, as: GithubRepoAdapter + alias CodeCorps.GitHub.Adapters.Repo, as: RepoAdapter alias Ecto.{Changeset, Multi} @@ -51,7 +51,7 @@ defmodule CodeCorps.GitHub.Event.Installation.Repos do # transaction step 2 @spec adapt_api_repo_list(map) :: {:ok, list(map)} defp adapt_api_repo_list(%{api_response: repositories}) do - adapter_results = repositories |> Enum.map(&GithubRepoAdapter.from_api/1) + adapter_results = repositories |> Enum.map(&RepoAdapter.from_api/1) {:ok, adapter_results} end diff --git a/lib/code_corps/github/event/pull_request.ex b/lib/code_corps/github/event/pull_request.ex new file mode 100644 index 000000000..9772e3833 --- /dev/null +++ b/lib/code_corps/github/event/pull_request.ex @@ -0,0 +1,96 @@ +defmodule CodeCorps.GitHub.Event.PullRequest do + @moduledoc ~S""" + In charge of handling a GitHub Webhook payload for the PullRequest event type + + [https://developer.github.com/v3/activity/events/types/#pullrequestevent](https://developer.github.com/v3/activity/events/types/#pullrequestevent) + """ + + @behaviour CodeCorps.GitHub.Event.Handler + + alias CodeCorps.{ + GitHub.Event.Common.RepoFinder, + GitHub.Event.PullRequest.PullRequestLinker, + GitHub.Event.PullRequest.TaskSyncer, + GitHub.Event.PullRequest.UserLinker, + GitHub.Event.PullRequest.Validator, + Repo, + Task + } + alias Ecto.Multi + + @type outcome :: {:ok, list(Task.t)} | + {:error, :not_fully_implemented} | + {:error, :unexpected_action} | + {:error, :unexpected_payload} | + {:error, :repository_not_found} | + {:error, :validation_error_on_inserting_user} | + {:error, :multiple_github_users_matched_same_cc_user} | + {:error, :validation_error_on_syncing_tasks} | + {:error, :unexpected_transaction_outcome} + + @doc ~S""" + Handles the "PullRequest" GitHub webhook + + The process is as follows + - validate the payload is structured as expected + - validate the action is properly supported + - match payload with affected `CodeCorps.GithubRepo` record using + `CodeCorps.GitHub.Event.Common.RepoFinder` + - match with a `CodeCorps.User` using + `CodeCorps.GitHub.Event.PullRequest.UserLinker` + - for each `CodeCorps.ProjectGithubRepo` belonging to matched repo + - match and update, or create a `CodeCorps.Task` on the associated + `CodeCorps.Project` + + If the process runs all the way through, the function will return an `:ok` + tuple with a list of affected (created or updated) tasks. + + If it fails, it will instead return an `:error` tuple, where the second + element is the atom indicating a reason. + """ + @spec handle(map) :: outcome + def handle(payload) do + Multi.new + |> Multi.run(:payload, fn _ -> payload |> validate_payload() end) + |> Multi.run(:action, fn _ -> payload |> validate_action() end) + |> Multi.run(:repo, fn _ -> RepoFinder.find_repo(payload) end) + |> Multi.run(:pull_request, fn %{repo: github_repo} -> link_pull_request(github_repo, payload) end) + |> Multi.run(:user, fn %{pull_request: github_pull_request} -> UserLinker.find_or_create_user(github_pull_request, payload) end) + |> Multi.run(:tasks, fn %{pull_request: github_pull_request, user: user} -> github_pull_request |> TaskSyncer.sync_all(user, payload) end) + |> Repo.transaction + |> marshall_result() + end + + @spec link_pull_request(GithubRepo.t, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t} + defp link_pull_request(github_repo, %{"pull_request" => attrs}) do + PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + end + + @spec marshall_result(tuple) :: tuple + defp marshall_result({:ok, %{tasks: tasks}}), do: {:ok, tasks} + defp marshall_result({:error, :payload, :invalid, _steps}), do: {:error, :unexpected_payload} + defp marshall_result({:error, :action, :not_fully_implemented, _steps}), do: {:error, :not_fully_implemented} + defp marshall_result({:error, :action, :unexpected_action, _steps}), do: {:error, :unexpected_action} + defp marshall_result({:error, :repo, :unmatched_project, _steps}), do: {:ok, []} + defp marshall_result({:error, :repo, :unmatched_repository, _steps}), do: {:error, :repository_not_found} + defp marshall_result({:error, :user, %Ecto.Changeset{}, _steps}), do: {:error, :validation_error_on_inserting_user} + defp marshall_result({:error, :user, :multiple_users, _steps}), do: {:error, :multiple_github_users_matched_same_cc_user} + defp marshall_result({:error, :tasks, {_tasks, _errors}, _steps}), do: {:error, :validation_error_on_syncing_tasks} + defp marshall_result({:error, _errored_step, _error_response, _steps}), do: {:error, :unexpected_transaction_outcome} + + @implemented_actions ~w(opened closed edited reopened) + @unimplemented_actions ~w(assigned unassigned review_requested review_request_removed labeled unlabeled) + + @spec validate_action(map) :: {:ok, :implemented} | {:error, :not_fully_implemented | :unexpected_action} + defp validate_action(%{"action" => action}) when action in @implemented_actions, do: {:ok, :implemented} + defp validate_action(%{"action" => action}) when action in @unimplemented_actions, do: {:error, :not_fully_implemented} + defp validate_action(_payload), do: {:error, :unexpected_action} + + @spec validate_payload(map) :: {:ok, :valid} | {:error, :invalid} + defp validate_payload(%{} = payload) do + case payload |> Validator.valid? do + true -> {:ok, :valid} + false -> {:error, :invalid} + end + end +end diff --git a/lib/code_corps/github/event/pull_request/body_parser.ex b/lib/code_corps/github/event/pull_request/body_parser.ex new file mode 100644 index 000000000..20bd882ba --- /dev/null +++ b/lib/code_corps/github/event/pull_request/body_parser.ex @@ -0,0 +1,25 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.BodyParser do + @moduledoc ~S""" + In charge of extracting ids from markdown content, paired to a predefined list + of keywords. + """ + + @doc ~S""" + Searchs for GitHub closing keyword format inside a content string. Returns all + unique ids matched, as integers. + """ + @spec extract_closing_ids(String.t) :: list(integer) + def extract_closing_ids(content) when is_binary(content) do + ~w(close closes closed fix fixes fixed resolve resolves resolved) + |> matching_regex() + |> Regex.scan(content) # [["closes #1", "closes", "1"], ["fixes #2", "fixes", "2"]] + |> Enum.map(&List.last/1) # ["1", "2"] + |> Enum.map(&String.to_integer/1) # [1, 2] + |> Enum.uniq + end + + defp matching_regex(keywords) do + matches = keywords |> Enum.join("|") + ~r/(?:(#{matches}))\s+#(\d+)/i + end +end diff --git a/lib/code_corps/github/event/pull_request/changeset_builder.ex b/lib/code_corps/github/event/pull_request/changeset_builder.ex new file mode 100644 index 000000000..4aad1c0d6 --- /dev/null +++ b/lib/code_corps/github/event/pull_request/changeset_builder.ex @@ -0,0 +1,83 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.ChangesetBuilder do + @moduledoc ~S""" + In charge of building a `Changeset` to update a `Task` with, when handling an + PullRequest webhook. + """ + + alias CodeCorps.{ + GithubPullRequest, + ProjectGithubRepo, + Repo, + Services.MarkdownRendererService, + Task, + TaskList, + User, + Validators.TimeValidator + } + alias CodeCorps.GitHub.Adapters.Task, as: TaskAdapter + alias Ecto.Changeset + + @doc ~S""" + Constructs a changeset for syncing a `Task` when processing a PullRequest + webhook. + + The changeset can be used to create or update a `Task` + """ + @spec build_changeset(Task.t, map, GithubPullRequest.t, ProjectGithubRepo.t, User.t) :: Changeset.t + def build_changeset( + %Task{id: task_id} = task, + %{"pull_request" => pull_request_attrs}, + %GithubPullRequest{} = github_pull_request, + %ProjectGithubRepo{} = project_github_repo, + %User{} = user) do + + case is_nil(task_id) do + true -> create_changeset(task, pull_request_attrs, github_pull_request, project_github_repo, user) + false -> update_changeset(task, pull_request_attrs) + end + end + + @create_attrs ~w(created_at markdown modified_at status title)a + @spec create_changeset(Task.t, map, GithubPullRequest.t, ProjectGithubRepo.t, User.t) :: Changeset.t + defp create_changeset( + %Task{} = task, + %{} = pull_request_attrs, + %GithubPullRequest{id: github_pull_request_id}, + %ProjectGithubRepo{project_id: project_id, github_repo_id: github_repo_id}, + %User{id: user_id}) do + + %TaskList{id: task_list_id} = + TaskList |> Repo.get_by(project_id: project_id, inbox: true) + + task + |> Changeset.cast(TaskAdapter.from_api(pull_request_attrs), @create_attrs) + |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) + |> Changeset.put_change(:created_from, "github") + |> Changeset.put_change(:modified_from, "github") + |> Changeset.put_change(:github_pull_request_id, github_pull_request_id) + |> Changeset.put_change(:github_repo_id, github_repo_id) + |> Changeset.put_change(:project_id, project_id) + |> Changeset.put_change(:task_list_id, task_list_id) + |> Changeset.put_change(:user_id, user_id) + |> Changeset.validate_required([:project_id, :task_list_id, :title, :user_id]) + |> Changeset.assoc_constraint(:github_pull_request) + |> Changeset.assoc_constraint(:github_repo) + |> Changeset.assoc_constraint(:project) + |> Changeset.assoc_constraint(:task_list) + |> Changeset.assoc_constraint(:user) + end + + @update_attrs ~w(markdown modified_at status title)a + @spec update_changeset(Task.t, map) :: Changeset.t + defp update_changeset(%Task{} = task, %{} = pull_request_attrs) do + task + |> Changeset.cast(TaskAdapter.from_api(pull_request_attrs), @update_attrs) + |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) + |> Changeset.put_change(:modified_from, "github") + |> TimeValidator.validate_time_after(:modified_at) + |> Changeset.validate_required([:project_id, :title, :user_id]) + |> Changeset.assoc_constraint(:github_repo) + |> Changeset.assoc_constraint(:project) + |> Changeset.assoc_constraint(:user) + end +end diff --git a/lib/code_corps/github/event/pull_request/pull_request_linker.ex b/lib/code_corps/github/event/pull_request/pull_request_linker.ex new file mode 100644 index 000000000..3637084a9 --- /dev/null +++ b/lib/code_corps/github/event/pull_request/pull_request_linker.ex @@ -0,0 +1,55 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.PullRequestLinker do + @moduledoc ~S""" + In charge of finding a pull request to link with a `GithubPullRequest` record + when processing the PullRequest webhook. + + The only entry point is `create_or_update_pull_request/1`. + """ + + alias CodeCorps.{ + GithubPullRequest, + GithubRepo, + Repo + } + + alias CodeCorps.GitHub.Adapters.PullRequest, as: PullRequestAdapter + + @typep linking_result :: {:ok, GithubPullRequest.t} | + {:error, Ecto.Changeset.t} + + @doc ~S""" + Finds or creates a `GithubPullRequest` using the data in a GitHub PullRequest + payload. + + The process is as follows: + + - Search for the pull request in our database with the payload data. + - If we return a single `GithubPullRequest`, then the `GithubPullRequest` + should be updated. + - If there are no matching `GithubPullRequest` records, then a + `GithubPullRequest`should be created. + """ + @spec create_or_update_pull_request(GithubRepo.t, map) :: linking_result + def create_or_update_pull_request(%GithubRepo{} = github_repo, %{"id" => github_pull_request_id} = attrs) do + params = PullRequestAdapter.from_api(attrs) + + case Repo.get_by(GithubPullRequest, github_id: github_pull_request_id) do + nil -> create_pull_request(github_repo, params) + %GithubPullRequest{} = pull_request -> update_pull_request(pull_request, params) + end + end + + defp create_pull_request(%GithubRepo{id: github_repo_id}, params) do + params = Map.put(params, :github_repo_id, github_repo_id) + + %GithubPullRequest{} + |> GithubPullRequest.create_changeset(params) + |> Repo.insert + end + + defp update_pull_request(%GithubPullRequest{} = github_pull_request, params) do + github_pull_request + |> GithubPullRequest.update_changeset(params) + |> Repo.update + end +end diff --git a/lib/code_corps/github/event/pull_request/task_syncer.ex b/lib/code_corps/github/event/pull_request/task_syncer.ex new file mode 100644 index 000000000..66cf9573e --- /dev/null +++ b/lib/code_corps/github/event/pull_request/task_syncer.ex @@ -0,0 +1,65 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.TaskSyncer do + alias CodeCorps.{ + GithubPullRequest, + GithubRepo, + GitHub.Event.Common.ResultAggregator, + GitHub.Event.PullRequest.ChangesetBuilder, + ProjectGithubRepo, + Task, + User, + Repo + } + + alias Ecto.Changeset + + @type outcome :: {:ok, list(Task.t)} | + {:error, {list(Task.t), list(Changeset.t)}} + + @doc """ + When provided a `CodeCorps.GithubPullRequest`, a `CodeCorps.User` and a + GitHub API payload, for each `CodeCorps.Project` associated to that + `CodeCorps.GithubRepo` via a `CodeCorps.ProjectGithubRepo`, it + creates or updates a `CodeCorps.Task`. + """ + @spec sync_all(GithubPullRequest.t, User.t, map) :: {:ok, list(Task.t)} + def sync_all(%GithubPullRequest{} = github_pull_request, %User{} = user, %{} = payload) do + + %GithubPullRequest{ + github_repo: %GithubRepo{project_github_repos: project_github_repos} + } = github_pull_request |> Repo.preload(github_repo: :project_github_repos) + + project_github_repos + |> Enum.map(&sync(github_pull_request, &1, user, payload)) + |> ResultAggregator.aggregate + end + + @spec sync(GithubPullRequest.t, ProjectGithubRepo.t, User.t, map) :: {:ok, ProjectGithubRepo.t} | {:error, Changeset.t} + defp sync(%GithubPullRequest{} = github_pull_request, %ProjectGithubRepo{} = project_github_repo, %User{} = user, %{} = payload) do + project_github_repo + |> find_or_init_task(github_pull_request) + |> ChangesetBuilder.build_changeset(payload, github_pull_request, project_github_repo, user) + |> commit() + end + + @spec find_or_init_task(ProjectGithubRepo.t, GithubPullRequest.t) :: Task.t + defp find_or_init_task( + %ProjectGithubRepo{project_id: project_id, github_repo_id: github_repo_id}, + %GithubPullRequest{id: github_pull_request_id} + ) do + + query_params = [ + github_pull_request_id: github_pull_request_id, + github_repo_id: github_repo_id, + project_id: project_id + ] + + case Task |> Repo.get_by(query_params) do + nil -> %Task{} + %Task{} = task -> task + end + end + + @spec commit(Changeset.t) :: {:ok, Task.t} | {:error, Changeset.t} + defp commit(%Changeset{data: %Task{id: nil}} = changeset), do: changeset |> Repo.insert + defp commit(%Changeset{} = changeset), do: changeset |> Repo.update +end diff --git a/lib/code_corps/github/event/pull_request/user_linker.ex b/lib/code_corps/github/event/pull_request/user_linker.ex new file mode 100644 index 000000000..ee5b10278 --- /dev/null +++ b/lib/code_corps/github/event/pull_request/user_linker.ex @@ -0,0 +1,69 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.UserLinker do + @moduledoc ~S""" + In charge of finding a user to link with a `CodeCorps.Task` when processing + a PullRequest webhook. + + The only entry point is `find_or_create_user/1`. + """ + + import Ecto.Query + + alias CodeCorps.{ + Accounts, + GithubPullRequest, + Repo, + Task, + User + } + + @typep linking_result :: {:ok, User.t} | + {:error, Ecto.Changeset.t} | + {:error, :multiple_users} + + @doc ~S""" + Finds or creates a user using information in a `GithubPullRequest` record and a + Github Issue payload. + + The process is as follows: + - Find all affected tasks and extract their user data. + - Search for the user in our database. + - If we match a single user, then the PR should be related to that user. + - If there are no matching users, then the PR was created on Github by + someone who does not have a matching GitHub-connected Code Corps account. + We create a placeholder user account until that GitHub user is claimed by + a Code Corps user. + - If there are multiple matching users, this is an unexpected scenario and + should error out. + """ + @spec find_or_create_user(GithubPullRequest.t, map) :: linking_result + def find_or_create_user(%GithubPullRequest{} = pull_request, %{"pull_request" => %{"user" => user_attrs}}) do + pull_request + |> match_users + |> marshall_response(user_attrs) + end + + @spec match_users(GithubPullRequest.t) :: list(User.t) + defp match_users(%GithubPullRequest{id: github_pull_request_id}) do + + query = from u in User, + distinct: u.id, + join: t in Task, on: u.id == t.user_id, where: t.github_pull_request_id == ^github_pull_request_id + + query |> Repo.all + end + + @spec marshall_response(list, map) :: linking_result + defp marshall_response([%User{} = single_user], %{}), do: {:ok, single_user} + defp marshall_response([], %{} = user_attrs) do + user_attrs |> find_or_create_disassociated_user() + end + defp marshall_response([_head | _tail], %{}), do: {:error, :multiple_users} + + @spec find_or_create_disassociated_user(map) :: {:ok, User.t} + def find_or_create_disassociated_user(%{"id" => github_id} = attrs) do + case User |> Repo.get_by(github_id: github_id) do + nil -> attrs |> Accounts.create_from_github + %User{} = user -> {:ok, user} + end + end +end diff --git a/lib/code_corps/github/event/pull_request/validator.ex b/lib/code_corps/github/event/pull_request/validator.ex new file mode 100644 index 000000000..b36565b4e --- /dev/null +++ b/lib/code_corps/github/event/pull_request/validator.ex @@ -0,0 +1,27 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.Validator do + @moduledoc ~S""" + In charge of validatng a GitHub PullRequest webhook payload. + + https://developer.github.com/v3/activity/events/types/#pullrequestevent + """ + + @doc ~S""" + Returns `true` if all keys required to properly handle an PullRequest webhook + are present in the provided payload. + """ + @spec valid?(map) :: boolean + def valid?(%{ + "action" => _, + "pull_request" => %{ + "id" => _, + "title" => _, + "user" => %{ + "id" => _ + } + }, + "repository" => %{ + "id" => _ + } + }), do: true + def valid?(_), do: false +end diff --git a/lib/code_corps/github/webhook/event_support.ex b/lib/code_corps/github/webhook/event_support.ex index 99b05495c..5f057ae5b 100644 --- a/lib/code_corps/github/webhook/event_support.ex +++ b/lib/code_corps/github/webhook/event_support.ex @@ -3,7 +3,9 @@ defmodule CodeCorps.GitHub.Webhook.EventSupport do Determines event support for a GitHub event type """ - @supported_events ~w(installation installation_repositories issue_comment issues) + @supported_events ~w( + installation installation_repositories issue_comment issues pull_request + ) @type support_status :: :supported | :unsupported diff --git a/lib/code_corps/github/webhook/handler.ex b/lib/code_corps/github/webhook/handler.ex index a6c4c2129..128a2a9f7 100644 --- a/lib/code_corps/github/webhook/handler.ex +++ b/lib/code_corps/github/webhook/handler.ex @@ -10,6 +10,7 @@ defmodule CodeCorps.GitHub.Webhook.Handler do GitHub.Event.InstallationRepositories, GitHub.Event.IssueComment, GitHub.Event.Issues, + GitHub.Event.PullRequest, GitHub.Webhook.EventSupport, Repo } @@ -51,4 +52,5 @@ defmodule CodeCorps.GitHub.Webhook.Handler do defp do_handle(payload, "installation_repositories"), do: InstallationRepositories.handle(payload) defp do_handle(payload, "issue_comment"), do: IssueComment.handle(payload) defp do_handle(payload, "issues"), do: Issues.handle(payload) + defp do_handle(payload, "pull_request"), do: PullRequest.handle(payload) end diff --git a/lib/code_corps/model/github_app_installation.ex b/lib/code_corps/model/github_app_installation.ex index 066fc1329..feaae9e91 100644 --- a/lib/code_corps/model/github_app_installation.ex +++ b/lib/code_corps/model/github_app_installation.ex @@ -9,22 +9,18 @@ defmodule CodeCorps.GithubAppInstallation do schema "github_app_installations" do field :access_token, :string field :access_token_expires_at, :utc_datetime - field :github_id, :integer - field :github_account_avatar_url, :string field :github_account_id, :integer field :github_account_login, :string field :github_account_type, :string - + field :github_id, :integer field :installed, :boolean + field :origin, :string, default: "codecorps" # "codecorps" or "github" field :sender_github_id, :integer # "unprocessed", "processing", "processed" or "errored" field :state, :string, default: "unprocessed" - # "codecorps" or "github" - field :origin, :string, default: "codecorps" - belongs_to :project, CodeCorps.Project # The originating project belongs_to :user, CodeCorps.User @@ -40,11 +36,11 @@ defmodule CodeCorps.GithubAppInstallation do def create_changeset(struct, params \\ %{}) do struct |> cast(params, [:project_id, :user_id]) - |> put_change(:state, "unprocessed") - |> put_change(:origin, "codecorps") |> validate_required([:project_id, :user_id]) |> assoc_constraint(:project) |> assoc_constraint(:user) + |> put_change(:origin, "codecorps") + |> put_change(:state, "unprocessed") end @doc ~S""" diff --git a/lib/code_corps/model/github_pull_request.ex b/lib/code_corps/model/github_pull_request.ex new file mode 100644 index 000000000..d69306ba5 --- /dev/null +++ b/lib/code_corps/model/github_pull_request.ex @@ -0,0 +1,73 @@ +defmodule CodeCorps.GithubPullRequest do + use Ecto.Schema + import Ecto.Changeset + + schema "github_pull_requests" do + field :additions, :integer + field :body, :string + field :changed_files, :integer + field :closed_at, :utc_datetime + field :comments, :integer + field :comments_url, :string + field :commits, :integer + field :commits_url, :string + field :deletions, :integer + field :diff_url, :string + field :github_created_at, :utc_datetime + field :github_id, :integer + field :github_updated_at, :utc_datetime + field :html_url, :string + field :issue_url, :string + field :locked, :boolean, default: false + field :merge_commit_sha, :string + field :mergeable_state, :string + field :merged, :boolean, default: false + field :merged_at, :utc_datetime + field :number, :integer + field :patch_url, :string + field :review_comment_url, :string + field :review_comments, :integer + field :review_comments_url, :string + field :state, :string + field :statuses_url, :string + field :title, :string + field :url, :string + + belongs_to :github_repo, CodeCorps.GithubRepo + + timestamps() + end + + @attrs [ + :additions, :body, :changed_files, :closed_at, :comments, :comments_url, + :commits, :commits_url, :deletions, :diff_url, :github_created_at, + :github_id, :github_updated_at, :html_url, :issue_url, :locked, + :merge_commit_sha, :mergeable_state, :merged, :merged_at, :number, + :patch_url, :review_comment_url, :review_comments, :review_comments_url, + :state, :statuses_url, :title, :url + ] + + @required_attrs [ + :github_created_at, :github_id, :github_updated_at, :html_url, :locked, + :merged, :number, :state, :title + ] + + @doc false + def changeset(struct, params) do + struct + |> cast(params, @attrs) + |> validate_required(@required_attrs) + end + + def create_changeset(struct, params) do + struct + |> changeset(params) + |> cast(params, [:github_repo_id]) + |> assoc_constraint(:github_repo) + end + + def update_changeset(struct, params) do + struct + |> changeset(params) + end +end diff --git a/lib/code_corps/model/task.ex b/lib/code_corps/model/task.ex index 231156c50..a7999ac09 100644 --- a/lib/code_corps/model/task.ex +++ b/lib/code_corps/model/task.ex @@ -25,6 +25,7 @@ defmodule CodeCorps.Task do field :position, :integer, virtual: true belongs_to :github_issue, CodeCorps.GithubIssue + belongs_to :github_pull_request, CodeCorps.GithubPullRequest belongs_to :github_repo, CodeCorps.GithubRepo belongs_to :project, CodeCorps.Project belongs_to :task_list, CodeCorps.TaskList diff --git a/lib/code_corps_web/controllers/github_pull_request_controller.ex b/lib/code_corps_web/controllers/github_pull_request_controller.ex new file mode 100644 index 000000000..fbe986109 --- /dev/null +++ b/lib/code_corps_web/controllers/github_pull_request_controller.ex @@ -0,0 +1,23 @@ +defmodule CodeCorpsWeb.GithubPullRequestController do + use CodeCorpsWeb, :controller + + alias CodeCorps.{GithubPullRequest, Helpers.Query} + + action_fallback CodeCorpsWeb.FallbackController + plug CodeCorpsWeb.Plug.DataToAttributes + plug CodeCorpsWeb.Plug.IdsToIntegers + + @spec index(Conn.t, map) :: Conn.t + def index(%Conn{} = conn, %{} = params) do + with github_pull_requests <- GithubPullRequest |> Query.id_filter(params) |> Repo.all do + conn |> render("index.json-api", data: github_pull_requests) + end + end + + @spec show(Conn.t, map) :: Conn.t + def show(%Conn{} = conn, %{"id" => id}) do + with %GithubPullRequest{} = github_pull_request <- GithubPullRequest |> Repo.get(id) do + conn |> render("show.json-api", data: github_pull_request) + end + end +end diff --git a/lib/code_corps_web/router.ex b/lib/code_corps_web/router.ex index 6ef231f09..8484ddf5f 100644 --- a/lib/code_corps_web/router.ex +++ b/lib/code_corps_web/router.ex @@ -108,6 +108,7 @@ defmodule CodeCorpsWeb.Router do resources "/donation-goals", DonationGoalController, only: [:index, :show] resources "/github-app-installations", GithubAppInstallationController, only: [:index, :show] resources "/github-issues", GithubIssueController, only: [:index, :show] + resources "/github-pull-requests", GithubPullRequestController, only: [:index, :show] resources "/github-repos", GithubRepoController, only: [:index, :show] resources "/organization-github-app-installations", OrganizationGithubAppInstallationController, only: [:index, :show] resources "/organizations", OrganizationController, only: [:index, :show] diff --git a/lib/code_corps_web/views/github_pull_request_view.ex b/lib/code_corps_web/views/github_pull_request_view.ex new file mode 100644 index 000000000..bd68c8a8e --- /dev/null +++ b/lib/code_corps_web/views/github_pull_request_view.ex @@ -0,0 +1,9 @@ +defmodule CodeCorpsWeb.GithubPullRequestView do + use CodeCorpsWeb.PreloadHelpers, default_preloads: [:github_repo] + use CodeCorpsWeb, :view + use JaSerializer.PhoenixView + + attributes [:github_created_at, :github_updated_at, :html_url, :merged, :number, :state] + + has_one :github_repo, serializer: CodeCorpsWeb.GithubRepoView +end diff --git a/lib/code_corps_web/views/task_view.ex b/lib/code_corps_web/views/task_view.ex index cfcf38d26..81529d76f 100644 --- a/lib/code_corps_web/views/task_view.ex +++ b/lib/code_corps_web/views/task_view.ex @@ -1,6 +1,6 @@ defmodule CodeCorpsWeb.TaskView do use CodeCorpsWeb.PreloadHelpers, - default_preloads: ~w(github_issue github_repo project user task_list task_skills comments user_task)a + default_preloads: ~w(github_issue github_repo github_pull_request project user task_list task_skills comments user_task)a use CodeCorpsWeb, :view use JaSerializer.PhoenixView @@ -10,6 +10,7 @@ defmodule CodeCorpsWeb.TaskView do ] has_one :github_issue, serializer: CodeCorpsWeb.GithubIssueView + has_one :github_pull_request, serializer: CodeCorpsWeb.GithubPullRequestView has_one :github_repo, serializer: CodeCorpsWeb.GithubRepoView has_one :project, serializer: CodeCorpsWeb.ProjectView has_one :task_list, serializer: CodeCorpsWeb.TaskListView diff --git a/priv/repo/migrations/20171016223356_create_github_pull_requests.exs b/priv/repo/migrations/20171016223356_create_github_pull_requests.exs new file mode 100644 index 000000000..8e4aea98f --- /dev/null +++ b/priv/repo/migrations/20171016223356_create_github_pull_requests.exs @@ -0,0 +1,43 @@ +defmodule CodeCorps.Repo.Migrations.CreateGithubPullRequests do + use Ecto.Migration + + def change do + create table(:github_pull_requests) do + add :additions, :integer + add :body, :text + add :changed_files, :integer + add :closed_at, :utc_datetime + add :comments, :integer + add :comments_url, :text + add :commits, :integer + add :commits_url, :text + add :deletions, :integer + add :diff_url, :text + add :github_created_at, :utc_datetime + add :github_id, :integer + add :github_updated_at, :utc_datetime + add :html_url, :text + add :issue_url, :text + add :locked, :boolean, default: false, null: false + add :merge_commit_sha, :text + add :mergeable_state, :text + add :merged, :boolean, default: false, null: false + add :merged_at, :utc_datetime + add :number, :integer + add :patch_url, :text + add :review_comment_url, :text + add :review_comments, :integer + add :review_comments_url, :text + add :state, :string + add :statuses_url, :text + add :title, :text + add :url, :text + + timestamps() + + add :github_repo_id, references(:github_repos) + end + + create unique_index(:github_pull_requests, [:github_id]) + end +end diff --git a/priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs b/priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs new file mode 100644 index 000000000..10a04be26 --- /dev/null +++ b/priv/repo/migrations/20171016235656_add_pull_requests_to_tasks.exs @@ -0,0 +1,15 @@ +defmodule CodeCorps.Repo.Migrations.AddPullRequestsToTasks do + use Ecto.Migration + + def up do + alter table(:tasks) do + add :github_pull_request_id, references(:github_pull_requests) + end + end + + def down do + alter table(:tasks) do + remove :github_pull_request_id + end + end +end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index e26edfe61..a439cc4c9 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -350,6 +350,66 @@ CREATE SEQUENCE github_issues_id_seq ALTER SEQUENCE github_issues_id_seq OWNED BY github_issues.id; +-- +-- Name: github_pull_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE github_pull_requests ( + id bigint NOT NULL, + additions integer, + body text, + changed_files integer, + closed_at timestamp without time zone, + comments integer, + comments_url text, + commits integer, + commits_url text, + deletions integer, + diff_url text, + github_created_at timestamp without time zone, + github_id integer, + github_updated_at timestamp without time zone, + html_url text, + issue_url text, + locked boolean DEFAULT false NOT NULL, + merge_commit_sha text, + mergeable_state text, + merged boolean DEFAULT false NOT NULL, + merged_at timestamp without time zone, + number integer, + patch_url text, + review_comment_url text, + review_comments integer, + review_comments_url text, + state character varying(255), + statuses_url text, + title text, + url text, + inserted_at timestamp without time zone NOT NULL, + updated_at timestamp without time zone NOT NULL, + github_repo_id bigint +); + + +-- +-- Name: github_pull_requests_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE github_pull_requests_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: github_pull_requests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE github_pull_requests_id_seq OWNED BY github_pull_requests.id; + + -- -- Name: github_repos; Type: TABLE; Schema: public; Owner: - -- @@ -1460,7 +1520,8 @@ CREATE TABLE tasks ( created_from character varying(255) DEFAULT 'code_corps'::character varying, modified_from character varying(255) DEFAULT 'code_corps'::character varying, archived boolean DEFAULT false NOT NULL, - github_issue_id bigint + github_issue_id bigint, + github_pull_request_id bigint ); @@ -1716,6 +1777,13 @@ ALTER TABLE ONLY github_events ALTER COLUMN id SET DEFAULT nextval('github_event ALTER TABLE ONLY github_issues ALTER COLUMN id SET DEFAULT nextval('github_issues_id_seq'::regclass); +-- +-- Name: github_pull_requests id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY github_pull_requests ALTER COLUMN id SET DEFAULT nextval('github_pull_requests_id_seq'::regclass); + + -- -- Name: github_repos id; Type: DEFAULT; Schema: public; Owner: - -- @@ -2018,6 +2086,14 @@ ALTER TABLE ONLY github_issues ADD CONSTRAINT github_issues_pkey PRIMARY KEY (id); +-- +-- Name: github_pull_requests github_pull_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY github_pull_requests + ADD CONSTRAINT github_pull_requests_pkey PRIMARY KEY (id); + + -- -- Name: github_repos github_repos_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -2306,6 +2382,13 @@ CREATE INDEX github_app_installations_project_id_index ON github_app_installatio CREATE INDEX github_app_installations_user_id_index ON github_app_installations USING btree (user_id); +-- +-- Name: github_pull_requests_github_id_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX github_pull_requests_github_id_index ON github_pull_requests USING btree (github_id); + + -- -- Name: github_repos_github_app_installation_id_index; Type: INDEX; Schema: public; Owner: - -- @@ -2777,6 +2860,14 @@ ALTER TABLE ONLY github_issues ADD CONSTRAINT github_issues_github_repo_id_fkey FOREIGN KEY (github_repo_id) REFERENCES github_repos(id); +-- +-- Name: github_pull_requests github_pull_requests_github_repo_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY github_pull_requests + ADD CONSTRAINT github_pull_requests_github_repo_id_fkey FOREIGN KEY (github_repo_id) REFERENCES github_repos(id); + + -- -- Name: github_repos github_repos_github_app_installation_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3097,6 +3188,14 @@ ALTER TABLE ONLY tasks ADD CONSTRAINT tasks_github_issue_id_fkey FOREIGN KEY (github_issue_id) REFERENCES github_issues(id); +-- +-- Name: tasks tasks_github_pull_request_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY tasks + ADD CONSTRAINT tasks_github_pull_request_id_fkey FOREIGN KEY (github_pull_request_id) REFERENCES github_pull_requests(id); + + -- -- Name: tasks tasks_github_repo_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -3197,5 +3296,5 @@ ALTER TABLE ONLY user_tasks -- PostgreSQL database dump complete -- -INSERT INTO "schema_migrations" (version) VALUES (20160723215749), (20160804000000), (20160804001111), (20160805132301), (20160805203929), (20160808143454), (20160809214736), (20160810124357), (20160815125009), (20160815143002), (20160816020347), (20160816034021), (20160817220118), (20160818000944), (20160818132546), (20160820113856), (20160820164905), (20160822002438), (20160822004056), (20160822011624), (20160822020401), (20160822044612), (20160830081224), (20160830224802), (20160911233738), (20160912002705), (20160912145957), (20160918003206), (20160928232404), (20161003185918), (20161019090945), (20161019110737), (20161020144622), (20161021131026), (20161031001615), (20161121005339), (20161121014050), (20161121043941), (20161121045709), (20161122015942), (20161123081114), (20161123150943), (20161124085742), (20161125200620), (20161126045705), (20161127054559), (20161205024856), (20161207112519), (20161209192504), (20161212005641), (20161214005935), (20161215052051), (20161216051447), (20161218005913), (20161219160401), (20161219163909), (20161220141753), (20161221085759), (20161226213600), (20161231063614), (20170102130055), (20170102181053), (20170104113708), (20170104212623), (20170104235423), (20170106013143), (20170115035159), (20170115230549), (20170121014100), (20170131234029), (20170201014901), (20170201025454), (20170201035458), (20170201183258), (20170220032224), (20170224233516), (20170226050552), (20170228085250), (20170308214128), (20170308220713), (20170308222552), (20170313130611), (20170318032449), (20170318082740), (20170324194827), (20170424215355), (20170501225441), (20170505224222), (20170526095401), (20170602000208), (20170622205732), (20170626231059), (20170628092119), (20170628213609), (20170629183404), (20170630140136), (20170706132431), (20170707213648), (20170711122252), (20170717092127), (20170725060612), (20170727052644), (20170731130121), (20170814131722), (20170913114958), (20170921014405), (20170925214512), (20170925230419), (20170926134646), (20170927100300), (20170928234412), (20171003134956), (20171003225853), (20171006063358), (20171006161407), (20171012215106), (20171012221231), (20171016125229), (20171016125516); +INSERT INTO "schema_migrations" (version) VALUES (20160723215749), (20160804000000), (20160804001111), (20160805132301), (20160805203929), (20160808143454), (20160809214736), (20160810124357), (20160815125009), (20160815143002), (20160816020347), (20160816034021), (20160817220118), (20160818000944), (20160818132546), (20160820113856), (20160820164905), (20160822002438), (20160822004056), (20160822011624), (20160822020401), (20160822044612), (20160830081224), (20160830224802), (20160911233738), (20160912002705), (20160912145957), (20160918003206), (20160928232404), (20161003185918), (20161019090945), (20161019110737), (20161020144622), (20161021131026), (20161031001615), (20161121005339), (20161121014050), (20161121043941), (20161121045709), (20161122015942), (20161123081114), (20161123150943), (20161124085742), (20161125200620), (20161126045705), (20161127054559), (20161205024856), (20161207112519), (20161209192504), (20161212005641), (20161214005935), (20161215052051), (20161216051447), (20161218005913), (20161219160401), (20161219163909), (20161220141753), (20161221085759), (20161226213600), (20161231063614), (20170102130055), (20170102181053), (20170104113708), (20170104212623), (20170104235423), (20170106013143), (20170115035159), (20170115230549), (20170121014100), (20170131234029), (20170201014901), (20170201025454), (20170201035458), (20170201183258), (20170220032224), (20170224233516), (20170226050552), (20170228085250), (20170308214128), (20170308220713), (20170308222552), (20170313130611), (20170318032449), (20170318082740), (20170324194827), (20170424215355), (20170501225441), (20170505224222), (20170526095401), (20170602000208), (20170622205732), (20170626231059), (20170628092119), (20170628213609), (20170629183404), (20170630140136), (20170706132431), (20170707213648), (20170711122252), (20170717092127), (20170725060612), (20170727052644), (20170731130121), (20170814131722), (20170913114958), (20170921014405), (20170925214512), (20170925230419), (20170926134646), (20170927100300), (20170928234412), (20171003134956), (20171003225853), (20171006063358), (20171006161407), (20171012215106), (20171012221231), (20171016125229), (20171016125516), (20171016223356), (20171016235656); diff --git a/test/fixtures/github/events/pull_request_closed.json b/test/fixtures/github/events/pull_request_closed.json new file mode 100644 index 000000000..f9893f477 --- /dev/null +++ b/test/fixtures/github/events/pull_request_closed.json @@ -0,0 +1,415 @@ +{ + "action": "closed", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "closed", + "locked": false, + "title": "Update the README with new information", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": "2075-05-05T23:45:49Z", + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "repository": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_edited.json b/test/fixtures/github/events/pull_request_edited.json new file mode 100644 index 000000000..3ab42497e --- /dev/null +++ b/test/fixtures/github/events/pull_request_edited.json @@ -0,0 +1,415 @@ +{ + "action": "edited", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "body": "This is a pretty easy change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:43:41Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "repository": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_opened.json b/test/fixtures/github/events/pull_request_opened.json new file mode 100644 index 000000000..e29280d36 --- /dev/null +++ b/test/fixtures/github/events/pull_request_opened.json @@ -0,0 +1,415 @@ +{ + "action": "opened", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "repository": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_opened_by_bot.json b/test/fixtures/github/events/pull_request_opened_by_bot.json new file mode 100644 index 000000000..ce98d19a7 --- /dev/null +++ b/test/fixtures/github/events/pull_request_opened_by_bot.json @@ -0,0 +1,415 @@ +{ + "action": "opened", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "Bot", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "repository": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 234 + } +} diff --git a/test/fixtures/github/events/pull_request_reopened.json b/test/fixtures/github/events/pull_request_reopened.json new file mode 100644 index 000000000..486bb23f8 --- /dev/null +++ b/test/fixtures/github/events/pull_request_reopened.json @@ -0,0 +1,415 @@ +{ + "action": "reopened", + "number": 1, + "pull_request": { + "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1", + "id": 34778301, + "html_url": "https://github.com/baxterthehacker/public-repo/pull/1", + "diff_url": "https://github.com/baxterthehacker/public-repo/pull/1.diff", + "patch_url": "https://github.com/baxterthehacker/public-repo/pull/1.patch", + "issue_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1", + "number": 1, + "state": "open", + "locked": false, + "title": "Update the README with new information", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "body": "This is a pretty simple change that we need to pull into master.", + "created_at": "2075-05-05T23:40:27Z", + "updated_at": "2075-05-05T23:40:27Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": null, + "assignee": null, + "milestone": null, + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "head": { + "label": "baxterthehacker:changes", + "ref": "changes", + "sha": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "base": { + "label": "baxterthehacker:master", + "ref": "master", + "sha": "9049f1265b7d61be4a8904a9a27120d2064dab3b", + "user": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + }, + "html": { + "href": "https://github.com/baxterthehacker/public-repo/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1" + }, + "comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/issues/1/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c" + } + }, + "merged": false, + "mergeable": null, + "mergeable_state": "unknown", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "commits": 1, + "additions": 1, + "deletions": 1, + "changed_files": 1 + }, + "repository": { + "id": 35129377, + "name": "public-repo", + "full_name": "baxterthehacker/public-repo", + "owner": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/baxterthehacker/public-repo", + "description": "", + "fork": false, + "url": "https://api.github.com/repos/baxterthehacker/public-repo", + "forks_url": "https://api.github.com/repos/baxterthehacker/public-repo/forks", + "keys_url": "https://api.github.com/repos/baxterthehacker/public-repo/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/baxterthehacker/public-repo/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/baxterthehacker/public-repo/teams", + "hooks_url": "https://api.github.com/repos/baxterthehacker/public-repo/hooks", + "issue_events_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/events{/number}", + "events_url": "https://api.github.com/repos/baxterthehacker/public-repo/events", + "assignees_url": "https://api.github.com/repos/baxterthehacker/public-repo/assignees{/user}", + "branches_url": "https://api.github.com/repos/baxterthehacker/public-repo/branches{/branch}", + "tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/tags", + "blobs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/baxterthehacker/public-repo/statuses/{sha}", + "languages_url": "https://api.github.com/repos/baxterthehacker/public-repo/languages", + "stargazers_url": "https://api.github.com/repos/baxterthehacker/public-repo/stargazers", + "contributors_url": "https://api.github.com/repos/baxterthehacker/public-repo/contributors", + "subscribers_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscribers", + "subscription_url": "https://api.github.com/repos/baxterthehacker/public-repo/subscription", + "commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/baxterthehacker/public-repo/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/baxterthehacker/public-repo/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/baxterthehacker/public-repo/contents/{+path}", + "compare_url": "https://api.github.com/repos/baxterthehacker/public-repo/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/baxterthehacker/public-repo/merges", + "archive_url": "https://api.github.com/repos/baxterthehacker/public-repo/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/baxterthehacker/public-repo/downloads", + "issues_url": "https://api.github.com/repos/baxterthehacker/public-repo/issues{/number}", + "pulls_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls{/number}", + "milestones_url": "https://api.github.com/repos/baxterthehacker/public-repo/milestones{/number}", + "notifications_url": "https://api.github.com/repos/baxterthehacker/public-repo/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/baxterthehacker/public-repo/labels{/name}", + "releases_url": "https://api.github.com/repos/baxterthehacker/public-repo/releases{/id}", + "created_at": "2075-05-05T23:40:12Z", + "updated_at": "2075-05-05T23:40:12Z", + "pushed_at": "2075-05-05T23:40:26Z", + "git_url": "git://github.com/baxterthehacker/public-repo.git", + "ssh_url": "git@github.com:baxterthehacker/public-repo.git", + "clone_url": "https://github.com/baxterthehacker/public-repo.git", + "svn_url": "https://github.com/baxterthehacker/public-repo", + "homepage": null, + "size": 0, + "stargazers_count": 0, + "watchers_count": 0, + "language": null, + "has_issues": true, + "has_downloads": true, + "has_wiki": true, + "has_pages": true, + "forks_count": 0, + "mirror_url": null, + "open_issues_count": 1, + "forks": 0, + "open_issues": 1, + "watchers": 0, + "default_branch": "master" + }, + "sender": { + "login": "baxterthehacker", + "id": 6752317, + "avatar_url": "https://avatars.githubusercontent.com/u/6752317?v=3", + "gravatar_id": "", + "url": "https://api.github.com/users/baxterthehacker", + "html_url": "https://github.com/baxterthehacker", + "followers_url": "https://api.github.com/users/baxterthehacker/followers", + "following_url": "https://api.github.com/users/baxterthehacker/following{/other_user}", + "gists_url": "https://api.github.com/users/baxterthehacker/gists{/gist_id}", + "starred_url": "https://api.github.com/users/baxterthehacker/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/baxterthehacker/subscriptions", + "organizations_url": "https://api.github.com/users/baxterthehacker/orgs", + "repos_url": "https://api.github.com/users/baxterthehacker/repos", + "events_url": "https://api.github.com/users/baxterthehacker/events{/privacy}", + "received_events_url": "https://api.github.com/users/baxterthehacker/received_events", + "type": "User", + "site_admin": false + }, + "installation": { + "id": 234 + } +} diff --git a/test/lib/code_corps/github/adapters/github_app_installation_test.exs b/test/lib/code_corps/github/adapters/app_installation_test.exs similarity index 77% rename from test/lib/code_corps/github/adapters/github_app_installation_test.exs rename to test/lib/code_corps/github/adapters/app_installation_test.exs index 45d14e442..3ed9086bd 100644 --- a/test/lib/code_corps/github/adapters/github_app_installation_test.exs +++ b/test/lib/code_corps/github/adapters/app_installation_test.exs @@ -1,17 +1,17 @@ -defmodule CodeCorps.GitHub.Adapters.GithubAppInstallationTest do +defmodule CodeCorps.GitHub.Adapters.AppInstallationTest do @moduledoc false use ExUnit.Case, async: true import CodeCorps.GitHub.TestHelpers - alias CodeCorps.GitHub.Adapters.GithubAppInstallation + alias CodeCorps.GitHub.Adapters.AppInstallation describe "from_installation_event/1" do test "maps api payload correctly" do payload = load_event_fixture("installation_created") - assert GithubAppInstallation.from_installation_event(payload) == %{ + assert AppInstallation.from_installation_event(payload) == %{ github_id: payload["installation"]["id"], github_account_id: payload["installation"]["account"]["id"], github_account_login: payload["installation"]["account"]["login"], diff --git a/test/lib/code_corps/github/adapters/pull_request_test.exs b/test/lib/code_corps/github/adapters/pull_request_test.exs new file mode 100644 index 000000000..0d4eff135 --- /dev/null +++ b/test/lib/code_corps/github/adapters/pull_request_test.exs @@ -0,0 +1,47 @@ +defmodule CodeCorps.GitHub.Adapters.PullRequestTest do + @moduledoc false + + use ExUnit.Case, async: true + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.GitHub.Adapters.PullRequest + + describe "from_api/1" do + test "maps api payload correctly" do + %{"pull_request" => payload} = load_event_fixture("pull_request_opened") + + assert PullRequest.from_api(payload) == %{ + additions: payload["additions"], + body: payload["body"], + changed_files: payload["changed_files"], + closed_at: payload["closed_at"], + comments: payload["comments"], + comments_url: payload["comments_url"], + commits: payload["commits"], + commits_url: payload["commits_url"], + deletions: payload["deletions"], + diff_url: payload["diff_url"], + github_created_at: payload["created_at"], + github_id: payload["id"], + github_updated_at: payload["updated_at"], + html_url: payload["html_url"], + issue_url: payload["issue_url"], + locked: payload["locked"], + merge_commit_sha: payload["merge_commit_sha"], + mergeable_state: payload["mergeable_state"], + merged: payload["merged"], + merged_at: payload["merged_at"], + number: payload["number"], + patch_url: payload["patch_url"], + review_comment_url: payload["review_comment_url"], + review_comments: payload["review_comments"], + review_comments_url: payload["review_comments_url"], + state: payload["state"], + statuses_url: payload["statuses_url"], + title: payload["title"], + url: payload["url"] + } + end + end +end diff --git a/test/lib/code_corps/github/adapters/github_repo_test.exs b/test/lib/code_corps/github/adapters/repo_test.exs similarity index 80% rename from test/lib/code_corps/github/adapters/github_repo_test.exs rename to test/lib/code_corps/github/adapters/repo_test.exs index 0f4a8fd88..3db47450c 100644 --- a/test/lib/code_corps/github/adapters/github_repo_test.exs +++ b/test/lib/code_corps/github/adapters/repo_test.exs @@ -1,17 +1,17 @@ -defmodule CodeCorps.GitHub.Adapters.GithubRepoTest do +defmodule CodeCorps.GitHub.Adapters.RepoTest do @moduledoc false use ExUnit.Case, async: true import CodeCorps.GitHub.TestHelpers - alias CodeCorps.GitHub.Adapters.GithubRepo + alias CodeCorps.GitHub.Adapters.Repo describe "from_api/1" do test "maps api payload correctly" do %{"repositories" => [repo]} = load_event_fixture("user_repositories") - assert GithubRepo.from_api(repo) == %{ + assert Repo.from_api(repo) == %{ github_id: repo |> get_in(["id"]), name: repo |> get_in(["name"]), github_account_id: repo |> get_in(["owner", "id"]), diff --git a/test/lib/code_corps/github/event/installation/repos_test.exs b/test/lib/code_corps/github/event/installation/repos_test.exs index 1ad88d121..54ea17c04 100644 --- a/test/lib/code_corps/github/event/installation/repos_test.exs +++ b/test/lib/code_corps/github/event/installation/repos_test.exs @@ -12,7 +12,7 @@ defmodule CodeCorps.GitHub.Event.Installation.ReposTest do Repo } - alias CodeCorps.GitHub.Adapters.GithubRepo, as: GithubRepoAdapter + alias CodeCorps.GitHub.Adapters.Repo, as: RepoAdapter # from fixture @installation_repositories load_endpoint_fixture("installation_repositories") @@ -40,8 +40,8 @@ defmodule CodeCorps.GitHub.Event.Installation.ReposTest do installation = insert(:github_app_installation, github_id: @app_github_id, state: "initiated_on_code_corps") %{"repositories" => [matched_repo_payload, new_repo_payload]} = @installation_repositories - matched_repo_attrs = matched_repo_payload |> GithubRepoAdapter.from_api - new_repo_attrs = new_repo_payload |> GithubRepoAdapter.from_api + matched_repo_attrs = matched_repo_payload |> RepoAdapter.from_api + new_repo_attrs = new_repo_payload |> RepoAdapter.from_api unmatched_repo = insert(:github_repo, github_app_installation: installation) _matched_repo = insert(:github_repo, matched_repo_attrs |> Map.put(:github_app_installation, installation)) diff --git a/test/lib/code_corps/github/event/pull_request/body_parser_test.exs b/test/lib/code_corps/github/event/pull_request/body_parser_test.exs new file mode 100644 index 000000000..a265fbeed --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/body_parser_test.exs @@ -0,0 +1,32 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.BodyParserTest do + @moduledoc false + + use ExUnit.Case, async: true + + + alias CodeCorps.{ + GitHub.Event.PullRequest.BodyParser + } + + describe "extract_closing_ids/1" do + test "correctly extracts ids using supported closing keywords" do + content = + """ + close #2, closes #3 closed #4: fixed #5 fixes #6 fix #7. + resolve #8 resolves #9 #resolved #10 + """ + + assert content |> BodyParser.extract_closing_ids == 2..10 |> Enum.to_list + end + + test "only returns unique results" do + content = + """ + close #2, closes #2 closed #3: fixed #4 fixes #5 fix #6. + resolve #7 resolves #8 #resolved #8 + """ + + assert content |> BodyParser.extract_closing_ids == 2..8 |> Enum.to_list + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/changeset_builder_test.exs b/test/lib/code_corps/github/event/pull_request/changeset_builder_test.exs new file mode 100644 index 000000000..387ce740c --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/changeset_builder_test.exs @@ -0,0 +1,82 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.ChangesetBuilderTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + import Ecto.Changeset + + alias CodeCorps.{ + GitHub.Event.PullRequest.ChangesetBuilder, + Task + } + + describe "build_changeset/3" do + test "assigns proper changes to the task" do + payload = load_event_fixture("pull_request_opened") + task = %Task{} + github_pull_request = insert(:github_pull_request) + project = insert(:project) + project_github_repo = insert(:project_github_repo, project: project) + user = insert(:user) + task_list = insert(:task_list, project: project, inbox: true) + + changeset = ChangesetBuilder.build_changeset( + task, payload, github_pull_request, project_github_repo, user + ) + + {:ok, created_at, _} = payload["pull_request"]["created_at"] |> DateTime.from_iso8601() + {:ok, updated_at, _} = payload["pull_request"]["updated_at"] |> DateTime.from_iso8601() + + # adapted fields + assert get_change(changeset, :created_at) == created_at + assert get_change(changeset, :markdown) == payload["pull_request"]["body"] + assert get_change(changeset, :modified_at) == updated_at + assert get_change(changeset, :name) == payload["pull_request"]["name"] + assert get_field(changeset, :status) == payload["pull_request"]["state"] + + # manual fields + assert get_change(changeset, :created_from) == "github" + assert get_change(changeset, :modified_from) == "github" + + # markdown was rendered into html + assert get_change(changeset, :body) == + payload["pull_request"]["body"] + |> Earmark.as_html!(%Earmark.Options{code_class_prefix: "language-"}) + + # relationships are proper + assert get_change(changeset, :github_pull_request_id) == github_pull_request.id + assert get_change(changeset, :github_repo_id) == project_github_repo.github_repo_id + assert get_change(changeset, :project_id) == project_github_repo.project_id + assert get_change(changeset, :task_list_id) == task_list.id + assert get_change(changeset, :user_id) == user.id + + assert changeset.valid? + end + + test "validates that modified_at has not already happened" do + payload = load_event_fixture("pull_request_opened") + %{"pull_request" => %{"updated_at" => updated_at}} = payload + + # Set the modified_at in the future + modified_at = + updated_at + |> Timex.parse!("{ISO:Extended:Z}") + |> Timex.shift(days: 1) + + project = insert(:project) + github_pull_request = insert(:github_pull_request) + github_repo = insert(:github_repo) + project_github_repo = insert(:project_github_repo, github_repo: github_repo, project: project) + user = insert(:user) + task = insert(:task, project: project, github_pull_request: github_pull_request, github_repo: github_repo, user: user, modified_at: modified_at) + + changeset = ChangesetBuilder.build_changeset( + task, payload, github_pull_request, project_github_repo, user + ) + + refute changeset.valid? + assert changeset.errors[:modified_at] == {"cannot be before the last recorded time", []} + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/pull_request_linker_test.exs b/test/lib/code_corps/github/event/pull_request/pull_request_linker_test.exs new file mode 100644 index 000000000..024a5fc89 --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/pull_request_linker_test.exs @@ -0,0 +1,58 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.PullRequestLinkerTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GithubPullRequest, + GitHub.Event.PullRequest.PullRequestLinker, + Repo + } + + alias CodeCorps.GitHub.Adapters.PullRequest, as: PullRequestAdapter + + @payload load_event_fixture("pull_request_opened") + + describe "create_or_update_pull_request/1" do + test "creates pull request if none exists" do + %{"pull_request" => attrs} = @payload + github_repo = insert(:github_repo) + {:ok, %GithubPullRequest{} = created_pull_request} = PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + + assert Repo.one(GithubPullRequest) + + created_attributes = + attrs + |> PullRequestAdapter.from_api + |> Map.delete(:closed_at) + |> Map.delete(:merge_commit_sha) + |> Map.delete(:merged_at) + returned_pull_request = Repo.get_by(GithubPullRequest, created_attributes) + assert returned_pull_request.id == created_pull_request.id + assert returned_pull_request.github_repo_id == github_repo.id + end + + test "updates pull request if it already exists" do + %{"pull_request" => %{"id" => pull_request_id} = attrs} = @payload + + github_repo = insert(:github_repo) + pull_request = insert(:github_pull_request, github_id: pull_request_id, github_repo: github_repo) + + {:ok, %GithubPullRequest{} = updated_pull_request} = PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + + assert updated_pull_request.id == pull_request.id + assert updated_pull_request.github_repo_id == github_repo.id + end + + test "returns changeset if payload is somehow not as expected" do + bad_payload = @payload |> put_in(["pull_request", "number"], nil) + %{"pull_request" => attrs} = bad_payload + github_repo = insert(:github_repo) + + {:error, changeset} = PullRequestLinker.create_or_update_pull_request(github_repo, attrs) + refute changeset.valid? + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/task_syncer_test.exs b/test/lib/code_corps/github/event/pull_request/task_syncer_test.exs new file mode 100644 index 000000000..596ff7278 --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/task_syncer_test.exs @@ -0,0 +1,69 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.TaskSyncerTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GitHub.Event.PullRequest.TaskSyncer, + Project, + Repo, + Task + } + + describe "sync all/3" do + @payload load_event_fixture("pull_request_opened") + + test "creates missing, updates existing tasks for each project associated with the github repo" do + user = insert(:user) + %{github_repo: github_repo} = github_pull_request = insert(:github_pull_request) + + %{"pull_request" => %{"body" => pull_request_body}} = @payload + + [%{project: project_1}, _, _] = project_github_repos = + insert_list(3, :project_github_repo, github_repo: github_repo) + + task_1 = insert(:task, project: project_1, github_pull_request: github_pull_request, github_repo: github_repo, user: user) + + project_ids = project_github_repos |> Enum.map(&Map.get(&1, :project_id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = github_pull_request |> TaskSyncer.sync_all(user, @payload) + + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + assert task.user_id == user.id + assert task.markdown == pull_request_body + assert task.github_pull_request_id == github_pull_request.id + end) + + task_ids = tasks |> Enum.map(&Map.get(&1, :id)) + assert task_1.id in task_ids + end + + test "fails on validation errors" do + %{github_repo: github_repo} = github_pull_request = insert(:github_pull_request) + + bad_payload = @payload |> put_in(~w(pull_request title), nil) + + %{project: project} = + insert(:project_github_repo, github_repo: github_repo) + + %{user: user} = insert(:task, project: project, github_pull_request: github_pull_request, github_repo: github_repo) + + insert(:task_list, project: project, inbox: true) + + {:error, {tasks, errors}} = + github_pull_request |> TaskSyncer.sync_all(user, bad_payload) + + assert tasks |> Enum.count == 0 + assert errors |> Enum.count == 1 + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/user_linker_test.exs b/test/lib/code_corps/github/event/pull_request/user_linker_test.exs new file mode 100644 index 000000000..c5eaa8af2 --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/user_linker_test.exs @@ -0,0 +1,119 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.UserLinkerTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GitHub.Event.PullRequest.UserLinker, + Repo, + User + } + + alias CodeCorps.GitHub.Adapters.User, as: UserAdapter + + @payload load_event_fixture("pull_request_opened") + @bot_payload load_event_fixture("pull_request_opened_by_bot") + @user_payload @payload["pull_request"]["user"] + @bot_user_payload @bot_payload["pull_request"]["user"] + + describe "find_or_create_user/1" do + test "finds user by task association" do + %{ + "pull_request" => %{"number" => number}, + "repository" => %{"id" => github_repo_id} + } = @payload + + user = insert(:user) + github_repo = insert(:github_repo, github_id: github_repo_id) + github_pull_request = insert(:github_pull_request, number: number, github_repo: github_repo) + # multiple tasks, all with same user is ok + insert_pair( + :task, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @payload) + + assert user.id == returned_user.id + end + + test "returns error if multiple users by task association found" do + %{ + "pull_request" => %{"number" => number}, + "repository" => %{"id" => github_repo_id} + } = @payload + + github_repo = insert(:github_repo, github_id: github_repo_id) + github_pull_request = insert(:github_pull_request, number: number, github_repo: github_repo) + # multiple tasks, each with different user is not ok + insert_pair(:task, github_repo: github_repo, github_pull_request: github_pull_request) + + assert {:error, :multiple_users} == + UserLinker.find_or_create_user(github_pull_request, @payload) + end + + test "returns user by github id if no user by task association found" do + %{"pull_request" => %{"number" => number}} = @payload + attributes = UserAdapter.from_github_user(@user_payload) + preinserted_user = insert(:user, attributes) + github_pull_request = insert(:github_pull_request, number: number) + + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @payload) + + assert preinserted_user.id == returned_user.id + assert Repo.get_by(User, attributes) + end + + test "creates user if none is found by any other method" do + %{"pull_request" => %{"number" => number}} = @payload + github_pull_request = insert(:github_pull_request, number: number) + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @payload) + + created_attributes = UserAdapter.from_github_user(@user_payload) + created_user = Repo.get_by(User, created_attributes) + assert created_user.id == returned_user.id + end + + test "if pull request opened by bot, finds user by task association" do + %{ + "pull_request" => %{ + "number" => number, "user" => %{"id" => bot_user_github_id}}, + "repository" => %{"id" => github_repo_id} + } = @bot_payload + + preinserted_user = insert(:user) + github_pull_request = insert(:github_pull_request, number: number) + repo = insert(:github_repo, github_id: github_repo_id) + insert( + :task, + user: preinserted_user, github_repo: repo, + github_pull_request: github_pull_request) + + {:ok, %User{} = returned_user} = + UserLinker.find_or_create_user(github_pull_request, @bot_payload) + + assert preinserted_user.id == returned_user.id + + refute Repo.get_by(User, github_id: bot_user_github_id) + end + + test "if pull request opened by bot, and no user by task association, creates a bot user" do + %{"pull_request" => %{"number" => number}} = @bot_payload + github_pull_request = insert(:github_pull_request, number: number) + {:ok, %User{} = returned_user} = UserLinker.find_or_create_user(github_pull_request, @bot_payload) + + created_attributes = UserAdapter.from_github_user(@bot_user_payload) + created_user = Repo.get_by(User, created_attributes) + assert created_user.id == returned_user.id + end + + test "returns changeset if payload is somehow not as expected" do + %{"pull_request" => %{"number" => number}} = @payload + github_pull_request = insert(:github_pull_request, number: number) + bad_payload = @payload |> put_in(["pull_request", "user", "type"], "Organization") + + {:error, changeset} = UserLinker.find_or_create_user(github_pull_request, bad_payload) + refute changeset.valid? + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request/validator_test.exs b/test/lib/code_corps/github/event/pull_request/validator_test.exs new file mode 100644 index 000000000..32338c26a --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request/validator_test.exs @@ -0,0 +1,24 @@ +defmodule CodeCorps.GitHub.Event.PullRequest.ValidatorTest do + @moduledoc false + + use ExUnit.Case, async: true + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.GitHub.Event.PullRequest.Validator + + describe "valid?/1" do + test "returns true for any PullRequest event fixture" do + assert "pull_request_opened" |> load_event_fixture() |> Validator.valid? + assert "pull_request_closed" |> load_event_fixture() |> Validator.valid? + assert "pull_request_edited" |> load_event_fixture() |> Validator.valid? + assert "pull_request_reopened" |> load_event_fixture() |> Validator.valid? + end + + test "returns false for an unsupported structure" do + refute Validator.valid?("foo") + refute Validator.valid?(%{"foo" => "bar"}) + refute Validator.valid?(%{"issue" => %{"bar" => "baz"}}) + end + end +end diff --git a/test/lib/code_corps/github/event/pull_request_test.exs b/test/lib/code_corps/github/event/pull_request_test.exs new file mode 100644 index 000000000..54ced321c --- /dev/null +++ b/test/lib/code_corps/github/event/pull_request_test.exs @@ -0,0 +1,513 @@ +defmodule CodeCorps.GitHub.Event.PullRequestTest do + @moduledoc false + + use CodeCorps.DbAccessCase + + import CodeCorps.GitHub.TestHelpers + + alias CodeCorps.{ + GitHub.Event.PullRequest, + Project, + Repo, + Task, + User + } + + describe "handle/2" do + @payload load_event_fixture("pull_request_opened") |> Map.put("action", "foo") + + test "returns error if action of the event is wrong" do + assert {:error, :unexpected_action} == PullRequest.handle(@payload) + end + end + + describe "handle/2 for PullRequest::opened" do + @payload load_event_fixture("pull_request_opened") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + describe "handle/2 for PullRequest::closed" do + @payload load_event_fixture("pull_request_closed") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "closed" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "closed" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + describe "handle/2 for PullRequest::edited" do + @payload load_event_fixture("pull_request_edited") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + describe "handle/2 for PullRequest::reopened" do + @payload load_event_fixture("pull_request_reopened") + + test "with unmatched user, creates user, creates task for each project associated to github repo" do + %{ + "pull_request" => %{ + "body" => markdown, "title" => title, "number" => number, + "user" => %{"id" => user_github_id} + }, + "repository" => %{"id" => repo_github_id} + } = @payload + + github_repo = insert(:github_repo, github_id: repo_github_id) + + project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + user = Repo.get_by(User, github_id: user_github_id) + assert user + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.user_id == user.id + assert task.github_pull_request_id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + end + + test "with unmatched user, returns error if unmatched repository" do + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + refute Repo.one(User) + end + + test "with matched user, creates or updates task for each project associated to github repo" do + %{ + "pull_request" => %{"id" => pull_request_github_id, "body" => markdown, "title" => title, "number" => number, "user" => %{"id" => user_github_id}}, + "repository" => %{"id" => repo_github_id} + } = @payload + + user = insert(:user, github_id: user_github_id) + + github_repo = insert(:github_repo, github_id: repo_github_id) + github_pull_request = insert(:github_pull_request, github_id: pull_request_github_id, github_repo: github_repo) + + [%{project: project} | _rest] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) + + project_ids = + project_github_repos + |> Enum.map(&Map.get(&1, :project)) + |> Enum.map(&Map.get(&1, :id)) + + project_ids |> Enum.each(fn project_id -> + project = Project |> Repo.get_by(id: project_id) + insert(:task_list, project: project, inbox: true) + end) + + %{id: existing_task_id} = + insert(:task, project: project, user: user, github_repo: github_repo, github_pull_request: github_pull_request) + + {:ok, tasks} = PullRequest.handle(@payload) + + assert Enum.count(tasks) == 3 + assert Repo.aggregate(Task, :count, :id) == 3 + + tasks |> Enum.each(fn task -> + task = task |> Repo.preload(:github_pull_request) + assert task.github_pull_request_id == github_pull_request.id + assert task.github_repo_id == github_repo.id + assert task.project_id in project_ids + assert task.markdown == markdown + assert task.title == title + assert task.github_pull_request.number == number + assert task.status == "open" + end) + + assert existing_task_id in (tasks |> Enum.map(&Map.get(&1, :id))) + end + + test "with matched user, returns error if unmatched repository" do + %{"pull_request" => %{"user" => %{"id" => user_github_id}}} = @payload + insert(:user, github_id: user_github_id) + + assert PullRequest.handle(@payload) == {:error, :repository_not_found} + end + + test "returns error if payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(%{}) + end + + test "returns error if repo payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("repository", "foo")) + end + + test "returns error if pull request payload is wrong" do + assert {:error, :unexpected_payload} == PullRequest.handle(@payload |> Map.put("pull_request", "foo")) + end + end + + @unimplemented_actions ~w( + assigned unassigned review_requested review_request_removed labeled + unlabeled + ) + + @unimplemented_actions |> Enum.each(fn action -> + describe "handle/2 for PullRequest::#{action}" do + @payload %{ + "action" => action, + "pull_request" => %{ + "id" => 1, "title" => "foo", "body" => "bar", "state" => "baz", + "user" => %{"id" => "bat"} + }, + "repository" => %{"id" => 2} + } + + test "is not implemented" do + assert PullRequest.handle(@payload) == {:error, :not_fully_implemented} + end + end + end) +end diff --git a/test/lib/code_corps/model/github_pull_request_test.exs b/test/lib/code_corps/model/github_pull_request_test.exs new file mode 100644 index 000000000..f2aa56402 --- /dev/null +++ b/test/lib/code_corps/model/github_pull_request_test.exs @@ -0,0 +1,31 @@ +defmodule CodeCorps.GithubPullRequestTest do + use CodeCorps.ModelCase + + alias CodeCorps.GithubPullRequest + + @valid_attrs %{ + github_created_at: "2075-05-05T23:40:27Z", + github_id: 34778301, + github_updated_at: "2075-05-05T23:40:27Z", + html_url: "https://github.com/baxterthehacker/public-repo/pull/1", + locked: false, + merged: false, + number: 1, + state: "open", + title: "Update the README with new information", + url: "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1" + } + @invalid_attrs %{} + + describe "create_changeset/2" do + test "with valid attributes" do + changeset = GithubPullRequest.create_changeset(%GithubPullRequest{}, @valid_attrs) + assert changeset.valid? + end + + test "with invalid attributes" do + changeset = GithubPullRequest.create_changeset(%GithubPullRequest{}, @invalid_attrs) + refute changeset.valid? + end + end +end diff --git a/test/lib/code_corps_web/controllers/github_pull_request_controller_test.exs b/test/lib/code_corps_web/controllers/github_pull_request_controller_test.exs new file mode 100644 index 000000000..afc71cc36 --- /dev/null +++ b/test/lib/code_corps_web/controllers/github_pull_request_controller_test.exs @@ -0,0 +1,39 @@ +defmodule CodeCorpsWeb.GithubPullRequestControllerTest do + use CodeCorpsWeb.ApiCase, resource_name: :github_pull_request + + describe "index" do + test "lists all resources", %{conn: conn} do + [record_1, record_2] = insert_pair(:github_pull_request) + + conn + |> request_index + |> json_response(200) + |> assert_ids_from_response([record_1.id, record_2.id]) + end + + test "filters resources by record id", %{conn: conn} do + [record_1, record_2 | _] = insert_list(3, :github_pull_request) + + path = "github-pull-requests/?filter[id]=#{record_1.id},#{record_2.id}" + + conn + |> get(path) + |> json_response(200) + |> assert_ids_from_response([record_1.id, record_2.id]) + end + end + + describe "show" do + test "shows chosen resource", %{conn: conn} do + record = insert(:github_pull_request) + conn + |> request_show(record) + |> json_response(200) + |> assert_id_from_response(record.id) + end + + test "renders 404 when id is nonexistent", %{conn: conn} do + assert conn |> request_show(:not_found) |> json_response(404) + end + end +end diff --git a/test/lib/code_corps_web/views/github_pull_request_view_test.exs b/test/lib/code_corps_web/views/github_pull_request_view_test.exs new file mode 100644 index 000000000..e89fb5167 --- /dev/null +++ b/test/lib/code_corps_web/views/github_pull_request_view_test.exs @@ -0,0 +1,35 @@ +defmodule CodeCorpsWeb.GithubPullRequestViewTest do + use CodeCorpsWeb.ViewCase + + test "renders all attributes and relationships properly" do + github_repo = insert(:github_repo) + github_pull_request = insert(:github_pull_request, github_repo: github_repo) + + rendered_json = render(CodeCorpsWeb.GithubPullRequestView, "show.json-api", data: github_pull_request) + + expected_json = %{ + "data" => %{ + "id" => github_pull_request.id |> Integer.to_string, + "type" => "github-pull-request", + "attributes" => %{ + "github-created-at" => github_pull_request.github_created_at, + "github-updated-at" => github_pull_request.github_updated_at, + "html-url" => github_pull_request.html_url, + "merged" => github_pull_request.merged, + "number" => github_pull_request.number, + "state" => github_pull_request.state + }, + "relationships" => %{ + "github-repo" => %{ + "data" => %{"id" => github_repo.id |> Integer.to_string, "type" => "github-repo"} + } + } + }, + "jsonapi" => %{ + "version" => "1.0" + } + } + + assert rendered_json == expected_json + end +end diff --git a/test/lib/code_corps_web/views/task_view_test.exs b/test/lib/code_corps_web/views/task_view_test.exs index eff733cd4..6686e2b77 100644 --- a/test/lib/code_corps_web/views/task_view_test.exs +++ b/test/lib/code_corps_web/views/task_view_test.exs @@ -3,8 +3,9 @@ defmodule CodeCorpsWeb.TaskViewTest do test "renders all attributes and relationships properly" do github_issue = insert(:github_issue) + github_pull_request = insert(:github_pull_request) github_repo = insert(:github_repo) - task = insert(:task, order: 1000, github_issue: github_issue, github_repo: github_repo) + task = insert(:task, order: 1000, github_issue: github_issue, github_pull_request: github_pull_request, github_repo: github_repo) comment = insert(:comment, task: task) task_skill = insert(:task_skill, task: task) user_task = insert(:user_task, task: task) @@ -50,6 +51,12 @@ defmodule CodeCorpsWeb.TaskViewTest do "type" => "github-issue" } }, + "github-pull-request" => %{ + "data" => %{ + "id" => task.github_pull_request_id |> Integer.to_string, + "type" => "github-pull-request" + } + }, "github-repo" => %{ "data" => %{ "id" => task.github_repo_id |> Integer.to_string, diff --git a/test/support/factories.ex b/test/support/factories.ex index 66bcbc7d6..919696f8a 100644 --- a/test/support/factories.ex +++ b/test/support/factories.ex @@ -67,6 +67,21 @@ defmodule CodeCorps.Factories do } end + def github_pull_request_factory do + %CodeCorps.GithubPullRequest{ + body: "Here's a change!", + github_created_at: DateTime.utc_now, + github_id: sequence(:id, (fn number -> number end)), + github_updated_at: DateTime.utc_now, + locked: false, + merged: false, + number: sequence(:id, (fn number -> number end)), + state: "open", + title: "Here's a change!", + github_repo: build(:github_repo) + } + end + def github_repo_factory do %CodeCorps.GithubRepo{ github_app_installation: build(:github_app_installation)