diff --git a/lib/plausible_web/controllers/api/external_controller.ex b/lib/plausible_web/controllers/api/external_controller.ex index 569b37d47f5ad4c7467699237f77fbd8ebd55fe8..fe737eae0f695fed28f7b7d5233ff40b3f184763 100644 --- a/lib/plausible_web/controllers/api/external_controller.ex +++ b/lib/plausible_web/controllers/api/external_controller.ex @@ -195,12 +195,18 @@ defmodule PlausibleWeb.Api.ExternalController do defp generate_user_id(conn, domain, hostname, salt) do user_agent = List.first(Plug.Conn.get_req_header(conn, "user-agent")) || "" ip_address = PlausibleWeb.RemoteIp.get(conn) + root_domain = get_root_domain(hostname) - if domain && hostname do - SipHash.hash!(salt, user_agent <> ip_address <> domain <> hostname) + if domain && root_domain do + SipHash.hash!(salt, user_agent <> ip_address <> domain <> root_domain) end end + defp get_root_domain(hostname) when is_binary(hostname) do + PublicSuffix.registrable_domain(hostname) + end + defp get_root_domain(hostname), do: hostname + defp calculate_screen_size(nil), do: nil defp calculate_screen_size(width) when width < 576, do: "Mobile" defp calculate_screen_size(width) when width < 992, do: "Tablet" diff --git a/mix.exs b/mix.exs index d385413d43c65af1ad9750b620c067245a3cf6a5..221a0b67037dde6afa1042860ef0d6f8bf5b5a19 100644 --- a/mix.exs +++ b/mix.exs @@ -96,7 +96,8 @@ defmodule Plausible.MixProject do {:kaffy, "~> 0.9.0"}, {:envy, "~> 1.1.1"}, {:phoenix_pagination, "~> 0.7.0"}, - {:hammer, "~> 6.0"} + {:hammer, "~> 6.0"}, + {:public_suffix, git: "https://github.com/axelson/publicsuffix-elixir"} ] end diff --git a/mix.lock b/mix.lock index 7aef3546a26c15475df215de21bcd6978f8afb81..27c2b1546f340d7b757f6d0547959d8bf4e413cd 100644 --- a/mix.lock +++ b/mix.lock @@ -76,6 +76,7 @@ "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"}, "postgrex": {:hex, :postgrex, "0.15.9", "46f8fe6f25711aeb861c4d0ae09780facfdf3adbd2fb5594ead61504dd489bda", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "610719103e4cb2223d4ab78f9f0f3e720320eeca6011415ab4137ddef730adee"}, + "public_suffix": {:git, "https://github.com/axelson/publicsuffix-elixir", "89372422ab8b433de508519ef474e39699fd11ca", []}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, "ref_inspector": {:hex, :ref_inspector, "1.3.1", "bb0489a4c4299dcd633f2b7a60c41a01f5590789d0b28225a60be484e1fbe777", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "3172eb1b08e5c69966f796e3fe0e691257546fa143a5eb0ecc18a6e39b233854"}, "sentry": {:hex, :sentry, "8.0.5", "5ca922b9238a50c7258b52f47364b2d545beda5e436c7a43965b34577f1ef61f", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.3", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "4972839fdbf52e886d7b3e694c8adf421f764f2fa79036b88fb4742049bd4b7c"}, diff --git a/test/plausible_web/controllers/api/external_controller_test.exs b/test/plausible_web/controllers/api/external_controller_test.exs index 6c6593207861ff6c7776fa034dc51662ab66b090..3c4e5e482b5fa51bdbca34734f49ebfef077ad1b 100644 --- a/test/plausible_web/controllers/api/external_controller_test.exs +++ b/test/plausible_web/controllers/api/external_controller_test.exs @@ -12,6 +12,16 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do ) end + defp get_events(domain) do + Plausible.Event.WriteBuffer.flush() + + ClickhouseRepo.all( + from e in Plausible.ClickhouseEvent, + where: e.domain == ^domain, + order_by: [desc: e.timestamp] + ) + end + @user_agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36" describe "POST /api/event" do @@ -357,340 +367,475 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do assert response(conn, 202) == "" assert pageview.referrer_source == "" end - end - test "screen size is calculated from screen_width", %{conn: conn} do - params = %{ - name: "pageview", - url: "http://gigride.live/", - screen_width: 480, - domain: "external-controller-test-16.com" - } + test "screen size is calculated from screen_width", %{conn: conn} do + params = %{ + name: "pageview", + url: "http://gigride.live/", + screen_width: 480, + domain: "external-controller-test-16.com" + } + + conn = + conn + |> put_req_header("user-agent", @user_agent) + |> post("/api/event", params) + + pageview = get_event("external-controller-test-16.com") + + assert response(conn, 202) == "" + assert pageview.screen_size == "Mobile" + end + + test "screen size is nil if screen_width is missing", %{conn: conn} do + params = %{ + name: "pageview", + url: "http://gigride.live/", + domain: "external-controller-test-17.com" + } + + conn = + conn + |> put_req_header("user-agent", @user_agent) + |> post("/api/event", params) + + pageview = get_event("external-controller-test-17.com") + + assert response(conn, 202) == "" + assert pageview.screen_size == "" + end + + test "can trigger a custom event", %{conn: conn} do + params = %{ + name: "custom event", + url: "http://gigride.live/", + domain: "external-controller-test-18.com" + } + + conn = + conn + |> put_req_header("user-agent", @user_agent) + |> post("/api/event", params) + + event = get_event("external-controller-test-18.com") + + assert response(conn, 202) == "" + assert event.name == "custom event" + end + + test "casts custom props to string", %{conn: conn} do + params = %{ + name: "Signup", + url: "http://gigride.live/", + domain: "custom-prop-test.com", + props: + Jason.encode!(%{ + bool_test: true, + number_test: 12 + }) + } - conn = conn - |> put_req_header("user-agent", @user_agent) |> post("/api/event", params) - pageview = get_event("external-controller-test-16.com") + event = get_event("custom-prop-test.com") - assert response(conn, 202) == "" - assert pageview.screen_size == "Mobile" - end + assert Map.get(event, :"meta.key") == ["bool_test", "number_test"] + assert Map.get(event, :"meta.value") == ["true", "12"] + end - test "screen size is nil if screen_width is missing", %{conn: conn} do - params = %{ - name: "pageview", - url: "http://gigride.live/", - domain: "external-controller-test-17.com" - } + test "ignores malformed custom props", %{conn: conn} do + params = %{ + name: "Signup", + url: "http://gigride.live/", + domain: "custom-prop-test-2.com", + props: "\"show-more:button\"" + } - conn = conn - |> put_req_header("user-agent", @user_agent) |> post("/api/event", params) - pageview = get_event("external-controller-test-17.com") + event = get_event("custom-prop-test-2.com") - assert response(conn, 202) == "" - assert pageview.screen_size == "" - end + assert Map.get(event, :"meta.key") == [] + assert Map.get(event, :"meta.value") == [] + end + + test "ignores a malformed referrer URL", %{conn: conn} do + params = %{ + name: "pageview", + url: "http://gigride.live/", + referrer: "https:://twitter.com", + domain: "external-controller-test-19.com" + } + + conn = + conn + |> put_req_header("user-agent", @user_agent) + |> post("/api/event", params) + + event = get_event("external-controller-test-19.com") - test "can trigger a custom event", %{conn: conn} do - params = %{ - name: "custom event", - url: "http://gigride.live/", - domain: "external-controller-test-18.com" - } + assert response(conn, 202) == "" + assert event.referrer == "" + end + + # Fake data is set up in config/test.exs + test "looks up the country from the ip address", %{conn: conn} do + params = %{ + name: "pageview", + domain: "external-controller-test-20.com", + url: "http://gigride.live/" + } - conn = conn - |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "1.1.1.1") |> post("/api/event", params) - event = get_event("external-controller-test-18.com") + pageview = get_event("external-controller-test-20.com") - assert response(conn, 202) == "" - assert event.name == "custom event" - end + assert pageview.country_code == "US" + end - test "casts custom props to string", %{conn: conn} do - params = %{ - name: "Signup", - url: "http://gigride.live/", - domain: "custom-prop-test.com", - props: - Jason.encode!(%{ - bool_test: true, - number_test: 12 - }) - } - - conn - |> post("/api/event", params) - - event = get_event("custom-prop-test.com") - - assert Map.get(event, :"meta.key") == ["bool_test", "number_test"] - assert Map.get(event, :"meta.value") == ["true", "12"] - end + test "scrubs port from x-forwarded-for", %{conn: conn} do + params = %{ + name: "pageview", + domain: "external-controller-test-x-forwarded-for-port.com", + url: "http://gigride.live/" + } - test "ignores malformed custom props", %{conn: conn} do - params = %{ - name: "Signup", - url: "http://gigride.live/", - domain: "custom-prop-test-2.com", - props: "\"show-more:button\"" - } + conn + |> put_req_header("x-forwarded-for", "1.1.1.1:123") + |> post("/api/event", params) - conn - |> post("/api/event", params) + pageview = get_event("external-controller-test-x-forwarded-for-port.com") - event = get_event("custom-prop-test-2.com") + assert pageview.country_code == "US" + end - assert Map.get(event, :"meta.key") == [] - assert Map.get(event, :"meta.value") == [] - end + test "works with ipv6 without port in x-forwarded-for", %{conn: conn} do + params = %{ + name: "pageview", + domain: "external-controller-test-x-forwarded-for-ipv6.com", + url: "http://gigride.live/" + } + + conn + |> put_req_header("x-forwarded-for", "1:1:1:1:1:1:1:1") + |> post("/api/event", params) - test "ignores a malformed referrer URL", %{conn: conn} do - params = %{ - name: "pageview", - url: "http://gigride.live/", - referrer: "https:://twitter.com", - domain: "external-controller-test-19.com" - } + pageview = get_event("external-controller-test-x-forwarded-for-ipv6.com") + + assert pageview.country_code == "US" + end + + test "works with ipv6 with a port number in x-forwarded-for", %{conn: conn} do + params = %{ + name: "pageview", + domain: "external-controller-test-x-forwarded-for-ipv6-port.com", + url: "http://gigride.live/" + } - conn = conn - |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "[1:1:1:1:1:1:1:1]:123") |> post("/api/event", params) - event = get_event("external-controller-test-19.com") + pageview = get_event("external-controller-test-x-forwarded-for-ipv6-port.com") - assert response(conn, 202) == "" - assert event.referrer == "" - end + assert pageview.country_code == "US" + end + + test "uses cloudflare's special header for client IP address if present", %{conn: conn} do + params = %{ + name: "pageview", + domain: "external-controller-test-cloudflare.com", + url: "http://gigride.live/" + } + + conn + |> put_req_header("x-forwarded-for", "0.0.0.0") + |> put_req_header("cf-connecting-ip", "1.1.1.1") + |> post("/api/event", params) - # Fake data is set up in config/test.exs - test "looks up the country from the ip address", %{conn: conn} do - params = %{ - name: "pageview", - domain: "external-controller-test-20.com", - url: "http://gigride.live/" - } + pageview = get_event("external-controller-test-cloudflare.com") - conn - |> put_req_header("x-forwarded-for", "1.1.1.1") - |> post("/api/event", params) + assert pageview.country_code == "US" + end - pageview = get_event("external-controller-test-20.com") + test "Uses the Forwarded header when cf-connecting-ip and x-forwarded-for are missing", %{ + conn: conn + } do + params = %{ + name: "pageview", + domain: "external-controller-test-forwarded.com", + url: "http://gigride.live/" + } - assert pageview.country_code == "US" - end + conn + |> put_req_header("forwarded", "by=0.0.0.0;for=1.1.1.1;host=somehost.com;proto=https") + |> post("/api/event", params) - test "scrubs port from x-forwarded-for", %{conn: conn} do - params = %{ - name: "pageview", - domain: "external-controller-test-x-forwarded-for-port.com", - url: "http://gigride.live/" - } + pageview = get_event("external-controller-test-forwarded.com") - conn - |> put_req_header("x-forwarded-for", "1.1.1.1:123") - |> post("/api/event", params) + assert pageview.country_code == "US" + end - pageview = get_event("external-controller-test-x-forwarded-for-port.com") + test "Forwarded header can parse ipv6", %{conn: conn} do + params = %{ + name: "pageview", + domain: "external-controller-test-forwarded-ipv6.com", + url: "http://gigride.live/" + } - assert pageview.country_code == "US" - end + conn + |> put_req_header( + "forwarded", + "by=0.0.0.0;for=\"[1:1:1:1:1:1:1:1]\",for=0.0.0.0;host=somehost.com;proto=https" + ) + |> post("/api/event", params) - test "works with ipv6 without port in x-forwarded-for", %{conn: conn} do - params = %{ - name: "pageview", - domain: "external-controller-test-x-forwarded-for-ipv6.com", - url: "http://gigride.live/" - } + pageview = get_event("external-controller-test-forwarded-ipv6.com") - conn - |> put_req_header("x-forwarded-for", "1:1:1:1:1:1:1:1") - |> post("/api/event", params) + assert pageview.country_code == "US" + end - pageview = get_event("external-controller-test-x-forwarded-for-ipv6.com") + test "URL is decoded", %{conn: conn} do + params = %{ + name: "pageview", + url: + "http://www.example.com/opportunity/category/%D8%AC%D9%88%D8%A7%D8%A6%D8%B2-%D9%88%D9%85%D8%B3%D8%A7%D8%A8%D9%82%D8%A7%D8%AA", + domain: "external-controller-test-21.com" + } - assert pageview.country_code == "US" - end + conn + |> post("/api/event", params) - test "works with ipv6 with a port number in x-forwarded-for", %{conn: conn} do - params = %{ - name: "pageview", - domain: "external-controller-test-x-forwarded-for-ipv6-port.com", - url: "http://gigride.live/" - } + pageview = get_event("external-controller-test-21.com") - conn - |> put_req_header("x-forwarded-for", "[1:1:1:1:1:1:1:1]:123") - |> post("/api/event", params) + assert pageview.pathname == "/opportunity/category/جوائز-ومسابقات" + end - pageview = get_event("external-controller-test-x-forwarded-for-ipv6-port.com") + test "accepts shorthand map keys", %{conn: conn} do + params = %{ + n: "pageview", + u: "http://www.example.com/opportunity", + d: "external-controller-test-22.com", + r: "https://facebook.com/page", + w: 300 + } - assert pageview.country_code == "US" - end + conn + |> post("/api/event", params) - test "uses cloudflare's special header for client IP address if present", %{conn: conn} do - params = %{ - name: "pageview", - domain: "external-controller-test-cloudflare.com", - url: "http://gigride.live/" - } + pageview = get_event("external-controller-test-22.com") - conn - |> put_req_header("x-forwarded-for", "0.0.0.0") - |> put_req_header("cf-connecting-ip", "1.1.1.1") - |> post("/api/event", params) + assert pageview.pathname == "/opportunity" + assert pageview.referrer_source == "Facebook" + assert pageview.referrer == "facebook.com/page" + assert pageview.screen_size == "Mobile" + end - pageview = get_event("external-controller-test-cloudflare.com") + test "records hash when in hash mode", %{conn: conn} do + params = %{ + n: "pageview", + u: "http://www.example.com/#page-a", + d: "external-controller-test-23.com", + h: 1 + } - assert pageview.country_code == "US" - end + conn + |> post("/api/event", params) - test "Uses the Forwarded header when cf-connecting-ip and x-forwarded-for are missing", %{ - conn: conn - } do - params = %{ - name: "pageview", - domain: "external-controller-test-forwarded.com", - url: "http://gigride.live/" - } + pageview = get_event("external-controller-test-23.com") - conn - |> put_req_header("forwarded", "by=0.0.0.0;for=1.1.1.1;host=somehost.com;proto=https") - |> post("/api/event", params) + assert pageview.pathname == "/#page-a" + end - pageview = get_event("external-controller-test-forwarded.com") + test "decodes URL pathname, fragment and search", %{conn: conn} do + params = %{ + n: "pageview", + u: + "https://test.com/%EF%BA%9D%EF%BB%AD%EF%BA%8E%EF%BA%8B%EF%BA%AF-%EF%BB%AE%EF%BB%A4%EF%BA%B3%EF%BA%8E%EF%BA%92%EF%BB%97%EF%BA%8E%EF%BA%97?utm_source=%25balle%25", + d: "url-decode-test.com", + h: 1 + } - assert pageview.country_code == "US" - end + conn + |> post("/api/event", params) - test "Forwarded header can parse ipv6", %{conn: conn} do - params = %{ - name: "pageview", - domain: "external-controller-test-forwarded-ipv6.com", - url: "http://gigride.live/" - } - - conn - |> put_req_header( - "forwarded", - "by=0.0.0.0;for=\"[1:1:1:1:1:1:1:1]\",for=0.0.0.0;host=somehost.com;proto=https" - ) - |> post("/api/event", params) + pageview = get_event("url-decode-test.com") - pageview = get_event("external-controller-test-forwarded-ipv6.com") + assert pageview.hostname == "test.com" + assert pageview.pathname == "/ﺝﻭﺎﺋﺯ-ﻮﻤﺳﺎﺒﻗﺎﺗ" + assert pageview.utm_source == "%balle%" + end - assert pageview.country_code == "US" - end + test "can use double quotes in query params", %{conn: conn} do + q = URI.encode_query(%{"utm_source" => "Something \"quoted\""}) - test "URL is decoded", %{conn: conn} do - params = %{ - name: "pageview", - url: - "http://www.example.com/opportunity/category/%D8%AC%D9%88%D8%A7%D8%A6%D8%B2-%D9%88%D9%85%D8%B3%D8%A7%D8%A8%D9%82%D8%A7%D8%AA", - domain: "external-controller-test-21.com" - } + params = %{ + n: "pageview", + u: "https://test.com/?" <> q, + d: "quote-encode-test.com", + h: 1 + } - conn - |> post("/api/event", params) + conn + |> post("/api/event", params) + + pageview = get_event("quote-encode-test.com") + + assert pageview.utm_source == "Something \"quoted\"" + end + + test "responds 400 when required fields are missing", %{conn: conn} do + params = %{ + domain: "some-domain.com", + name: "pageview" + } - pageview = get_event("external-controller-test-21.com") + conn = + conn + |> put_req_header("user-agent", @user_agent) + |> post("/api/event", params) - assert pageview.pathname == "/opportunity/category/جوائز-ومسابقات" + assert response(conn, 400) == "" + end end - test "accepts shorthand map keys", %{conn: conn} do - params = %{ - n: "pageview", - u: "http://www.example.com/opportunity", - d: "external-controller-test-22.com", - r: "https://facebook.com/page", - w: 300 - } + describe "user_id generation" do + test "with same IP address and user agent, the same user ID is generated", %{conn: conn} do + params = %{ + url: "https://user-id-test-domain.com/", + domain: "user-id-test-domain.com", + name: "pageview" + } + + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", params) - conn - |> post("/api/event", params) + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", params) - pageview = get_event("external-controller-test-22.com") + [one, two] = get_events("user-id-test-domain.com") - assert pageview.pathname == "/opportunity" - assert pageview.referrer_source == "Facebook" - assert pageview.referrer == "facebook.com/page" - assert pageview.screen_size == "Mobile" - end + assert one.user_id == two.user_id + end - test "records hash when in hash mode", %{conn: conn} do - params = %{ - n: "pageview", - u: "http://www.example.com/#page-a", - d: "external-controller-test-23.com", - h: 1 - } + test "different IP address results in different user ID", %{conn: conn} do + params = %{ + url: "https://user-id-test-domain.com/", + domain: "user-id-test-domain-2.com", + name: "pageview" + } - conn - |> post("/api/event", params) + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", params) - pageview = get_event("external-controller-test-23.com") + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.2") + |> post("/api/event", params) - assert pageview.pathname == "/#page-a" - end + [one, two] = get_events("user-id-test-domain-2.com") - test "decodes URL pathname, fragment and search", %{conn: conn} do - params = %{ - n: "pageview", - u: - "https://test.com/%EF%BA%9D%EF%BB%AD%EF%BA%8E%EF%BA%8B%EF%BA%AF-%EF%BB%AE%EF%BB%A4%EF%BA%B3%EF%BA%8E%EF%BA%92%EF%BB%97%EF%BA%8E%EF%BA%97?utm_source=%25balle%25", - d: "url-decode-test.com", - h: 1 - } + assert one.user_id != two.user_id + end - conn - |> post("/api/event", params) + test "different user agent results in different user ID", %{conn: conn} do + params = %{ + url: "https://user-id-test-domain.com/", + domain: "user-id-test-domain-3.com", + name: "pageview" + } - pageview = get_event("url-decode-test.com") + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", params) - assert pageview.hostname == "test.com" - assert pageview.pathname == "/ﺝﻭﺎﺋﺯ-ﻮﻤﺳﺎﺒﻗﺎﺗ" - assert pageview.utm_source == "%balle%" - end + conn + |> put_req_header("user-agent", @user_agent <> "!!") + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", params) - test "can use double quotes in query params", %{conn: conn} do - q = URI.encode_query(%{"utm_source" => "Something \"quoted\""}) + [one, two] = get_events("user-id-test-domain-3.com") - params = %{ - n: "pageview", - u: "https://test.com/?" <> q, - d: "quote-encode-test.com", - h: 1 - } + assert one.user_id != two.user_id + end - conn - |> post("/api/event", params) + test "different domain value results in different user ID", %{conn: conn} do + params = %{ + url: "https://user-id-test-domain.com/", + domain: "user-id-test-domain-4.com", + name: "pageview" + } - pageview = get_event("quote-encode-test.com") + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", params) - assert pageview.utm_source == "Something \"quoted\"" - end + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", Map.put(params, :domain, "other-domain.com")) - test "responds 400 when required fields are missing", %{conn: conn} do - params = %{ - domain: "some-domain.com", - name: "pageview" - } + one = get_event("user-id-test-domain-4.com") + two = get_event("other-domain.com") + + assert one.user_id != two.user_id + end + + test "different hostname results in different user ID", %{conn: conn} do + params = %{ + url: "https://user-id-test-domain.com/", + domain: "user-id-test-domain-5.com", + name: "pageview" + } - conn = conn |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") |> post("/api/event", params) - assert response(conn, 400) == "" + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", Map.put(params, :url, "https://other-domain.com/")) + + [one, two] = get_events("user-id-test-domain-5.com") + + assert one.user_id != two.user_id + end + + test "different hostname results in the same user ID when the root domain in the same", %{conn: conn} do + params = %{ + url: "https://user-id-test-domain.com/", + domain: "user-id-test-domain-6.com", + name: "pageview" + } + + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", params) + + conn + |> put_req_header("user-agent", @user_agent) + |> put_req_header("x-forwarded-for", "127.0.0.1") + |> post("/api/event", Map.put(params, :url, "https://app.user-id-test-domain.com/")) + + [one, two] = get_events("user-id-test-domain-6.com") + + assert one.user_id == two.user_id + end end describe "GET /api/health" do