Skip to content

Commit

Permalink
Write policy and tests for Stripe Customer
Browse files Browse the repository at this point in the history
Add StripeCustomerPolicy.create?
Add StripeCustomerController.create and .show
Moved slugged routes to bottom of router, to allow for authenticated :show endpoints
Call stripe service to create customer (requires test API key in dev)
Add attributes to stripe_customer, including email and id which hide when not current user
  • Loading branch information
amyschools authored and joshsmith committed Nov 20, 2016
1 parent 7ebaf31 commit f06c57e
Show file tree
Hide file tree
Showing 27 changed files with 398 additions and 50 deletions.
2 changes: 1 addition & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ config :code_corps, CodeCorps.Endpoint,
code_reloader: true,
check_origin: false


# Watch static and templates for browser reloading.
config :code_corps, CodeCorps.Endpoint,
live_reload: [
Expand Down Expand Up @@ -47,6 +46,7 @@ config :guardian, Guardian,
secret_key: "e62fb6e2746f6b1bf8b5b735ba816c2eae1d5d76e64f18f3fc647e308b0c159e"

config :code_corps, :analytics, CodeCorps.Analytics.InMemoryAPI
config :code_corps, :stripe, Stripe

config :sentry,
environment_name: Mix.env || :dev
2 changes: 2 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ config :guardian, Guardian,

config :code_corps, :analytics, CodeCorps.Analytics.TestAPI

config :code_corps, :stripe, CodeCorps.Stripe.InMemory

config :code_corps, :icon_color_generator, CodeCorps.RandomIconColor.TestGenerator

# Set Corsica logging to output no console warning when rejecting a request
Expand Down
9 changes: 9 additions & 0 deletions lib/code_corps/map_utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,13 @@ defmodule CodeCorps.MapUtils do
|> Map.put(new_key, map |> Map.get(old_key))
|> Map.delete(old_key)
end

def keys_to_string(map) do
for {key, val} <- map, into: %{} do
cond do
is_atom(key) -> {Atom.to_string(key), val}
true -> {key, val}
end
end
end
end
28 changes: 25 additions & 3 deletions lib/code_corps/stripe/adapters/stripe_customer.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
defmodule CodeCorps.Stripe.Adapters.StripeCustomer do
import CodeCorps.MapUtils, only: [rename: 3]
import CodeCorps.MapUtils, only: [rename: 3, keys_to_string: 1]

def params_from_stripe(%{} = stripe_map) do
stripe_map |> rename("id", "id_from_stripe")
def to_params(%Stripe.Customer{} = customer) do
customer
|> Map.from_struct
|> Map.take([:created, :currency, :delinquent, :id, :email])
|> rename(:id, :id_from_stripe)
|> keys_to_string
end

@non_stripe_attribute_keys ["user_id"]

def add_non_stripe_attributes(%{} = params, %{} = attributes) do
attributes
|> get_non_stripe_attributes
|> add_to(params)
end

defp get_non_stripe_attributes(%{} = attributes) do
attributes
|> Map.take(@non_stripe_attribute_keys)
end

defp add_to(%{} = attributes, %{} = params) do
params
|> Map.merge(attributes)
end
end
24 changes: 24 additions & 0 deletions lib/code_corps/stripe/in_memory.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule CodeCorps.Stripe.InMemory do
defmodule Customer do
def create(map) do
{:ok, do_create(map)}
end

defp do_create(_) do
{:ok, created} = DateTime.from_unix(1479472835)

%Stripe.Customer{
id: "cus_9aMOFmqy1esIRE",
account_balance: 0,
created: created,
currency: "usd",
default_source: nil,
delinquent: false,
description: nil,
email: "mail@test.com",
livemode: false,
metadata: %{}
}
end
end
end
7 changes: 7 additions & 0 deletions lib/code_corps/stripe/stripe_service.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule CodeCorps.StripeService do
@stripe Application.get_env(:code_corps, :stripe)

def create_customer(map) do
@stripe.Customer.create(map)
end
end
7 changes: 5 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ defmodule CodeCorps.Mixfile do
:scrivener_ecto,
:segment,
:sentry,
:stripity_stripe
:stripity_stripe,
:timex_ecto
]
]
end
Expand Down Expand Up @@ -84,7 +85,9 @@ defmodule CodeCorps.Mixfile do
{:scrivener_ecto, "~> 1.0"}, # DB query pagination
{:segment, github: "stueccles/analytics-elixir"}, # Segment analytics
{:sentry, "~> 1.0"}, # Sentry error tracking
{:stripity_stripe, "~> 2.0.0-alpha.1"} # Stripe
{:stripity_stripe, "~> 2.0.0-alpha.2"}, # Stripe
{:timex, "~> 3.0"},
{:timex_ecto, "~> 3.0"}
]
end

