rakeshtripathy

rakeshtripathy

Exunit test error Ecto.Changeset.cast/4

I’m testing the test_controller and I’m getting this error:

** (FunctionClauseError) no function clause matching in Ecto.Changeset.cast/4
when attempting to create or update an object.

The following arguments were given to Ecto.Changeset.cast/4:

     # 1
     MyTest.Whatwasit.Version
 
     # 2
     %{action: "update", item_id: 730, item_type: "AdminUser", item_user_id: nil, object: %{admin_author_id: nil, availability_flag: false, email: "weren1930-1@gmail.com", failed_log_in_attempts: 0, full_name: "Petra fhghgf", groups: nil, id: 730, inserted_at: ~N[2023-06-09 10:46:47.691601], last_locked_at: nil, last_log_in_at: nil, last_log_in_remote_ip: nil, last_password_reset_at: nil, last_seen_at: nil, log_in_count: 0, reset_password_sent_at: nil, reset_password_token: nil, roles: ["admin"], updated_at: ~N[2023-06-09 10:46:47.692937], uuid: "df6789e9-f5a0-9876-80c4-0bgb72d777d2"}, whodoneit_admin_id: 729, whodoneit_admin_name: "test acc", whodoneit_id: nil, whodoneit_name: nil}
 
     # 3
     [:item_type, :item_id, :item_user_id, :object, :action, :whodoneit_id, :whodoneit_name, :whodoneit_admin_id, :whodoneit_admin_name]
 
     # 4
     []
 
 Attempted function clauses (showing 5 out of 5):
 
     def cast(_data, %{__struct__: _} = params, _permitted, _opts)
     def cast({data, types}, params, permitted, opts) when is_map(data)
     def cast(%Ecto.Changeset{types: nil}, _params, _permitted, _opts)
     def cast(%Ecto.Changeset{changes: changes, data: data, types: types, empty_values: empty_values} =
   changeset, params, permitted, opts)
     def cast(%{__struct__: module} = data, params, permitted, opts)
 
 code: patch(
 stacktrace:
   (ecto 3.3.4) Ecto.Changeset.cast/4
   (my_test 4.0.0) lib/my_test/shared/whatwasit_version.ex:42: MyTest.Whatwasit.Version.module_changeset/2
   (my_test 4.0.0) lib/my_test/shared/whatwasit_version.ex:117: MyTest.Whatwasit.Version.insert_version/3
   (ecto 3.3.4) lib/ecto/repo/schema.ex:528: anonymous fn/2 in Ecto.Repo.Schema.run_prepare/2
   (elixir 1.13.4) lib/enum.ex:2396: Enum."-reduce/3-lists^foldl/2-0-"/3
   (ecto 3.3.4) lib/ecto/repo/schema.ex:326: anonymous fn/15 in Ecto.Repo.Schema.do_update/4
   (ecto 3.3.4) lib/ecto/repo/schema.ex:919: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
   (ecto_sql 3.3.3) lib/ecto/adapters/sql.ex:886: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
   (db_connection 2.2.1) lib/db_connection.ex:1427: DBConnection.run_transaction/4
   (my_test 4.0.0) lib/my_test_web/controllers/admin/test_controller.ex:97: MyTestWeb.Admin.TestController.update/3
   (my_test 4.0.0) lib/my_test_web/controllers/admin/test_controller.ex:1: MyTestWeb.Admin.TestController.action/2
   (my_test 4.0.0) lib/my_test_web/controllers/admin/test_controller.ex:1: MyTestWeb.Admin.TestController.phoenix_controller_pipeline/2
   (phoenix 1.5.14) lib/phoenix/router.ex:352: Phoenix.Router.__call__/2
   (my_test 4.0.0) lib/my_test_web/endpoint.ex:1: MyTestWeb.Endpoint.plug_builder_call/2
   (my_test 4.0.0) lib/my_test_web/endpoint.ex:1: MyTestWeb.Endpoint.call/2
   (phoenix 1.5.14) lib/phoenix/test/conn_test.ex:225: Phoenix.ConnTest.dispatch/5
   test/my_test_web/controllers/admin/test_controller_test.exs:72: (test)

test_controller.ex

def update(conn, %{“admin_user” => admin_params}, current_admin) do
changeset =
AdminUser.common_changeset(conn.assigns.admin_user, admin_params, whodoneit(current_admin))

case Repo.update(changeset) do
  {:ok, _admin} ->
    msg = gettext("user updated successfully")

    conn
    |> put_flash(:success, msg)
    |> redirect(to: Routes.test_user_path(conn, :index))

  {:error, changeset} ->
    render(conn, "edit.html", changeset: changeset)
end

end

test_controller_test.ex

test “Cannot add ‘root’ role to admin-user”, %{auth_conn: conn} do
admin = insert(:admin_user)

  patch(
    conn,
    Routes.test_user_path(conn, :update, admin),
    %{admin_user: %{roles: ~w[root admin]}}
  )
  
end

end

It seems like Elixir is matching a Ecto.Changeset.cast/3 into a Ecto.Changeset.cast/4 adding an empty array as the 4th parameter, and the error is throwing in Repo.update(changeset).

Note: This exunit test was running earlier, but after upgrading the elixir version to 1.13.4 and phoenix version to 1.5.14 similar unit tests are failing.

Please help to resolve this error.

Thanks,
Rakesh

Marked As Solved

jswanner

jswanner

Going back to your original stack trace:

We can see the error is raised when calling Ecto.Changeset.cast/4, which is called from MyTest.Whatwasit.Version.module_changeset/2 (not provided), which is called by MyTest.Whatwasit.Version.insert_version/3 (provided).

We can see the call to MyTest.Whatwasit.Version.module_changeset/2, the first argument is an atom (version_module):

We know from your original post that atom is being passed as the first argument to cast/4, but it should be a struct or a changeset. What we don’t yet know is where the struct is intended to be created from that atom.

If that module_changeset/2 function has code to create a struct in it, then you’ll need to figure out why it’s not happening. Otherwise, you could change the code in the version_changeset function to create a struct. Such as:

version_module.module_changeset(
  struct!(version_module),
  %{…}
)

Also Liked

ruslandoga

ruslandoga

:waving_hand:

Could you please share your AdminUser.common_changeset and whodoneit implementations?

But judging just by the error, you are using an atom MyTest.Whatwasit.Version instead of a struct %MyTest.Whatwasit.Version{} for the first argument in Ecto.Changeset.cast and it causes the match error.

jswanner

jswanner

It looks like your problem is with

It’s making another call to Ecto.Changeset.cast/4, but instead of passing a struct or changeset as the first argument, it’s passing the atom MyTest.Whatwasit.Version.

rakeshtripathy

rakeshtripathy

Thank you @ruslandoga, Below are the details

In AdminUser.common_changeset :

def common_changeset(struct), do: common_changeset(struct, %{})

def common_changeset(struct, params) when is_map(params),
    do: common_changeset(struct, params, [])

def common_changeset(struct, opts) when is_list(opts), do: common_changeset(struct, %{}, opts)   
	   
def common_changeset(struct, params, opts) do
    max_length = Application.get_env(:my_test, :max_attribute_length) || 255
   
    struct
    |> email_changeset(params)
    |> cast(params, ~w[full_name roles groups availability_flag]a)
    |> validate_required(:full_name)
    |> validate_length(:full_name, max: max_length)
    |> put_roles
    |> put_groups
    |> prepare_version(opts)
end

whodoneit implementations :

def whodoneit(%User{} = user) do
    name =
      if user.first_name || user.last_name do
        [user.first_name, user.last_name]
        |> Enum.reject(&is_nil/1)
        |> Enum.join(" ")
      else
        user.email
      end

    [whodoneit: user, whodoneit_name: name]
  end

  def whodoneit(%AdminUser{} = admin) do
    [whodoneit_admin: admin, whodoneit_admin_name: admin.full_name]
  end

  def whodoneit(_), do: []
  def whodoneit, do: []

Also if I do inspect of Changeset, I am getting below result.

#Ecto.Changeset<
  action: nil,
  changes: %{roles: ["administrator"]},
  errors: [],
  data: #MyTest.AdminUser<>,
  valid?: true
>

Here Changeset valid is true, and I suspect issue is in Repo.update(changeset),
Please help to fix this issue.

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
New
shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New

Other popular topics Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

We're in Beta

About us Mission Statement