2019-04-29 20:27:42 -04:00
# frozen_string_literal: true
2022-07-27 22:27:38 -04:00
RSpec . describe HasCustomFields do
2022-07-27 12:14:14 -04:00
describe " custom_fields " do
2014-04-25 08:10:54 -04:00
before do
2018-06-19 02:13:14 -04:00
DB . exec ( " create temporary table custom_fields_test_items(id SERIAL primary key) " )
2019-07-06 14:42:03 -04:00
DB . exec (
" create temporary table custom_fields_test_item_custom_fields(id SERIAL primary key, custom_fields_test_item_id int, name varchar(256) not null, value text, created_at TIMESTAMP, updated_at TIMESTAMP) " ,
)
2019-07-04 13:25:09 -04:00
DB . exec ( << ~ SQL )
CREATE UNIQUE INDEX ON custom_fields_test_item_custom_fields ( custom_fields_test_item_id )
WHERE NAME = 'rare'
SQL
2014-04-25 08:10:54 -04:00
2014-04-28 04:37:34 -04:00
class CustomFieldsTestItem < ActiveRecord :: Base
2014-04-28 04:31:51 -04:00
include HasCustomFields
2014-04-25 08:10:54 -04:00
end
2014-04-28 04:37:34 -04:00
class CustomFieldsTestItemCustomField < ActiveRecord :: Base
belongs_to :custom_fields_test_item
2014-04-25 08:10:54 -04:00
end
end
after do
2018-06-19 02:13:14 -04:00
DB . exec ( " drop table custom_fields_test_items " )
DB . exec ( " drop table custom_fields_test_item_custom_fields " )
2014-04-25 08:10:54 -04:00
2019-04-30 02:58:18 -04:00
# this weakref in the descendant tracker should clean up the two tests
# if this becomes an issue we can revisit (watch out for erratic tests)
Object . send ( :remove_const , :CustomFieldsTestItem )
Object . send ( :remove_const , :CustomFieldsTestItemCustomField )
2014-04-25 08:10:54 -04:00
end
2022-01-20 23:29:51 -05:00
it " allows preloading of custom fields " do
test_item = CustomFieldsTestItem . new
CustomFieldsTestItem . preload_custom_fields ( [ test_item ] , [ " test_field " ] )
expect ( test_item . preloaded_custom_fields ) . to eq ( { " test_field " = > nil } )
end
it " errors if a custom field is not preloaded " do
test_item = CustomFieldsTestItem . new
CustomFieldsTestItem . preload_custom_fields ( [ test_item ] , [ " test_field " ] )
expect { test_item . custom_fields [ " other_field " ] } . to raise_error (
HasCustomFields :: NotPreloadedError ,
)
end
it " resets the preloaded_custom_fields if preload_custom_fields is called twice " do
test_item = CustomFieldsTestItem . new
CustomFieldsTestItem . preload_custom_fields ( [ test_item ] , [ " test_field " ] )
CustomFieldsTestItem . preload_custom_fields ( [ test_item ] , [ " other_field " ] )
expect ( test_item . preloaded_custom_fields ) . to eq ( { " other_field " = > nil } )
end
it " does not error with NotPreloadedError if preload_custom_fields is called twice " do
test_item = CustomFieldsTestItem . new
CustomFieldsTestItem . preload_custom_fields ( [ test_item ] , [ " test_field " ] )
expect { test_item . custom_fields [ " test_field " ] } . not_to raise_error
CustomFieldsTestItem . preload_custom_fields ( [ test_item ] , [ " other_field " ] )
expect { test_item . custom_fields [ " other_field " ] } . not_to raise_error
end
2019-07-04 13:25:09 -04:00
it " allows simple modification of custom fields " do
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . new
2014-04-25 08:10:54 -04:00
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( nil )
2014-04-25 08:10:54 -04:00
test_item . custom_fields [ " bob " ] = " marley "
test_item . custom_fields [ " jack " ] = " black "
2014-04-25 13:15:23 -04:00
2014-04-25 08:10:54 -04:00
test_item . save
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . find ( test_item . id )
2014-04-25 08:10:54 -04:00
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " bob " ] ) . to eq ( " marley " )
expect ( test_item . custom_fields [ " jack " ] ) . to eq ( " black " )
2014-04-25 08:10:54 -04:00
test_item . custom_fields . delete ( " bob " )
test_item . custom_fields [ " jack " ] = " jill "
test_item . save
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . find ( test_item . id )
2014-04-25 08:10:54 -04:00
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields ) . to eq ( " jack " = > " jill " )
2014-04-25 08:10:54 -04:00
end
2014-04-25 13:15:23 -04:00
it " casts integers to string without error " do
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . new
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( nil )
2014-04-25 13:15:23 -04:00
test_item . custom_fields [ " a " ] = 0
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( 0 )
2014-04-25 13:15:23 -04:00
test_item . save
# should be casted right after saving
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-25 13:15:23 -04:00
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-25 13:15:23 -04:00
end
2019-07-04 13:25:09 -04:00
it " reloads from the database " do
2014-04-29 13:23:13 -04:00
test_item = CustomFieldsTestItem . new
test_item . custom_fields [ " a " ] = 0
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( 0 )
2014-04-29 13:23:13 -04:00
test_item . save
# should be casted right after saving
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-29 13:23:13 -04:00
2018-06-19 02:13:14 -04:00
DB . exec (
" UPDATE custom_fields_test_item_custom_fields SET value='1' WHERE custom_fields_test_item_id=? AND name='a' " ,
test_item . id ,
)
2014-04-29 13:23:13 -04:00
# still the same, did not load
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-29 13:23:13 -04:00
# refresh loads from database
2015-01-09 11:34:37 -05:00
expect ( test_item . reload . custom_fields [ " a " ] ) . to eq ( " 1 " )
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 1 " )
2014-04-29 13:23:13 -04:00
end
2014-04-25 12:22:49 -04:00
2019-07-04 13:25:09 -04:00
it " actually saves on double save " do
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . new
2014-04-25 12:22:49 -04:00
test_item . custom_fields = { " a " = > " b " }
test_item . save
test_item . custom_fields [ " c " ] = " d "
test_item . save
2014-04-28 04:37:34 -04:00
db_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-09 11:34:37 -05:00
expect ( db_item . custom_fields ) . to eq ( " a " = > " b " , " c " = > " d " )
2014-04-25 12:22:49 -04:00
end
2014-04-25 13:15:23 -04:00
it " handles arrays properly " do
2018-05-22 02:48:39 -04:00
CustomFieldsTestItem . register_custom_field_type " array " , [ :integer ]
test_item = CustomFieldsTestItem . new
test_item . custom_fields = { " array " = > [ " 1 " ] }
test_item . save
db_item = CustomFieldsTestItem . find ( test_item . id )
expect ( db_item . custom_fields ) . to eq ( " array " = > [ 1 ] )
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . new
2014-04-25 13:15:23 -04:00
test_item . custom_fields = { " a " = > %w[ b c d ] }
test_item . save
2014-04-28 04:37:34 -04:00
db_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-09 11:34:37 -05:00
expect ( db_item . custom_fields ) . to eq ( " a " = > %w[ b c d ] )
2014-04-25 13:15:23 -04:00
2015-01-02 15:56:44 -05:00
db_item . custom_fields . update ( " a " = > %w[ c d ] )
2014-04-25 13:15:23 -04:00
db_item . save
2015-01-09 11:34:37 -05:00
expect ( db_item . custom_fields ) . to eq ( " a " = > %w[ c d ] )
2014-04-25 13:15:23 -04:00
2015-01-15 15:31:31 -05:00
# It can be updated to the exact same value
db_item . custom_fields . update ( " a " = > [ " c " ] )
db_item . save
expect ( db_item . custom_fields ) . to eq ( " a " = > " c " )
db_item . custom_fields . update ( " a " = > [ " c " ] )
db_item . save
expect ( db_item . custom_fields ) . to eq ( " a " = > " c " )
2015-01-02 15:56:44 -05:00
db_item . custom_fields . delete ( " a " )
2015-01-09 11:34:37 -05:00
expect ( db_item . custom_fields ) . to eq ( { } )
2014-04-25 13:15:23 -04:00
end
2021-06-25 05:34:51 -04:00
it " deletes nil-filled arrays " do
test_item = CustomFieldsTestItem . create!
db_item = CustomFieldsTestItem . find ( test_item . id )
db_item . custom_fields . update ( " a " = > [ nil , nil ] )
db_item . save_custom_fields
db_item . custom_fields . delete ( " a " )
expect ( db_item . custom_fields ) . to eq ( { } )
db_item . save_custom_fields
expect ( db_item . custom_fields ) . to eq ( { } )
end
2014-04-25 13:15:23 -04:00
it " casts integers in arrays properly without error " do
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . new
2014-04-25 13:15:23 -04:00
test_item . custom_fields = { " a " = > [ " b " , 10 , " d " ] }
test_item . save
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields ) . to eq ( " a " = > %w[ b 10 d ] )
2014-04-25 13:15:23 -04:00
2014-04-28 04:37:34 -04:00
db_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-09 11:34:37 -05:00
expect ( db_item . custom_fields ) . to eq ( " a " = > %w[ b 10 d ] )
2014-04-25 13:15:23 -04:00
end
2021-05-20 21:43:47 -04:00
it " supports type coercion " do
2014-06-16 22:42:12 -04:00
test_item = CustomFieldsTestItem . new
CustomFieldsTestItem . register_custom_field_type ( " bool " , :boolean )
CustomFieldsTestItem . register_custom_field_type ( " int " , :integer )
2015-04-23 13:33:29 -04:00
CustomFieldsTestItem . register_custom_field_type ( " json " , :json )
2014-06-16 22:42:12 -04:00
2015-04-23 13:33:29 -04:00
test_item . custom_fields = { " bool " = > true , " int " = > 1 , " json " = > { " foo " = > " bar " } }
2014-06-16 22:42:12 -04:00
test_item . save
test_item . reload
2015-04-23 13:33:29 -04:00
expect ( test_item . custom_fields ) . to eq (
" bool " = > true ,
" int " = > 1 ,
" json " = > {
" foo " = > " bar " ,
} ,
)
2017-08-16 17:04:40 -04:00
before_ids =
CustomFieldsTestItemCustomField . where ( custom_fields_test_item_id : test_item . id ) . pluck ( :id )
test_item . custom_fields [ " bool " ] = false
test_item . save
after_ids =
CustomFieldsTestItemCustomField . where ( custom_fields_test_item_id : test_item . id ) . pluck ( :id )
# we updated only 1 custom field, so there should be only 1 different id
expect ( ( before_ids - after_ids ) . size ) . to eq ( 1 )
2014-06-16 22:42:12 -04:00
end
2019-07-04 13:25:09 -04:00
it " doesn't allow simple modifications to interfere " do
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . new
2014-04-25 08:10:54 -04:00
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( nil )
2014-04-25 08:10:54 -04:00
test_item . custom_fields [ " bob " ] = " marley "
test_item . custom_fields [ " jack " ] = " black "
test_item . save
2014-04-28 04:37:34 -04:00
test_item2 = CustomFieldsTestItem . new
2014-04-25 08:10:54 -04:00
2015-01-09 11:34:37 -05:00
expect ( test_item2 . custom_fields [ " x " ] ) . to eq ( nil )
2014-04-25 08:10:54 -04:00
test_item2 . custom_fields [ " sixto " ] = " rodriguez "
test_item2 . custom_fields [ " de " ] = " la playa "
test_item2 . save
2014-04-28 04:37:34 -04:00
test_item = CustomFieldsTestItem . find ( test_item . id )
test_item2 = CustomFieldsTestItem . find ( test_item2 . id )
2014-04-25 08:10:54 -04:00
2015-01-09 11:34:37 -05:00
expect ( test_item . custom_fields ) . to eq ( " jack " = > " black " , " bob " = > " marley " )
expect ( test_item2 . custom_fields ) . to eq ( " sixto " = > " rodriguez " , " de " = > " la playa " )
2014-04-25 08:10:54 -04:00
end
2014-05-14 14:38:04 -04:00
2018-09-13 03:59:17 -04:00
it " supports arrays in json fields " do
field_type = " json_array "
CustomFieldsTestItem . register_custom_field_type ( field_type , :json )
item = CustomFieldsTestItem . new
item . custom_fields = { " json_array " = > [ { a : " test " } , { b : " another " } ] }
item . save
item . reload
expect ( item . custom_fields [ field_type ] ) . to eq ( [ { " a " = > " test " } , { " b " = > " another " } ] )
item . custom_fields [ " json_array " ] = %w[ a b ]
item . save
item . reload
expect ( item . custom_fields [ field_type ] ) . to eq ( %w[ a b ] )
end
it " will not fail to load custom fields if json is corrupt " do
field_type = " bad_json "
CustomFieldsTestItem . register_custom_field_type ( field_type , :json )
item = CustomFieldsTestItem . create!
CustomFieldsTestItemCustomField . create! (
custom_fields_test_item_id : item . id ,
name : field_type ,
value : " {test " ,
)
item = item . reload
expect ( item . custom_fields [ field_type ] ) . to eq ( { } )
end
2014-05-14 14:38:04 -04:00
it " supports bulk retrieval with a list of ids " do
item1 = CustomFieldsTestItem . new
2020-07-26 20:23:54 -04:00
item1 . custom_fields = { " a " = > %w[ b c d ] , " not_allowlisted " = > " secret " }
2014-05-14 14:38:04 -04:00
item1 . save
item2 = CustomFieldsTestItem . new
item2 . custom_fields = { " e " = > " hallo " }
item2 . save
fields = CustomFieldsTestItem . custom_fields_for_ids ( [ item1 . id , item2 . id ] , %w[ a e ] )
2015-01-09 11:34:37 -05:00
expect ( fields ) . to be_present
expect ( fields [ item1 . id ] [ " a " ] ) . to match_array ( %w[ b c d ] )
2020-07-26 20:23:54 -04:00
expect ( fields [ item1 . id ] [ " not_allowlisted " ] ) . to be_blank
2015-01-09 11:34:37 -05:00
expect ( fields [ item2 . id ] [ " e " ] ) . to eq ( " hallo " )
2014-05-14 14:38:04 -04:00
end
2017-11-07 22:10:20 -05:00
it " handles interleaving saving properly " do
field_type = " deep-nest-test "
CustomFieldsTestItem . register_custom_field_type ( field_type , :json )
test_item = CustomFieldsTestItem . create!
test_item . custom_fields [ field_type ] || = { }
test_item . custom_fields [ field_type ] [ " b " ] || = { }
test_item . custom_fields [ field_type ] [ " b " ] [ " c " ] = " d "
test_item . save_custom_fields ( true )
db_item = CustomFieldsTestItem . find ( test_item . id )
db_item . custom_fields [ field_type ] [ " b " ] [ " e " ] = " f "
test_item . custom_fields [ field_type ] [ " b " ] [ " e " ] = " f "
expected = { field_type = > { " b " = > { " c " = > " d " , " e " = > " f " } } }
db_item . save_custom_fields ( true )
expect ( db_item . reload . custom_fields ) . to eq ( expected )
test_item . save_custom_fields ( true )
expect ( test_item . reload . custom_fields ) . to eq ( expected )
end
2018-03-02 12:45:34 -05:00
2022-04-25 12:19:39 -04:00
it " determines clean state correctly for mutable fields " do
json_field = " json_field "
array_field = " array_field "
CustomFieldsTestItem . register_custom_field_type ( json_field , :json )
CustomFieldsTestItem . register_custom_field_type ( array_field , :array )
item_with_array = CustomFieldsTestItem . new
expect ( item_with_array . custom_fields_clean? ) . to eq ( true )
item_with_array . custom_fields [ array_field ] = [ 1 ]
expect ( item_with_array . custom_fields_clean? ) . to eq ( false )
item_with_array . save!
expect ( item_with_array . custom_fields_clean? ) . to eq ( true )
item_with_array . custom_fields [ array_field ] << 2
expect ( item_with_array . custom_fields_clean? ) . to eq ( false )
item_with_array . save!
expect ( item_with_array . custom_fields_clean? ) . to eq ( true )
item_with_json = CustomFieldsTestItem . new
expect ( item_with_json . custom_fields_clean? ) . to eq ( true )
item_with_json . custom_fields [ json_field ] = { " hello " = > " world " }
expect ( item_with_json . custom_fields_clean? ) . to eq ( false )
item_with_json . save!
expect ( item_with_json . custom_fields_clean? ) . to eq ( true )
item_with_json . custom_fields [ json_field ] [ " hello " ] = " world2 "
expect ( item_with_json . custom_fields_clean? ) . to eq ( false )
item_with_json . save!
expect ( item_with_json . custom_fields_clean? ) . to eq ( true )
end
2019-07-04 13:25:09 -04:00
describe " create_singular " do
it " creates new records " do
item = CustomFieldsTestItem . create!
item . create_singular ( " hello " , " world " )
expect ( item . reload . custom_fields [ " hello " ] ) . to eq ( " world " )
end
it " upserts on a database constraint error " do
item0 = CustomFieldsTestItem . new
item0 . custom_fields = { " rare " = > " gem " }
item0 . save
2019-07-06 14:42:03 -04:00
expect ( item0 . reload . custom_fields [ " rare " ] ) . to eq ( " gem " )
2019-07-04 13:25:09 -04:00
item0 . create_singular ( " rare " , " diamond " )
expect ( item0 . reload . custom_fields [ " rare " ] ) . to eq ( " diamond " )
end
end
2018-03-02 12:45:34 -05:00
describe " upsert_custom_fields " do
it " upserts records " do
test_item = CustomFieldsTestItem . create
test_item . upsert_custom_fields ( " hello " = > " world " , " abc " = > " def " )
# In memory
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " def " )
# Persisted
test_item . reload
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " def " )
# In memory
test_item . upsert_custom_fields ( " abc " = > " ghi " )
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " ghi " )
# Persisted
test_item . reload
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " ghi " )
end
2019-10-29 14:34:28 -04:00
it " allows upsert to use keywords " do
test_item = CustomFieldsTestItem . create
test_item . upsert_custom_fields ( hello : " world " , abc : " def " )
# In memory
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " def " )
# Persisted
test_item . reload
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " def " )
# In memory
test_item . upsert_custom_fields ( " abc " = > " ghi " )
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " ghi " )
# Persisted
test_item . reload
expect ( test_item . custom_fields [ " hello " ] ) . to eq ( " world " )
expect ( test_item . custom_fields [ " abc " ] ) . to eq ( " ghi " )
end
2020-08-25 02:09:34 -04:00
2020-08-25 06:28:41 -04:00
it " allows using string and symbol indices interchangeably " do
2020-08-25 02:09:34 -04:00
test_item = CustomFieldsTestItem . new
test_item . custom_fields [ " bob " ] = " marley "
test_item . custom_fields [ " jack " ] = " black "
2022-01-20 23:29:51 -05:00
# In memory
expect ( test_item . custom_fields [ :bob ] ) . to eq ( " marley " )
expect ( test_item . custom_fields [ :jack ] ) . to eq ( " black " )
2020-08-25 02:09:34 -04:00
2022-01-20 23:29:51 -05:00
# Persisted
test_item . save
test_item . reload
expect ( test_item . custom_fields [ :bob ] ) . to eq ( " marley " )
expect ( test_item . custom_fields [ :jack ] ) . to eq ( " black " )
2020-08-25 02:09:34 -04:00
2022-01-20 23:29:51 -05:00
# Update via string index again
test_item . custom_fields [ " bob " ] = " the builder "
2020-08-25 02:09:34 -04:00
2022-01-20 23:29:51 -05:00
expect ( test_item . custom_fields [ :bob ] ) . to eq ( " the builder " )
test_item . save
test_item . reload
2020-08-25 02:09:34 -04:00
2022-01-20 23:29:51 -05:00
expect ( test_item . custom_fields [ :bob ] ) . to eq ( " the builder " )
2020-08-25 02:09:34 -04:00
end
2018-03-02 12:45:34 -05:00
end
2014-04-25 08:10:54 -04:00
end
end