Fix tests and credo

This commit is contained in:
Alex
2024-07-11 16:33:02 +02:00
parent 9f3f4284ed
commit 3817539c5c
20 changed files with 263 additions and 443 deletions

View File

@@ -97,7 +97,7 @@ defmodule Claper.Events do
event = Repo.get_by!(Event, uuid: id)
is_leader =
Claper.Events.is_leaded_by(current_user.email, event) || event.user_id == current_user.id
Claper.Events.leaded_by?(current_user.email, event) || event.user_id == current_user.id
if is_leader do
event |> Repo.preload(preload)
@@ -177,12 +177,12 @@ defmodule Claper.Events do
## Examples
iex> is_leaded_by("email@example.com", 123)
iex> leaded_by?("email@example.com", 123)
true
"""
def is_leaded_by(email, event) do
def leaded_by?(email, event) do
from(a in ActivityLeader,
join: u in Claper.Accounts.User,
on: u.email == a.email,

View File

@@ -7,11 +7,24 @@ defmodule ClaperWeb.Lti.GradeController do
def create(conn, _params) do
resource_id = conn |> get_session(:resource_id) |> String.to_integer()
user_id = conn |> get_session(:user_id)
timestamp = get_timestamp()
case fetch_access_token() do
{:ok, access_token} ->
handle_line_item(conn, resource_id, user_id, timestamp, access_token)
{:error, msg} ->
conn |> send_resp(500, msg)
end
end
defp get_timestamp do
{:ok, dt} = DateTime.now("Etc/UTC")
timestamp = DateTime.to_iso8601(dt)
DateTime.to_iso8601(dt)
end
case Lti13.Tool.Services.AccessToken.fetch_access_token(
defp fetch_access_token do
Lti13.Tool.Services.AccessToken.fetch_access_token(
%{
auth_token_url: "http://localhost.charlesproxy.com/mod/lti/token.php",
client_id: "NQQ8egz8Kj1s1qw",
@@ -22,8 +35,10 @@ defmodule ClaperWeb.Lti.GradeController do
"https://purl.imsglobal.org/spec/lti-ags/scope/score"
],
"http://localhost:4000"
) do
{:ok, access_token} ->
)
end
defp handle_line_item(conn, resource_id, user_id, timestamp, access_token) do
case AGS.fetch_or_create_line_item(
"http://localhost.charlesproxy.com/mod/lti/services.php/2/lineitems?type_id=2",
resource_id,
@@ -32,6 +47,12 @@ defmodule ClaperWeb.Lti.GradeController do
access_token
) do
{:ok, line_item} ->
post_score(line_item, user_id, timestamp, access_token)
conn |> send_resp(200, "")
end
end
defp post_score(line_item, user_id, timestamp, access_token) do
AGS.post_score(
%Score{
scoreGiven: 90.0,
@@ -45,12 +66,5 @@ defmodule ClaperWeb.Lti.GradeController do
line_item,
access_token
)
conn |> send_resp(200, "")
end
{:error, msg} ->
conn |> send_resp(500, msg)
end
end
end

View File

@@ -178,8 +178,6 @@ defmodule ClaperWeb.EventLive.EventCardComponent do
<div>
<%= if not @is_leader do %>
<a
data-phx-link="patch"
data-phx-link-state="push"
href={~p"/events/#{@event.uuid}/edit"}
class="flex w-full lg:w-auto rounded-md tracking-wide focus:outline-none focus:shadow-outline text-primary-500 text-sm items-center"
>
@@ -199,8 +197,6 @@ defmodule ClaperWeb.EventLive.EventCardComponent do
<div>
<%= if not @is_leader do %>
<a
data-phx-link="patch"
data-phx-link-state="push"
href={~p"/events/#{@event.uuid}/edit"}
class="flex w-full lg:w-auto rounded-md tracking-wide focus:outline-none focus:shadow-outline text-primary-500 text-sm items-center"
>

View File

@@ -18,7 +18,7 @@ defmodule ClaperWeb.EventLive.Manage do
presentation_file: [:polls, :presentation_state]
])
if is_nil(event) || not is_leader(socket, event) do
if is_nil(event) || not leader?(socket, event) do
{:ok,
socket
|> put_flash(:error, gettext("Event doesn't exist"))
@@ -77,11 +77,11 @@ defmodule ClaperWeb.EventLive.Manage do
end
end
defp is_leader(%{assigns: %{current_user: current_user}} = _socket, event) do
Claper.Events.is_leaded_by(current_user.email, event) || event.user.id == current_user.id
defp leader?(%{assigns: %{current_user: current_user}} = _socket, event) do
Claper.Events.leaded_by?(current_user.email, event) || event.user.id == current_user.id
end
defp is_leader(_socket, _event), do: false
defp leader?(_socket, _event), do: false
@impl true
def handle_info(%{event: "presence_diff"}, %{assigns: %{event: event}} = socket) do

View File

@@ -22,19 +22,19 @@ defmodule ClaperWeb.EventLive.PostComponent do
<img src="/images/icons/ellipsis-horizontal-white.svg" class="h-5" />
</button>
<%= if @post.name || is_a_leader(@post, @event, @leaders) || is_pinned(@post) do %>
<%= if @post.name || leader?(@post, @event, @leaders) || pinned?(@post) do %>
<div class="inline-flex items-center">
<%= if @post.name do %>
<p class="text-white text-xs font-semibold mb-2 mr-2"><%= @post.name %></p>
<% end %>
<%= if is_a_leader(@post, @event, @leaders) do %>
<%= if leader?(@post, @event, @leaders) do %>
<div class="inline-flex items-center space-x-1 justify-center px-3 py-0.5 rounded-full text-xs font-medium bg-supporting-yellow-100 text-supporting-yellow-800 mb-2">
<img src="/images/icons/star.svg" class="h-3" />
<span><%= gettext("Host") %></span>
</div>
<% end %>
<%= if is_pinned(@post) do %>
<%= if pinned?(@post) do %>
<div class="inline-flex items-center space-x-1 justify-center px-3 py-0.5 rounded-full text-xs font-medium bg-supporting-yellow-100 text-supporting-yellow-800 mb-2 ml-1">
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -101,12 +101,12 @@ defmodule ClaperWeb.EventLive.PostComponent do
</div>
<% else %>
<div class="px-4 pt-3 pb-8 rounded-b-lg rounded-tr-lg bg-white text-black relative z-0 break-all">
<%= if @post.name || is_a_leader(@post, @event, @leaders) do %>
<%= if @post.name || leader?(@post, @event, @leaders) do %>
<div class="inline-flex items-center">
<%= if @post.name do %>
<p class="text-black text-xs font-semibold mb-2 mr-2"><%= @post.name %></p>
<% end %>
<%= if is_a_leader(@post, @event, @leaders) do %>
<%= if leader?(@post, @event, @leaders) do %>
<div class="inline-flex items-center space-x-1 justify-center px-3 py-0.5 rounded-full text-xs font-medium bg-supporting-yellow-100 text-supporting-yellow-800 mb-2">
<img src="/images/icons/star.svg" class="h-3" />
<span><%= gettext("Host") %></span>
@@ -150,7 +150,7 @@ defmodule ClaperWeb.EventLive.PostComponent do
</div>
<% end %>
<%= if is_pinned(@post) do %>
<%= if pinned?(@post) do %>
<div class="inline-flex items-center space-x-1 justify-center px-3 py-0.5 rounded-full text-xs font-medium bg-supporting-yellow-100 text-supporting-yellow-800 mb-2 ml-1">
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -265,7 +265,7 @@ defmodule ClaperWeb.EventLive.PostComponent do
"""
end
defp is_a_leader(post, event, leaders) do
defp leader?(post, event, leaders) do
!is_nil(post.user_id) &&
(post.user_id == event.user_id ||
Enum.any?(leaders, fn leader ->
@@ -273,7 +273,7 @@ defmodule ClaperWeb.EventLive.PostComponent do
end))
end
defp is_pinned(post) do
defp pinned?(post) do
post.pinned == true
end
end

