Why am I getting this foreign key error in my phoenix tests?

I’ve got the following test in Phoenix:

test "list_orders/0 returns all orders" do
  {:ok, user} = %User{}
                |> User.changeset(@user_attrs)
                |> Repo.insert()

  changeset = %Order{}
              |> Order.changeset(@valid_attrs)
              |> Ecto.Changeset.put_change(:user_id, user.id)

  {:ok, order} = Repo.insert(changeset)

  assert MWS.list_orders() == [order]
end

Which is failing to insert changeset into the Repo because user_id is failing a foreign key constraint. But I’m using the user.id that’s returned from the user successfully being inserted into the database?

Any idea what I’m doing wrong here?

Thanks!

This is also only happening in test. I can create the user and order in my dev console.

Does @user_attrs contain hardcoded user ID?

nope: @user_attrs %{encrypted_password: "some encrypted_password", username: "some username", mws_token: "some mwstoken"}

BTW: here’s the migration where I added orders to users

defmodule BarronWatchCompany.Repo.Migrations.AddUsersToOrders do
  use Ecto.Migration

  def change do

    alter table(:orders) do
      add :user_id, references(:orders, on_delete: :nothing)
    end

    create index(:orders, [:user_id])
  end
end

Any hints, tips or tricks would be greatly appreciated. I’ve been stuck on this for too long :slight_smile:

Does the user id get set by ecto or the database? If it is set by the database it may not be read back by default which means you would be trying to insert an order with a nil user id. Can you show the inserted user struct and the full exception with stacktrace?

I’ve added a check to make sure the user is in the database:

    test "list_orders/0 returns all orders" do
      #user = insert!(:user_with_orders)


      {:ok, user} = %User{}
                    |> User.changeset(@user_attrs)
                    |> Repo.insert()

      assert user = Accounts.get_user!(user.id)

      IO.inspect(user)

      changeset = %Order{}
                  |> Order.changeset(@valid_attrs)
                  |> Ecto.Changeset.put_change(:user_id, user.id)

      {:ok, order} = Repo.insert(changeset)

      assert MWS.list_orders() == [order]
    end

And, here’s the stack trace:

  * test orders list_orders/0 returns all orders%BarronWatchCompany.Accounts.User{
  __meta__: #Ecto.Schema.Metadata<:loaded, "users">,
  encrypted_password: "$2b$12$TO1.pVVTkfV8ntK0kt8/Ie.mfCBo9YqKwW1RD9EbJimI/EEfNs8HG",
  id: 108,
  inserted_at: ~N[2019-10-16 20:28:33],
  mws_token: "some mwstoken",
  orders: #Ecto.Association.NotLoaded<association :orders is not loaded>,
  updated_at: ~N[2019-10-16 20:28:33],
  username: "some username"
}
  * test orders list_orders/0 returns all orders (432.2ms)

  1) test orders list_orders/0 returns all orders (BarronWatchCompany.MWSTest)
     test/barron_watch_company/mws/mws_test.exs:67
     ** (MatchError) no match of right hand side value: {:error, #Ecto.Changeset<action: :insert, changes: %{amazon_order_id: "some amazon_order_id", buyer_email: "some buyer_email", buyer_name: "some buyer_name", earliest_delivery_date: "some earliest_delivery_date", last_update_date: "some last_update_date", latest_delivery_date: "some latest_delivery_date", order_status: "some order_status", order_total: "some order_total", purchase_date: "some purchase_date", user_id: 108}, errors: [user_id: {"does not exist", [constraint: :foreign, constraint_name: "orders_user_id_fkey"]}], data: #BarronWatchCompany.MWS.Order<>, valid?: false>}
     code: {:ok, order} = Repo.insert(changeset)
     stacktrace:
       test/barron_watch_company/mws/mws_test.exs:83: (test)

--max-failures reached, aborting test suite

Finished in 0.9 seconds
7 tests, 1 failure

Randomized with seed 0

The MatchError is complaining about the same user.id:
** (MatchError) no match of right hand side value: {:error, #Ecto.Changeset<action: :insert, changes: %{... user_id: 108}, errors: [user_id: {"does not exist", [constraint: :foreign, constraint_name: "orders_user_id_fkey"]}], data: #BarronWatchCompany.MWS.Order<>, valid?: false>}

Found it!!!

My migration was busted. I was referencing orders instead of users. This migration gets the test to pass:

defmodule BarronWatchCompany.Repo.Migrations.AddUserToOrders do
  use Ecto.Migration

  def change do

    alter table(:orders) do
      remove :user_id
      add :user_id, references(:users, on_delete: :nothing)
    end

    create index(:orders, [:user_id])
  end
end

Now I just need to figure out how to write the migration so I don’t loose all of the associations.

1 Like