From 703f6cd845527a17c5c484d0042b5c9817e0cbea Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 27 Aug 2026 19:40:00 +0200 Subject: [PATCH] test: add orm store, model, and relation tests --- test/test_orm_model.rb | 107 +++++++++++++++++++++++++++++++++++ test/test_orm_relation.rb | 56 ++++++++++++++++++ test/test_orm_store.rb | 116 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 test/test_orm_model.rb create mode 100644 test/test_orm_relation.rb create mode 100644 test/test_orm_store.rb diff --git a/test/test_orm_model.rb b/test/test_orm_model.rb new file mode 100644 index 0000000..29c6bd5 --- /dev/null +++ b/test/test_orm_model.rb @@ -0,0 +1,107 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' + +class OrmModelTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-model-store') + @store = Prism::Orm::Store.new(File.join(@dir, 'test.pstore')) + @store.create_table(:widgets, %w[id name price created_at updated_at]) + define_widget_class + end + + def teardown + FileUtils.remove_entry(@dir) + Object.send(:remove_const, :Widget) if Object.const_defined?(:Widget) + end + + def define_widget_class + store = @store + widget_class = Class.new(Prism::Orm::Model) do + table_name :widgets + self.store = store + end + Object.const_set(:Widget, widget_class) + end + + def test_create_persists_and_returns_record + widget = Widget.create(name: 'gizmo', price: 10) + assert widget.persisted? + refute_nil widget.id + assert_equal 'gizmo', Widget.find(widget.id).name + end + + def test_dynamic_attribute_accessors + widget = Widget.new(name: 'gadget') + assert_equal 'gadget', widget.name + widget.name = 'renamed' + assert_equal 'renamed', widget.name + end + + def test_find_raises_for_missing_record + assert_raises(Prism::Orm::RecordNotFound) { Widget.find(9999) } + end + + def test_update_persists_new_values + widget = Widget.create(name: 'gizmo', price: 10) + widget.update(price: 25) + assert_equal 25, Widget.find(widget.id).price + end + + def test_destroy_removes_record + widget = Widget.create(name: 'gizmo') + id = widget.id + widget.destroy + assert widget.destroyed? + assert_raises(Prism::Orm::RecordNotFound) { Widget.find(id) } + end + + def test_reload_refreshes_attributes + widget = Widget.create(name: 'gizmo', price: 10) + Widget.find(widget.id).update(price: 99) + widget.reload + assert_equal 99, widget.price + end + + def test_timestamps_are_touched_on_save + widget = Widget.create(name: 'gizmo') + refute_nil widget.created_at + refute_nil widget.updated_at + created_at = widget.created_at + widget.update(price: 5) + assert_equal created_at, widget.created_at + refute_nil widget.updated_at + end + + def test_equality_by_class_and_id + widget = Widget.create(name: 'gizmo') + same = Widget.find(widget.id) + assert_equal widget, same + end + + def test_save_fails_and_returns_false_when_invalid + Widget.validates(:name, presence: true) + widget = Widget.new(name: '') + refute widget.save + assert_includes widget.errors[:name], 'must be present' + ensure + Widget.validators.clear + end + + def test_save_bang_raises_on_invalid_record + Widget.validates(:name, presence: true) + widget = Widget.new(name: '') + assert_raises(Prism::Orm::RecordInvalid) { widget.save! } + ensure + Widget.validators.clear + end + + def test_reset_schema_picks_up_columns_added_after_the_model_was_first_used + Widget.new # forces load_schema! against the original column set + @store.add_column(:widgets, :sku) + Widget.reset_schema! + + widget = Widget.create(name: 'gizmo', sku: 'SKU-1') + assert_equal 'SKU-1', widget.sku + end +end diff --git a/test/test_orm_relation.rb b/test/test_orm_relation.rb new file mode 100644 index 0000000..e934e8e --- /dev/null +++ b/test/test_orm_relation.rb @@ -0,0 +1,56 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' + +class OrmRelationTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-relation-store') + @store = Prism::Orm::Store.new(File.join(@dir, 'test.pstore')) + @store.create_table(:items, %w[id name category]) + item_class = Class.new(Prism::Orm::Model) do + table_name :items + end + item_class.store = @store + Object.const_set(:Item, item_class) + end + + def teardown + FileUtils.remove_entry(@dir) + Object.send(:remove_const, :Item) if Object.const_defined?(:Item) + end + + def test_offset_skips_leading_rows + %w[a b c d].each { |name| Item.create!(name: name) } + names = Item.order(:name).offset(2).map(&:name) + assert_equal %w[c d], names + end + + def test_offset_combined_with_limit_paginates + %w[a b c d e].each { |name| Item.create!(name: name) } + page_two = Item.order(:name).offset(2).limit(2).map(&:name) + assert_equal %w[c d], page_two + end + + def test_chained_where_calls_combine_with_and_semantics + Item.create!(name: 'match', category: 'tools') + Item.create!(name: 'match', category: 'other') + Item.create!(name: 'nope', category: 'tools') + + result = Item.where(name: 'match').where(category: 'tools').to_a + assert_equal 1, result.size + assert_equal 'match', result.first.name + end + + def test_relation_is_enumerable + Item.create!(name: 'b') + Item.create!(name: 'a') + assert_equal %w[a b], Item.order(:name).sort_by(&:name).map(&:name) + assert_equal 2, Item.all.count + end + + def test_exists_predicate + refute Item.where(name: 'ghost').exists? + Item.create!(name: 'ghost') + assert Item.where(name: 'ghost').exists? + end +end diff --git a/test/test_orm_store.rb b/test/test_orm_store.rb new file mode 100644 index 0000000..1384817 --- /dev/null +++ b/test/test_orm_store.rb @@ -0,0 +1,116 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' + +class OrmStoreTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-store') + @path = File.join(@dir, 'test.pstore') + @store = Prism::Orm::Store.new(@path) + end + + def teardown + FileUtils.remove_entry(@dir) + end + + def test_create_table_and_insert + @store.create_table(:users, %w[id name]) + id = @store.insert(:users, name: 'foksiu') + assert_equal 1, id + assert_equal({ 'id' => 1, 'name' => 'foksiu' }, @store.find(:users, 1)) + end + + def test_create_table_twice_raises + @store.create_table(:users, %w[id name]) + assert_raises(Prism::Orm::Store::TableExists) { @store.create_table(:users, %w[id name]) } + end + + def test_insert_assigns_incrementing_ids + @store.create_table(:users, %w[id name]) + first = @store.insert(:users, name: 'a') + second = @store.insert(:users, name: 'b') + assert_equal 1, first + assert_equal 2, second + end + + def test_update_merges_attributes + @store.create_table(:users, %w[id name role]) + id = @store.insert(:users, name: 'foksiu', role: 'admin') + @store.update(:users, id, role: 'owner') + row = @store.find(:users, id) + assert_equal 'foksiu', row['name'] + assert_equal 'owner', row['role'] + end + + def test_update_missing_record_raises + @store.create_table(:users, %w[id name]) + assert_raises(Prism::Orm::Store::RecordNotFound) { @store.update(:users, 999, name: 'nope') } + end + + def test_delete_removes_row + @store.create_table(:users, %w[id name]) + id = @store.insert(:users, name: 'foksiu') + @store.delete(:users, id) + assert_nil @store.find(:users, id) + end + + def test_all_returns_every_row + @store.create_table(:users, %w[id name]) + @store.insert(:users, name: 'a') + @store.insert(:users, name: 'b') + assert_equal ['a', 'b'], @store.all(:users).map { |row| row['name'] } + end + + def test_count + @store.create_table(:users, %w[id name]) + @store.insert(:users, name: 'a') + @store.insert(:users, name: 'b') + assert_equal 2, @store.count(:users) + end + + def test_unknown_table_raises + assert_raises(Prism::Orm::Store::UnknownTable) { @store.find(:ghosts, 1) } + end + + def test_add_column_defaults_existing_rows + @store.create_table(:users, %w[id name]) + id = @store.insert(:users, name: 'foksiu') + @store.add_column(:users, :active, default: true) + assert_equal true, @store.find(:users, id)['active'] + assert_includes @store.columns(:users), 'active' + end + + def test_remove_column_strips_existing_rows + @store.create_table(:users, %w[id name role]) + id = @store.insert(:users, name: 'foksiu', role: 'admin') + @store.remove_column(:users, :role) + refute @store.find(:users, id).key?('role') + refute_includes @store.columns(:users), 'role' + end + + def test_drop_table + @store.create_table(:users, %w[id name]) + @store.drop_table(:users) + refute @store.table_exists?(:users) + end + + def test_migration_bookkeeping + assert_empty @store.applied_migrations + @store.record_migration('20260101000000') + assert_equal ['20260101000000'], @store.applied_migrations + @store.record_migration('20260101000000') + assert_equal ['20260101000000'], @store.applied_migrations, 'recording twice must not duplicate' + @store.forget_migration('20260101000000') + assert_empty @store.applied_migrations + end + + def test_data_survives_reopening_the_store + @store.create_table(:users, %w[id name]) + @store.insert(:users, name: 'persisted-foksiu') + + reopened = Prism::Orm::Store.new(@path) + row = reopened.find(:users, 1) + refute_nil row + assert_equal 'persisted-foksiu', row['name'] + end +end