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