Skip to content

Commit

Permalink
Improve examples (#481)
Browse files Browse the repository at this point in the history
* Update outdated dependencies in plug example

* Fix InvalidHeaderError in plug example

The error:

** (Plug.Conn.WrapperError) ** (Plug.Conn.InvalidHeaderError) header key is not lowercase: "Content-Type"

* Add tests for the plug app example

* Add a CI check to test examples

* Correctly ignore SQLite DB files
  • Loading branch information
zorbash authored Jul 25, 2022
1 parent dc3caf8 commit db84e88
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 13 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@ jobs:
- run: mix format --check-formatted
- run: mix credo --strict --all
- run: mix dialyzer

test_examples:
runs-on: ubuntu-latest
name: Test Sample Applications
env:
MIX_ENV: test
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-beam@v1
with:
otp-version: 24
elixir-version: 1.13
- uses: actions/cache@v2
with:
key: |
${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plug-build
restore-keys: |
${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.otp-version }}-plug-build
path: |
_build
- name: run Plug sample app tests
working_directory: ./examples/plug_app
run: mix do deps.get, test

test:
runs-on: ubuntu-latest
name: Test (OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}})
Expand Down
2 changes: 1 addition & 1 deletion examples/plug_app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ erl_crash.dump
*.ez

# Database files
priv/repo/plug_app.db
priv/repo/*.db
4 changes: 4 additions & 0 deletions examples/plug_app/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ config :plug_app, PlugApp.Repo,
database: "priv/repo/plug_app_#{Mix.env()}.db"

config :logger, level: :debug

if File.exists?(Path.join("config", "#{Mix.env()}.exs")) do
import_config "#{Mix.env()}.exs"
end
3 changes: 3 additions & 0 deletions examples/plug_app/config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use Mix.Config

config :plug_app, PlugApp.Repo, pool: Ecto.Adapters.SQL.Sandbox
7 changes: 4 additions & 3 deletions examples/plug_app/lib/plug_app/user_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule PlugApp.UserHandler do
users = Accounts.list_users()

conn
|> Plug.Conn.put_resp_header("Content-Type", "application/json")
|> Plug.Conn.put_resp_header("content-type", "application/json")
|> Plug.Conn.send_resp(200, render(users))
end

Expand Down Expand Up @@ -81,7 +81,7 @@ defmodule PlugApp.UserHandler do

def show(conn = %Plug.Conn{assigns: %{user: user}}, _opts) do
conn
|> put_resp_header("Content-Type", "application/json")
|> put_resp_header("content-type", "application/json")
|> send_resp(200, render(user))
end

Expand Down Expand Up @@ -116,7 +116,8 @@ defmodule PlugApp.UserHandler do
required: true
),
responses: %{
201 => response("User", "application/json", Schemas.UserResponse)
201 => response("User", "application/json", Schemas.UserResponse),
422 => OpenApiSpex.JsonErrorResponse.response()
}
}
end
Expand Down
17 changes: 16 additions & 1 deletion examples/plug_app/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ defmodule PlugApp.Mixfile do
version: "0.1.0",
elixir: "~> 1.5",
start_permanent: Mix.env() == :prod,
deps: deps()
elixirc_paths: elixirc_paths(Mix.env()),
deps: deps(),
aliases: aliases()
]
end

Expand All @@ -32,4 +34,17 @@ defmodule PlugApp.Mixfile do
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
]
end

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

defp aliases do
[
setup: ["deps.get", "ecto.setup"],
"ecto.setup": ["ecto.create", "ecto.migrate"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
]
end
end
17 changes: 9 additions & 8 deletions examples/plug_app/mix.lock
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
%{
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
"cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
"cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"db_connection": {:hex, :db_connection, "1.1.3", "89b30ca1ef0a3b469b1c779579590688561d586694a3ce8792985d4d7e575a61", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm", "5f0a16a58312a610d5eb0b07506280c65f5137868ad479045f2a2dc4ced80550"},
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
"ecto": {:hex, :ecto, "2.2.11", "4bb8f11718b72ba97a2696f65d247a379e739a0ecabf6a13ad1face79844791c", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm", "e7e50d6bb2254777d304bad064af31cc1d76a3bf043bbd9913990c450d428109"},
"esqlite": {:hex, :esqlite, "0.4.1", "ba5d0bab6b9c8432ffe1bf12fee8e154a50f1c3c40eadc3a9c870c23ca94d961", [:rebar3], [], "hexpm", "3584ca33172f4815ce56e96eed9835f5d8c987a9000fbc8c376c86acef8bf965"},
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
"plug": {:hex, :plug, "1.10.1", "c56a6d9da7042d581159bcbaef873ba9d87f15dce85420b0d287bca19f40f9bd", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "b5cd52259817eb8a31f2454912ba1cff4990bca7811918878091cb2ab9e52cb8"},
"plug_cowboy": {:hex, :plug_cowboy, "2.2.2", "7a09aa5d10e79b92d332a288f21cc49406b1b994cbda0fde76160e7f4cc890ea", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e82364b29311dbad3753d588febd7e5ef05062cd6697d8c231e0e007adab3727"},
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"},
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
"plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"},
"plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"},
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"sbroker": {:hex, :sbroker, "1.0.0", "28ff1b5e58887c5098539f236307b36fe1d3edaa2acff9d6a3d17c2dcafebbd0", [:rebar3], [], "hexpm", "ba952bfa35b374e1e5d84bc5f5efe8360c6f99dc93b3118f714a9a2dff6c9e19"},
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.4.1", "a292b807c7670f1c2c136f7224934bf5929c998ea1b9ded63b710f3dd5140e4c", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "2.2.11", [hex: :ecto, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: false]}, {:sqlitex, "~> 1.6", [hex: :sqlitex, repo: "hexpm", optional: false]}], "hexpm", "19358813676eb058d1b0630cd6914817e7dbf001022c49159510241f14f0eea7"},
"sqlitex": {:hex, :sqlitex, "1.7.1", "022d477aab2ae999c43ae6fbd1782ff1457e0e95c251c7b5fa6f7b7b102040ff", [:mix], [{:decimal, "~> 1.7", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.4", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm", "ef16cda37b151136a47a6c0830dc9eb5e5f8f5f029b649e9f3a58a6eed634b80"},
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
}
26 changes: 26 additions & 0 deletions examples/plug_app/test/support/conn_case.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule PlugApp.ConnCase do
use ExUnit.CaseTemplate

using do
quote do
use Plug.Test
import Plug.Conn
import OpenApiSpex.TestAssertions

import OpenApiSpex.Schema, only: [example: 1]
end
end

setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(PlugApp.Repo)

unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(PlugApp.Repo, {:shared, self()})
end

# Added to the context to validate responses with assert_schema/3
api_spec = PlugApp.ApiSpec.spec()

{:ok, api_spec: api_spec}
end
end
103 changes: 103 additions & 0 deletions examples/plug_app/test/user_handler_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
defmodule UserHandlerTest do
use PlugApp.ConnCase

alias PlugApp.Accounts
alias PlugApp.Schemas
alias PlugApp.Router

@opts Router.init([])

describe "GET /api/users" do
setup do
{:ok, user} = Accounts.create_user(%{name: "Joe", email: "joe@example.com"})

%{user: user}
end

test "responds with 200 OK and a JSON body matching the API schema", %{
user: %{id: user_id},
api_spec: api_spec
} do
%{resp_body: body} =
conn =
conn(:get, "/api/users")
|> Router.call(@opts)

assert %{status: 200} = conn

json_response = Jason.decode!(body)

assert %{"data" => [%{"id" => user_id}]} = json_response

assert_schema(json_response, "UsersResponse", api_spec)
end
end

describe "GET /api/users/:id" do
setup do
{:ok, user} = Accounts.create_user(%{name: "Joe", email: "joe@example.com"})

%{user: user}
end

test "responds with 200 OK and a JSON body matching the API schema", %{
user: %{id: user_id},
api_spec: api_spec
} do
%{resp_body: body} =
conn =
conn(:get, "/api/users/#{user_id}")
|> Router.call(@opts)

assert %{status: 200} = conn

json_response = Jason.decode!(body)

assert %{"data" => %{"id" => user_id}} = json_response

assert_schema(json_response, "UserResponse", api_spec)
end
end

describe "POST /api/users" do
@payload example(Schemas.UserRequest)

test "responds with 201 Created and a JSON body matching the API schema", %{api_spec: api_spec} do
%{resp_body: body} =
conn =
conn(:post, "/api/users", Jason.encode!(@payload))
|> Plug.Conn.put_req_header("content-type", "application/json")
|> Router.call(@opts)

assert %{status: 201} = conn

json_response = Jason.decode!(body)

assert_schema(json_response, "UserResponse", api_spec)
end
end

describe "POST /api/users with invalid payload" do
@payload %{
user: %{
email: "joe@example.com"
}
}

test "responds with 422 Unprocessable Entity and a JSON body matching the API schema when ", %{
api_spec: api_spec
} do
%{resp_body: body} =
conn =
conn(:post, "/api/users", Jason.encode!(@payload))
|> Plug.Conn.put_req_header("content-type", "application/json")
|> Router.call(@opts)

assert %{status: 422} = conn

json_response = Jason.decode!(body)

assert_schema(json_response, "JsonErrorResponse", api_spec)
end
end
end

0 comments on commit db84e88

Please sign in to comment.