2019-04-30 08:27:42 +08:00
# frozen_string_literal: true
2022-07-28 10:27:38 +08:00
RSpec . describe HasCustomFields do
2022-07-28 00:14:14 +08:00
describe " custom_fields " do
2014-04-25 20:10:54 +08:00
before do
2018-06-19 14:13:14 +08:00
DB . exec ( " create temporary table custom_fields_test_items(id SERIAL primary key) " )
2019-07-07 02:42:03 +08: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-05 01:25:09 +08: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 20:10:54 +08:00
2014-04-28 16:37:34 +08:00
class CustomFieldsTestItem < ActiveRecord :: Base
2014-04-28 16:31:51 +08:00
include HasCustomFields
2014-04-25 20:10:54 +08:00
end
2014-04-28 16:37:34 +08:00
class CustomFieldsTestItemCustomField < ActiveRecord :: Base
belongs_to :custom_fields_test_item
2014-04-25 20:10:54 +08:00
end
end
after do
2018-06-19 14:13:14 +08:00
DB . exec ( " drop table custom_fields_test_items " )
DB . exec ( " drop table custom_fields_test_item_custom_fields " )
2014-04-25 20:10:54 +08:00
2019-04-30 14:58:18 +08: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 20:10:54 +08:00
end
2022-01-21 12:29:51 +08: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-05 01:25:09 +08:00
it " allows simple modification of custom fields " do
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . new
2014-04-25 20:10:54 +08:00
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( nil )
2014-04-25 20:10:54 +08:00
test_item . custom_fields [ " bob " ] = " marley "
test_item . custom_fields [ " jack " ] = " black "
2014-04-26 01:15:23 +08:00
2014-04-25 20:10:54 +08:00
test_item . save
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . find ( test_item . id )
2014-04-25 20:10:54 +08:00
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " bob " ] ) . to eq ( " marley " )
expect ( test_item . custom_fields [ " jack " ] ) . to eq ( " black " )
2014-04-25 20:10:54 +08:00
test_item . custom_fields . delete ( " bob " )
test_item . custom_fields [ " jack " ] = " jill "
test_item . save
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . find ( test_item . id )
2014-04-25 20:10:54 +08:00
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields ) . to eq ( " jack " = > " jill " )
2014-04-25 20:10:54 +08:00
end
2014-04-26 01:15:23 +08:00
it " casts integers to string without error " do
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . new
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( nil )
2014-04-26 01:15:23 +08:00
test_item . custom_fields [ " a " ] = 0
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( 0 )
2014-04-26 01:15:23 +08:00
test_item . save
# should be casted right after saving
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-26 01:15:23 +08:00
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-26 01:15:23 +08:00
end
2019-07-05 01:25:09 +08:00
it " reloads from the database " do
2014-04-30 01:23:13 +08:00
test_item = CustomFieldsTestItem . new
test_item . custom_fields [ " a " ] = 0
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( 0 )
2014-04-30 01:23:13 +08:00
test_item . save
# should be casted right after saving
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-30 01:23:13 +08:00
2018-06-19 14:13:14 +08: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-30 01:23:13 +08:00
# still the same, did not load
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 0 " )
2014-04-30 01:23:13 +08:00
# refresh loads from database
2015-01-10 00:34:37 +08:00
expect ( test_item . reload . custom_fields [ " a " ] ) . to eq ( " 1 " )
expect ( test_item . custom_fields [ " a " ] ) . to eq ( " 1 " )
2014-04-30 01:23:13 +08:00
end
2014-04-26 00:22:49 +08:00
2019-07-05 01:25:09 +08:00
it " actually saves on double save " do
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . new
2014-04-26 00:22:49 +08:00
test_item . custom_fields = { " a " = > " b " }
test_item . save
test_item . custom_fields [ " c " ] = " d "
test_item . save
2014-04-28 16:37:34 +08:00
db_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-10 00:34:37 +08:00
expect ( db_item . custom_fields ) . to eq ( " a " = > " b " , " c " = > " d " )
2014-04-26 00:22:49 +08:00
end
2014-04-26 01:15:23 +08:00
it " handles arrays properly " do
2018-05-22 14:48:39 +08: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 16:37:34 +08:00
test_item = CustomFieldsTestItem . new
2014-04-26 01:15:23 +08:00
test_item . custom_fields = { " a " = > %w[ b c d ] }
test_item . save
2014-04-28 16:37:34 +08:00
db_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-10 00:34:37 +08:00
expect ( db_item . custom_fields ) . to eq ( " a " = > %w[ b c d ] )
2014-04-26 01:15:23 +08:00
2015-01-03 04:56:44 +08:00
db_item . custom_fields . update ( " a " = > %w[ c d ] )
2014-04-26 01:15:23 +08:00
db_item . save
2015-01-10 00:34:37 +08:00
expect ( db_item . custom_fields ) . to eq ( " a " = > %w[ c d ] )
2014-04-26 01:15:23 +08:00
2015-01-16 04:31:31 +08: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-03 04:56:44 +08:00
db_item . custom_fields . delete ( " a " )
2015-01-10 00:34:37 +08:00
expect ( db_item . custom_fields ) . to eq ( { } )
2014-04-26 01:15:23 +08:00
end
2021-06-25 17:34:51 +08: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-26 01:15:23 +08:00
it " casts integers in arrays properly without error " do
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . new
2014-04-26 01:15:23 +08:00
test_item . custom_fields = { " a " = > [ " b " , 10 , " d " ] }
test_item . save
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields ) . to eq ( " a " = > %w[ b 10 d ] )
2014-04-26 01:15:23 +08:00
2014-04-28 16:37:34 +08:00
db_item = CustomFieldsTestItem . find ( test_item . id )
2015-01-10 00:34:37 +08:00
expect ( db_item . custom_fields ) . to eq ( " a " = > %w[ b 10 d ] )
2014-04-26 01:15:23 +08:00
end
2021-05-21 09:43:47 +08:00
it " supports type coercion " do
2014-06-17 10:42:12 +08:00
test_item = CustomFieldsTestItem . new
CustomFieldsTestItem . register_custom_field_type ( " bool " , :boolean )
CustomFieldsTestItem . register_custom_field_type ( " int " , :integer )
2015-04-24 01:33:29 +08:00
CustomFieldsTestItem . register_custom_field_type ( " json " , :json )
2014-06-17 10:42:12 +08:00
2015-04-24 01:33:29 +08:00
test_item . custom_fields = { " bool " = > true , " int " = > 1 , " json " = > { " foo " = > " bar " } }
2014-06-17 10:42:12 +08:00
test_item . save
test_item . reload
2015-04-24 01:33:29 +08:00
expect ( test_item . custom_fields ) . to eq (
" bool " = > true ,
" int " = > 1 ,
" json " = > {
" foo " = > " bar " ,
} ,
)
2017-08-17 05:04:40 +08: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-17 10:42:12 +08:00
end
2019-07-05 01:25:09 +08:00
it " doesn't allow simple modifications to interfere " do
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . new
2014-04-25 20:10:54 +08:00
2015-01-10 00:34:37 +08:00
expect ( test_item . custom_fields [ " a " ] ) . to eq ( nil )
2014-04-25 20:10:54 +08:00
test_item . custom_fields [ " bob " ] = " marley "
test_item . custom_fields [ " jack " ] = " black "
test_item . save
2014-04-28 16:37:34 +08:00
test_item2 = CustomFieldsTestItem . new
2014-04-25 20:10:54 +08:00
2015-01-10 00:34:37 +08:00
expect ( test_item2 . custom_fields [ " x " ] ) . to eq ( nil )
2014-04-25 20:10:54 +08:00
test_item2 . custom_fields [ " sixto " ] = " rodriguez "
test_item2 . custom_fields [ " de " ] = " la playa "
test_item2 . save
2014-04-28 16:37:34 +08:00
test_item = CustomFieldsTestItem . find ( test_item . id )
test_item2 = CustomFieldsTestItem . find ( test_item2 . id )
2014-04-25 20:10:54 +08:00
2015-01-10 00:34:37 +08: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 20:10:54 +08:00
end
2014-05-15 02:38:04 +08:00
2018-09-13 15:59:17 +08: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-15 02:38:04 +08:00
it " supports bulk retrieval with a list of ids " do
item1 = CustomFieldsTestItem . new
2020-07-27 08:23:54 +08:00
item1 . custom_fields = { " a " = > %w[ b c d ] , " not_allowlisted " = > " secret " }
2014-05-15 02:38:04 +08: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-10 00:34:37 +08:00
expect ( fields ) . to be_present
expect ( fields [ item1 . id ] [ " a " ] ) . to match_array ( %w[ b c d ] )
2020-07-27 08:23:54 +08:00
expect ( fields [ item1 . id ] [ " not_allowlisted " ] ) . to be_blank
2015-01-10 00:34:37 +08:00
expect ( fields [ item2 . id ] [ " e " ] ) . to eq ( " hallo " )
2014-05-15 02:38:04 +08:00
end
2017-11-08 11:10:20 +08: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-03 01:45:34 +08:00
2022-04-26 00:19:39 +08: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-05 01:25:09 +08: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-07 02:42:03 +08:00
expect ( item0 . reload . custom_fields [ " rare " ] ) . to eq ( " gem " )
2019-07-05 01:25:09 +08:00
item0 . create_singular ( " rare " , " diamond " )
expect ( item0 . reload . custom_fields [ " rare " ] ) . to eq ( " diamond " )
end
end
2018-03-03 01:45:34 +08: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-30 02:34:28 +08: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 14:09:34 +08:00
2020-08-25 18:28:41 +08:00
it " allows using string and symbol indices interchangeably " do
2020-08-25 14:09:34 +08:00
test_item = CustomFieldsTestItem . new
test_item . custom_fields [ " bob " ] = " marley "
test_item . custom_fields [ " jack " ] = " black "
2022-01-21 12:29:51 +08:00
# In memory
expect ( test_item . custom_fields [ :bob ] ) . to eq ( " marley " )
expect ( test_item . custom_fields [ :jack ] ) . to eq ( " black " )
2020-08-25 14:09:34 +08:00
2022-01-21 12:29:51 +08: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 14:09:34 +08:00
2022-01-21 12:29:51 +08:00
# Update via string index again
test_item . custom_fields [ " bob " ] = " the builder "
2020-08-25 14:09:34 +08:00
2022-01-21 12:29:51 +08:00
expect ( test_item . custom_fields [ :bob ] ) . to eq ( " the builder " )
test_item . save
test_item . reload
2020-08-25 14:09:34 +08:00
2022-01-21 12:29:51 +08:00
expect ( test_item . custom_fields [ :bob ] ) . to eq ( " the builder " )
2020-08-25 14:09:34 +08:00
end
2018-03-03 01:45:34 +08:00
end
2014-04-25 20:10:54 +08:00
end
end