defmodule Plausible.Segments.SegmentTest do use ExUnit.Case alias Plausible.Segments.Segment doctest Segment, import: true setup do segment = %Segment{ name: "any name", type: :personal, segment_data: %{"filters" => ["is", "visit:page", ["/blog"]]}, owner_id: 1, site_id: 100 } {:ok, segment: segment} end test "changeset has required fields" do assert Segment.changeset(%Segment{}, %{}).errors == [ segment_data: {"property \"filters\" must be an array with at least one member", []}, name: {"can't be blank", [validation: :required]}, segment_data: {"can't be blank", [validation: :required]}, site_id: {"can't be blank", [validation: :required]}, type: {"can't be blank", [validation: :required]}, owner_id: {"can't be blank", [validation: :required]} ] end test "changeset does not allow setting owner_id to nil (setting to nil happens with database triggers)", %{segment: valid_segment} do assert Segment.changeset( valid_segment, %{ owner_id: nil } ).errors == [ owner_id: {"can't be blank", [validation: :required]} ] end test "changeset forbids too long name", %{segment: valid_segment} do assert Segment.changeset( valid_segment, %{ name: String.duplicate("a", 256) } ).errors == [ name: {"should be at most %{count} byte(s)", [{:count, 255}, {:validation, :length}, {:kind, :max}, {:type, :binary}]} ] end test "changeset forbids too large segment_data", %{segment: valid_segment} do assert Segment.changeset( valid_segment, %{ segment_data: Jason.decode!( ~s({"filters": ["is", "visit:exit_page", [#{Enum.map_join(1..(5 * 1024), ",", fn i -> "#{i}" end)}]]}) ) } ).errors == [ segment_data: {"should be at most %{count} byte(s)", [{:count, 5120}]} ] end test "changeset allows setting nil owner_id to a user id (to be able to recover dangling site segments)", %{segment: valid_segment = %Segment{}} do assert Segment.changeset( %Segment{ valid_segment | owner_id: nil }, %{ owner_id: 100_100 } ).valid? == true end test "changeset requires segment_data to be structured as expected", %{segment: valid_segment} do assert Segment.changeset( valid_segment, %{ segment_data: %{"filters" => 1, "labels" => true, "other" => []} } ).errors == [ {:segment_data, {"property \"labels\" must be map or nil", []}}, {:segment_data, {"property \"filters\" must be an array with at least one member", []}}, {:segment_data, {"must not contain any other property except \"filters\" and \"labels\"", []}} ] end test "changeset forbids empty filters list", %{segment: valid_segment} do assert Segment.changeset( valid_segment, %{ segment_data: %{ "filters" => [] } } ).errors == [ {:segment_data, {"property \"filters\" must be an array with at least one member", []}} ] end test "changeset permits well-structured segment data", %{segment: valid_segment} do assert Segment.changeset( valid_segment, %{ segment_data: %{ "filters" => [["is", "visit:country", ["DE"]]], "labels" => %{"DE" => "Germany"} } } ).valid? == true end end