Skip to content

Setting a relationship explicitly to null that uses a CPK triggers odd and incorrect validation error #612

@erikbelusic

Description

@erikbelusic

We are on rails 6.1 and CPK 13, however this persists in rails 7.0 and CPK 14 and rails 7.1 without CPK

class CreateOrganizations < ActiveRecord::Migration[6.1]
  def change
    create_table :organizations do |t|
      t.text :name
      t.timestamps
    end
  end
end

class CreateV1PmtUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :v1_pmt_users do |t|
      t.belongs_to :organization, null: false
      t.text :name
      t.timestamps
    end
  end
end

class CreateV1Issues < ActiveRecord::Migration[6.1]
  def change
    create_table :v1_issues do |t|
      t.belongs_to :organization, null: false
      t.belongs_to :reporter_pmt_user
      t.text :description
      t.timestamps
    end
  end
end


class CreateV2Issues < ActiveRecord::Migration[6.1]
  def change
    create_table :v2_issues, primary_key: [:organization_id, :pmt_id, :external_id] do |t|
      t.integer :organization_id, null: false
      t.integer :pmt_id, null: false
      t.text :external_id, null: false

      t.text :reporter_pmt_user_external_id
      t.text :title, null: false

      t.timestamps
    end
  end
end

class CreateV2PmtUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :v2_pmt_users, primary_key: [:organization_id, :pmt_id, :external_id] do |t|
      t.integer :organization_id, null: false
      t.integer :pmt_id, null: false
      t.text :external_id, null: false
      t.text :name, null: false

      t.timestamps
    end
  end
end

class Organization < ApplicationRecord
end

class V1Issue < ApplicationRecord
  belongs_to :organization
  belongs_to :reporter_pmt_user, class_name: "V1PmtUser", optional: true
end

class V1PmtUser < ApplicationRecord
  belongs_to :organization
end

class V2Issue < ApplicationRecord
  self.primary_keys = :organization_id, :pmt_id, :external_id

  belongs_to :organization
  belongs_to :reporter_pmt_user, class_name: "V2PmtUser", foreign_key: %i[organization_id pmt_id reporter_pmt_user_external_id], optional: true
end

class V2PmtUser < ApplicationRecord
  self.primary_keys = :organization_id, :pmt_id, :external_id

  belongs_to :organization

end

Tests for non CPK models in rspec - all pass

require 'rails_helper'

describe "models without CPK - saving relationships" do
  before(:each) do
    @organization = Organization.create!(name: "test org")
  end

  it "creates without setting reporter" do
    issue = V1Issue.find_or_initialize_by(organization_id: @organization.id, description: "test1")
    expect do
      issue.save!
    end.to change(V1Issue, :count).by(1)
  end

  it "creates with setting reporter" do
    issue = V1Issue.find_or_initialize_by(organization_id: @organization.id, description: "test1")
    expect do
      reporter_pmt_user = V1PmtUser.create!(organization_id: @organization.id, name: "test1")
      issue.update!(reporter_pmt_user: reporter_pmt_user)
    end.to change(V1Issue, :count).by(1).and change(V1PmtUser, :count).by(1)
  end

  it "creates with explicitly setting reporter to nil" do
    issue = V1Issue.find_or_initialize_by(organization_id: @organization.id, description: "test1")
    expect do
      issue.update!(reporter_pmt_user: nil)
    end.to change(V1Issue, :count).by(1)
  end
end

Tests for CPK models - last test fails

require 'rails_helper'

describe "models with CPK - saving relationships" do
  before(:each) do
    @organization = Organization.create!(name: "test org")
    @pmt_id = 1
    @issue_external_id = "1"
  end

  it "creates without setting reporter" do
    issue = V2Issue.find_or_initialize_by(organization_id: @organization.id, pmt_id: @pmt_id, external_id: @issue_external_id)
    expect do
      issue.update!(title: "test")
    end.to change(V2Issue, :count).by(1)
  end

  it "creates with setting reporter" do
    issue = V2Issue.find_or_initialize_by(organization_id: @organization.id, pmt_id: @pmt_id, external_id: @issue_external_id)
    reporter_pmt_user = nil # init
    expect do
      reporter_pmt_user = V2PmtUser.create!(organization_id: @organization.id, pmt_id: @pmt_id, external_id: "abc", name: "test1")
      issue.update!(title: "test", reporter_pmt_user: reporter_pmt_user)
    end.to change(V2Issue, :count).by(1).and change(V2PmtUser, :count).by(1)

    expect(issue).to have_attributes(title: "test", reporter_pmt_user: reporter_pmt_user)
  end

  # The failing spec
  it "creates with explicitly setting reporter to nil" do
    issue = V2Issue.find_or_initialize_by(organization_id: @organization.id, pmt_id: @pmt_id, external_id: @issue_external_id)
    expect do
      issue.update!(reporter_pmt_user: nil)
    end.to change(V2Issue, :count).by(1)
  end
end

failure:

1) models with CPK - saving relationships creates with explicitly setting reporter to nil
     Failure/Error: issue.update!(title: "test", reporter_pmt_user: nil)

     ActiveRecord::RecordInvalid:
       Validation failed: Organization must exist

The expected behavior is that i can null out the relationship the same way i can in the non CPK example

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions