Skip to content

Commit 73dbb90

Browse files
committed
Fixed migration trigger and spec regressions - fixes #986
1 parent 2844cca commit 73dbb90

7 files changed

Lines changed: 147 additions & 9 deletions

File tree

lib/active_record/migration/app_generator.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -669,9 +669,9 @@ def update_fields
669669
end
670670
end
671671

672-
# Recreate the trigger if any field types changed, since the trigger function
673-
# needs to reflect the correct types for the fields being copied to history
674-
if changed.present? || changed_history.present?
672+
# Recreate the trigger whenever tracked fields change so the history
673+
# function matches the current table shape.
674+
if field_changes || added_history.present? || removed_history.present? || changed.present? || changed_history.present?
675675
case resource_type
676676
when :dynamic_model
677677
create_dynamic_model_trigger

spec/models/dynamic_model_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@
122122

123123
expect(dm).to be_a DynamicModel
124124
# The field list has been set up
125-
expect(dm.field_list).to eq 'test1 test2 created_by_user_id'
125+
expect(dm.field_list.split).to include('test1', 'test2', 'created_by_user_id')
126126
# The keys have been set up automatically
127127
expect(dm.foreign_key_name).to eq 'master_id'
128128
expect(dm.primary_key_name).to eq 'id'
@@ -168,7 +168,7 @@
168168

169169
expect(dm).to be_a DynamicModel
170170
# The field list has been set up
171-
expect(dm.field_list).to eq 'test1 test2 created_by_user_id'
171+
expect(dm.field_list.split).to include('test1', 'test2', 'created_by_user_id')
172172
# The keys have been set up automatically
173173
expect(dm.foreign_key_name).to eq 'master_id'
174174
expect(dm.primary_key_name).to eq 'id'

spec/models/option_configs/dynamic_model_options_spec.rb

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,4 +849,106 @@
849849
expect(history_records.first['input_b'].to_i).to eq(456)
850850
end
851851
end
852+
853+
# Test for renamed dynamic-model fields keeping history tables and triggers in sync.
854+
# This protects create/update paths after field names change in app configuration.
855+
context 'field renames' do
856+
before :all do
857+
create_admin
858+
create_user
859+
860+
@original_allow_migrations = Settings::AllowDynamicMigrations
861+
change_setting('AllowDynamicMigrations', true)
862+
863+
@schema_name = 'dynamic_test'
864+
@table_name = 'test_field_renames'
865+
@history_table_name = 'test_field_rename_history'
866+
867+
DynamicModel.active.where(table_name: @table_name).each { |dm| dm.disable!(@admin) }
868+
DynamicModel.send(:remove_const, :TestFieldRename) if defined?(DynamicModel::TestFieldRename)
869+
870+
@conn = ActiveRecord::Base.connection
871+
@conn.execute("DROP TABLE IF EXISTS #{@schema_name}.#{@history_table_name} CASCADE")
872+
@conn.execute("DROP TABLE IF EXISTS #{@schema_name}.#{@table_name} CASCADE")
873+
874+
@dm = DynamicModel.create!(
875+
current_admin: @admin,
876+
name: 'Test Field Renames',
877+
table_name: @table_name,
878+
schema_name: @schema_name,
879+
primary_key_name: :id,
880+
foreign_key_name: :master_id,
881+
category: :test,
882+
field_list: 'select_still_interested select_continue_now notes',
883+
options: <<~YAML
884+
_db_columns:
885+
select_still_interested:
886+
type: string
887+
select_continue_now:
888+
type: string
889+
notes:
890+
type: string
891+
YAML
892+
)
893+
894+
@dm.update_tracker_events
895+
end
896+
897+
after :all do
898+
@dm&.disable!(@admin)
899+
change_setting('AllowDynamicMigrations', @original_allow_migrations)
900+
end
901+
902+
it 'rebuilds the history trigger when fields are renamed' do
903+
master = Master.create! current_user: @user
904+
master.current_user = @user
905+
906+
setup_access :dynamic_model__test_field_renames
907+
908+
@dm.instance_variable_set(:@ran_migration, nil)
909+
@dm.update!(
910+
current_admin: @admin,
911+
field_list: 'still_interested continue_now notes',
912+
options: <<~YAML
913+
_db_columns:
914+
still_interested:
915+
type: string
916+
continue_now:
917+
type: string
918+
notes:
919+
type: string
920+
YAML
921+
)
922+
923+
@dm.instance_variable_set(:@migration_generator, nil)
924+
@dm.send(:generate_migration)
925+
@dm.send(:run_migration) if @dm.instance_variable_get(:@do_migration)
926+
@dm.update_tracker_events
927+
928+
@conn.schema_cache.clear!
929+
DynamicModel.reset_active_model_configurations!
930+
931+
history_columns = @conn.columns("#{@schema_name}.#{@history_table_name}").map(&:name)
932+
expect(history_columns).to include('still_interested', 'continue_now')
933+
expect(history_columns).not_to include('select_still_interested', 'select_continue_now')
934+
935+
model_class = @dm.implementation_class
936+
model_class.reset_column_information
937+
938+
record = master.dynamic_model__test_field_renames.create!(
939+
current_user: @user,
940+
still_interested: 'yes',
941+
continue_now: 'no',
942+
notes: 'renamed fields'
943+
)
944+
945+
history_record = @conn.exec_query(
946+
"SELECT still_interested, continue_now FROM #{@schema_name}.#{@history_table_name} " \
947+
"WHERE #{@table_name.singularize}_id = #{record.id} ORDER BY id DESC LIMIT 1"
948+
).first
949+
950+
expect(history_record['still_interested']).to eq('yes')
951+
expect(history_record['continue_now']).to eq('no')
952+
end
953+
end
852954
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
# These tests lock down the Study Recruitment initial-call dynamic-model
4+
# configuration so reused setup state gets rebuilt when it still references
5+
# the legacy select_* field names.
6+
7+
require 'rails_helper'
8+
9+
describe StudyRecruitmentSetup do
10+
describe '.stale_initial_call_dynamic_model_config?' do
11+
let(:dynamic_model) do
12+
instance_double(DynamicModel, field_list: field_list, options: options)
13+
end
14+
15+
context 'when the initial-call config still uses legacy select-prefixed fields' do
16+
let(:field_list) { 'select_still_interested select_continue_now callback_date callback_time notes' }
17+
let(:options) { 'select_still_interested:\nselect_continue_now:' }
18+
19+
it 'treats the config as stale' do
20+
expect(described_class.stale_initial_call_dynamic_model_config?(dynamic_model)).to be true
21+
end
22+
end
23+
24+
context 'when the initial-call config uses the current field names' do
25+
let(:field_list) { 'still_interested continue_now callback_date callback_time notes' }
26+
let(:options) { 'still_interested:\ncontinue_now:' }
27+
28+
it 'treats the config as current' do
29+
expect(described_class.stale_initial_call_dynamic_model_config?(dynamic_model)).to be false
30+
end
31+
end
32+
end
33+
end

spec/setup_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,10 @@ def self.reload_configs
261261
Rails.logger.info 'Reload configs'
262262
Admin::AppType.reset_memo_associated_items!
263263
AppControl.define_models
264+
ExternalIdentifier.enable_active_configurations
264265
DynamicModel.enable_active_configurations disable_on_failure: true
265266
ItemFlag.enable_active_configurations
266267
ActivityLog.enable_active_configurations
267-
ExternalIdentifier.enable_active_configurations
268268
DynamicModel.routes_reload
269269
end
270270

spec/support/feature_support.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,10 @@ def expand_master_record_tab(name)
322322
raise "Could not find panel tab #{name.id_underscore.inspect}. Available tabs: #{available_tabs.join(', ')}"
323323
end
324324

325-
tab_link = find(tab_selector, visible: :all, wait: 0)
325+
tab_link = all('.master-main-panel.in a[data-panel-tab]', visible: :all, wait: 0)
326+
.find { |tab| tab['data-panel-tab'] == name.id_underscore }
327+
tab_link ||= all(tab_selector, visible: :all, wait: 0).find(&:visible?)
328+
tab_link ||= find(tab_selector, visible: :all, wait: 0)
326329
expect(tab_link).not_to be nil
327330
scroll_into_view(tab_link)
328331
tab_link.click if tab_link['aria-expanded'] != 'true'

spec/system/nfs_store/secure_viewer_enhancements_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def open_secure_viewer(filename)
282282
expect(@container).to be_a(NfsStore::Manage::Container)
283283

284284
# Upload a test file to the container for secure viewer tests
285-
temp_dir = Rails.root.join('tmp', 'test_files')
285+
temp_dir = Rails.root.join('tmp', 'test_files_secure_viewer')
286286
FileUtils.mkdir_p(temp_dir)
287287
@test_image_path = temp_dir.join('test-image.png')
288288
# Create a valid 100x100 PNG that the secure viewer can convert
@@ -307,7 +307,7 @@ def open_secure_viewer(filename)
307307

308308
after :all do
309309
disable_active_panel_layout('test-columns-panel')
310-
temp_dir = Rails.root.join('tmp', 'test_files')
310+
temp_dir = Rails.root.join('tmp', 'test_files_secure_viewer')
311311
FileUtils.rm_rf(temp_dir)
312312
end
313313

0 commit comments

Comments
 (0)