enum_test.rb 9.8 KB
Newer Older
1 2 3
require 'cases/helper'
require 'models/book'

4
class EnumTest < ActiveRecord::TestCase
5 6 7
  fixtures :books

  setup do
8
    @book = books(:awdr)
9 10 11 12 13 14
  end

  test "query state by predicate" do
    assert @book.proposed?
    assert_not @book.written?
    assert_not @book.published?
Y
Yury Korolev 已提交
15 16

    assert @book.unread?
17
  end
18

R
Robin Dupret 已提交
19
  test "query state with strings" do
20 21
    assert_equal "proposed", @book.status
    assert_equal "unread", @book.read_status
22 23
  end

24 25
  test "find via scope" do
    assert_equal @book, Book.proposed.first
Y
Yury Korolev 已提交
26
    assert_equal @book, Book.unread.first
27 28
  end

G
Godfrey Chan 已提交
29 30 31 32 33 34 35 36 37
  test "find via where with values" do
    proposed, written = Book.statuses[:proposed], Book.statuses[:written]

    assert_equal @book, Book.where(status: proposed).first
    refute_equal @book, Book.where(status: written).first
    assert_equal @book, Book.where(status: [proposed]).first
    refute_equal @book, Book.where(status: [written]).first
    refute_equal @book, Book.where("status <> ?", proposed).first
    assert_equal @book, Book.where("status <> ?", written).first
38 39
  end

40
  test "find via where with symbols" do
G
Godfrey Chan 已提交
41 42 43 44
    assert_equal @book, Book.where(status: :proposed).first
    refute_equal @book, Book.where(status: :written).first
    assert_equal @book, Book.where(status: [:proposed]).first
    refute_equal @book, Book.where(status: [:written]).first
45 46
    refute_equal @book, Book.where.not(status: :proposed).first
    assert_equal @book, Book.where.not(status: :written).first
47 48 49
  end

  test "find via where with strings" do
50 51
    assert_equal @book, Book.where(status: "proposed").first
    refute_equal @book, Book.where(status: "written").first
52 53
    assert_equal @book, Book.where(status: ["proposed"]).first
    refute_equal @book, Book.where(status: ["written"]).first
54 55
    refute_equal @book, Book.where.not(status: "proposed").first
    assert_equal @book, Book.where.not(status: "written").first
56 57
  end

G
Godfrey Chan 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71
  test "build from scope" do
    assert Book.written.build.written?
    refute Book.written.build.proposed?
  end

  test "build from where" do
    assert Book.where(status: Book.statuses[:written]).build.written?
    refute Book.where(status: Book.statuses[:written]).build.proposed?
    assert Book.where(status: :written).build.written?
    refute Book.where(status: :written).build.proposed?
    assert Book.where(status: "written").build.written?
    refute Book.where(status: "written").build.proposed?
  end

72 73 74 75
  test "update by declaration" do
    @book.written!
    assert @book.written?
  end
76

77 78 79 80
  test "update by setter" do
    @book.update! status: :written
    assert @book.written?
  end
81 82 83 84 85

  test "enum methods are overwritable" do
    assert_equal "do publish work...", @book.published!
    assert @book.published?
  end
86 87 88 89 90 91

  test "direct assignment" do
    @book.status = :written
    assert @book.written?
  end

92 93 94 95 96
  test "assign string value" do
    @book.status = "written"
    assert @book.written?
  end

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
  test "enum changed attributes" do
    old_status = @book.status
    @book.status = :published
    assert_equal old_status, @book.changed_attributes[:status]
  end

  test "enum changes" do
    old_status = @book.status
    @book.status = :published
    assert_equal [old_status, 'published'], @book.changes[:status]
  end

  test "enum attribute was" do
    old_status = @book.status
    @book.status = :published
    assert_equal old_status, @book.attribute_was(:status)
  end

  test "enum attribute changed" do
    @book.status = :published
    assert @book.attribute_changed?(:status)
  end

  test "enum attribute changed to" do
    @book.status = :published
    assert @book.attribute_changed?(:status, to: 'published')
  end

  test "enum attribute changed from" do
    old_status = @book.status
    @book.status = :published
    assert @book.attribute_changed?(:status, from: old_status)
  end

  test "enum attribute changed from old status to new status" do
    old_status = @book.status
    @book.status = :published
    assert @book.attribute_changed?(:status, from: old_status, to: 'published')
  end

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
  test "enum didn't change" do
    old_status = @book.status
    @book.status = old_status
    assert_not @book.attribute_changed?(:status)
  end

  test "persist changes that are dirty" do
    @book.status = :published
    assert @book.attribute_changed?(:status)
    @book.status = :written
    assert @book.attribute_changed?(:status)
  end

  test "reverted changes that are not dirty" do
    old_status = @book.status
    @book.status = :published
    assert @book.attribute_changed?(:status)
    @book.status = old_status
    assert_not @book.attribute_changed?(:status)
  end

  test "reverted changes are not dirty going from nil to value and back" do
    book = Book.create!(nullable_status: nil)

    book.nullable_status = :married
    assert book.attribute_changed?(:nullable_status)

    book.nullable_status = nil
    assert_not book.attribute_changed?(:nullable_status)
  end

168 169 170 171 172 173
  test "assign non existing value raises an error" do
    e = assert_raises(ArgumentError) do
      @book.status = :unknown
    end
    assert_equal "'unknown' is not a valid status", e.message
  end
174

175 176 177 178 179
  test "NULL values from database should be casted to nil" do
    Book.where(id: @book.id).update_all("status = NULL")
    assert_nil @book.reload.status
  end