View File

@@ -18,7 +18,7 @@ defmodule ClaperWeb.EventLive.Presenter do
presentation_file: [:polls, :presentation_state]
])
if is_nil(event) || not is_leader(socket, event) do
if is_nil(event) || not leader?(socket, event) do
{:ok,
socket
|> put_flash(:error, gettext("Event doesn't exist"))
@@ -75,11 +75,11 @@ defmodule ClaperWeb.EventLive.Presenter do
end)
end
defp is_leader(%{assigns: %{current_user: current_user}} = _socket, event) do
Claper.Events.is_leaded_by(current_user.email, event) || event.user.id == current_user.id
defp leader?(%{assigns: %{current_user: current_user}} = _socket, event) do
Claper.Events.leaded_by?(current_user.email, event) || event.user.id == current_user.id
end
defp is_leader(_socket, _event), do: false
defp leader?(_socket, _event), do: false
@impl true
def handle_info(%{event: "presence_diff"}, %{assigns: %{event: event}} = socket) do

View File

@@ -108,7 +108,7 @@ defmodule ClaperWeb.EventLive.Show do
defp check_leader(%{assigns: %{current_user: current_user} = _assigns} = socket, event)
when is_map(current_user) do
is_leader =
current_user.id == event.user_id || Claper.Events.is_leaded_by(current_user.email, event)
current_user.id == event.user_id || Claper.Events.leaded_by?(current_user.email, event)
socket |> assign(:is_leader, is_leader)
end

