tests: add tests

This commit is contained in:
carla 2026-02-09 13:34:57 +01:00
parent e1266944b1
commit 9115d53198
6 changed files with 503 additions and 649 deletions

View file

@ -14,6 +14,11 @@ defmodule MvWeb.MemberExportControllerTest do
end
end
# Export uses humanize_field (e.g. "first_name" -> "First name"); normalize \r\n line endings
defp export_lines(body) do
body |> String.split(~r/\r?\n/, trim: true)
end
describe "POST /members/export.csv" do
setup %{conn: conn} do
# Create 3 members for export tests
@ -41,7 +46,7 @@ defmodule MvWeb.MemberExportControllerTest do
%{member1: m1, member2: m2, member3: m3, conn: conn}
end
test "selected export: returns 200, text/csv, header + exactly 2 data rows", %{
test "exports selected members with specified fields", %{
conn: conn,
member1: m1,
member2: m2
@ -68,18 +73,18 @@ defmodule MvWeb.MemberExportControllerTest do
assert get_resp_header(conn, "content-type") |> List.first() =~ "text/csv"
body = response(conn, 200)
lines = String.split(body, "\n", trim: true)
lines = export_lines(body)
header = hd(lines)
# Header + 2 data rows (headers are localized labels)
# Header + 2 data rows (controller uses humanize_field: "first_name" -> "First name")
assert length(lines) == 3
assert hd(lines) =~ "First Name"
assert hd(lines) =~ "Email"
assert header =~ "First Name,Last Name,Email"
assert body =~ "Alice"
assert body =~ "Bob"
refute body =~ "Carol"
end
test "all export: selected_ids=[] returns all members (at least 3 data rows)", %{
test "exports all members when selected_ids is empty", %{
conn: conn,
member1: _m1,
member2: _m2,
@ -105,17 +110,16 @@ defmodule MvWeb.MemberExportControllerTest do
assert conn.status == 200
body = response(conn, 200)
lines = String.split(body, "\n", trim: true)
lines = export_lines(body)
# Header + at least 3 data rows (headers are localized labels)
# Header + at least 3 data rows (controller uses humanize_field)
assert length(lines) >= 4
assert hd(lines) =~ "First Name"
assert body =~ "Alice"
assert body =~ "Bob"
assert body =~ "Carol"
end
test "whitelist: unknown member_fields are not in header", %{conn: conn, member1: m1} do
test "filters out unknown member fields from export", %{conn: conn, member1: m1} do
payload = %{
"selected_ids" => [m1.id],
"member_fields" => ["first_name", "unknown_field", "email"],
@ -136,20 +140,20 @@ defmodule MvWeb.MemberExportControllerTest do
assert conn.status == 200
body = response(conn, 200)
header = body |> String.split("\n", trim: true) |> hd()
header = body |> export_lines() |> hd()
assert header =~ "First Name"
assert header =~ "Email"
assert header =~ "First Name,Email"
refute header =~ "unknown_field"
end
test "export includes membership_fee_status column when requested", %{
test "export includes membership_fee_status computed field when requested", %{
conn: conn,
member1: m1
} do
payload = %{
"selected_ids" => [m1.id],
"member_fields" => ["first_name", "membership_fee_status"],
"member_fields" => ["first_name"],
"computed_fields" => ["membership_fee_status"],
"custom_field_ids" => [],
"query" => nil,
"sort_field" => nil,
@ -167,44 +171,13 @@ defmodule MvWeb.MemberExportControllerTest do
assert conn.status == 200
body = response(conn, 200)
header = body |> String.split("\n", trim: true) |> hd()
header = body |> export_lines() |> hd()
assert header =~ "First Name"
assert header =~ "Membership Fee Status"
assert header =~ "First Name,Membership Fee Status"
assert body =~ "Alice"
end
test "export with payment_status alias: header shows Membership Fee Status", %{
conn: conn,
member1: m1
} do
payload = %{
"selected_ids" => [m1.id],
"member_fields" => ["first_name", "payment_status"],
"custom_field_ids" => [],
"query" => nil,
"sort_field" => nil,
"sort_order" => nil
}
conn = get(conn, "/members")
csrf_token = csrf_token_from_conn(conn)
conn =
post(conn, "/members/export.csv", %{
"payload" => Jason.encode!(payload),
"_csrf_token" => csrf_token
})
assert conn.status == 200
body = response(conn, 200)
header = body |> String.split("\n", trim: true) |> hd()
assert header =~ "Membership Fee Status"
assert body =~ "Alice"
end
test "export with show_current_cycle: membership fee status column exists stably", %{
test "exports membership fee status computed field with show_current_cycle option", %{
conn: conn,
member1: _m1,
member2: _m2,
@ -212,7 +185,8 @@ defmodule MvWeb.MemberExportControllerTest do
} do
payload = %{
"selected_ids" => [],
"member_fields" => ["first_name", "email", "membership_fee_status"],
"member_fields" => [],
"computed_fields" => ["membership_fee_status"],
"custom_field_ids" => [],
"query" => nil,
"sort_field" => nil,
@ -231,13 +205,300 @@ defmodule MvWeb.MemberExportControllerTest do
assert conn.status == 200
body = response(conn, 200)
lines = String.split(body, "\n", trim: true)
assert length(lines) >= 4
lines = export_lines(body)
header = hd(lines)
assert header =~ "First Name"
assert header =~ "Email"
assert header =~ "Membership Fee Status"
end
setup %{conn: conn} do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Create custom fields for different types
{:ok, string_field} =
Mv.Membership.CustomField
|> Ash.Changeset.for_create(:create, %{
name: "Phone Number",
value_type: :string
})
|> Ash.create(actor: system_actor)
{:ok, integer_field} =
Mv.Membership.CustomField
|> Ash.Changeset.for_create(:create, %{
name: "Membership Number",
value_type: :integer
})
|> Ash.create(actor: system_actor)
{:ok, boolean_field} =
Mv.Membership.CustomField
|> Ash.Changeset.for_create(:create, %{
name: "Active Member",
value_type: :boolean
})
|> Ash.create(actor: system_actor)
# Create members with custom field values
{:ok, member_with_string} =
Mv.Membership.create_member(
%{
first_name: "Test",
last_name: "String",
email: "test.string@example.com"
},
actor: system_actor
)
{:ok, _cfv_string} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_with_string.id,
custom_field_id: string_field.id,
value: "+49 123 456789"
})
|> Ash.create(actor: system_actor)
{:ok, member_with_integer} =
Mv.Membership.create_member(
%{
first_name: "Test",
last_name: "Integer",
email: "test.integer@example.com"
},
actor: system_actor
)
{:ok, _cfv_integer} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_with_integer.id,
custom_field_id: integer_field.id,
value: 12345
})
|> Ash.create(actor: system_actor)
{:ok, member_with_boolean} =
Mv.Membership.create_member(
%{
first_name: "Test",
last_name: "Boolean",
email: "test.boolean@example.com"
},
actor: system_actor
)
{:ok, _cfv_boolean} =
Mv.Membership.CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member_with_boolean.id,
custom_field_id: boolean_field.id,
value: true
})
|> Ash.create(actor: system_actor)
{:ok, member_without_value} =
Mv.Membership.create_member(
%{
first_name: "Test",
last_name: "NoValue",
email: "test.novalue@example.com"
},
actor: system_actor
)
%{
conn: conn,
string_field: string_field,
integer_field: integer_field,
boolean_field: boolean_field,
member_with_string: member_with_string,
member_with_integer: member_with_integer,
member_with_boolean: member_with_boolean,
member_without_value: member_without_value
}
end
test "export includes custom field column with string value", %{
conn: conn,
string_field: string_field,
member_with_string: member
} do
payload = %{
"selected_ids" => [member.id],
"member_fields" => ["first_name", "last_name"],
"custom_field_ids" => [string_field.id],
"query" => nil,
"sort_field" => nil,
"sort_order" => nil
}
conn = get(conn, "/members")
csrf_token = csrf_token_from_conn(conn)
conn =
post(conn, "/members/export.csv", %{
"payload" => Jason.encode!(payload),
"_csrf_token" => csrf_token
})
assert conn.status == 200
body = response(conn, 200)
lines = export_lines(body)
header = hd(lines)
assert header =~ "First Name"
assert header =~ "Last Name"
assert header =~ "Phone Number"
assert body =~ "Test"
assert body =~ "String"
assert body =~ "+49 123 456789"
end
test "export includes custom field column with integer value", %{
conn: conn,
integer_field: integer_field,
member_with_integer: member
} do
payload = %{
"selected_ids" => [member.id],
"member_fields" => ["first_name"],
"custom_field_ids" => [integer_field.id],
"query" => nil,
"sort_field" => nil,
"sort_order" => nil
}
conn = get(conn, "/members")
csrf_token = csrf_token_from_conn(conn)
conn =
post(conn, "/members/export.csv", %{
"payload" => Jason.encode!(payload),
"_csrf_token" => csrf_token
})
assert conn.status == 200
body = response(conn, 200)
header = body |> export_lines() |> hd()
assert header =~ "First Name"
assert header =~ "Membership Number"
assert body =~ "Test"
assert body =~ "12345"
end
test "export includes custom field column with boolean value", %{
conn: conn,
boolean_field: boolean_field,
member_with_boolean: member
} do
payload = %{
"selected_ids" => [member.id],
"member_fields" => ["first_name"],
"custom_field_ids" => [boolean_field.id],
"query" => nil,
"sort_field" => nil,
"sort_order" => nil
}
conn = get(conn, "/members")
csrf_token = csrf_token_from_conn(conn)
conn =
post(conn, "/members/export.csv", %{
"payload" => Jason.encode!(payload),
"_csrf_token" => csrf_token
})
assert conn.status == 200
body = response(conn, 200)
header = body |> export_lines() |> hd()
assert header =~ "First Name"
assert header =~ "Active Member"
assert body =~ "Test"
# Boolean values are formatted as "Yes" or "No" by CustomFieldValueFormatter
assert body =~ "Yes"
end
test "export shows empty cell for member without custom field value", %{
conn: conn,
string_field: string_field,
member_without_value: member
} do
payload = %{
"selected_ids" => [member.id],
"member_fields" => ["first_name", "last_name"],
"custom_field_ids" => [string_field.id],
"query" => nil,
"sort_field" => nil,
"sort_order" => nil
}
conn = get(conn, "/members")
csrf_token = csrf_token_from_conn(conn)
conn =
post(conn, "/members/export.csv", %{
"payload" => Jason.encode!(payload),
"_csrf_token" => csrf_token
})
assert conn.status == 200
body = response(conn, 200)
lines = export_lines(body)
header = hd(lines)
data_line = Enum.at(lines, 1)
assert header =~ "Phone Number"
# Empty custom field value should result in empty cell (two consecutive commas)
assert data_line =~ "Test,NoValue,"
end
test "export includes multiple custom fields in correct order", %{
conn: conn,
string_field: string_field,
integer_field: integer_field,
boolean_field: boolean_field,
member_with_string: member
} do
payload = %{
"selected_ids" => [member.id],
"member_fields" => ["first_name"],
"custom_field_ids" => [string_field.id, integer_field.id, boolean_field.id],
"query" => nil,
"sort_field" => nil,
"sort_order" => nil
}
conn = get(conn, "/members")
csrf_token = csrf_token_from_conn(conn)
conn =
post(conn, "/members/export.csv", %{
"payload" => Jason.encode!(payload),
"_csrf_token" => csrf_token
})
assert conn.status == 200
body = response(conn, 200)
header = body |> export_lines() |> hd()
assert header =~ "First Name"
assert header =~ "Phone Number"
assert header =~ "Membership Number"
assert header =~ "Active Member"
# Verify order: member fields first, then custom fields in the order specified
header_parts = String.split(header, ",")
first_name_idx = Enum.find_index(header_parts, &String.contains?(&1, "First Name"))
phone_idx = Enum.find_index(header_parts, &String.contains?(&1, "Phone Number"))
membership_idx = Enum.find_index(header_parts, &String.contains?(&1, "Membership Number"))
active_idx = Enum.find_index(header_parts, &String.contains?(&1, "Active Member"))
assert first_name_idx < phone_idx
assert phone_idx < membership_idx
assert membership_idx < active_idx
end
end
end