Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions