mitgliederverwaltung/test/mv/membership/members_csv_test.exs
carla b429a4dbb6
Some checks failed
continuous-integration/drone/push Build is failing
test: adds tests
2026-02-04 16:43:12 +01:00

124 lines
4.1 KiB
Elixir

defmodule Mv.Membership.MembersCSVTest do
use ExUnit.Case, async: true
alias Mv.Membership.MembersCSV
describe "export/3" do
test "returns CSV with header and one data row (member fields only)" do
member = %{first_name: "Jane", email: "jane@example.com"}
member_fields = ["first_name", "email"]
custom_fields_by_id = %{}
iodata = MembersCSV.export([member], member_fields, custom_fields_by_id)
csv = IO.iodata_to_binary(iodata)
assert csv =~ "first_name"
assert csv =~ "email"
assert csv =~ "Jane"
assert csv =~ "jane@example.com"
# One header line, one data line
lines = String.split(csv, "\n", trim: true)
assert length(lines) == 2
end
test "escapes cell containing comma (RFC 4180 quoted)" do
member = %{first_name: "Doe, John", email: "john@example.com"}
member_fields = ["first_name", "email"]
custom_fields_by_id = %{}
iodata = MembersCSV.export([member], member_fields, custom_fields_by_id)
csv = IO.iodata_to_binary(iodata)
# Comma inside value must be quoted so the cell is one field
assert csv =~ ~s("Doe, John")
assert csv =~ "john@example.com"
end
test "escapes cell containing double-quote (RFC 4180 doubled and quoted)" do
member = %{first_name: ~s(He said "Hi"), email: "a@b.com"}
member_fields = ["first_name", "email"]
custom_fields_by_id = %{}
iodata = MembersCSV.export([member], member_fields, custom_fields_by_id)
csv = IO.iodata_to_binary(iodata)
# Double-quote inside value must be doubled and cell quoted
assert csv =~ ~s("He said ""Hi""")
assert csv =~ "a@b.com"
end
test "formats date as ISO8601 for member fields" do
member = %{first_name: "D", email: "d@d.com", join_date: ~D[2024-03-15]}
iodata = MembersCSV.export([member], ["first_name", "email", "join_date"], %{})
csv = IO.iodata_to_binary(iodata)
assert csv =~ "2024-03-15"
assert csv =~ "join_date"
end
test "formats nil as empty string" do
member = %{first_name: "Only", last_name: nil, email: "x@y.com"}
member_fields = ["first_name", "last_name", "email"]
custom_fields_by_id = %{}
iodata = MembersCSV.export([member], member_fields, custom_fields_by_id)
csv = IO.iodata_to_binary(iodata)
assert csv =~ "first_name"
assert csv =~ "Only"
assert csv =~ "x@y.com"
# Nil becomes empty; between Only and x@y we have empty (e.g. Only,,x@y.com)
assert csv =~ "Only,,x@y"
end
test "formats boolean as true/false" do
# Use a field we can set to boolean via a custom-like struct - member has no boolean field.
# So we test via custom field instead.
custom_cf = %{id: "cf-1", name: "Active", value_type: :boolean}
custom_fields_by_id = %{"cf-1" => custom_cf}
member_with_cfv = %{
first_name: "Test",
email: "e@e.com",
custom_field_values: [
%{custom_field_id: "cf-1", value: true, custom_field: custom_cf}
]
}
iodata =
MembersCSV.export(
[member_with_cfv],
["first_name", "email"],
custom_fields_by_id
)
csv = IO.iodata_to_binary(iodata)
assert csv =~ "Active"
# Formatter yields "Yes" for true (gettext)
assert csv =~ "Yes"
end
test "includes custom field columns in header and rows (order from map)" do
cf1 = %{id: "a", name: "Custom1", value_type: :string}
cf2 = %{id: "b", name: "Custom2", value_type: :string}
# Map order: a then b
custom_fields_by_id = %{"a" => cf1, "b" => cf2}
member = %{
first_name: "M",
email: "m@m.com",
custom_field_values: [
%{custom_field_id: "a", value: "v1", custom_field: cf1},
%{custom_field_id: "b", value: "v2", custom_field: cf2}
]
}
iodata = MembersCSV.export([member], ["first_name", "email"], custom_fields_by_id)
csv = IO.iodata_to_binary(iodata)
assert csv =~ "first_name,email,Custom1,Custom2"
assert csv =~ "v1"
assert csv =~ "v2"
end
end
end