diff --git a/test/test_orm_enum.rb b/test/test_orm_enum.rb new file mode 100644 index 0000000..8c15ed4 --- /dev/null +++ b/test/test_orm_enum.rb @@ -0,0 +1,179 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' + +class OrmEnumTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-enum-store') + @store = Prism::Orm::Store.new(File.join(@dir, 'test.pstore')) + @store.create_table(:posts, %w[id title status role]) + post_class = Class.new(Prism::Orm::Model) do + table_name :posts + enum :status, { draft: 0, published: 1, archived: 2 } + enum role: { author: 0, editor: 1 }, prefix: true + end + post_class.store = @store + Object.const_set(:EnumPost, post_class) + end + + def teardown + FileUtils.remove_entry(@dir) + Object.send(:remove_const, :EnumPost) if Object.const_defined?(:EnumPost) + end + + def test_reader_translates_raw_value_to_label + post = EnumPost.create!(title: 'a', status: :published) + assert_equal 'published', post.status + assert_equal 1, post['status'] + end + + def test_writer_accepts_symbol_string_or_raw_value + post = EnumPost.new(title: 'a') + post.status = :draft + assert_equal 0, post['status'] + post.status = 'published' + assert_equal 1, post['status'] + post.status = 2 + assert_equal 'archived', post.status + end + + def test_invalid_value_raises_argument_error_immediately + post = EnumPost.new(title: 'a') + assert_raises(ArgumentError) { post.status = :deleted } + end + + def test_nil_clears_the_attribute + post = EnumPost.new(title: 'a', status: :draft) + post.status = nil + assert_nil post.status + assert_nil post['status'] + end + + def test_predicate_methods + post = EnumPost.new(title: 'a', status: :draft) + assert post.draft? + refute post.published? + post.status = :published + assert post.published? + refute post.draft? + end + + def test_bang_method_sets_and_persists + post = EnumPost.create!(title: 'a', status: :draft) + post.published! + assert_equal 'published', post.status + assert_equal 'published', EnumPost.find(post.id).status + end + + def test_class_scope_matches_generated_predicate + EnumPost.create!(title: 'draft one', status: :draft) + EnumPost.create!(title: 'published one', status: :published) + EnumPost.create!(title: 'published two', status: :published) + + assert_equal 2, EnumPost.published.count + assert_equal ['published one', 'published two'], EnumPost.published.order(:title).map(&:title) + end + + def test_mapping_accessor + assert_equal({ 'draft' => 0, 'published' => 1, 'archived' => 2 }, EnumPost.statuses) + end + + def test_prefixed_enum_method_names + post = EnumPost.new(title: 'a', role: :editor) + assert post.role_editor? + refute post.role_author? + assert EnumPost.respond_to?(:role_editor) + end + + def test_duplicate_raw_values_are_rejected_at_declaration + assert_raises(ArgumentError) do + Class.new(Prism::Orm::Model) do + table_name :posts + enum :status, { draft: 0, published: 0 } + end + end + end + + def test_serializable_hash_shows_the_label_not_the_raw_value + post = EnumPost.create!(title: 'a', status: :published, role: :editor) + json = post.as_json + assert_equal 'published', json['status'] + assert_equal 'editor', json['role'] + end + + def test_where_translates_symbol_label_to_raw_value + draft = EnumPost.create!(title: 'a', status: :draft) + EnumPost.create!(title: 'b', status: :published) + + assert_equal [draft], EnumPost.where(status: :draft).to_a + end + + def test_where_translates_string_label_to_raw_value + EnumPost.create!(title: 'a', status: :draft) + published = EnumPost.create!(title: 'b', status: :published) + + assert_equal [published], EnumPost.where(status: 'published').to_a + end + + def test_where_still_accepts_the_raw_stored_value_directly + draft = EnumPost.create!(title: 'a', status: :draft) + EnumPost.create!(title: 'b', status: :published) + + assert_equal [draft], EnumPost.where(status: 0).to_a + end + + def test_where_translates_an_array_of_labels_for_in_style_matching + draft = EnumPost.create!(title: 'a', status: :draft) + published = EnumPost.create!(title: 'b', status: :published) + EnumPost.create!(title: 'c', status: :archived) + + result = EnumPost.where(status: [:draft, 'published']).order(:title).to_a + assert_equal [draft, published], result + end + + def test_where_translates_labels_inside_an_operator_hash + EnumPost.create!(title: 'a', status: :draft) + published = EnumPost.create!(title: 'b', status: :published) + archived = EnumPost.create!(title: 'c', status: :archived) + + result = EnumPost.where(status: { not_in: [:draft] }).order(:title).to_a + assert_equal [published, archived], result + + gt_result = EnumPost.where(status: { gt: :draft }).order(:title).to_a + assert_equal [published, archived], gt_result + end + + def test_not_translates_enum_labels + draft = EnumPost.create!(title: 'a', status: :draft) + EnumPost.create!(title: 'b', status: :published) + + assert_equal [draft], EnumPost.not(status: :published).to_a + end + + def test_find_by_translates_enum_labels + published = EnumPost.create!(title: 'a', status: :published) + assert_equal published, EnumPost.find_by(status: :published) + assert_nil EnumPost.find_by(status: :archived) + end + + def test_where_with_unknown_enum_label_raises_argument_error + assert_raises(ArgumentError) { EnumPost.where(status: :deleted).to_a } + end + + def test_where_on_non_enum_column_is_unaffected + post = EnumPost.create!(title: 'unique-title', status: :draft) + assert_equal [post], EnumPost.where(title: 'unique-title').to_a + end + + def test_subclass_inherits_enum_definitions + subclass = Class.new(EnumPost) + subclass.store = @store + subclass.table_name(:posts) + Object.const_set(:EnumSubPost, subclass) + post = EnumSubPost.new(title: 'a', status: :archived) + assert_equal 'archived', post.status + assert post.archived? + ensure + Object.send(:remove_const, :EnumSubPost) if Object.const_defined?(:EnumSubPost) + end +end