View File

@@ -6,8 +6,7 @@ defmodule Lti13.Jwks.Utils.KeyGenerator do
Create a random passphrase of size given (defaults to 256)
"""
def passphrase(len \\ 256) do
Enum.map(1..len, fn _i -> Enum.random(@chars) end)
|> Enum.join("")
Enum.map_join(1..len, "", fn _i -> Enum.random(@chars) end)
end
@doc """
@@ -21,7 +20,7 @@ defmodule Lti13.Jwks.Utils.KeyGenerator do
def generate_key_pair do
key_id = passphrase()
{:ok, rsa_priv_key} = generate_key(:rsa, 4096, 65537)
{:ok, rsa_priv_key} = generate_key(:rsa, 4096, 65_537)
{:ok, public_key} = public_key_from_private_key(rsa_priv_key)
{:ok, private_key_pem} = pem_entry_encode(rsa_priv_key, :RSAPrivateKey)
{:ok, public_key_pem} = pem_entry_encode(public_key, :RSAPublicKey)

View File

@@ -93,21 +93,21 @@ defmodule Lti13.Jwks.Validator do
end
end
def validate_user(%{
def validate_user(
%{
"sub" => sub,
"name" => name,
"email" => email,
"iss" => issuer,
"aud" => client_id,
"https://purl.imsglobal.org/spec/lti/claim/roles" => roles
}) do
},
registration
) do
case Lti13.Users.get_or_create_user(%{
sub: sub,
name: name,
email: email,
roles: roles,
issuer: issuer,
client_id: client_id
registration_id: registration.id
}) do
{:error, _} ->
{:error, %{reason: :invalid_user, msg: "Invalid user"}}
@@ -146,19 +146,29 @@ defmodule Lti13.Jwks.Validator do
def fetch_public_key(key_set_url, kid) do
public_key_set =
case Req.get(key_set_url) do
{:ok, %Req.Response{status: 200, body: body}} ->
body
error ->
error
{:ok, %Req.Response{status: 200, body: body}} -> body
error -> error
end
if container?(public_key_set) do
case Enum.find(public_key_set["keys"], fn key -> container?(key) && key["kid"] == kid end) do
nil ->
return_key_not_found(kid)
find_and_process_key(public_key_set, kid)
end
public_key_json ->
defp find_and_process_key(public_key_set, kid) do
if container?(public_key_set) do
find_key_in_set(public_key_set, kid)
else
return_key_not_found(kid)
end
end
defp find_key_in_set(public_key_set, kid) do
case Enum.find(public_key_set["keys"], fn key -> container?(key) && key["kid"] == kid end) do
nil -> return_key_not_found(kid)
public_key_json -> process_public_key(public_key_json)
end
end
defp process_public_key(public_key_json) do
public_key =
public_key_json
|> convert_map_to_base64url()
@@ -166,10 +176,6 @@ defmodule Lti13.Jwks.Validator do
{:ok, public_key}
end
else
return_key_not_found(kid)
end
end
defp container?(container) do
Keyword.keyword?(container) || is_map(container) || is_struct(container)

View File

@@ -20,7 +20,7 @@ defmodule Lti13.Nonces do
end
# 86400 seconds = 24 hours
def delete_expired_nonces(nonce_ttl_sec \\ 86_4000) do
def delete_expired_nonces(nonce_ttl_sec \\ 86_400) do
nonce_expiry = DateTime.utc_now() |> DateTime.add(-nonce_ttl_sec, :second)
Repo.delete_all(from(n in Nonce, where: n.inserted_at < ^nonce_expiry))
end

View File