180 181
  test "assign nil value" do
    @book.status = nil
182
    assert_nil @book.status
183 184 185 186
  end

  test "assign empty string value" do
    @book.status = ''
187
    assert_nil @book.status
188 189 190 191
  end

  test "assign long empty string value" do
    @book.status = '   '
192
    assert_nil @book.status
193 194
  end

195
  test "constant to access the mapping" do
196 197 198
    assert_equal 0, Book.statuses[:proposed]
    assert_equal 1, Book.statuses["written"]
    assert_equal 2, Book.statuses[:published]
199
  end
R
Robin Dupret 已提交
200

201 202 203 204
  test "building new objects with enum scopes" do
    assert Book.written.build.written?
    assert Book.read.build.read?
  end
R
Robin Dupret 已提交
205

206 207 208
  test "creating new objects with enum scopes" do
    assert Book.written.create.written?
    assert Book.read.create.read?
R
Robin Dupret 已提交
209
  end
210 211

  test "_before_type_cast returns the enum label (required for form fields)" do
212 213 214 215 216
    if @book.status_came_from_user?
      assert_equal "proposed", @book.status_before_type_cast
    else
      assert_equal "proposed", @book.status
    end
217
  end
218 219 220 221 222 223 224 225 226 227 228 229 230 231

  test "reserved enum names" do
    klass = Class.new(ActiveRecord::Base) do
      self.table_name = "books"
      enum status: [:proposed, :written, :published]
    end

    conflicts = [
      :column,     # generates class method .columns, which conflicts with an AR method
      :logger,     # generates #logger, which conflicts with an AR method
      :attributes, # generates #attributes=, which conflicts with an AR method
    ]

    conflicts.each_with_index do |name, i|
F
Franky W 已提交
232
      e = assert_raises(ArgumentError) do
233 234
        klass.class_eval { enum name => ["value_#{i}"] }
      end
F
Franky W 已提交
235
      assert_match(/You tried to define an enum named \"#{name}\" on the model/, e.message)
236 237 238 239 240 241 242 243 244 245 246 247 248 249
    end
  end

  test "reserved enum values" do
    klass = Class.new(ActiveRecord::Base) do
      self.table_name = "books"
      enum status: [:proposed, :written, :published]
    end

    conflicts = [
      :new,      # generates a scope that conflicts with an AR class method
      :valid,    # generates #valid?, which conflicts with an AR method
      :save,     # generates #save!, which conflicts with an AR method
      :proposed, # same value as an existing enum
250 251
      :public, :private, :protected, # some important methods on Module and Class
      :name, :parent, :superclass
252 253 254
    ]

    conflicts.each_with_index do |value, i|
F
Franky W 已提交
255
      e = assert_raises(ArgumentError, "enum value `#{value}` should not be allowed") do
256 257
        klass.class_eval { enum "status_#{i}" => [value] }
      end
F
Franky W 已提交
258
      assert_match(/You tried to define an enum named .* on the model/, e.message)
259 260 261 262 263
    end
  end

  test "overriding enum method should not raise" do
    assert_nothing_raised do
T
Tim Fenney 已提交
264
      Class.new(ActiveRecord::Base) do
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
        self.table_name = "books"

        def published!
          super
          "do publish work..."
        end

        enum status: [:proposed, :written, :published]

        def written!
          super
          "do written work..."
        end
      end
    end
  end
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307

  test "validate uniqueness" do
    klass = Class.new(ActiveRecord::Base) do
      def self.name; 'Book'; end
      enum status: [:proposed, :written]
      validates_uniqueness_of :status
    end
    klass.delete_all
    klass.create!(status: "proposed")
    book = klass.new(status: "written")
    assert book.valid?
    book.status = "proposed"
    assert_not book.valid?
  end

  test "validate inclusion of value in array" do
    klass = Class.new(ActiveRecord::Base) do
      def self.name; 'Book'; end
      enum status: [:proposed, :written]
      validates_inclusion_of :status, in: ["written"]
    end
    klass.delete_all
    invalid_book = klass.new(status: "proposed")
    assert_not invalid_book.valid?
    valid_book = klass.new(status: "written")
    assert valid_book.valid?
  end
E
Evan Whalen 已提交
308 309

  test "enums are distinct per class" do
310 311 312 313 314 315 316 317
    klass1 = Class.new(ActiveRecord::Base) do
      self.table_name = "books"
      enum status: [:proposed, :written]
    end

    klass2 = Class.new(ActiveRecord::Base) do
      self.table_name = "books"
      enum status: [:drafted, :uploaded]
E
Evan Whalen 已提交
318
    end
319 320 321 322 323 324 325 326

    book1 = klass1.proposed.create!
    book1.status = :written
    assert_equal ['proposed', 'written'], book1.status_change

    book2 = klass2.drafted.create!
    book2.status = :uploaded
    assert_equal ['drafted', 'uploaded'], book2.status_change
E
Evan Whalen 已提交
327 328 329
  end

  test "enums are inheritable" do
330 331 332 333
    subklass1 = Class.new(Book)

    subklass2 = Class.new(Book) do
      enum status: [:drafted, :uploaded]
E
Evan Whalen 已提交
334
    end
335 336 337 338 339 340 341 342

    book1 = subklass1.proposed.create!
    book1.status = :written
    assert_equal ['proposed', 'written'], book1.status_change

    book2 = subklass2.drafted.create!
    book2.status = :uploaded
    assert_equal ['drafted', 'uploaded'], book2.status_change
E
Evan Whalen 已提交
343
  end
344
end