diff --git a/.gitignore b/.gitignore index 553398e..93c4867 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ app_api-*.tar # we ignore priv/static. You may want to comment # this depending on your deployment strategy. /priv/static/ -.elixir_ls \ No newline at end of file +.elixir_ls +cover \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9ca03be --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: elixir +elixir: + - 1.9 +otp_release: + - 22.1.8 +env: + - MIX_ENV=test +cache: + directories: + - _build + - deps +services: + - postgresql +env: + global: + - MIX_ENV=test +before_script: + - mix do ecto.create, ecto.migrate +script: + - mix cover +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/coveralls.json b/coveralls.json new file mode 100644 index 0000000..9778e26 --- /dev/null +++ b/coveralls.json @@ -0,0 +1,9 @@ +{ + "coverage_options": { + "minimum_coverage": 80 + }, + "skip_files": [ + "test/", + "lib/app_api.ex" + ] +} \ No newline at end of file diff --git a/lib/app_api/captures.ex b/lib/app_api/captures.ex index 5f36bab..f50b13f 100644 --- a/lib/app_api/captures.ex +++ b/lib/app_api/captures.ex @@ -8,6 +8,7 @@ defmodule AppApi.Captures do alias AppApi.Captures.Capture alias AppApi.Timers.Timer + alias AppApi.Tags.Tag @doc """ Returns the list of captures. @@ -20,6 +21,8 @@ defmodule AppApi.Captures do """ def list_captures do Repo.all(Capture) + |> Repo.preload(timers: from(t in Timer, order_by: [desc: t.inserted_at])) + |> Repo.preload(tags: from(t in Tag, order_by: [asc: t.text])) end @doc """ @@ -39,6 +42,7 @@ defmodule AppApi.Captures do def get_capture!(id) do Repo.get!(Capture, id) |> Repo.preload(timers: from(t in Timer, order_by: [desc: t.inserted_at])) + |> Repo.preload(tags: from(t in Tag, order_by: [asc: t.text])) end def get_capture_by_id_person(id_person) do @@ -46,7 +50,10 @@ defmodule AppApi.Captures do from c in Capture, where: c.id_person == ^id_person, order_by: [desc: c.inserted_at], - preload: [timers: ^from(t in Timer, order_by: [desc: t.inserted_at])] + preload: [ + timers: ^from(t in Timer, order_by: [desc: t.inserted_at]), + tags: ^from(t in Tag, order_by: [asc: t.text]) + ] Repo.all(query) end @@ -68,6 +75,7 @@ defmodule AppApi.Captures do |> Capture.changeset(attrs) |> Repo.insert!() |> Repo.preload(timers: from(t in Timer, order_by: [desc: t.inserted_at])) + |> Repo.preload(tags: from(t in Tag, order_by: [asc: t.text])) end @doc """ @@ -87,6 +95,7 @@ defmodule AppApi.Captures do |> Capture.changeset(attrs) |> Repo.update!() |> Repo.preload(timers: from(t in Timer, order_by: [desc: t.inserted_at])) + |> Repo.preload(tags: from(t in Tag, order_by: [asc: t.text])) end @doc """ diff --git a/lib/app_api/captures/capture.ex b/lib/app_api/captures/capture.ex index 8eb3907..8129678 100644 --- a/lib/app_api/captures/capture.ex +++ b/lib/app_api/captures/capture.ex @@ -2,6 +2,9 @@ defmodule AppApi.Captures.Capture do use Ecto.Schema import Ecto.Changeset alias AppApi.Timers.Timer + alias AppApi.Tags.Tag + import Ecto.Query, warn: false + alias AppApi.Repo schema "captures" do field :completed, :boolean, default: false @@ -9,6 +12,10 @@ defmodule AppApi.Captures.Capture do field :text, :string has_many :timers, Timer + many_to_many :tags, Tag, + join_through: "captures_tags", + on_replace: :delete + timestamps() end @@ -17,5 +24,30 @@ defmodule AppApi.Captures.Capture do capture |> cast(attrs, [:id_person, :text, :completed]) |> validate_required([:id_person, :text, :completed]) + |> put_assoc(:tags, parse_tags(attrs)) + end + + defp parse_tags(params) do + tags = + (params[:tags] || "") + |> String.split(",") + |> Enum.map(&String.trim/1) + |> Enum.reject(&(&1 == "")) + + insert_and_get_all(tags, params[:id_person]) + end + + defp insert_and_get_all([], _id_person) do + [] + end + + defp insert_and_get_all(tags, id_person) do + Enum.each(tags, fn t -> + attrs = %{text: t, id_person: id_person} + changeset = Tag.changeset(%Tag{}, attrs) + Repo.insert!(changeset, on_conflict: :nothing) + end) + + Repo.all(from t in Tag, where: t.text in ^tags) end end diff --git a/lib/app_api/tags.ex b/lib/app_api/tags.ex new file mode 100644 index 0000000..53bc4f0 --- /dev/null +++ b/lib/app_api/tags.ex @@ -0,0 +1,45 @@ +defmodule AppApi.Tags do + @moduledoc """ + The Tags context. + """ + + import Ecto.Query, warn: false + alias AppApi.Repo + + alias AppApi.Tags.Tag + + @doc """ + Returns the list of tags created by a person + i.e. default tags + """ + def get_tags_by_id_person(id_person) do + query = + from t in Tag, + where: t.id_person == ^id_person, + order_by: [asc: t.text] + + Repo.all(query) + end + + @doc """ + Returns the list of tags where the person is not defined + i.e. default tags + """ + def get_default_tags() do + query = + from t in Tag, + where: is_nil(t.id_person), + order_by: [asc: t.text] + + Repo.all(query) + end + + @doc """ + Creates a tag. + """ + def create_tag(attrs \\ %{}) do + %Tag{} + |> Tag.changeset(attrs) + |> Repo.insert!() + end +end diff --git a/lib/app_api/tags/tag.ex b/lib/app_api/tags/tag.ex new file mode 100644 index 0000000..9d61a03 --- /dev/null +++ b/lib/app_api/tags/tag.ex @@ -0,0 +1,18 @@ +defmodule AppApi.Tags.Tag do + use Ecto.Schema + import Ecto.Changeset + + schema "tags" do + field :text, :string + field :id_person, :integer + + timestamps() + end + + @doc false + def changeset(tag, attrs) do + tag + |> cast(attrs, [:text, :id_person]) + |> validate_required([:text]) + end +end diff --git a/lib/app_api/timers.ex b/lib/app_api/timers.ex index ae118f5..1eee9c3 100644 --- a/lib/app_api/timers.ex +++ b/lib/app_api/timers.ex @@ -8,19 +8,6 @@ defmodule AppApi.Timers do alias AppApi.Timers.Timer - @doc """ - Returns the list of timers. - - ## Examples - - iex> list_timers() - [%Timer{}, ...] - - """ - def list_timers do - Repo.all(Timer) - end - @doc """ Gets a single timer. diff --git a/lib/app_api_web/controllers/capture_controller.ex b/lib/app_api_web/controllers/capture_controller.ex index 5659c76..f56893d 100644 --- a/lib/app_api_web/controllers/capture_controller.ex +++ b/lib/app_api_web/controllers/capture_controller.ex @@ -29,7 +29,8 @@ defmodule AppApiWeb.CaptureController do updates = %{ completed: params["completed"], - text: params["text"] + text: params["text"], + tags: params["tags"] } udpatedCatpure = Captures.update_capture(capture, updates) diff --git a/lib/app_api_web/controllers/tag_controller.ex b/lib/app_api_web/controllers/tag_controller.ex new file mode 100644 index 0000000..de6d49f --- /dev/null +++ b/lib/app_api_web/controllers/tag_controller.ex @@ -0,0 +1,10 @@ +defmodule AppApiWeb.TagController do + use AppApiWeb, :controller + alias AppApi.Tags + + def index(conn, _params) do + tags = Tags.get_tags_by_id_person(conn.assigns.person.id_person) + default_tags = Tags.get_default_tags() + render(conn, "index.json", tags: default_tags ++ tags) + end +end diff --git a/lib/app_api_web/router.ex b/lib/app_api_web/router.ex index fc03fb7..a1e82fe 100644 --- a/lib/app_api_web/router.ex +++ b/lib/app_api_web/router.ex @@ -20,5 +20,6 @@ defmodule AppApiWeb.Router do resources "/capture", CaptureController, only: [:index, :create, :show, :update] do resources "/timers", TimerController, only: [:index, :create, :update] end + get "/tags", TagController, :index end end diff --git a/lib/app_api_web/views/capture_view.ex b/lib/app_api_web/views/capture_view.ex index 25143b7..c4cef5f 100644 --- a/lib/app_api_web/views/capture_view.ex +++ b/lib/app_api_web/views/capture_view.ex @@ -1,6 +1,7 @@ defmodule AppApiWeb.CaptureView do use AppApiWeb, :view alias AppApiWeb.TimerView + alias AppApiWeb.TagView def render("index.json", %{captures: captures}) do %{data: Enum.map(captures, &capture_to_json/1)} @@ -20,7 +21,8 @@ defmodule AppApiWeb.CaptureView do id_person: capture.id_person, text: capture.text, completed: capture.completed, - timers: Enum.map(capture.timers, &TimerView.timer_to_json/1) + timers: Enum.map(capture.timers, &TimerView.timer_to_json/1), + tags: Enum.map(capture.tags, &TagView.tag_to_json/1) } end end diff --git a/lib/app_api_web/views/tag_view.ex b/lib/app_api_web/views/tag_view.ex new file mode 100644 index 0000000..828dc9d --- /dev/null +++ b/lib/app_api_web/views/tag_view.ex @@ -0,0 +1,14 @@ +defmodule AppApiWeb.TagView do + use AppApiWeb, :view + + def render("index.json", %{tags: tags}) do + %{data: Enum.map(tags, &tag_to_json/1)} + end + + def tag_to_json(tag) do + %{ + text: tag.text, + id_person: tag.id_person + } + end +end diff --git a/lib/plugs/validate_token.ex b/lib/plugs/validate_token.ex index 7e525fe..e802553 100644 --- a/lib/plugs/validate_token.ex +++ b/lib/plugs/validate_token.ex @@ -9,9 +9,13 @@ defmodule AppApi.Plugs.ValidateToken do def init(opts), do: opts def call(conn, _) do - case AppApiWeb.AuthServiceApi.get_person_information(conn) do - {:error, _} -> unauthorized(conn) - {:ok, person} -> assign(conn, :person, person) + if Mix.env() == :test do + assign(conn, :person, %{email: "email", name: "name", id_person: 42}) + else + case AppApiWeb.AuthServiceApi.get_person_information(conn) do + {:error, _} -> unauthorized(conn) + {:ok, person} -> assign(conn, :person, person) + end end end @@ -21,5 +25,4 @@ defmodule AppApi.Plugs.ValidateToken do |> send_resp(401, "unauthorized") |> halt() end - end diff --git a/mix.exs b/mix.exs index fa3480e..ca732b3 100644 --- a/mix.exs +++ b/mix.exs @@ -10,7 +10,14 @@ defmodule AppApi.MixProject do compilers: [:phoenix, :gettext] ++ Mix.compilers(), start_permanent: Mix.env() == :prod, aliases: aliases(), - deps: deps() + deps: deps(), + test_coverage: [tool: ExCoveralls], + preferred_cli_env: [ + coveralls: :test, + "coveralls.detail": :test, + "coveralls.post": :test, + "coveralls.html": :test + ] ] end @@ -43,7 +50,8 @@ defmodule AppApi.MixProject do {:plug_cowboy, "~> 2.0"}, {:cors_plug, "~> 2.0"}, {:httpoison, "~> 1.6"}, - {:poison, "~> 4.0"} + {:poison, "~> 4.0"}, + {:excoveralls, "~> 0.12.2", only: [:dev, :test]} ] end @@ -57,7 +65,9 @@ defmodule AppApi.MixProject do [ "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.reset": ["ecto.drop", "ecto.setup"], - test: ["ecto.create --quiet", "ecto.migrate", "test"] + test: ["ecto.create --quiet", "ecto.migrate", "test"], + cover: ["coveralls.json"], + "cover.html": ["coveralls.html"] ] end end diff --git a/mix.lock b/mix.lock index 6c91314..0500962 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,6 @@ %{ "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, + "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, "cors_plug": {:hex, :cors_plug, "2.0.1", "61b9fb38a599d1d4a7f8c7cec9f81ae548890e2f832426cea13897ecbf2f5f8b", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "571eea27ca6d08d46f44b27ce6ad59d29255111c3cefab833ea55c375db2b4eb"}, "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, @@ -8,6 +9,7 @@ "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "ecto": {:hex, :ecto, "3.3.2", "002aa428c752a8ee4bb65baa9d1041f0514d7435d2e21d58cb6aa69a1076721d", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "c651e3ea0919241da314f518404b84449c8569525c4d4cf0f3d16f1d5d0a560c"}, "ecto_sql": {:hex, :ecto_sql, "3.3.3", "7d8962d39f16181c1df1bbd0f64aa392bd9ce0b9f8ff5ff21d43dca3d624eee7", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3d5b8e14836d930448d79ab6ac325501c3c62caed83c34791d5af77718766795"}, + "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, "gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"}, "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"}, "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, @@ -28,5 +30,7 @@ "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"}, "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"}, + "timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"}, + "tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, } diff --git a/priv/repo/migrations/20200420133108_create_tags.exs b/priv/repo/migrations/20200420133108_create_tags.exs new file mode 100644 index 0000000..b7ac669 --- /dev/null +++ b/priv/repo/migrations/20200420133108_create_tags.exs @@ -0,0 +1,15 @@ +defmodule AppApi.Repo.Migrations.CreateTags do + use Ecto.Migration + + def change do + create table(:tags) do + add :text, :string + add :id_person, :integer + + timestamps() + end + + create unique_index(:tags, [:text]) + end + +end diff --git a/priv/repo/migrations/20200420133553_captures_tags.exs b/priv/repo/migrations/20200420133553_captures_tags.exs new file mode 100644 index 0000000..3063335 --- /dev/null +++ b/priv/repo/migrations/20200420133553_captures_tags.exs @@ -0,0 +1,19 @@ +defmodule AppApi.Repo.Migrations.CapturesTags do + use Ecto.Migration + + def change do + create table(:captures_tags, primary_key: false) do + add(:capture_id, references(:captures, on_delete: :delete_all), primary_key: true) + add(:tag_id, references(:tags, on_delete: :delete_all), primary_key: true) + timestamps() + end + + create(index(:captures_tags, [:capture_id])) + create(index(:captures_tags, [:tag_id])) + + create( + unique_index(:captures_tags, [:capture_id, :tag_id], name: :capture_id_tag_id_unique_index) + ) + + end +end diff --git a/priv/repo/migrations/20200421121617_remove_timestamp_captures_tags.exs b/priv/repo/migrations/20200421121617_remove_timestamp_captures_tags.exs new file mode 100644 index 0000000..2bf61d2 --- /dev/null +++ b/priv/repo/migrations/20200421121617_remove_timestamp_captures_tags.exs @@ -0,0 +1,10 @@ +defmodule AppApi.Repo.Migrations.RemoveTimestampCapturesTags do + use Ecto.Migration + + def change do + alter table(:captures_tags) do + remove :inserted_at + remove :updated_at + end + end +end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 94a2689..1761cd9 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -9,3 +9,8 @@ # # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. + +alias AppApi.Tags + +Tags.create_tag(%{text: "note", id_person: nil}) +Tags.create_tag(%{text: "task", id_person: nil}) diff --git a/test/app_api/captures_test.exs b/test/app_api/captures_test.exs index f8a0781..0e8ec32 100644 --- a/test/app_api/captures_test.exs +++ b/test/app_api/captures_test.exs @@ -6,17 +6,14 @@ defmodule AppApi.CapturesTest do describe "captures" do alias AppApi.Captures.Capture - @valid_attrs %{completed: true, id_person: 42, text: "some text"} - @update_attrs %{completed: false, id_person: 43, text: "some updated text"} - @invalid_attrs %{completed: nil, id_person: nil, text: nil} + @valid_attrs %{completed: true, id_person: 42, text: "some text", tags: "tag1, tag2"} + @update_attrs %{completed: false, id_person: 43, text: "some updated text", tags: "tag3, tag4" } + @invalid_attrs %{completed: nil, id_person: nil, text: nil, tags: ""} def capture_fixture(attrs \\ %{}) do - {:ok, capture} = attrs |> Enum.into(@valid_attrs) |> Captures.create_capture() - - capture end test "list_captures/0 returns all captures" do @@ -30,19 +27,24 @@ defmodule AppApi.CapturesTest do end test "create_capture/1 with valid data creates a capture" do - assert {:ok, %Capture{} = capture} = Captures.create_capture(@valid_attrs) + assert %Capture{} = capture = Captures.create_capture(@valid_attrs) assert capture.completed == true assert capture.id_person == 42 assert capture.text == "some text" end + test "get_capture_by_id_person/1 returns the capture with given id person" do + capture = capture_fixture() # id person is 42 + assert Captures.get_capture_by_id_person(42) == [capture] + end + test "create_capture/1 with invalid data returns error changeset" do - assert {:error, %Ecto.Changeset{}} = Captures.create_capture(@invalid_attrs) + assert catch_error(Captures.create_capture(@invalid_attrs)) end test "update_capture/2 with valid data updates the capture" do capture = capture_fixture() - assert {:ok, %Capture{} = capture} = Captures.update_capture(capture, @update_attrs) + assert %Capture{} = capture = Captures.update_capture(capture, @update_attrs) assert capture.completed == false assert capture.id_person == 43 assert capture.text == "some updated text" @@ -50,7 +52,7 @@ defmodule AppApi.CapturesTest do test "update_capture/2 with invalid data returns error changeset" do capture = capture_fixture() - assert {:error, %Ecto.Changeset{}} = Captures.update_capture(capture, @invalid_attrs) + assert catch_error(Captures.update_capture(capture, @invalid_attrs)) assert capture == Captures.get_capture!(capture.id) end @@ -64,5 +66,10 @@ defmodule AppApi.CapturesTest do capture = capture_fixture() assert %Ecto.Changeset{} = Captures.change_capture(capture) end + + test "add tag" do + capture = capture_fixture(%{tags: "hello"}) + assert %Capture{} = capture + end end end diff --git a/test/app_api/tags_test.exs b/test/app_api/tags_test.exs new file mode 100644 index 0000000..f228553 --- /dev/null +++ b/test/app_api/tags_test.exs @@ -0,0 +1,39 @@ +defmodule AppApi.TagsTest do + use AppApi.DataCase + + alias AppApi.Tags + + describe "tags" do + alias AppApi.Tags.Tag + + @valid_attrs %{id_person: 42, text: "tag1"} + @invalid_attrs %{id_person: 42, text: ""} + + def tag_fixture(attrs \\ %{}) do + attrs + |> Enum.into(@valid_attrs) + |> Tags.create_tag() + end + + test "get_default_tags/0 returns all tags" do + tag = tag_fixture(%{id_person: nil}) + assert Tags.get_default_tags() == [tag] + end + + test "get_tags_by_id_person/1 returns all tags for a person" do + tag1 = tag_fixture(%{text: "a", id_person: 32}) + tag2 = tag_fixture(%{text: "b", id_person: 32}) + assert Tags.get_tags_by_id_person(32) == [tag1, tag2] # order by tag name + end + + test "create tag with wrong attr return an error" do + assert catch_error(Tags.create_tag(@invalid_attrs)) + end + + test "create_tag/1 with valid data creates a tag" do + assert %Tag{} = tag = Tags.create_tag(@valid_attrs) + assert tag.id_person == 42 + assert tag.text == "tag1" + end + end +end diff --git a/test/app_api/timers_test.exs b/test/app_api/timers_test.exs new file mode 100644 index 0000000..fe5c2fe --- /dev/null +++ b/test/app_api/timers_test.exs @@ -0,0 +1,40 @@ +defmodule AppApi.TimersTest do + use AppApi.DataCase + + alias AppApi.Timers + alias AppApi.Captures + describe "tags" do + @valid_attrs_capture %{completed: true, id_person: 42, text: "some text", tags: "tag1, tag2"} + @valid_attrs %{started_at: DateTime.utc_now(), stopped_at: nil} + + def capture_fixture(attrs \\ %{}) do + attrs + |> Enum.into(@valid_attrs_capture) + |> Captures.create_capture() + end + + def timer_fixture(attrs \\ %{}) do + capture = capture_fixture() + + timer_attrs = attrs + |> Enum.into(@valid_attrs) + + {:ok, timer} = Timers.create_timer(capture, timer_attrs) + timer + end + + test "stop timer" do + timer = timer_fixture() + assert is_nil(timer.stopped_at) + timer_updated = Timers.stop_timer(timer.id) + assert is_nil(timer_updated.stopped_at) == false + end + + test "get capture' timers" do + capture = capture_fixture() + assert Timers.get_capture_timers(capture.id) == [] + {:ok, timer} = Timers.create_timer(capture, @valid_attrs) + assert Enum.count(Timers.get_capture_timers(capture.id)) == 1 + end + end +end diff --git a/test/app_api_web/controllers/capture_controller_test.exs b/test/app_api_web/controllers/capture_controller_test.exs new file mode 100644 index 0000000..7f4732a --- /dev/null +++ b/test/app_api_web/controllers/capture_controller_test.exs @@ -0,0 +1,20 @@ +defmodule AppApi.CaptureControllerTest do + use AppApiWeb.ConnCase + alias AppApiWeb.CaptureController + + describe "test captures endpoint" do + test "index and show and create endpoints", %{conn: conn} do + conn = post(conn, Routes.capture_path(conn, :create, text: "text capture")) + create_response = json_response(conn, 200) + assert create_response["data"]["text"] == "text capture" + + conn = get(conn, Routes.capture_path(conn, :index)) + list_captures = json_response(conn, 200) + assert Enum.count(list_captures) == 1 + + conn = get(conn, Routes.capture_path(conn, :show, create_response["data"]["capture_id"])) + get_capture_response = json_response(conn, 200) + assert get_capture_response["data"]["text"] == "text capture" + end + end +end diff --git a/test/app_api_web/controllers/login_controller_test.exs b/test/app_api_web/controllers/login_controller_test.exs new file mode 100644 index 0000000..9f63dd5 --- /dev/null +++ b/test/app_api_web/controllers/login_controller_test.exs @@ -0,0 +1,10 @@ +defmodule AppApi.LoginControllerTest do + use AppApiWeb.ConnCase + + describe "test login controller" do + test "index page redirects to auth service", %{conn: conn} do + conn = get(conn, Routes.login_path(conn, :index)) + assert conn.status == 302 + end + end +end diff --git a/test/app_api_web/controllers/person_controller_test.exs b/test/app_api_web/controllers/person_controller_test.exs new file mode 100644 index 0000000..47d2411 --- /dev/null +++ b/test/app_api_web/controllers/person_controller_test.exs @@ -0,0 +1,23 @@ +defmodule AppApi.PersonControllerTest do + use AppApiWeb.ConnCase + alias AppApi.Tags + + describe "test person endoints" do + test "attempt to get person info without a jwt returns an unauthorized error", %{conn: conn} do + conn = get(conn, Routes.person_path(conn, :index)) + assert conn.status == 401 + end + + test "attempt to get person without a valid jwt", %{conn: conn} do + conn + + conn = + conn + |> put_req_header("content-type", "application/json") + |> put_req_header("authorization", "Bearer 123") + |> get(Routes.person_path(conn, :index)) + + assert conn.status == 401 + end + end +end diff --git a/test/app_api_web/controllers/tag_controller_test.exs b/test/app_api_web/controllers/tag_controller_test.exs new file mode 100644 index 0000000..ee41cea --- /dev/null +++ b/test/app_api_web/controllers/tag_controller_test.exs @@ -0,0 +1,21 @@ +defmodule AppApi.TagControllerTest do + use AppApiWeb.ConnCase + alias AppApi.Tags + + @valid_attrs %{id_person: 42, text: "tag1"} + + def tag_fixture(attrs \\ %{}) do + attrs + |> Enum.into(@valid_attrs) + |> Tags.create_tag() + end + + describe "test tag endoints" do + test "index endpoint", %{conn: conn} do + tag_fixture() + conn = get(conn, Routes.tag_path(conn, :index)) + response = json_response(conn, 200) + assert Enum.count(response["data"]) == 1 + end + end +end diff --git a/test/app_api_web/controllers/timer_controller_test.exs b/test/app_api_web/controllers/timer_controller_test.exs new file mode 100644 index 0000000..5323f59 --- /dev/null +++ b/test/app_api_web/controllers/timer_controller_test.exs @@ -0,0 +1,100 @@ +defmodule AppApi.TimerControllerTest do + use AppApiWeb.ConnCase + alias AppApi.Timers + alias AppApi.Captures + + @valid_attrs_capture %{completed: true, id_person: 42, text: "some text", tags: "tag1, tag2"} + @valid_attrs %{started_at: DateTime.utc_now(), stopped_at: nil} + + def capture_fixture(attrs \\ %{}) do + attrs + |> Enum.into(@valid_attrs_capture) + |> Captures.create_capture() + end + + def timer_fixture(attrs \\ %{}) do + capture = capture_fixture() + + timer_attrs = + attrs + |> Enum.into(@valid_attrs) + + {:ok, timer} = Timers.create_timer(capture, timer_attrs) + timer + end + + describe "test timer endoints" do + test "create endpoint", %{conn: conn} do + capture = capture_fixture() + conn = post(conn, Routes.capture_timer_path(conn, :create, capture.id)) + json_response(conn, 200) + + conn = get(conn, Routes.capture_timer_path(conn, :index, capture.id)) + response_timers = json_response(conn, 200) + assert Enum.count(response_timers["data"]) == 1 + end + + test "create endpoint with different id person returns 401", %{conn: conn} do + capture = capture_fixture(%{id_person: 1}) + conn = post(conn, Routes.capture_timer_path(conn, :create, capture.id)) + assert conn.status == 401 + end + + test "update endpoint", %{conn: conn} do + timer = timer_fixture() + + conn = + put( + conn, + Routes.capture_timer_path( + conn, + :update, + timer.capture.id, + timer.id + ), + %{"capture_id" => timer.capture.id, "timer_id" => timer.id, "action" => "stop"} + ) + + response_update = json_response(conn, 200) + refute is_nil(response_update["data"]["stopped_at"]) + end + + test "update timer with wrong action", %{conn: conn} do + timer = timer_fixture() + + conn = + put( + conn, + Routes.capture_timer_path( + conn, + :update, + timer.capture.id, + timer.id + ), + %{"capture_id" => timer.capture.id, "timer_id" => timer.id, "action" => "wrong action"} + ) + + # action not found + assert conn.status == 404 + end + + test "update with wrong catpure returns 401", %{conn: conn} do + timer = timer_fixture() + capture = capture_fixture(%{id_person: 1}) + + conn = + put( + conn, + Routes.capture_timer_path( + conn, + :update, + capture.id, + timer.id + ), + %{"capture_id" => timer.capture.id, "timer_id" => timer.id, "action" => "stop"} + ) + + assert conn.status == 401 + end + end +end diff --git a/test/app_api_web/views/person_view_test.exs b/test/app_api_web/views/person_view_test.exs new file mode 100644 index 0000000..fae6419 --- /dev/null +++ b/test/app_api_web/views/person_view_test.exs @@ -0,0 +1,11 @@ +defmodule AppApiWeb.PersonViewTest do + use AppApiWeb.ConnCase, async: true + + # Bring render/3 and render_to_string/3 for testing custom views + import Phoenix.View + + test "renders index.json" do + person = %{email: "email", name: "name"} + assert render(AppApiWeb.PersonView, "index.json", %{person: person}) == %{data: person} + end +end