@@ -14,7 +14,10 @@ defmodule Lti13.Resources do
where: r.resource_id == ^resource_id and r.registration_id == ^registration_id
)
|> Repo.one()
|> Repo.preload(:event)
|> case do
nil -> nil
resource -> resource |> Repo.preload(:event)
end
end
@doc """

View File

@@ -25,7 +25,7 @@ defmodule Lti13.Tool.LaunchValidation do
{:ok} <- validate_timestamps(jwt_body),
{:ok} <- validate_deployment(registration, jwt_body),
{:ok} <- validate_message(jwt_body),
{:ok, lti_user} <- validate_user(jwt_body),
{:ok, lti_user} <- validate_user(jwt_body, registration),
{:ok} <- validate_nonce(lti_user, jwt_body, "validate_launch"),
{:ok, is_instructor} <- validate_role(jwt_body),
{:ok, resource} <- validate_resource(jwt_body, lti_user, registration, is_instructor),
@@ -46,24 +46,21 @@ defmodule Lti13.Tool.LaunchValidation do
"State from session is missing. Make sure cookies are enabled and configured correctly"
}}
session_state ->
case params["state"] do
nil ->
{:error, %{reason: :invalid_oidc_state, msg: "State from OIDC request is missing"}}
_ ->
compare_oidc_states(params["state"], session_state)
end
end
request_state ->
if request_state == session_state do
{:ok}
else
defp compare_oidc_states(nil, _),
do: {:error, %{reason: :invalid_oidc_state, msg: "State from OIDC request is missing"}}
defp compare_oidc_states(request_state, session_state) when request_state == session_state,
do: {:ok}
defp compare_oidc_states(_, _),
do:
{:error,
%{
reason: :invalid_oidc_state,
msg: "State from OIDC request does not match session"
}}
end
end
end
end
%{reason: :invalid_oidc_state, msg: "State from OIDC request does not match session"}}
defp validate_registration(params) do
with {:ok, issuer, client_id} <- peek_issuer_client_id(params) do
@@ -102,34 +99,37 @@ defmodule Lti13.Tool.LaunchValidation do
is_instructor
) do
case Lti13.Resources.get_resource_by_id_and_registration(resource_id, registration.id) do
nil ->
case is_instructor do
true ->
nil -> handle_missing_resource(title, resource_id, lti_user, is_instructor)
resource -> handle_existing_resource(resource, lti_user, is_instructor)
end
end
defp handle_missing_resource(title, resource_id, lti_user, true) do
case Lti13.Resources.create_resource_with_event(%{
title: title,
resource_id: resource_id,
lti_user: lti_user
}) do
{:ok, resource} ->
{:ok, resource} -> {:ok, resource}
{:error, _} -> {:error, %{reason: :invalid_resource, msg: "Failed to create resource"}}
end
end
defp handle_missing_resource(_, _, _, false),
do: {:error, %{reason: :invalid_resource, msg: "User is not authorized to create resource"}}
defp handle_existing_resource(resource, lti_user, true) do
maybe_create_activity_leader(resource, lti_user)
{:ok, resource}
{:error, _} ->
{:error, %{reason: :invalid_resource, msg: "Failed to create resource"}}
end
false ->
{:error,
%{reason: :invalid_resource, msg: "User is not authorized to create resource"}}
end
defp handle_existing_resource(resource, _, false), do: {:ok, resource}
resource ->
case is_instructor do
true ->
with activity_leaders <-
Claper.Events.get_activity_leaders_for_event(resource.event_id),
activity_leaders_emails <- Enum.map(activity_leaders, fn al -> al.email end) do
if lti_user.email not in activity_leaders_emails &&
resource.event.user_id != lti_user.user_id do
defp maybe_create_activity_leader(resource, lti_user) do
activity_leaders = Claper.Events.get_activity_leaders_for_event(resource.event_id)
activity_leaders_emails = Enum.map(activity_leaders, fn al -> al.email end)
if lti_user.email not in activity_leaders_emails && resource.event.user_id != lti_user.user_id do
Claper.Events.create_activity_leader(%{
email: lti_user.email,
user_id: lti_user.id,
@@ -138,14 +138,6 @@ defmodule Lti13.Tool.LaunchValidation do
end
end
{:ok, resource}
false ->
{:ok, resource}
end
end
end
defp validate_role(jwt) do
roles = jwt["https://purl.imsglobal.org/spec/lti/claim/roles"]
is_instructor = Enum.any?(roles, fn role -> role in @authorized_to_create_event_roles end)
@@ -187,15 +179,12 @@ defmodule Lti13.Tool.LaunchValidation do
{:error, %{reason: :invalid_message_type, msg: "Missing message type"}}
message_type ->
# no more than one message validator should apply for a given mesage,
# so use the first validator we find that applies
validation_result =
case Enum.find(@message_validators, fn mv -> mv.can_validate(jwt_body) end) do
nil -> nil
validator -> validator.validate(jwt_body)
validate_message_type(jwt_body, message_type)
end
end
case validation_result do
defp validate_message_type(jwt_body, message_type) do
case apply_message_validator(jwt_body) do
nil ->
{:error,
%{
@@ -214,5 +203,11 @@ defmodule Lti13.Tool.LaunchValidation do
{:ok}
end
end
defp apply_message_validator(jwt_body) do
case Enum.find(@message_validators, fn mv -> mv.can_validate(jwt_body) end) do
nil -> nil
validator -> validator.validate(jwt_body)
end
end
end

View File

@@ -10,37 +10,31 @@ defmodule Lti13.Users do
|> Repo.insert()
end
def get_user_by_sub(sub) do
Repo.get_by(User, sub: sub)
def get_user_by_sub_and_registration_id(sub, registration_id) do
Repo.get_by(User, sub: sub, registration_id: registration_id)
end
def get_or_create_user(%{sub: sub, email: email, issuer: issuer, client_id: client_id} = attrs) do
case get_user_by_sub(sub) do
nil ->
case Claper.Accounts.get_user_by_email_or_create(email) do
{:ok, claper_user} ->
%{id: registration_id} =
Lti13.Registrations.get_registration_by_issuer_client_id(issuer, client_id)
def get_or_create_user(
%{
sub: sub,
email: email,
registration_id: registration_id
} = attrs
) do
case get_user_by_sub_and_registration_id(sub, registration_id) do
nil -> create_new_user(attrs, email, registration_id)
%User{} = user -> {:ok, user |> Repo.preload(:user)}
end
end
updated_attrs =
attrs
|> Map.put(:user_id, claper_user.id)
|> Map.put(:registration_id, registration_id)
case create_user(updated_attrs) do
{:ok, user} ->
{:ok, user |> Repo.preload(:user)}
{:error, _} ->
{:error, %{reason: :invalid_user, msg: "Invalid user"}}
end
{:error, _} ->
{:error, %{reason: :invalid_user, msg: "Invalid Claper user"}}
end
%User{} = user ->
defp create_new_user(attrs, email, registration_id) do
with {:ok, claper_user} <- Claper.Accounts.get_user_by_email_or_create(email),
updated_attrs <-
Map.merge(attrs, %{user_id: claper_user.id, registration_id: registration_id}),
{:ok, user} <- create_user(updated_attrs) do
{:ok, user |> Repo.preload(:user)}
else
_ -> {:error, %{reason: :invalid_user, msg: "Invalid user"}}
end
end
end

View File

@@ -39,7 +39,7 @@ defmodule Claper.Repo.Migrations.AddLtiTables do
timestamps()
end
create unique_index(:lti_13_users, :sub)
create unique_index(:lti_13_users, [:sub, :registration_id])
create table(:lti_13_nonces) do
add :value, :string

View File

@@ -14,21 +14,28 @@ defmodule Claper.EmbedsTest do
presentation_file = presentation_file_fixture()
embed = embed_fixture(%{presentation_file_id: presentation_file.id})
assert Embeds.list_embeds(presentation_file.id) == [embed]
embeds = Embeds.list_embeds(presentation_file.id)
assert [%Embed{} | _] = embeds
assert length(embeds) == 1
assert hd(embeds).id == embed.id
end
test "list_embeds_at_position/2 returns all embeds from a presentation at a given position" do
presentation_file = presentation_file_fixture()
embed = embed_fixture(%{presentation_file_id: presentation_file.id, position: 5})
assert Embeds.list_embeds_at_position(presentation_file.id, 5) == [embed]
embeds = Embeds.list_embeds_at_position(presentation_file.id, 5)
assert [%Embed{} | _] = embeds
assert length(embeds) == 1
assert hd(embeds).id == embed.id
assert hd(embeds).position == 5
end
test "get_embed!/1 returns the embed with given id" do
presentation_file = presentation_file_fixture()
embed = embed_fixture(%{presentation_file_id: presentation_file.id})
assert Embeds.get_embed!(embed.id) == embed
assert Embeds.get_embed!(embed.id, presentation_file: [:event]) == embed
end
test "create_embed/1 with valid data creates a embed" do
@@ -79,7 +86,7 @@ defmodule Claper.EmbedsTest do
assert {:error, %Ecto.Changeset{}} =
Embeds.update_embed(presentation_file.event_id, embed, @invalid_attrs)
assert embed == Embeds.get_embed!(embed.id)
assert embed == Embeds.get_embed!(embed.id, presentation_file: [:event])
end
test "delete_embed/2 deletes the embed" do

View File

@@ -14,21 +14,33 @@ defmodule Claper.FormsTest do
presentation_file = presentation_file_fixture()
form = form_fixture(%{presentation_file_id: presentation_file.id})
assert Forms.list_forms(presentation_file.id) == [form]
forms = Forms.list_forms(presentation_file.id)
assert [%Form{} | _] = forms
assert length(forms) == 1
assert hd(forms).id == form.id
end
test "list_forms_at_position/2 returns all forms from a presentation at a given position" do
presentation_file = presentation_file_fixture()
form = form_fixture(%{presentation_file_id: presentation_file.id, position: 5})
assert Forms.list_forms_at_position(presentation_file.id, 5) == [form]
forms = Forms.list_forms_at_position(presentation_file.id, 5)
assert [%Form{} | _] = forms
assert length(forms) == 1
assert hd(forms).id == form.id
assert hd(forms).position == 5
end
test "get_form!/1 returns the form with given id" do
presentation_file = presentation_file_fixture()
form = form_fixture(%{presentation_file_id: presentation_file.id})
assert Forms.get_form!(form.id) == form
fetched_form = Forms.get_form!(form.id)
assert fetched_form.id == form.id
assert fetched_form.position == form.position
assert fetched_form.title == form.title
assert fetched_form.fields == form.fields
end
test "create_form/1 with valid data creates a form" do
@@ -70,7 +82,9 @@ defmodule Claper.FormsTest do
assert {:error, %Ecto.Changeset{}} =
Forms.update_form(presentation_file.event_id, form, @invalid_attrs)
assert form == Forms.get_form!(form.id)
fetched_form = Forms.get_form!(form.id)
assert fetched_form.title == form.title
end
test "delete_form/2 deletes the form" do

View File

@@ -14,14 +14,21 @@ defmodule Claper.PollsTest do
presentation_file = presentation_file_fixture()
poll = poll_fixture(%{presentation_file_id: presentation_file.id})
assert Polls.list_polls(presentation_file.id) == [poll]
polls = Polls.list_polls(presentation_file.id)
assert [%Poll{} | _] = polls
assert length(polls) == 1
assert hd(polls).id == poll.id
end
test "list_polls_at_position/2 returns all polls from a presentation at a given position" do
presentation_file = presentation_file_fixture()
poll = poll_fixture(%{presentation_file_id: presentation_file.id, position: 5})
assert Polls.list_polls_at_position(presentation_file.id, 5) == [poll]
polls = Polls.list_polls_at_position(presentation_file.id, 5)
assert [%Poll{} | _] = polls
assert length(polls) == 1
assert hd(polls).id == poll.id
assert hd(polls).position == 5
end
test "get_poll!/1 returns the poll with given id" do
@@ -31,7 +38,12 @@ defmodule Claper.PollsTest do
poll_fixture(%{presentation_file_id: presentation_file.id})
|> Claper.Polls.set_percentages()
assert Polls.get_poll!(poll.id) == poll
fetched_poll = Polls.get_poll!(poll.id)
assert fetched_poll.id == poll.id
assert fetched_poll.position == poll.position
assert fetched_poll.poll_opts == poll.poll_opts
assert fetched_poll.title == poll.title
end
test "create_poll/1 with valid data creates a poll" do
@@ -73,7 +85,12 @@ defmodule Claper.PollsTest do
assert {:error, %Ecto.Changeset{}} =
Polls.update_poll(presentation_file.event_id, poll, @invalid_attrs)
assert poll |> Claper.Polls.set_percentages() == Polls.get_poll!(poll.id)
fetched_poll = Polls.get_poll!(poll.id)
poll = poll |> Claper.Polls.set_percentages()
assert fetched_poll.poll_opts == poll.poll_opts
assert fetched_poll.poll_votes == poll.poll_votes
assert fetched_poll.title == poll.title
end
test "delete_poll/2 deletes the poll" do

View File

@@ -23,14 +23,7 @@ defmodule ClaperWeb.EventLiveTest do
end
test "updates event in listing", %{conn: conn, presentation_file: presentation_file} do
{:ok, index_live, _html} = live(conn, ~p"/events")
assert index_live
|> element("#event-#{presentation_file.event.uuid} a", "Edit")
|> render_click() =~
"Edit"
assert_patch(index_live, ~p"/events/#{presentation_file.event.uuid}/edit")
{:ok, index_live, _html} = live(conn, ~p"/events/#{presentation_file.event.uuid}/edit")
{:ok, conn} =
index_live
@@ -43,12 +36,7 @@ defmodule ClaperWeb.EventLiveTest do
end
test "deletes event in listing", %{conn: conn, presentation_file: presentation_file} do
{:ok, index_live, _html} = live(conn, ~p"/events")
assert index_live
|> element("#event-#{presentation_file.event.uuid} a", "Edit")
|> render_click() =~
"Edit"
{:ok, index_live, _html} = live(conn, ~p"/events/#{presentation_file.event.uuid}/edit")
{:ok, conn} =
index_live

View File

@@ -1,212 +0,0 @@
defmodule Lti_1p3.DataProviders.EctoProvider.TestHelpers do
import Lti_1p3.Config
alias Lti_1p3.Test.Lti_1p3_User
alias Lti_1p3.Jwk
alias Lti_1p3.Tool.Registration
alias Lti_1p3.Tool.Deployment
Mox.defmock(Lti_1p3.Test.MockHTTPoison, for: HTTPoison.Base)
def lti_1p3_user(attrs \\ %{}) do
params =
attrs
|> Enum.into(%{
id: 0,
sub: "a6d5c443-1f51-4783-ba1a-7686ffe3b54a",
name: "Ms Jane Marie Doe",
given_name: "Jane",
family_name: "Doe",
middle_name: "Marie",
picture: "https://platform.example.edu/jane.jpg",
email: "jane#{System.unique_integer([:positive])}@platform.example.edu",
locale: "en-US",
platform_roles:
"http://purl.imsglobal.org/vocab/lis/v2/system/person#User,http://purl.imsglobal.org/vocab/lis/v2/institution/person#Student",
context_roles: "http://purl.imsglobal.org/vocab/lis/v2/membership#Learner"
})
struct!(Lti_1p3_User, params)
end
def jwk_fixture(attrs \\ %{}) do
%{private_key: private_key} = Lti_1p3.KeyGenerator.generate_key_pair()
params =
attrs
|> Enum.into(%{
pem: private_key,
typ: "JWT",
alg: "RS256",
kid: UUID.uuid4(),
active: true
})
{:ok, jwk} = provider!().create_jwk(struct!(Jwk, params))
jwk
end
def registration_fixture(%{tool_jwk_id: tool_jwk_id} = attrs) do
params =
attrs
|> Enum.into(%{
issuer: "https://lti-ri.imsglobal.org",
client_id: "12345",
key_set_url: "some key_set_url",
auth_token_url: "some auth_token_url",
auth_login_url: "some auth_login_url",
auth_server: "some auth_server",
tool_jwk_id: tool_jwk_id
})
{:ok, registration} = provider!().create_registration(struct(Registration, params))
registration
end
def deployment_fixture(
%{deployment_id: deployment_id, registration_id: registration_id} = attrs
) do
params =
attrs
|> Enum.into(%{
deployment_id: deployment_id,
registration_id: registration_id
})
{:ok, deployment} = provider!().create_deployment(struct(Deployment, params))
deployment
end
def generate_id_token(jwk, kid, claims) do
# create a signer
signer =
Joken.Signer.create("RS256", %{"pem" => jwk.pem}, %{
"kid" => kid
})
{:ok, claims} = Joken.generate_claims(%{}, claims)
Joken.generate_and_sign!(%{}, claims, signer)
end
def all_default_claims() do
%{}
|> Map.merge(security_detail_data())
|> Map.merge(user_detail_data())
|> Map.merge(claims_data())
|> Map.merge(example_extension_data())
end
def security_detail_data() do
%{
"iss" => "https://lti-ri.imsglobal.org",
"sub" => "a73d59affc5b2c4cd493",
"aud" => "12345",
"exp" => Timex.now() |> Timex.add(Timex.Duration.from_minutes(5)) |> Timex.to_unix(),
"iat" => Timex.now() |> Timex.to_unix(),
"nonce" => UUID.uuid4()
}
end
def user_detail_data() do
%{
"given_name" => "Chelsea",
"family_name" => "Conroy",
"middle_name" => "Reichel",
"picture" => "http://example.org/Chelsea.jpg",
"email" => "Chelsea.Conroy@example.org",
"name" => "Chelsea Reichel Conroy",
"locale" => "en-US"
}
end
def claims_data() do
%{
"https://purl.imsglobal.org/spec/lti-ags/claim/endpoint" => %{
"lineitems" => "https://lti-ri.imsglobal.org/platforms/1237/contexts/10337/line_items",
"scope" => [
"https://purl.imsglobal.org/spec/lti-ags/scope/lineitem",
"https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly"
]
},
"https://purl.imsglobal.org/spec/lti-ces/claim/caliper-endpoint-service" => %{
"caliper_endpoint_url" => "https://lti-ri.imsglobal.org/platforms/1237/sensors",
"caliper_federated_session_id" => "urn:uuid:7bec5956c5297eacf382",
"scopes" => ["https://purl.imsglobal.org/spec/lti-ces/v1p0/scope/send"]
},
"https://purl.imsglobal.org/spec/lti-nrps/claim/namesroleservice" => %{
"context_memberships_url" =>
"https://lti-ri.imsglobal.org/platforms/1237/contexts/10337/memberships",
"service_versions" => ["2.0"]
},
"https://purl.imsglobal.org/spec/lti/claim/context" => %{
"id" => "10337",
"label" => "My Course",
"title" => "My Course",
"type" => ["Course"]
},
"https://purl.imsglobal.org/spec/lti/claim/custom" => %{
"myCustomValue" => "123"
},
"https://purl.imsglobal.org/spec/lti/claim/deployment_id" => "1",
"https://purl.imsglobal.org/spec/lti/claim/launch_presentation" => %{
"document_target" => "iframe",
"height" => 320,
"return_url" => "https://lti-ri.imsglobal.org/platforms/1237/returns",
"width" => 240
},
"https://purl.imsglobal.org/spec/lti/claim/message_type" => "LtiResourceLinkRequest",
"https://purl.imsglobal.org/spec/lti/claim/resource_link" => %{
"description" => "my course",
"id" => "20052",
"title" => "My Course"
},
"https://purl.imsglobal.org/spec/lti/claim/role_scope_mentor" => ["a62c52c02ba262003f5e"],
"https://purl.imsglobal.org/spec/lti/claim/roles" => [
"http://purl.imsglobal.org/vocab/lis/v2/membership#Learner",
"http://purl.imsglobal.org/vocab/lis/v2/institution/person#Student",
"http://purl.imsglobal.org/vocab/lis/v2/membership#Mentor"
],
"https://purl.imsglobal.org/spec/lti/claim/target_link_uri" =>
"https://lti-ri.imsglobal.org/lti/tools/1193/launches",
"https://purl.imsglobal.org/spec/lti/claim/tool_platform" => %{
"contact_email" => "",
"description" => "",
"guid" => 1237,
"name" => "lti-test",
"product_family_code" => "",
"url" => "",
"version" => "1.0"
},
"https://purl.imsglobal.org/spec/lti/claim/version" => "1.3.0"
}
end
def example_extension_data() do
%{
"https://www.example.com/extension" => %{"color" => "violet"}
}
end
def mock_get_jwk_keys(jwk) do
body =
Jason.encode!(%{
keys: [
jwk.pem
|> JOSE.JWK.from_pem()
|> JOSE.JWK.to_public()
|> JOSE.JWK.to_map()
|> (fn {_kty, public_jwk} -> public_jwk end).()
|> Map.put("typ", jwk.typ)
|> Map.put("alg", jwk.alg)
|> Map.put("kid", jwk.kid)
|> Map.put("use", "sig")
]
})
{:ok, %HTTPoison.Response{status_code: 200, body: body}}
end
end

View File

@@ -53,7 +53,9 @@ defmodule Lti13.UsersTest do
{:ok, %User{} = user} = Users.create_user(attrs)
assert %User{id: id} = Users.get_user_by_sub(attrs.sub)
assert %User{id: id} =
Users.get_user_by_sub_and_registration_id(attrs.sub, attrs.registration_id)
assert id == user.id
end
@@ -66,8 +68,7 @@ defmodule Lti13.UsersTest do
name: "John Doe",
roles: ["role1", "role2"],
email: claper_user.email,
client_id: registration.client_id,
issuer: registration.issuer
registration_id: registration.id
}
assert {:ok, %User{} = user} = Users.get_or_create_user(attrs)
@@ -89,8 +90,6 @@ defmodule Lti13.UsersTest do
name: "John Doe",
roles: ["role1", "role2"],
email: claper_user.email,
client_id: registration.client_id,
issuer: registration.issuer,
registration_id: registration.id,
user_id: claper_user.id
}