From 339d4e2b871b2ff1e642d8e5e5a276477a2df032 Mon Sep 17 00:00:00 2001 From: Nikola Begedin Date: Mon, 6 Nov 2017 15:28:21 +0100 Subject: [PATCH] Assign task lists and archived during sync - Associate task with proper task list when syncing from github - Add archiving of task during github sync - Ditch usage of issue attrs from Sync.Issue.Task.Changeset.build_changeset - Remove unused adapter clause - Unset task list if archived - Add migration to archive outdated tasks - Add testing of column sync to acceptance test --- lib/code_corps/github/adapters/issue.ex | 33 +++-- lib/code_corps/github/sync/issue/issue.ex | 2 +- .../github/sync/issue/task/changeset.ex | 91 ++++++++----- lib/code_corps/github/sync/issue/task/task.ex | 29 ++-- .../20171106200036_archive_outdated_tasks.exs | 20 +++ priv/repo/structure.sql | 2 +- .../code_corps/github/adapters/issue_test.exs | 17 +-- .../github/event/issues/issues_test.exs | 2 + .../event/pull_request/pull_request_test.exs | 2 +- .../github/sync/issue/issue_test.exs | 2 +- .../github/sync/issue/task/changeset_test.exs | 128 ++++++++++++++---- .../github/sync/issue/task/task_test.exs | 19 +-- test/lib/code_corps/github/sync/sync_test.exs | 16 +++ test/support/github/test_helpers.ex | 4 + 14 files changed, 250 insertions(+), 117 deletions(-) create mode 100644 priv/repo/migrations/20171106200036_archive_outdated_tasks.exs diff --git a/lib/code_corps/github/adapters/issue.ex b/lib/code_corps/github/adapters/issue.ex index 1bbafab91..1ed9f225c 100644 --- a/lib/code_corps/github/adapters/issue.ex +++ b/lib/code_corps/github/adapters/issue.ex @@ -11,7 +11,7 @@ defmodule CodeCorps.GitHub.Adapters.Issue do Task } - @issue_mapping [ + @github_payload_to_github_issue_mapping [ {:body, ["body"]}, {:closed_at, ["closed_at"]}, {:comments_url, ["comments_url"]}, @@ -34,10 +34,10 @@ defmodule CodeCorps.GitHub.Adapters.Issue do """ @spec to_issue(map) :: map def to_issue(%{} = payload) do - payload |> MapTransformer.transform(@issue_mapping) + payload |> MapTransformer.transform(@github_payload_to_github_issue_mapping) end - @task_mapping [ + @github_payload_to_task_mapping [ {:created_at, ["created_at"]}, {:markdown, ["body"]}, {:modified_at, ["updated_at"]}, @@ -45,24 +45,23 @@ defmodule CodeCorps.GitHub.Adapters.Issue do {:title, ["title"]} ] + @github_issue_to_task_mapping [ + {:created_at, [:github_created_at]}, + {:markdown, [:body]}, + {:modified_at, [:github_updated_at]}, + {:status, [:state]}, + {:title, [:title]} + ] + @doc ~S""" Converts a GitHub Issue payload into a set of attributes used to create or update a `Task` record. """ - @spec to_task(map) :: map - def to_task(%{} = payload) do - payload |> MapTransformer.transform(@task_mapping) - end - - @doc ~S""" - Converts a `GithubIssue` record into attributes with the same keys as the - GitHub API's Issue - """ - @spec to_issue_attrs(GithubIssue.t) :: map - def to_issue_attrs(%GithubIssue{} = github_issue) do + @spec to_task(GithubIssue.t) :: map + def to_task(%GithubIssue{} = github_issue) do github_issue |> Map.from_struct - |> MapTransformer.transform_inverse(@issue_mapping) + |> MapTransformer.transform(@github_issue_to_task_mapping) end @autogenerated_github_keys ~w(closed_at comments_url created_at events_url html_url id labels_url number updated_at url) @@ -75,14 +74,14 @@ defmodule CodeCorps.GitHub.Adapters.Issue do def to_api(%GithubIssue{} = github_issue) do github_issue |> Map.from_struct - |> MapTransformer.transform_inverse(@issue_mapping) + |> MapTransformer.transform_inverse(@github_payload_to_github_issue_mapping) |> Map.drop(@autogenerated_github_keys) |> BodyDecorator.add_code_corps_header(github_issue) end def to_api(%Task{} = task) do task |> Map.from_struct - |> MapTransformer.transform_inverse(@task_mapping) + |> MapTransformer.transform_inverse(@github_payload_to_task_mapping) |> Map.drop(@autogenerated_github_keys) |> BodyDecorator.add_code_corps_header(task) end diff --git a/lib/code_corps/github/sync/issue/issue.ex b/lib/code_corps/github/sync/issue/issue.ex index 10e691562..0966bec42 100644 --- a/lib/code_corps/github/sync/issue/issue.ex +++ b/lib/code_corps/github/sync/issue/issue.ex @@ -40,7 +40,7 @@ defmodule CodeCorps.GitHub.Sync.Issue do Multi.new |> Multi.run(:github_issue, fn _ -> link_issue({github_repo, github_pull_request}, payload) end) |> Multi.run(:issue_user, fn %{github_issue: github_issue} -> UserRecordLinker.link_to(github_issue, payload) end) - |> Multi.run(:tasks, fn %{github_issue: github_issue, issue_user: user} -> github_issue |> IssueTaskSyncer.sync_all(user, payload) end) + |> Multi.run(:tasks, fn %{github_issue: github_issue, issue_user: user} -> github_issue |> IssueTaskSyncer.sync_all(user) end) end @spec link_issue({GithubRepo.t, GithubPullRequest.t}, map) :: {:ok, GithubIssue.t} | {:error, Ecto.Changeset.t} diff --git a/lib/code_corps/github/sync/issue/task/changeset.ex b/lib/code_corps/github/sync/issue/task/changeset.ex index 2b8eb32ee..1926edac0 100644 --- a/lib/code_corps/github/sync/issue/task/changeset.ex +++ b/lib/code_corps/github/sync/issue/task/changeset.ex @@ -23,20 +23,6 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task.Changeset do The changeset can be used to create or update a `Task` """ - @spec build_changeset(Task.t, map, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t - def build_changeset( - %Task{id: task_id} = task, - %{} = issue_attrs, - %GithubIssue{} = github_issue, - %ProjectGithubRepo{} = project_github_repo, - %User{} = user) do - - case is_nil(task_id) do - true -> create_changeset(task, issue_attrs, github_issue, project_github_repo, user) - false -> update_changeset(task, issue_attrs) - end - end - @spec build_changeset(Task.t, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t def build_changeset( %Task{id: task_id} = task, @@ -44,56 +30,99 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task.Changeset do %ProjectGithubRepo{} = project_github_repo, %User{} = user) do - issue_attrs = github_issue |> IssueAdapter.to_issue_attrs() case is_nil(task_id) do - true -> create_changeset(task, issue_attrs, github_issue, project_github_repo, user) - false -> update_changeset(task, issue_attrs) + true -> create_changeset(task, github_issue, project_github_repo, user) + false -> update_changeset(task, github_issue, project_github_repo) end end @create_attrs ~w(created_at markdown modified_at status title)a - @spec create_changeset(Task.t, map, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t + @spec create_changeset(Task.t, GithubIssue.t, ProjectGithubRepo.t, User.t) :: Changeset.t defp create_changeset( %Task{} = task, - %{} = issue_attrs, - %GithubIssue{id: github_issue_id}, + %GithubIssue{id: github_issue_id} = github_issue, %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(IssueAdapter.to_task(issue_attrs), @create_attrs) + |> Changeset.cast(github_issue |> IssueAdapter.to_task, @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_issue_id, github_issue_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) + |> assign_task_list(github_issue, project_id) |> Changeset.put_change(:user_id, user_id) |> Changeset.validate_required([:project_id, :task_list_id, :title, :user_id]) |> Changeset.assoc_constraint(:github_issue) |> Changeset.assoc_constraint(:github_repo) |> Changeset.assoc_constraint(:project) - |> Changeset.assoc_constraint(:task_list) |> Changeset.assoc_constraint(:user) - |> Task.order_task() + |> maybe_archive() + |> Task.handle_archived() end @update_attrs ~w(markdown modified_at status title)a - @spec update_changeset(Task.t, map) :: Changeset.t - defp update_changeset(%Task{} = task, %{} = issue_attrs) do + @spec update_changeset(Task.t, GithubIssue.t, ProjectGithubRepo.t) :: Changeset.t + defp update_changeset( + %Task{} = task, + %GithubIssue{} = github_issue, + %ProjectGithubRepo{project_id: project_id}) do task - |> Changeset.cast(IssueAdapter.to_task(issue_attrs), @update_attrs) + |> Changeset.cast(github_issue |> IssueAdapter.to_task, @update_attrs) |> MarkdownRendererService.render_markdown_to_html(:markdown, :body) |> Changeset.put_change(:modified_from, "github") |> TimeValidator.validate_time_not_before(:modified_at) + |> assign_task_list(github_issue, project_id) |> Changeset.validate_required([:project_id, :title, :user_id]) |> Changeset.assoc_constraint(:github_repo) |> Changeset.assoc_constraint(:project) |> Changeset.assoc_constraint(:user) - |> Task.order_task() + |> maybe_archive() + |> Task.handle_archived() + end + + @spec assign_task_list(Changeset.t, GithubIssue.t, integer) :: Changeset.t + defp assign_task_list( + %Changeset{} = changeset, %GithubIssue{} = github_issue, project_id) + do + %TaskList{id: task_list_id} = + github_issue + |> get_task_list_type() + |> get_task_list(project_id) + + changeset + |> Changeset.put_change(:task_list_id, task_list_id) + |> Changeset.assoc_constraint(:task_list) + end + + @spec get_task_list_type(GithubIssue.t) :: atom + defp get_task_list_type(%GithubIssue{state: "closed"}), do: :done + defp get_task_list_type(%GithubIssue{state: "open", github_pull_request_id: pr_id}) + when not is_nil(pr_id), do: :pull_requests + defp get_task_list_type(%GithubIssue{state: "open"}), do: :inbox + + @spec get_task_list(atom, integer) :: TaskList.t + defp get_task_list(:done, project_id) do + TaskList |> Repo.get_by(project_id: project_id, done: true) + end + defp get_task_list(:inbox, project_id) do + TaskList |> Repo.get_by(project_id: project_id, inbox: true) + end + defp get_task_list(:pull_requests, project_id) do + TaskList |> Repo.get_by(project_id: project_id, pull_requests: true) + end + + @spec maybe_archive(Changeset.t) :: Changeset.t + defp maybe_archive(%Changeset{} = changeset) do + modified_at = changeset |> Changeset.get_field(:modified_at) + status = changeset |> Changeset.get_field(:status) + + case {status, Timex.now |> Timex.diff(modified_at, :days)} do + {"closed", days_since_modified} when days_since_modified > 30 -> + changeset |> Changeset.put_change(:archived, true) + _ -> changeset + end end end diff --git a/lib/code_corps/github/sync/issue/task/task.ex b/lib/code_corps/github/sync/issue/task/task.ex index ba96ac175..ef410ddf8 100644 --- a/lib/code_corps/github/sync/issue/task/task.ex +++ b/lib/code_corps/github/sync/issue/task/task.ex @@ -16,20 +16,18 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task do {:error, {list(Task.t), list(Changeset.t)}} @doc """ - When provided a `CodeCorps.GithubIssue`, 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`. + When provided a `GithubIssue` and a `User`, for each `Project` associated to + that `GithubRepo` via a `ProjectGithubRepo`, it creates or updates a `Task`. """ - @spec sync_all(GithubIssue.t, User.t, map) :: {:ok, list(Task.t)} - def sync_all(%GithubIssue{} = github_issue, %User{} = user, %{} = payload) do + @spec sync_all(GithubIssue.t, User.t) :: {:ok, list(Task.t)} + def sync_all(%GithubIssue{} = github_issue, %User{} = user) do %GithubIssue{ github_repo: %GithubRepo{project_github_repos: project_github_repos} } = github_issue |> Repo.preload(github_repo: :project_github_repos) project_github_repos - |> Enum.map(&sync(github_issue, &1, user, payload)) + |> Enum.map(&sync(github_issue, &1, user)) |> ResultAggregator.aggregate end @@ -45,9 +43,7 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task do """ def sync_project_github_repo(%ProjectGithubRepo{github_repo: %GithubRepo{} = _} = project_github_repo) do %ProjectGithubRepo{ - github_repo: %GithubRepo{ - github_issues: github_issues - } + github_repo: %GithubRepo{github_issues: github_issues} } = project_github_repo |> Repo.preload([:project, github_repo: [github_issues: [github_user: [:user]]]]) github_issues @@ -55,7 +51,10 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task do |> ResultAggregator.aggregate end - defp find_or_create_task(%GithubIssue{github_user: %GithubUser{user: %User{} = user}} = github_issue, %ProjectGithubRepo{} = project_github_repo) do + defp find_or_create_task( + %GithubIssue{github_user: %GithubUser{user: %User{} = user}} = github_issue, + %ProjectGithubRepo{} = project_github_repo) do + sync(github_issue, project_github_repo, user) end @@ -67,14 +66,6 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task do |> Repo.insert_or_update() end - @spec sync(GithubIssue.t, ProjectGithubRepo.t, User.t, map) :: {:ok, ProjectGithubRepo.t} | {:error, Changeset.t} - defp sync(%GithubIssue{} = github_issue, %ProjectGithubRepo{} = project_github_repo, %User{} = user, %{} = payload) do - project_github_repo - |> find_or_init_task(github_issue) - |> TaskChangeset.build_changeset(payload, github_issue, project_github_repo, user) - |> Repo.insert_or_update() - end - @spec find_or_init_task(ProjectGithubRepo.t, GithubIssue.t) :: Task.t defp find_or_init_task( %ProjectGithubRepo{project_id: project_id}, diff --git a/priv/repo/migrations/20171106200036_archive_outdated_tasks.exs b/priv/repo/migrations/20171106200036_archive_outdated_tasks.exs new file mode 100644 index 000000000..41252c36a --- /dev/null +++ b/priv/repo/migrations/20171106200036_archive_outdated_tasks.exs @@ -0,0 +1,20 @@ +defmodule CodeCorps.Repo.Migrations.ArchiveOutdatedTasks do + use Ecto.Migration + + import Ecto.Query + + alias CodeCorps.Repo + + def up do + from( + t in "tasks", + where: t.status == "closed", + where: date_add(t.modified_at, 30, "day") > ^Date.utc_today, + update: [set: [archived: true, task_list_id: nil]] + ) |> Repo.update_all([]) + end + + def down do + # no-op + end +end diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql index b1188b6e4..f3056e581 100644 --- a/priv/repo/structure.sql +++ b/priv/repo/structure.sql @@ -3867,5 +3867,5 @@ ALTER TABLE ONLY users -- 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), (20171016223356), (20171016235656), (20171017235433), (20171019191035), (20171025184225), (20171026010933), (20171027061833), (20171028011642), (20171028173508), (20171030182857), (20171031232023), (20171031234356), (20171101023309), (20171104013543), (20171106045740), (20171106050209), (20171106103153); +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), (20171017235433), (20171019191035), (20171025184225), (20171026010933), (20171027061833), (20171028011642), (20171028173508), (20171030182857), (20171031232023), (20171031234356), (20171101023309), (20171104013543), (20171106045740), (20171106050209), (20171106103153), (20171106200036); diff --git a/test/lib/code_corps/github/adapters/issue_test.exs b/test/lib/code_corps/github/adapters/issue_test.exs index e1dda2614..ccb414f6a 100644 --- a/test/lib/code_corps/github/adapters/issue_test.exs +++ b/test/lib/code_corps/github/adapters/issue_test.exs @@ -4,6 +4,7 @@ defmodule CodeCorps.GitHub.Adapters.IssueTest do use ExUnit.Case, async: true import CodeCorps.GitHub.TestHelpers + import CodeCorps.Factories alias CodeCorps.{GitHub.Adapters, GithubIssue, Task} @@ -31,15 +32,15 @@ defmodule CodeCorps.GitHub.Adapters.IssueTest do end describe "to_task/1" do - test "maps api payload correctly" do - %{"issue" => payload} = load_event_fixture("issues_opened") + test "maps github issue correctly" do + github_issue = build(:github_issue) - assert Adapters.Issue.to_task(payload) == %{ - created_at: payload["created_at"], - markdown: payload["body"], - modified_at: payload["updated_at"], - status: payload["state"], - title: payload["title"] + assert github_issue |> Adapters.Issue.to_task == %{ + created_at: github_issue.github_created_at, + markdown: github_issue.body, + modified_at: github_issue.github_updated_at, + status: github_issue.state, + title: github_issue.title } end end diff --git a/test/lib/code_corps/github/event/issues/issues_test.exs b/test/lib/code_corps/github/event/issues/issues_test.exs index f9abc246e..b38848ab3 100644 --- a/test/lib/code_corps/github/event/issues/issues_test.exs +++ b/test/lib/code_corps/github/event/issues/issues_test.exs @@ -33,7 +33,9 @@ defmodule CodeCorps.GitHub.Event.IssuesTest do github_repo = insert(:github_repo, github_id: repo_github_id) %{project: project} = insert(:project_github_repo, github_repo: github_repo) + insert(:task_list, project: project, done: true) insert(:task_list, project: project, inbox: true) + insert(:task_list, project: project, pull_requests: true) {:ok, tasks} = Issues.handle(@payload) diff --git a/test/lib/code_corps/github/event/pull_request/pull_request_test.exs b/test/lib/code_corps/github/event/pull_request/pull_request_test.exs index 371ec7615..9572a8d81 100644 --- a/test/lib/code_corps/github/event/pull_request/pull_request_test.exs +++ b/test/lib/code_corps/github/event/pull_request/pull_request_test.exs @@ -34,7 +34,7 @@ defmodule CodeCorps.GitHub.Event.PullRequestTest do github_repo = insert(:github_repo, github_id: repo_github_id) %{project: project} = insert(:project_github_repo, github_repo: github_repo) - insert(:task_list, project: project, inbox: true) + insert(:task_list, project: project, pull_requests: true) {:ok, %{github_pull_request: github_pull_request}} = PullRequest.handle(@payload) diff --git a/test/lib/code_corps/github/sync/issue/issue_test.exs b/test/lib/code_corps/github/sync/issue/issue_test.exs index 1a735b69c..05c6b6aba 100644 --- a/test/lib/code_corps/github/sync/issue/issue_test.exs +++ b/test/lib/code_corps/github/sync/issue/issue_test.exs @@ -132,7 +132,7 @@ defmodule CodeCorps.GitHub.Sync.IssueTest do project_ids |> Enum.each(fn project_id -> project = Project |> Repo.get_by(id: project_id) - insert(:task_list, project: project, inbox: true) + insert(:task_list, project: project, pull_requests: true) end) %{id: existing_task_id} = diff --git a/test/lib/code_corps/github/sync/issue/task/changeset_test.exs b/test/lib/code_corps/github/sync/issue/task/changeset_test.exs index ec2e64eec..348e2a0c2 100644 --- a/test/lib/code_corps/github/sync/issue/task/changeset_test.exs +++ b/test/lib/code_corps/github/sync/issue/task/changeset_test.exs @@ -3,7 +3,6 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task.ChangesetTest do use CodeCorps.DbAccessCase - import CodeCorps.GitHub.TestHelpers import Ecto.Changeset alias CodeCorps.Task @@ -11,27 +10,26 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task.ChangesetTest do describe "build_changeset/3" do test "assigns proper changes to the task" do - %{"issue" => issue} = load_event_fixture("issues_opened") task = %Task{} - github_issue = insert(:github_issue) + github_issue = insert( + :github_issue, + github_created_at: DateTime.utc_now |> Timex.shift(minutes: 1), + github_updated_at: DateTime.utc_now |> Timex.shift(hours: 1)) 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 = TaskChangeset.build_changeset( - task, issue, github_issue, project_github_repo, user + task, github_issue, project_github_repo, user ) - {:ok, created_at, _} = issue["created_at"] |> DateTime.from_iso8601() - {:ok, updated_at, _} = issue["updated_at"] |> DateTime.from_iso8601() - # adapted fields - assert get_change(changeset, :created_at) == created_at - assert get_change(changeset, :markdown) == issue["body"] - assert get_change(changeset, :modified_at) == updated_at - assert get_change(changeset, :name) == issue["name"] - assert get_field(changeset, :status) == issue["state"] + assert get_change(changeset, :created_at) == github_issue.github_created_at + assert get_change(changeset, :markdown) == github_issue.body + assert get_change(changeset, :modified_at) == github_issue.github_updated_at + assert get_change(changeset, :title) == github_issue.title + assert get_field(changeset, :status) == github_issue.state # manual fields assert get_change(changeset, :created_from) == "github" @@ -39,7 +37,7 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task.ChangesetTest do # markdown was rendered into html assert get_change(changeset, :body) == - issue["body"] + github_issue.body |> Earmark.as_html!(%Earmark.Options{code_class_prefix: "language-"}) # relationships are proper @@ -49,29 +47,109 @@ defmodule CodeCorps.GitHub.Sync.Issue.Task.ChangesetTest do assert get_change(changeset, :task_list_id) == task_list.id assert get_change(changeset, :user_id) == user.id - assert changeset.valid? assert changeset.changes[:position] + assert changeset |> get_field(:archived) == false + + assert changeset.valid? end - test "validates that modified_at has not already happened" do - %{"issue" => issue} = load_event_fixture("issues_opened") - %{"updated_at" => updated_at} = issue + test "assigns task to inbox list if github issue is open" do + task = %Task{} + github_issue = insert(:github_issue, state: "open") + 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 = TaskChangeset.build_changeset( + task, github_issue, project_github_repo, user + ) + + assert get_change(changeset, :task_list_id) == task_list.id + end - # Set the modified_at in the future - modified_at = - updated_at - |> Timex.parse!("{ISO:Extended:Z}") - |> Timex.shift(days: 1) + test "assigns task to pull request list if github issue is associated with pull request" do + task = %Task{} + github_pull_request = insert(:github_pull_request) + github_issue = insert(:github_issue, github_pull_request: github_pull_request, state: "open") + project = insert(:project) + project_github_repo = insert(:project_github_repo, project: project) + user = insert(:user) + task_list = insert(:task_list, project: project, pull_requests: true) + + changeset = TaskChangeset.build_changeset( + task, github_issue, project_github_repo, user + ) + + assert get_change(changeset, :task_list_id) == task_list.id + end + + test "assigns task to 'done' list if github issue is closed" do + task = %Task{} + github_issue = insert(:github_issue, state: "closed") + project = insert(:project) + project_github_repo = insert(:project_github_repo, project: project) + user = insert(:user) + task_list = insert(:task_list, project: project, done: true) + changeset = TaskChangeset.build_changeset( + task, github_issue, project_github_repo, user + ) + + assert get_change(changeset, :task_list_id) == task_list.id + end + + test "assigns task to 'done' list if github issue is closed and associated to pull request" do + task = %Task{} + github_pull_request = insert(:github_pull_request) + github_issue = insert(:github_issue, github_pull_request: github_pull_request, state: "closed") + project = insert(:project) + project_github_repo = insert(:project_github_repo, project: project) + user = insert(:user) + task_list = insert(:task_list, project: project, done: true) + + changeset = TaskChangeset.build_changeset( + task, github_issue, project_github_repo, user + ) + + assert get_change(changeset, :task_list_id) == task_list.id + end + + test "archives task and removes from task list if issue is closed and unmodified for over a month" do + over_a_month_ago = Timex.now |> Timex.shift(days: -35) + + task = %Task{} + github_pull_request = insert(:github_pull_request) + github_issue = insert( + :github_issue, + github_pull_request: github_pull_request, + state: "closed", + github_updated_at: over_a_month_ago) + + project = insert(:project) + project_github_repo = insert(:project_github_repo, project: project) + user = insert(:user) + insert(:task_list, project: project, done: true) + + changeset = TaskChangeset.build_changeset( + task, github_issue, project_github_repo, user + ) + + assert get_field(changeset, :archived) == true + assert get_field(changeset, :task_list_id) == nil + end + + test "validates that modified_at has not already happened" do project = insert(:project) - github_issue = insert(:github_issue) + github_issue = insert(:github_issue, github_updated_at: DateTime.utc_now |> Timex.shift(minutes: -1), state: "open") 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_issue: github_issue, github_repo: github_repo, user: user, modified_at: modified_at) + task = insert(:task, project: project, github_issue: github_issue, github_repo: github_repo, user: user, modified_at: DateTime.utc_now) + insert(:task_list, project: project, inbox: true) changeset = TaskChangeset.build_changeset( - task, issue, github_issue, project_github_repo, user + task, github_issue, project_github_repo, user ) refute changeset.valid? diff --git a/test/lib/code_corps/github/sync/issue/task/task_test.exs b/test/lib/code_corps/github/sync/issue/task/task_test.exs index 2bd4272ef..77e211ee8 100644 --- a/test/lib/code_corps/github/sync/issue/task/task_test.exs +++ b/test/lib/code_corps/github/sync/issue/task/task_test.exs @@ -3,8 +3,6 @@ defmodule CodeCorps.GitHub.Sync.Issue.TaskTest do use CodeCorps.DbAccessCase - import CodeCorps.GitHub.TestHelpers - alias CodeCorps.{ Project, Repo, @@ -13,13 +11,11 @@ defmodule CodeCorps.GitHub.Sync.Issue.TaskTest do alias CodeCorps.GitHub.Sync.Issue.Task, as: IssueTaskSyncer describe "sync all/3" do - @payload load_event_fixture("issues_opened") - test "creates missing, updates existing tasks for each project associated with the github repo" do user = insert(:user) - %{github_repo: github_repo} = github_issue = insert(:github_issue) - %{"issue" => %{"body" => issue_body} = issue} = @payload + %{github_repo: github_repo} = github_issue = + insert(:github_issue, github_updated_at: DateTime.utc_now |> Timex.shift(hours: 1)) [%{project: project_1}, _, _] = project_github_repos = insert_list(3, :project_github_repo, github_repo: github_repo) @@ -33,13 +29,13 @@ defmodule CodeCorps.GitHub.Sync.Issue.TaskTest do insert(:task_list, project: project, inbox: true) end) - {:ok, tasks} = github_issue |> IssueTaskSyncer.sync_all(user, issue) + {:ok, tasks} = github_issue |> IssueTaskSyncer.sync_all(user) assert Repo.aggregate(Task, :count, :id) == 3 tasks |> Enum.each(fn task -> assert task.user_id == user.id - assert task.markdown == issue_body + assert task.markdown == github_issue.body assert task.github_issue_id == github_issue.id end) @@ -48,9 +44,7 @@ defmodule CodeCorps.GitHub.Sync.Issue.TaskTest do end test "fails on validation errors" do - %{github_repo: github_repo} = github_issue = insert(:github_issue) - - %{"issue" => issue} = @payload |> put_in(~w(issue title), nil) + %{github_repo: github_repo} = github_issue = insert(:github_issue, title: nil) %{project: project} = insert(:project_github_repo, github_repo: github_repo) @@ -59,8 +53,7 @@ defmodule CodeCorps.GitHub.Sync.Issue.TaskTest do insert(:task_list, project: project, inbox: true) - {:error, {tasks, errors}} = - github_issue |> IssueTaskSyncer.sync_all(user, issue) + {:error, {tasks, errors}} = github_issue |> IssueTaskSyncer.sync_all(user) assert tasks |> Enum.count == 0 assert errors |> Enum.count == 1 diff --git a/test/lib/code_corps/github/sync/sync_test.exs b/test/lib/code_corps/github/sync/sync_test.exs index 030045cbf..c67011bcf 100644 --- a/test/lib/code_corps/github/sync/sync_test.exs +++ b/test/lib/code_corps/github/sync/sync_test.exs @@ -16,6 +16,7 @@ defmodule CodeCorps.GitHub.SyncTest do GithubUser, Repo, Task, + TaskList, User } @@ -46,7 +47,9 @@ defmodule CodeCorps.GitHub.SyncTest do github_repo = insert(:github_repo, github_id: repo_github_id) %{project: project} = insert(:project_github_repo, github_repo: github_repo) + insert(:task_list, project: project, done: true) insert(:task_list, project: project, inbox: true) + insert(:task_list, project: project, pull_requests: true) {:ok, [comment]} = Sync.issue_comment_event(@payload) @@ -96,7 +99,9 @@ defmodule CodeCorps.GitHub.SyncTest do github_app_installation = insert(:github_app_installation, github_account_login: owner) github_repo = insert(:github_repo, github_app_installation: github_app_installation, name: repo, github_account_id: 6752317, github_account_avatar_url: "https://avatars3.githubusercontent.com/u/6752317?v=4", github_account_type: "User", github_id: 35129377) %{project: project} = project_github_repo = insert(:project_github_repo, github_repo: github_repo) + insert(:task_list, project: project, done: true) insert(:task_list, project: project, inbox: true) + insert(:task_list, project: project, pull_requests: true) # Sync a first time @@ -156,6 +161,17 @@ defmodule CodeCorps.GitHub.SyncTest do assert Repo.aggregate(Comment, :count, :id) == 2 assert Repo.aggregate(Task, :count, :id) == 3 assert Repo.aggregate(User, :count, :id) == 2 + + %TaskList{tasks: done_tasks} = + TaskList |> Repo.get_by(done: true) |> Repo.preload(:tasks) + %TaskList{tasks: inbox_tasks} = + TaskList |> Repo.get_by(inbox: true) |> Repo.preload(:tasks) + %TaskList{tasks: pull_requests_tasks} = + TaskList |> Repo.get_by(pull_requests: true) |> Repo.preload(:tasks) + + assert Enum.count(done_tasks) == 1 + assert Enum.count(inbox_tasks) == 1 + assert Enum.count(pull_requests_tasks) == 1 end end end diff --git a/test/support/github/test_helpers.ex b/test/support/github/test_helpers.ex index c6634150f..51a82ad29 100644 --- a/test/support/github/test_helpers.ex +++ b/test/support/github/test_helpers.ex @@ -42,9 +42,13 @@ defmodule CodeCorps.GitHub.TestHelpers do # - user creating an organization # - organization creating a project # - project being bootstrapped with an inbox task list to receive new tasks + # - project being bootstrapped with a done task list to receive closed tasks + # - project being bootstrapped with a pull_requests tasks list to receive pull requests organization = insert(:organization, owner: user) project = insert(:project, organization: organization) insert(:task_list, project: project, inbox: true) + insert(:task_list, project: project, done: true) + insert(:task_list, project: project, pull_requests: true) # Create the GitHub App installation on the organization #