PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/plugins/acts_as_state_machine/test/test_acts_as_state_machine.rb

https://bitbucket.org/eder.esilva/webtv
Ruby | 631 lines | 509 code | 116 blank | 6 comment | 1 complexity | e0e7b4f208c5d7c5c7db99ffd5d3c232 MD5 | raw file
Possible License(s): MIT
  1. RAILS_ROOT = File.dirname(__FILE__)
  2. require "rubygems"
  3. require "test/unit"
  4. require "active_record"
  5. require "active_record/fixtures"
  6. $:.unshift File.dirname(__FILE__) + "/../lib"
  7. require File.dirname(__FILE__) + "/../init"
  8. # Log everything to a global StringIO object instead of a file.
  9. require "stringio"
  10. $LOG = StringIO.new
  11. $LOGGER = Logger.new($LOG)
  12. ActiveRecord::Base.logger = $LOGGER
  13. ActiveRecord::Base.configurations = {
  14. "sqlite" => {
  15. :adapter => "sqlite",
  16. :dbfile => "state_machine.sqlite.db"
  17. },
  18. "sqlite3" => {
  19. :adapter => "sqlite3",
  20. :dbfile => "state_machine.sqlite3.db"
  21. },
  22. "mysql" => {
  23. :adapter => "mysql",
  24. :host => "localhost",
  25. :username => "rails",
  26. :password => nil,
  27. :database => "state_machine_test"
  28. },
  29. "postgresql" => {
  30. :min_messages => "ERROR",
  31. :adapter => "postgresql",
  32. :username => "postgres",
  33. :password => "postgres",
  34. :database => "state_machine_test"
  35. }
  36. }
  37. # Connect to the database.
  38. ActiveRecord::Base.establish_connection(ENV["DB"] || "sqlite")
  39. # Create table for conversations.
  40. ActiveRecord::Migration.verbose = false
  41. ActiveRecord::Schema.define(:version => 1) do
  42. create_table :conversations, :force => true do |t|
  43. t.column :state_machine, :string
  44. t.column :subject, :string
  45. t.column :closed, :boolean
  46. end
  47. end
  48. class Test::Unit::TestCase
  49. self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
  50. self.use_transactional_fixtures = true
  51. self.use_instantiated_fixtures = false
  52. def create_fixtures(*table_names, &block)
  53. Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names, &block)
  54. end
  55. end
  56. class Conversation < ActiveRecord::Base
  57. attr_writer :can_close
  58. attr_accessor :read_enter, :read_exit,
  59. :needs_attention_enter, :needs_attention_after,
  60. :read_after_first, :read_after_second,
  61. :closed_after
  62. # How's THAT for self-documenting? ;-)
  63. def always_true
  64. true
  65. end
  66. def can_close?
  67. !!@can_close
  68. end
  69. def read_enter_action
  70. self.read_enter = true
  71. end
  72. def read_after_first_action
  73. self.read_after_first = true
  74. end
  75. def read_after_second_action
  76. self.read_after_second = true
  77. end
  78. def closed_after_action
  79. self.closed_after = true
  80. end
  81. end
  82. class ActsAsStateMachineTest < Test::Unit::TestCase
  83. include ScottBarron::Acts::StateMachine
  84. fixtures :conversations
  85. def teardown
  86. Conversation.class_eval do
  87. write_inheritable_attribute :states, {}
  88. write_inheritable_attribute :initial_state, nil
  89. write_inheritable_attribute :transition_table, {}
  90. write_inheritable_attribute :event_table, {}
  91. write_inheritable_attribute :state_column, "state"
  92. # Clear out any callbacks that were set by acts_as_state_machine.
  93. write_inheritable_attribute :before_create, []
  94. write_inheritable_attribute :after_create, []
  95. end
  96. end
  97. def test_no_initial_value_raises_exception
  98. assert_raises(NoInitialState) do
  99. Conversation.class_eval { acts_as_state_machine }
  100. end
  101. end
  102. def test_state_column
  103. Conversation.class_eval do
  104. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  105. state :needs_attention
  106. end
  107. assert_equal "state_machine", Conversation.state_column
  108. end
  109. def test_initial_state_value
  110. Conversation.class_eval do
  111. acts_as_state_machine :initial => :needs_attention
  112. state :needs_attention
  113. end
  114. assert_equal :needs_attention, Conversation.initial_state
  115. end
  116. def test_initial_state
  117. Conversation.class_eval do
  118. acts_as_state_machine :initial => :needs_attention
  119. state :needs_attention
  120. end
  121. c = Conversation.create!
  122. assert_equal :needs_attention, c.current_state
  123. assert c.needs_attention?
  124. end
  125. def test_states_were_set
  126. Conversation.class_eval do
  127. acts_as_state_machine :initial => :needs_attention
  128. state :needs_attention
  129. state :read
  130. state :closed
  131. state :awaiting_response
  132. state :junk
  133. end
  134. [:needs_attention, :read, :closed, :awaiting_response, :junk].each do |state|
  135. assert Conversation.states.include?(state)
  136. end
  137. end
  138. def test_query_methods_created
  139. Conversation.class_eval do
  140. acts_as_state_machine :initial => :needs_attention
  141. state :needs_attention
  142. state :read
  143. state :closed
  144. state :awaiting_response
  145. state :junk
  146. end
  147. c = Conversation.create!
  148. [:needs_attention?, :read?, :closed?, :awaiting_response?, :junk?].each do |query|
  149. assert c.respond_to?(query)
  150. end
  151. end
  152. def test_event_methods_created
  153. Conversation.class_eval do
  154. acts_as_state_machine :initial => :needs_attention
  155. state :needs_attention
  156. state :read
  157. state :closed
  158. state :awaiting_response
  159. state :junk
  160. event(:new_message) {}
  161. event(:view) {}
  162. event(:reply) {}
  163. event(:close) {}
  164. event(:junk, :note => "finished") {}
  165. event(:unjunk) {}
  166. end
  167. c = Conversation.create!
  168. [:new_message!, :view!, :reply!, :close!, :junk!, :unjunk!].each do |event|
  169. assert c.respond_to?(event)
  170. end
  171. end
  172. def test_transition_table
  173. Conversation.class_eval do
  174. acts_as_state_machine :initial => :needs_attention
  175. state :needs_attention
  176. state :read
  177. state :closed
  178. state :awaiting_response
  179. state :junk
  180. event :new_message do
  181. transitions :to => :needs_attention, :from => [:read, :closed, :awaiting_response]
  182. end
  183. end
  184. tt = Conversation.transition_table
  185. assert tt[:new_message].include?(SupportingClasses::StateTransition.new(:from => :read, :to => :needs_attention))
  186. assert tt[:new_message].include?(SupportingClasses::StateTransition.new(:from => :closed, :to => :needs_attention))
  187. assert tt[:new_message].include?(SupportingClasses::StateTransition.new(:from => :awaiting_response, :to => :needs_attention))
  188. end
  189. def test_next_state_for_event
  190. Conversation.class_eval do
  191. acts_as_state_machine :initial => :needs_attention
  192. state :needs_attention
  193. state :read
  194. event :view do
  195. transitions :to => :read, :from => [:needs_attention, :read]
  196. end
  197. end
  198. c = Conversation.create!
  199. assert_equal :read, c.next_state_for_event(:view)
  200. end
  201. def test_change_state
  202. Conversation.class_eval do
  203. acts_as_state_machine :initial => :needs_attention
  204. state :needs_attention
  205. state :read
  206. event :view do
  207. transitions :to => :read, :from => [:needs_attention, :read]
  208. end
  209. end
  210. c = Conversation.create!
  211. c.view!
  212. assert c.read?
  213. end
  214. def test_can_go_from_read_to_closed_because_guard_passes
  215. Conversation.class_eval do
  216. acts_as_state_machine :initial => :needs_attention
  217. state :needs_attention
  218. state :read
  219. state :closed
  220. state :awaiting_response
  221. event :view do
  222. transitions :to => :read, :from => [:needs_attention, :read]
  223. end
  224. event :reply do
  225. transitions :to => :awaiting_response, :from => [:read, :closed]
  226. end
  227. event :close do
  228. transitions :to => :closed, :from => [:read, :awaiting_response], :guard => lambda { |o| o.can_close? }
  229. end
  230. end
  231. c = Conversation.create!
  232. c.can_close = true
  233. c.view!
  234. c.reply!
  235. c.close!
  236. assert_equal :closed, c.current_state
  237. end
  238. def test_cannot_go_from_read_to_closed_because_of_guard
  239. Conversation.class_eval do
  240. acts_as_state_machine :initial => :needs_attention
  241. state :needs_attention
  242. state :read
  243. state :closed
  244. state :awaiting_response
  245. event :view do
  246. transitions :to => :read, :from => [:needs_attention, :read]
  247. end
  248. event :reply do
  249. transitions :to => :awaiting_response, :from => [:read, :closed]
  250. end
  251. event :close do
  252. transitions :to => :closed, :from => [:read, :awaiting_response], :guard => lambda { |o| o.can_close? }
  253. transitions :to => :read, :from => [:read, :awaiting_response], :guard => :always_true
  254. end
  255. end
  256. c = Conversation.create!
  257. c.can_close = false
  258. c.view!
  259. c.reply!
  260. c.close!
  261. assert_equal :read, c.current_state
  262. end
  263. def test_ignore_invalid_events
  264. Conversation.class_eval do
  265. acts_as_state_machine :initial => :needs_attention
  266. state :needs_attention
  267. state :read
  268. state :closed
  269. state :awaiting_response
  270. state :junk
  271. event :new_message do
  272. transitions :to => :needs_attention, :from => [:read, :closed, :awaiting_response]
  273. end
  274. event :view do
  275. transitions :to => :read, :from => [:needs_attention, :read]
  276. end
  277. event :junk, :note => "finished" do
  278. transitions :to => :junk, :from => [:read, :closed, :awaiting_response]
  279. end
  280. end
  281. c = Conversation.create
  282. c.view!
  283. c.junk!
  284. # This is the invalid event
  285. c.new_message!
  286. assert_equal :junk, c.current_state
  287. end
  288. def test_entry_action_executed
  289. Conversation.class_eval do
  290. acts_as_state_machine :initial => :needs_attention
  291. state :needs_attention
  292. state :read, :enter => :read_enter_action
  293. event :view do
  294. transitions :to => :read, :from => [:needs_attention, :read]
  295. end
  296. end
  297. c = Conversation.create!
  298. c.read_enter = false
  299. c.view!
  300. assert c.read_enter
  301. end
  302. def test_after_actions_executed
  303. Conversation.class_eval do
  304. acts_as_state_machine :initial => :needs_attention
  305. state :needs_attention
  306. state :closed, :after => :closed_after_action
  307. state :read, :enter => :read_enter_action,
  308. :exit => Proc.new { |o| o.read_exit = true },
  309. :after => [:read_after_first_action, :read_after_second_action]
  310. event :view do
  311. transitions :to => :read, :from => [:needs_attention, :read]
  312. end
  313. event :close do
  314. transitions :to => :closed, :from => [:read, :awaiting_response]
  315. end
  316. end
  317. c = Conversation.create!
  318. c.read_after_first = false
  319. c.read_after_second = false
  320. c.closed_after = false
  321. c.view!
  322. assert c.read_after_first
  323. assert c.read_after_second
  324. c.can_close = true
  325. c.close!
  326. assert c.closed_after
  327. assert_equal :closed, c.current_state
  328. end
  329. def test_after_actions_not_run_on_loopback_transition
  330. Conversation.class_eval do
  331. acts_as_state_machine :initial => :needs_attention
  332. state :needs_attention
  333. state :closed, :after => :closed_after_action
  334. state :read, :after => [:read_after_first_action, :read_after_second_action]
  335. event :view do
  336. transitions :to => :read, :from => :needs_attention
  337. end
  338. event :close do
  339. transitions :to => :closed, :from => :read
  340. end
  341. end
  342. c = Conversation.create!
  343. c.view!
  344. c.read_after_first = false
  345. c.read_after_second = false
  346. c.view!
  347. assert !c.read_after_first
  348. assert !c.read_after_second
  349. c.can_close = true
  350. c.close!
  351. c.closed_after = false
  352. c.close!
  353. assert !c.closed_after
  354. end
  355. def test_exit_action_executed
  356. Conversation.class_eval do
  357. acts_as_state_machine :initial => :needs_attention
  358. state :junk
  359. state :needs_attention
  360. state :read, :exit => lambda { |o| o.read_exit = true }
  361. event :view do
  362. transitions :to => :read, :from => :needs_attention
  363. end
  364. event :junk, :note => "finished" do
  365. transitions :to => :junk, :from => :read
  366. end
  367. end
  368. c = Conversation.create!
  369. c.read_exit = false
  370. c.view!
  371. c.junk!
  372. assert c.read_exit
  373. end
  374. def test_entry_and_exit_not_run_on_loopback_transition
  375. Conversation.class_eval do
  376. acts_as_state_machine :initial => :needs_attention
  377. state :needs_attention
  378. state :read, :exit => lambda { |o| o.read_exit = true }
  379. event :view do
  380. transitions :to => :read, :from => [:needs_attention, :read]
  381. end
  382. end
  383. c = Conversation.create!
  384. c.view!
  385. c.read_enter = false
  386. c.read_exit = false
  387. c.view!
  388. assert !c.read_enter
  389. assert !c.read_exit
  390. end
  391. def test_entry_and_after_actions_called_for_initial_state
  392. Conversation.class_eval do
  393. acts_as_state_machine :initial => :needs_attention
  394. state :needs_attention, :enter => lambda { |o| o.needs_attention_enter = true },
  395. :after => lambda { |o| o.needs_attention_after = true }
  396. end
  397. c = Conversation.create!
  398. assert c.needs_attention_enter
  399. assert c.needs_attention_after
  400. end
  401. def test_run_transition_action_is_private
  402. Conversation.class_eval do
  403. acts_as_state_machine :initial => :needs_attention
  404. state :needs_attention
  405. end
  406. c = Conversation.create!
  407. assert_raises(NoMethodError) { c.run_transition_action :foo }
  408. end
  409. def test_find_all_in_state
  410. Conversation.class_eval do
  411. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  412. state :needs_attention
  413. state :read
  414. end
  415. cs = Conversation.find_in_state(:all, :read)
  416. assert_equal 2, cs.size
  417. end
  418. def test_find_first_in_state
  419. Conversation.class_eval do
  420. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  421. state :needs_attention
  422. state :read
  423. end
  424. c = Conversation.find_in_state(:first, :read)
  425. assert_equal conversations(:first).id, c.id
  426. end
  427. def test_find_all_in_state_with_conditions
  428. Conversation.class_eval do
  429. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  430. state :needs_attention
  431. state :read
  432. end
  433. cs = Conversation.find_in_state(:all, :read, :conditions => ['subject = ?', conversations(:second).subject])
  434. assert_equal 1, cs.size
  435. assert_equal conversations(:second).id, cs.first.id
  436. end
  437. def test_find_first_in_state_with_conditions
  438. Conversation.class_eval do
  439. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  440. state :needs_attention
  441. state :read
  442. end
  443. c = Conversation.find_in_state(:first, :read, :conditions => ['subject = ?', conversations(:second).subject])
  444. assert_equal conversations(:second).id, c.id
  445. end
  446. def test_count_in_state
  447. Conversation.class_eval do
  448. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  449. state :needs_attention
  450. state :read
  451. end
  452. cnt0 = Conversation.count(:conditions => ['state_machine = ?', 'read'])
  453. cnt = Conversation.count_in_state(:read)
  454. assert_equal cnt0, cnt
  455. end
  456. def test_count_in_state_with_conditions
  457. Conversation.class_eval do
  458. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  459. state :needs_attention
  460. state :read
  461. end
  462. cnt0 = Conversation.count(:conditions => ['state_machine = ? AND subject = ?', 'read', 'Foo'])
  463. cnt = Conversation.count_in_state(:read, :conditions => ['subject = ?', 'Foo'])
  464. assert_equal cnt0, cnt
  465. end
  466. def test_find_in_invalid_state_raises_exception
  467. Conversation.class_eval do
  468. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  469. state :needs_attention
  470. state :read
  471. end
  472. assert_raises(InvalidState) do
  473. Conversation.find_in_state(:all, :dead)
  474. end
  475. end
  476. def test_count_in_invalid_state_raises_exception
  477. Conversation.class_eval do
  478. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  479. state :needs_attention
  480. state :read
  481. end
  482. assert_raise(InvalidState) do
  483. Conversation.count_in_state(:dead)
  484. end
  485. end
  486. def test_can_access_events_via_event_table
  487. Conversation.class_eval do
  488. acts_as_state_machine :initial => :needs_attention, :column => "state_machine"
  489. state :needs_attention
  490. state :junk
  491. event :junk, :note => "finished" do
  492. transitions :to => :junk, :from => :needs_attention
  493. end
  494. end
  495. event = Conversation.event_table[:junk]
  496. assert_equal :junk, event.name
  497. assert_equal "finished", event.opts[:note]
  498. end
  499. def test_custom_state_values
  500. Conversation.class_eval do
  501. acts_as_state_machine :initial => "NEEDS_ATTENTION", :column => "state_machine"
  502. state :needs_attention, :value => "NEEDS_ATTENTION"
  503. state :read, :value => "READ"
  504. event :view do
  505. transitions :to => "READ", :from => ["NEEDS_ATTENTION", "READ"]
  506. end
  507. end
  508. c = Conversation.create!
  509. assert_equal "NEEDS_ATTENTION", c.state_machine
  510. assert c.needs_attention?
  511. c.view!
  512. assert_equal "READ", c.state_machine
  513. assert c.read?
  514. end
  515. end