From f27152711968c4366d57ebd83ce77ed124d1711d Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 19 Oct 2023 06:23:40 -0400 Subject: [PATCH] Fix integration tests for otp 26 (#569) --- integration_test/sql/logging.exs | 336 +++++++++++++++++++++---------- 1 file changed, 231 insertions(+), 105 deletions(-) diff --git a/integration_test/sql/logging.exs b/integration_test/sql/logging.exs index 124e5a31..be1a6475 100644 --- a/integration_test/sql/logging.exs +++ b/integration_test/sql/logging.exs @@ -203,62 +203,93 @@ defmodule Ecto.Integration.LoggingTest do int = 1 uuid = Ecto.UUID.generate() uuid_query = from l in Logging, where: l.int == ^int and l.uuid == ^uuid, select: l.uuid - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + datetime = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) row1 = [ int: int, uuid: uuid_query, - inserted_at: now, - updated_at: now + inserted_at: datetime, + updated_at: datetime ] # Row 2 int2 = 2 uuid2 = Ecto.UUID.generate() int_query = from l in Logging, where: l.int == ^int2 and l.uuid == ^uuid2, select: l.int - now2 = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + datetime2 = NaiveDateTime.add(datetime, 1) row2 = [ int: int_query, uuid: uuid2, - inserted_at: now2, - updated_at: now2 + inserted_at: datetime2, + updated_at: datetime2 ] - # Ensure parameters are complete and in correct order + # Extract the parameters from the log: + # 1. Remove the colour codes + # 2. Remove the log level + # 3. Capture everything inside of the square brackets log = capture_log(fn -> TestRepo.insert_all(Logging, [row1, row2], log: :info) end) - param_regex = - ~r/\[\"(?.+)\", (?.+), (?.+), (?.+), (?.+), \"(?.+)\", \"(?.+)\", (?.+), (?.+), \"(?.+)\", (?.+), \"(?.+)\"\]/ + log = Regex.replace(~r/\e\[[0-9]+m/, log, "") + log = Regex.replace(~r/\[info\]/, log, "") + log = Regex.named_captures(~r/\[(?.+)\]/, log) + log_params = String.split(log["params"], ",") + + # Compute the expected parameters in the right order. + # This involves recreating the headers in the same order + # as `insert_all`. The user values come first and then + # the autogenerated id + headers = + row1 + |> Enum.reduce(%{}, fn {field, _}, headers -> Map.put(headers, field, true) end) + |> Map.put(:bid, true) + |> Map.keys() + + row1_regex = [ + int: "#{int}", + uuid: ["#{int}", uuid], + inserted_at: inspect(datetime), + updated_at: inspect(datetime), + bid: @uuid_regex + ] - param_logs = Regex.named_captures(param_regex, log) + row2_regex = [ + int: ["#{int2}", uuid2], + uuid: uuid2, + inserted_at: inspect(datetime2), + updated_at: inspect(datetime2), + bid: @uuid_regex + ] + + expected_param_regex = + Enum.flat_map([row1_regex, row2_regex], fn row -> + Enum.flat_map(headers, fn field -> + case Keyword.get(row, field) do + params when is_list(params) -> params + param -> [param] + end + end) + end) - # Autogenerated fields - assert param_logs["bid1"] =~ @uuid_regex - assert param_logs["bid2"] =~ @uuid_regex - # Row value parameters - assert param_logs["int1"] == Integer.to_string(int) - assert param_logs["inserted_at1"] == "~N[#{now}]" - assert param_logs["inserted_at2"] == "~N[#{now}]" - assert param_logs["uuid1_query_int"] == Integer.to_string(int) - assert param_logs["uuid1_query_uuid"] == uuid - assert param_logs["int2_query_int"] == Integer.to_string(int2) - assert param_logs["int2_query_uuid"] == uuid2 - assert param_logs["inserted_at2"] == "~N[#{now2}]" - assert param_logs["updated_at2"] == "~N[#{now2}]" - assert param_logs["uuid2"] == uuid2 + assert length(log_params) == length(expected_param_regex) + + Enum.zip(log_params, expected_param_regex) + |> Enum.each(fn {log, expected_regex} -> + assert log =~ expected_regex + end) end @tag :insert_select @tag :placeholders test "for insert_all with entries and placeholders" do # Placeholders - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - now2 = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - placeholder_map = %{now: now, now2: now2} + datetime = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + datetime2 = NaiveDateTime.add(datetime, 1) + placeholder_map = %{datetime: datetime, datetime2: datetime2} # Row 1 int = 1 @@ -268,8 +299,8 @@ defmodule Ecto.Integration.LoggingTest do row1 = [ int: int, uuid: uuid_query, - inserted_at: {:placeholder, :now}, - updated_at: {:placeholder, :now} + inserted_at: {:placeholder, :datetime}, + updated_at: {:placeholder, :datetime} ] # Row 2 @@ -280,34 +311,69 @@ defmodule Ecto.Integration.LoggingTest do row2 = [ int: int_query, uuid: uuid2, - inserted_at: {:placeholder, :now2}, - updated_at: {:placeholder, :now2} + inserted_at: {:placeholder, :datetime2}, + updated_at: {:placeholder, :datetime2} ] - # Ensure parameters are complete and in correct order + # Extract the parameters from the log: + # 1. Remove the colour codes + # 2. Remove the log level + # 3. Capture everything inside of the square brackets log = capture_log(fn -> - TestRepo.insert_all(Logging, [row1, row2], placeholders: placeholder_map, log: :info) + TestRepo.insert_all(Logging, [row1, row2], + placeholders: placeholder_map, + log: :info + ) end) - param_regex = - ~r/\[(?.+), (?.+), \"(?.+)\", (?.+), (?.+), \"(?.+)\", \"(?.+)\", (?.+), \"(?.+)\", \"(?.+)\"\]/ + log = Regex.replace(~r/\e\[[0-9]+m/, log, "") + log = Regex.replace(~r/\[info\]/, log, "") + log = Regex.named_captures(~r/\[(?.+)\]/, log) + log_params = String.split(log["params"], ",") + + # Compute the expected parameters in the right order. + # This involves recreating the headers in the same order + # as `insert_all`. The placeholders come first and then + # the user values and then the autogenerated id + headers = + row1 + |> Enum.reduce(%{}, fn {field, _}, headers -> Map.put(headers, field, true) end) + |> Map.put(:bid, true) + |> Map.drop([:inserted_at, :updated_at]) + |> Map.keys() + + row1_regex = [ + int: "#{int}", + uuid: ["#{int}", uuid], + bid: @uuid_regex + ] - param_logs = Regex.named_captures(param_regex, log) + row2_regex = [ + int: ["#{int2}", uuid2], + uuid: uuid2, + bid: @uuid_regex + ] - # Placeholders - assert param_logs["now_placeholder"] == "~N[#{now}]" - assert param_logs["now2_placeholder"] == "~N[#{now2}]" - # Autogenerated fields - assert param_logs["bid1"] =~ @uuid_regex - assert param_logs["bid2"] =~ @uuid_regex - # Row value parameters - assert param_logs["int1"] == Integer.to_string(int) - assert param_logs["uuid1_query_int"] == Integer.to_string(int) - assert param_logs["uuid1_query_uuid"] == uuid - assert param_logs["int2_query_int"] == Integer.to_string(int2) - assert param_logs["int2_query_uuid"] == uuid2 - assert param_logs["uuid2"] == uuid2 + row_param_regex = + Enum.flat_map([row1_regex, row2_regex], fn row -> + Enum.flat_map(headers, fn field -> + case Keyword.get(row, field) do + params when is_list(params) -> params + param -> [param] + end + end) + end) + + placeholder_regex = [inspect(datetime), inspect(datetime2)] + expected_param_regex = placeholder_regex ++ row_param_regex + + assert length(log_params) == length(expected_param_regex) + + Enum.zip(log_params, expected_param_regex) + |> Enum.each(fn {log, expected_regex} -> + assert log =~ expected_regex + end) end @tag :with_conflict_target @@ -362,26 +428,26 @@ defmodule Ecto.Integration.LoggingTest do int = 1 uuid = Ecto.UUID.generate() uuid_query = from l in Logging, where: l.int == ^int and l.uuid == ^uuid, select: l.uuid - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + datetime = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) row1 = [ int: int, uuid: uuid_query, - inserted_at: now, - updated_at: now + inserted_at: datetime, + updated_at: datetime ] # Row 2 int2 = 2 uuid2 = Ecto.UUID.generate() int_query = from l in Logging, where: l.int == ^int2 and l.uuid == ^uuid2, select: l.int - now2 = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + datetime2 = NaiveDateTime.add(datetime, 1) row2 = [ int: int_query, uuid: uuid2, - inserted_at: now2, - updated_at: now2 + inserted_at: datetime2, + updated_at: datetime2 ] # Conflict query @@ -394,7 +460,10 @@ defmodule Ecto.Integration.LoggingTest do where: l.int == ^conflict_int and l.uuid == ^conflict_uuid, update: [set: [int: ^conflict_update]] - # Ensure parameters are complete and in correct order + # Extract the parameters from the log: + # 1. Remove the colour codes + # 2. Remove the log level + # 3. Capture everything inside of the square brackets log = capture_log(fn -> TestRepo.insert_all(Logging, [row1, row2], @@ -404,29 +473,56 @@ defmodule Ecto.Integration.LoggingTest do ) end) - param_regex = - ~r/\[\"(?.+)\", (?.+), (?.+), (?.+), (?.+), \"(?.+)\", \"(?.+)\", (?.+), (?.+), \"(?.+)\", (?.+), \"(?.+)\", (?.+), (?.+), \"(?.+)\"\]/ + log = Regex.replace(~r/\e\[[0-9]+m/, log, "") + log = Regex.replace(~r/\[info\]/, log, "") + log = Regex.named_captures(~r/\[(?.+)\]/, log) + log_params = String.split(log["params"], ",") + + # Compute the expected parameters in the right order. + # This involves recreating the headers in the same order + # as `insert_all`. The user values come first, then + # the autogenerated id and then the conflict params + headers = + row1 + |> Enum.reduce(%{}, fn {field, _}, headers -> Map.put(headers, field, true) end) + |> Map.put(:bid, true) + |> Map.keys() + + row1_regex = [ + int: "#{int}", + uuid: ["#{int}", uuid], + bid: @uuid_regex, + inserted_at: inspect(datetime), + updated_at: inspect(datetime) + ] - param_logs = Regex.named_captures(param_regex, log) + row2_regex = [ + int: ["#{int2}", uuid2], + uuid: uuid2, + bid: @uuid_regex, + inserted_at: inspect(datetime2), + updated_at: inspect(datetime2) + ] - # Autogenerated fields - assert param_logs["bid1"] =~ @uuid_regex - assert param_logs["bid2"] =~ @uuid_regex - # Row value parameters - assert param_logs["int1"] == Integer.to_string(int) - assert param_logs["inserted_at1"] == "~N[#{now2}]" - assert param_logs["updated_at1"] == "~N[#{now2}]" - assert param_logs["uuid1_query_int"] == Integer.to_string(int) - assert param_logs["uuid1_query_uuid"] == uuid - assert param_logs["int2_query_int"] == Integer.to_string(int2) - assert param_logs["int2_query_uuid"] == uuid2 - assert param_logs["inserted_at2"] == "~N[#{now2}]" - assert param_logs["updated_at2"] == "~N[#{now2}]" - assert param_logs["uuid2"] == uuid2 - # Conflict query parameters - assert param_logs["conflict_update"] == Integer.to_string(conflict_update) - assert param_logs["conflict_int"] == Integer.to_string(conflict_int) - assert param_logs["conflict_uuid"] == conflict_uuid + row_param_regex = + Enum.flat_map([row1_regex, row2_regex], fn row -> + Enum.flat_map(headers, fn field -> + case Keyword.get(row, field) do + row_params when is_list(row_params) -> row_params + row_param -> [row_param] + end + end) + end) + + conflict_param_regex = ["#{conflict_update}", "#{conflict_int}", conflict_uuid] + expected_param_regex = row_param_regex ++ conflict_param_regex + + assert length(log_params) == length(expected_param_regex) + + Enum.zip(log_params, expected_param_regex) + |> Enum.each(fn {log, expected_regex} -> + assert log =~ expected_regex + end) end @tag :insert_select @@ -441,8 +537,8 @@ defmodule Ecto.Integration.LoggingTest do row1 = [ int: int, uuid: uuid_query, - inserted_at: {:placeholder, :now}, - updated_at: {:placeholder, :now2} + inserted_at: {:placeholder, :datetime}, + updated_at: {:placeholder, :datetime2} ] # Row 2 @@ -453,14 +549,14 @@ defmodule Ecto.Integration.LoggingTest do row2 = [ int: int_query, uuid: uuid2, - inserted_at: {:placeholder, :now}, - updated_at: {:placeholder, :now2} + inserted_at: {:placeholder, :datetime}, + updated_at: {:placeholder, :datetime2} ] # Placeholders - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - now2 = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - placeholder_map = %{now: now, now2: now2} + datetime = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + datetime2 = NaiveDateTime.add(datetime, 1) + placeholder_map = %{datetime: datetime, datetime2: datetime2} # Conflict query conflict_int = 0 @@ -472,7 +568,10 @@ defmodule Ecto.Integration.LoggingTest do where: l.int == ^conflict_int and l.uuid == ^conflict_uuid, update: [set: [int: ^conflict_update]] - # Ensure parameters are complete and in correct order + # Extract the parameters from the log: + # 1. Remove the colour codes + # 2. Remove the log level + # 3. Capture everything inside of the square brackets log = capture_log(fn -> TestRepo.insert_all(Logging, [row1, row2], @@ -483,28 +582,55 @@ defmodule Ecto.Integration.LoggingTest do ) end) - param_regex = - ~r/\[(?.+), (?.+), \"(?.+)\", (?.+), (?.+), \"(?.+)\", \"(?.+)\", (?.+), \"(?.+)\", \"(?.+)\", (?.+), (?.+), \"(?.+)\"\]/ + log = Regex.replace(~r/\e\[[0-9]+m/, log, "") + log = Regex.replace(~r/\[info\]/, log, "") + log = Regex.named_captures(~r/\[(?.+)\]/, log) + log_params = String.split(log["params"], ",") + + # Compute the expected parameters in the right order. + # This involves recreating the headers in the same order + # as `insert_all`. The placeholders come first, then the + # user value, then the autogenerated id and then the conflict + # params + headers = + row1 + |> Enum.reduce(%{}, fn {field, _}, headers -> Map.put(headers, field, true) end) + |> Map.put(:bid, true) + |> Map.drop([:inserted_at, :updated_at]) + |> Map.keys() + + row1_regex = [ + int: "#{int}", + uuid: ["#{int}", uuid], + bid: @uuid_regex + ] - param_logs = Regex.named_captures(param_regex, log) + row2_regex = [ + int: ["#{int2}", uuid2], + uuid: uuid2, + bid: @uuid_regex + ] - # Placeholders - assert param_logs["now_placeholder"] == "~N[#{now}]" - assert param_logs["now2_placeholder"] == "~N[#{now2}]" - # Autogenerated fields - assert param_logs["bid1"] =~ @uuid_regex - assert param_logs["bid2"] =~ @uuid_regex - # Row value parameters - assert param_logs["int1"] == Integer.to_string(int) - assert param_logs["uuid1_query_int"] == Integer.to_string(int) - assert param_logs["uuid1_query_uuid"] == uuid - assert param_logs["int2_query_int"] == Integer.to_string(int2) - assert param_logs["int2_query_uuid"] == uuid2 - assert param_logs["uuid2"] == uuid2 - # Conflict query parameters - assert param_logs["conflict_update"] == Integer.to_string(conflict_update) - assert param_logs["conflict_int"] == Integer.to_string(conflict_int) - assert param_logs["conflict_uuid"] == conflict_uuid + row_param_regex = + Enum.flat_map([row1_regex, row2_regex], fn row -> + Enum.flat_map(headers, fn field -> + case Keyword.get(row, field) do + row_params when is_list(row_params) -> row_params + row_param -> [row_param] + end + end) + end) + + placeholder_regex = [inspect(datetime), inspect(datetime2)] + conflict_param_regex = ["#{conflict_update}", "#{conflict_int}", conflict_uuid] + expected_param_regex = placeholder_regex ++ row_param_regex ++ conflict_param_regex + + assert length(log_params) == length(expected_param_regex) + + Enum.zip(log_params, expected_param_regex) + |> Enum.each(fn {log, expected_regex} -> + assert log =~ expected_regex + end) end test "for insert" do