Expand Down
6 changes: 5 additions & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"canada": {:hex, :canada, "1.0.1", "da96d0ff101a0c2a6cc7b07d92b8884ff6508f058781d3679999416feacf41c5", [:mix], []},
"canary": {:hex, :canary, "1.0.0", "55e130bd015001d2fcdc16be23994c6bb711205a625f340d7a74f1c29825d4ba", [:mix], [{:canada, "~> 1.0.0", [hex: :canada, optional: false]}, {:ecto, ">= 1.1.0", [hex: :ecto, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]},
"certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []},
"combine": {:hex, :combine, "0.9.3", "192e609b48b3f2210494e26f85db1712657be1a8f15795656710317ea43fc449", [:mix], []},
"comeonin": {:hex, :comeonin, "2.6.0", "74c288338b33205f9ce97e2117bb9a2aaab103a1811d243443d76fdb62f904ac", [:make, :make, :mix], []},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []},
"corsica": {:hex, :corsica, "0.5.0", "eb5b2fccc5bc4f31b8e2b77dd15f5f302aca5d63286c953e8e916f806056d50c", [:mix], [{:cowboy, ">= 1.0.0", [hex: :cowboy, optional: false]}, {:plug, ">= 0.9.0", [hex: :plug, optional: false]}]},
Expand Down Expand Up @@ -52,5 +53,8 @@
"segment": {:git, "https://github.com/stueccles/analytics-elixir.git", "8fe520c16a8a9290d55c849bf4d67420396e1cdd", []},
"sentry": {:hex, :sentry, "1.1.0", "7a035a02d24578e26c55fbea36f52d8705925ee7d5ce0ca766245ca78c91226f", [:mix], [{:hackney, "~> 1.6.1", [hex: :hackney, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}, {:uuid, "~> 1.0", [hex: :uuid, optional: false]}]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []},
"stripity_stripe": {:hex, :stripity_stripe, "2.0.0-alpha.1", "d86faf49639cf8963c06fb92ce3f010c52f3710ef902a986e6a9391ab48ec27e", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]},
"stripity_stripe": {:hex, :stripity_stripe, "2.0.0-alpha.2", "dd40e85bc0ca375c442e71a875855d9106106bbbf1508604126d3c0aecce1305", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]},
"timex": {:hex, :timex, "3.1.5", "413d6d8d6f0162a5d47080cb8ca520d790184ac43e097c95191c7563bf25b428", [:mix], [{:combine, "~> 0.7", [hex: :combine, optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, optional: false]}]},
"timex_ecto": {:hex, :timex_ecto, "3.0.5", "3ec6c25e10d2c0020958e5df64d2b5e690e441faa2c2259da8bc6bd3d7f39256", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:timex, "~> 3.0", [hex: :timex, optional: false]}]},
"tzdata": {:hex, :tzdata, "0.5.9", "575be217b039057a47e133b72838cbe104fb5329b19906ea4e66857001c37edb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, optional: false]}]},
"uuid": {:hex, :uuid, "1.1.5", "96cb36d86ee82f912efea4d50464a5df606bf3f1163d6bdbb302d98474969369", [:mix], []}}
3 changes: 0 additions & 3 deletions test/controllers/comment_controller_test.exs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
defmodule CodeCorps.CommentControllerTest do
use CodeCorps.ApiCase, resource_name: :comment

alias CodeCorps.Comment
alias CodeCorps.Repo

@valid_attrs %{markdown: "I love elixir!"}
@invalid_attrs %{markdown: ""}

Expand Down
2 changes: 0 additions & 2 deletions test/controllers/preview_controller_test.exs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
defmodule CodeCorps.PreviewControllerTest do
use CodeCorps.ApiCase, resource_name: :preview

alias CodeCorps.Preview

@valid_attrs %{markdown: "A **strong** element"}

describe "create" do
Expand Down
48 changes: 48 additions & 0 deletions test/controllers/stripe_customer_controller_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
defmodule CodeCorps.StripeCustomerControllerTest do
use CodeCorps.ApiCase, resource_name: :stripe_customer

describe "show" do
@tag :authenticated
test "shows chosen resource when user is authenticated and authorized", %{conn: conn, current_user: current_user} do
stripe_customer = insert(:stripe_customer, user: current_user)

conn
|> request_show(stripe_customer)
|> json_response(200)
|> Map.get("data")
|> assert_result_id(stripe_customer.id)
end

test "renders 401 when unauthenticated", %{conn: conn} do
stripe_customer = insert(:stripe_customer)
assert conn |> request_show(stripe_customer) |> json_response(401)
end

@tag :authenticated
test "renders 403 when not authorized", %{conn: conn} do
stripe_customer = insert(:stripe_customer)
assert conn |> request_show(stripe_customer) |> json_response(403)
end

@tag :authenticated
test "renders 404 when id is nonexistent", %{conn: conn} do
assert conn |> request_show(:not_found) |> json_response(404)
end
end

describe "create" do
@tag :authenticated
test "creates and renders resource user is authenticated and authorized", %{conn: conn, current_user: current_user} do
assert conn |> request_create(%{user: current_user}) |> json_response(201)
end

test "does not create resource and renders 401 when unauthenticated", %{conn: conn} do
assert conn |> request_create |> json_response(401)
end

