require "spec" require "./db-cars.cr" # NOTE: the following tests access both database and triggers' caches directly. # It must not be done in real applications. # `data` and `lru` properties are implementation details. describe "SPECDB::Common" do it "basics, 3 values" do car0 = Car.new "Corvet-0", "red", [] of String car1 = Car.new "Corvet-1", "red", [] of String car2 = Car.new "Corvet-2", "red", [] of String car3 = Car.new "Corvet-3", "red", [] of String db = SPECDB::Common(Car).new "", 3 db.data.keys.sort.should eq([] of Int32) db << car0 db.data.keys.sort.should eq([0] of Int32) db << car1 db.data.keys.sort.should eq([0, 1] of Int32) db << car2 db.data.keys.sort.should eq([0, 1, 2] of Int32) db[0] # Let's use the first value, it shouldn't be the one to be dropped. db << car3 db.data.keys.sort.should eq([0, 2, 3] of Int32) db.lru.to_s.should eq "[ 3, 0, 2 ]" db.delete 2 db.data.keys.sort.should eq([0, 3] of Int32) db.lru.to_s.should eq "[ 3, 0 ]" end it "add some values, reindex, check the values" do car0 = Car.new "Corvet-0", "red", [] of String car1 = Car.new "Corvet-1", "red", [] of String car2 = Car.new "Corvet-2", "red", [] of String car3 = Car.new "Corvet-3", "red", [] of String db = SPECDB::Common(Car).new "reindex", 2 index_name = db.new_index "name", &.name index_color = db.new_partition "color", &.color db.data.keys.sort.should eq([] of Int32) db << car0 db.data.keys.sort.should eq([0] of Int32) db << car1 db.data.keys.sort.should eq([0, 1] of Int32) db.lru.to_s.should eq "[ 1, 0 ]" db.data.keys.sort.should eq([0, 1] of Int32) db.lru.to_s.should eq "[ 1, 0 ]" db << car2 db.data.keys.sort.should eq([1, 2] of Int32) db.lru.to_s.should eq "[ 2, 1 ]" db[0] # Let's use the first value, it shouldn't be the one to be dropped. db << car3 db.data.keys.sort.should eq([0, 3] of Int32) db.lru.to_s.should eq "[ 3, 0 ]" # Actually remove the value indexed "2". db.delete 2 # The "delete" action actually puts the value in the cache before removing it. db.data.keys.sort.should eq([3] of Int32) db.lru.to_s.should eq "[ 3 ]" # db2: further tests for the LRU behavior, with `reindex_everything!` for example. db2 = SPECDB::Common(Car).new "reindex", 2, false index2_name = db2.new_index "name", &.name # For now, we didn't use any value in the database. # Also, triggers should have empty caches. db2.data.keys.sort.should eq([] of Int32) db2.lru.to_s.should eq "[ ]" index2_name.get?("Corvet-0").should eq car0 # Reindex removes all triggers (hard rm -rf trigger-dir) then reads all # data from the file-system to run the triggers. Data is put into the cache # of the `Common` database. db2.reindex_everything! # Values 1 and 3 are the last recently used values following `reindex_everything!`. db2.data.keys.sort.should eq([1, 3] of Int32) db2.lru.to_s.should eq "[ 3, 1 ]" total_values = 0 db2.each_with_key do |v, k| total_values += 1 end total_values.should eq 3 # db3: no need for a reindex to access values from the database, # with `each` or `each_with_key` for example. db3 = SPECDB::Common(Car).new "reindex", 2, false total_values = 0 db3.each do |v| total_values += 1 end total_values.should eq 3 # db4: no need for a reindex to access values from triggers. db4 = SPECDB::Common(Car).new "reindex", 2, false index4_name = db4.new_index "name", &.name index4_color = db4.new_partition "color", &.color index4_name.data.size.should eq 0 # trigger cache is empty index4_name.get?("Corvet-3").should eq car3 index4_name.data.size.should eq 1 # trigger cache: one value, Corvet-3 index4_color.data.size.should eq 0 # trigger cache is empty index4_color.get("red").size.should eq 3 # read from the disk index4_color.data.size.should eq 1 # trigger cache: 1, the color "red" index4_color.data["red"].size.should eq 3 # 3 values in "red" end it "testing caches: add some values, clear cache, check the values" do car0 = Car.new "Corvet-0", "red", [] of String car1 = Car.new "Corvet-1", "red", [] of String car2 = Car.new "Corvet-2", "blue", [] of String car3 = Car.new "Corvet-3", "violet", [] of String db = SPECDB::Common(Car).new "reindex", 2 index_name = db.new_index "name", &.name index_color = db.new_partition "color", &.color db << car0 # First entry, each cache should have one item. index_name.data.size.should eq 1 index_color.data.size.should eq 1 db << car1 db << car2 db << car3 # Storage cache should only have the maximum number of allowed entries (2). db.data.keys.size.should eq 2 # Trigger caches don't have limits on the number of entries, therefore they should have: # 4 entries for the name index (each entry = 1 car). index_name.data.size.should eq 4 # 3 entries for the color index (each entry = 1 color). index_color.data.size.should eq 3 # All cached entries are dropped. db.clear_cache! # Caches should be empty. db.data.keys.sort.should eq([] of Int32) index_name.data.size.should eq 0 index_color.data.size.should eq 0 # Get a car from stored data based on its name. index_name.get?("Corvet-2").should eq car2 index_name.data.size.should eq 1 # This doesn't fill up the cache in the color index. index_color.data.size.should eq 0 # Get a car from stored data based on its color. index_color.get?("violet").size.should eq 1 # The "Storage" database should have both found items in cache. db.data.keys.sort.should eq([2, 3] of Int32) # Trigger caches should have a single value given that we searched a single item through each one. index_name.data.size.should eq 1 index_color.data.size.should eq 1 # Loop over entries, filling up the cache for Storage. total_values = 0 db.each_with_key do |v, k| total_values += 1 end total_values.should eq 4 # Caches should still have a single entry since we only searched through them only once. index_name.data.size.should eq 1 index_color.data.size.should eq 1 end end