104 lines
3.5 KiB
Elixir
104 lines
3.5 KiB
Elixir
defmodule MvWeb.ImportTemplateControllerTest do
|
|
use MvWeb.ConnCase, async: true
|
|
|
|
setup %{conn: conn} do
|
|
actor = Mv.Helpers.SystemActor.get_system_actor()
|
|
|
|
{:ok, custom_field} =
|
|
Mv.Membership.CustomField
|
|
|> Ash.Changeset.for_create(:create, %{name: "Lieblingsfarbe", value_type: :string})
|
|
|> Ash.create(actor: actor)
|
|
|
|
%{conn: conn, custom_field: custom_field}
|
|
end
|
|
|
|
describe "authenticated EN template" do
|
|
setup %{conn: conn} do
|
|
admin = Mv.Fixtures.user_with_role_fixture("admin")
|
|
%{conn: MvWeb.ConnCase.conn_with_password_user(conn, admin)}
|
|
end
|
|
|
|
test "returns CSV with English headers and current custom fields", %{conn: conn} do
|
|
conn = get(conn, ~p"/admin/import/template/en")
|
|
|
|
assert response_content_type(conn, :csv) =~ "text/csv"
|
|
body = response(conn, 200)
|
|
|
|
header = body |> String.split("\n") |> List.first()
|
|
assert header =~ "email"
|
|
# EN headers use the canonical English variant from HeaderMapper, not the
|
|
# underscore form, so the template stays faithful to the documented variant list.
|
|
assert header =~ "first name"
|
|
assert header =~ "last name"
|
|
refute header =~ "first_name"
|
|
assert header =~ "house number"
|
|
refute header =~ "house_number"
|
|
assert header =~ "Lieblingsfarbe"
|
|
|
|
assert get_resp_header(conn, "content-disposition")
|
|
|> Enum.any?(&(&1 =~ "member_import_en.csv"))
|
|
end
|
|
|
|
test "neutralizes formula-injection in a custom field header", %{conn: conn} do
|
|
actor = Mv.Helpers.SystemActor.get_system_actor()
|
|
|
|
{:ok, _} =
|
|
Mv.Membership.CustomField
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
name: "=cmd|'/c calc'!A1",
|
|
value_type: :string
|
|
})
|
|
|> Ash.create(actor: actor)
|
|
|
|
conn = get(conn, ~p"/admin/import/template/en")
|
|
body = response(conn, 200)
|
|
header = body |> String.split("\n") |> List.first()
|
|
|
|
# The dangerous cell must be prefixed with a single quote so spreadsheet
|
|
# software does not evaluate it as a formula, matching the export writer.
|
|
refute header =~ ~r/(^|;)=cmd/
|
|
assert header =~ "'=cmd|'/c calc'!A1"
|
|
end
|
|
end
|
|
|
|
describe "authenticated DE template" do
|
|
setup %{conn: conn} do
|
|
admin = Mv.Fixtures.user_with_role_fixture("admin")
|
|
%{conn: MvWeb.ConnCase.conn_with_password_user(conn, admin)}
|
|
end
|
|
|
|
test "returns CSV with German headers and current custom fields", %{conn: conn} do
|
|
conn = get(conn, ~p"/admin/import/template/de")
|
|
|
|
body = response(conn, 200)
|
|
header = body |> String.split("\n") |> List.first()
|
|
|
|
assert header =~ "E-Mail"
|
|
assert header =~ "Vorname"
|
|
assert header =~ "Lieblingsfarbe"
|
|
|
|
assert get_resp_header(conn, "content-disposition")
|
|
|> Enum.any?(&(&1 =~ "member_import_de.csv"))
|
|
end
|
|
end
|
|
|
|
describe "authorization" do
|
|
@tag role: :unauthenticated
|
|
test "unauthenticated request does not receive a CSV", %{conn: conn} do
|
|
conn = get(conn, ~p"/admin/import/template/en")
|
|
|
|
refute conn.status == 200
|
|
refute get_resp_header(conn, "content-type") |> Enum.any?(&(&1 =~ "text/csv"))
|
|
refute to_string(conn.resp_body) =~ "email"
|
|
end
|
|
|
|
@tag role: :member
|
|
test "user without import permission is forbidden", %{conn: conn} do
|
|
conn = get(conn, ~p"/admin/import/template/en")
|
|
|
|
refute conn.status == 200
|
|
refute get_resp_header(conn, "content-type") |> Enum.any?(&(&1 =~ "text/csv"))
|
|
refute to_string(conn.resp_body) =~ "email"
|
|
end
|
|
end
|
|
end
|