@tag :authenticated
test "does not create resource and renders 403 when not authorized", %{conn: conn} do
assert conn |> request_create |> json_response(403)
end
end
end
6 changes: 3 additions & 3 deletions test/controllers/task_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ defmodule CodeCorps.TaskControllerTest do
test "lists all entries newest first", %{conn: conn} do
# Has to be done manually. Inserting as a list is too quick.
# Field lacks the resolution to differentiate.
task_1 = insert(:task, inserted_at: Ecto.DateTime.cast!("2000-01-15T00:00:10"))
task_2 = insert(:task, inserted_at: Ecto.DateTime.cast!("2000-01-15T00:00:20"))
task_3 = insert(:task, inserted_at: Ecto.DateTime.cast!("2000-01-15T00:00:30"))
task_1 = insert(:task, inserted_at: Timex.to_date({2000, 1, 1}))
task_2 = insert(:task, inserted_at: Timex.to_date({2000, 1, 2}))
task_3 = insert(:task, inserted_at: Timex.to_date({2000, 1, 3}))

path = conn |> task_path(:index)
json = conn |> get(path) |> json_response(200)
Expand Down
1 change: 0 additions & 1 deletion test/controllers/user_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ defmodule CodeCorps.UserControllerTest do

alias CodeCorps.User
alias CodeCorps.Repo
alias CodeCorps.SluggedRoute

@valid_attrs %{
email: "test@user.com",
Expand Down
42 changes: 37 additions & 5 deletions test/lib/code_corps/stripe/adapters/stripe_customer_test.exs
Original file line number Diff line number Diff line change
@@ -1,14 +1,46 @@
defmodule CodeCorps.Stripe.Adapters.StripeCustomerTest do
use ExUnit.Case, async: true

import CodeCorps.Stripe.Adapters.StripeCustomer, only: [params_from_stripe: 1]
import CodeCorps.Stripe.Adapters.StripeCustomer, only: [to_params: 1, add_non_stripe_attributes: 2]

@stripe_map %{"id" => "str_123", "foo" => "bar"}
@local_map %{"id_from_stripe" => "str_123", "foo" => "bar"}
{:ok, timestamp} = DateTime.from_unix(1479472835)

describe "params_from_stripe/1" do
@stripe_customer %Stripe.Customer{
id: "cus_9aMOFmqy1esIRE",
account_balance: 0,
created: timestamp,
currency: "usd",
default_source: nil,
delinquent: false,
description: nil,
email: "mail@stripe.com",
livemode: false,
metadata: %{}
}

@local_map %{
"id_from_stripe" => "cus_9aMOFmqy1esIRE",
"created" => timestamp,
"currency" => "usd",
"delinquent" => false,
"email" => "mail@stripe.com"
}

describe "to_params/1" do
test "converts from stripe map to local properly" do
assert @stripe_map |> params_from_stripe == @local_map
assert @stripe_customer |> to_params == @local_map
end
end

describe "add_non_stripe_attributes/2" do
test "adds 'user_id' from second hash into first hash" do
params = %{"id_from_stripe" => "cus_123"}
attributes = %{"user_id" =>123, "foo" => "bar"}

actual_output = params |> add_non_stripe_attributes(attributes)
expected_output = %{"id_from_stripe" => "cus_123", "user_id" => 123}

assert actual_output == expected_output
end
end
end
28 changes: 28 additions & 0 deletions test/policies/stripe_customer_policy_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule CodeCorps.StripeCustomerPolicyTest do
use CodeCorps.PolicyCase

import CodeCorps.StripeCustomerPolicy, only: [show?: 2]

describe "show?" do
test "returns true when user is an admin" do
user = build(:user, admin: true)
stripe_customer = build(:stripe_customer)

assert show?(user, stripe_customer)
end

test "returns true when user is viewing their own information" do
user = insert(:user)
stripe_customer = insert(:stripe_customer, user: user)

assert show?(user, stripe_customer)
end

test "returns false when user id is not the StripeCustomer's user_id" do
[user, another_user] = insert_pair(:user)
stripe_customer = insert(:stripe_customer, user: user)

refute show?(another_user, stripe_customer)
end
end
end
10 changes: 9 additions & 1 deletion test/support/factories.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ defmodule CodeCorps.Factories do
# with Ecto
use ExMachina.Ecto, repo: CodeCorps.Repo


def category_factory do
%CodeCorps.Category{
name: sequence(:name, &"Category #{&1}"),
Expand Down Expand Up @@ -106,6 +105,15 @@ defmodule CodeCorps.Factories do
}
end

def stripe_customer_factory do
%CodeCorps.StripeCustomer{
created: Timex.now,
email: sequence(:email, &"email_#{&1}@mail.com"),
id_from_stripe: sequence(:id_from_stripe, &"stripe_id_#{&1}"),
user: build(:user)
}
end

def stripe_plan_factory do
%CodeCorps.StripePlan{
id_from_stripe: sequence(:id_from_stripe, &"stripe_id_#{&1}"),
Expand Down

0 comments on commit f06c57e

Please sign in to comment.