riot-rails 0.0.10.pre.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG +144 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.markdown +31 -0
  5. data/Rakefile +43 -0
  6. data/VERSION +1 -0
  7. data/lib/riot/action_controller/context_macros/asserts_response.rb +15 -0
  8. data/lib/riot/action_controller/context_middleware.rb +45 -0
  9. data/lib/riot/action_controller/http_methods.rb +19 -0
  10. data/lib/riot/action_controller.rb +4 -0
  11. data/lib/riot/active_record/assertion_macros.rb +3 -0
  12. data/lib/riot/active_record/context_middleware.rb +15 -0
  13. data/lib/riot/active_record/database_macros.rb +58 -0
  14. data/lib/riot/active_record/reflection_macros.rb +106 -0
  15. data/lib/riot/active_record/transactional_middleware.rb +21 -0
  16. data/lib/riot/active_record/validation_macros.rb +187 -0
  17. data/lib/riot/active_record.rb +4 -0
  18. data/lib/riot/rails.rb +1 -0
  19. data/rails/init.rb +1 -0
  20. data/riot-rails.gemspec +130 -0
  21. data/test/action_controller/context_macros/asserts_response_test.rb +35 -0
  22. data/test/action_controller/context_middleware_test.rb +66 -0
  23. data/test/action_controller/delete_request_test.rb +45 -0
  24. data/test/action_controller/get_request_test.rb +37 -0
  25. data/test/action_controller/post_request_test.rb +45 -0
  26. data/test/action_controller/put_request_test.rb +45 -0
  27. data/test/action_controller/restful_delete_request_test.rb +28 -0
  28. data/test/action_controller/restful_get_request_test.rb +25 -0
  29. data/test/action_controller/restful_post_request_test.rb +25 -0
  30. data/test/action_controller/restful_put_request_test.rb +28 -0
  31. data/test/active_record/allowing_values_test.rb +64 -0
  32. data/test/active_record/attribute_is_invalid_test.rb +20 -0
  33. data/test/active_record/belongs_to_test.rb +22 -0
  34. data/test/active_record/context_middleware_test.rb +18 -0
  35. data/test/active_record/has_and_belongs_to_many_test.rb +22 -0
  36. data/test/active_record/has_database_index_on_test.rb +73 -0
  37. data/test/active_record/has_many_test.rb +22 -0
  38. data/test/active_record/has_one_test.rb +22 -0
  39. data/test/active_record/validates_length_of_test.rb +31 -0
  40. data/test/active_record/validates_presence_of_test.rb +14 -0
  41. data/test/active_record/validates_uniqueness_of_test.rb +23 -0
  42. data/test/rails_root/app/controllers/gremlins_controller.rb +21 -0
  43. data/test/rails_root/app/controllers/parties_controller.rb +17 -0
  44. data/test/rails_root/app/controllers/rooms_controller.rb +22 -0
  45. data/test/rails_root/app/views/rendered_templates/foo_bar.html.erb +0 -0
  46. data/test/rails_root/config/database.yml +4 -0
  47. data/test/rails_root/config/environment.rb +46 -0
  48. data/test/rails_root/config/routes.rb +7 -0
  49. data/test/rails_root/db/schema.rb +8 -0
  50. data/test/teststrap.rb +84 -0
  51. data/test/transactional_middleware_test.rb +26 -0
  52. metadata +201 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ test/rails_root/log/*
2
+ pkg/*
3
+ riot-rails.tmproj
4
+ test/rails_root/db/*.db
data/CHANGELOG ADDED
@@ -0,0 +1,144 @@
1
+ *0.0.10.pre.3*
2
+
3
+ * Renamed to riot-rails and am hoping @dasch is kind of enough to let me be an owner on the gem he originally pushed. The ol' bait and and switch. [jaknowlden]
4
+
5
+ * Added asserts_response context macro. You can also provide method names to be called against the response. [jaknowlden]
6
+
7
+ context FoosController do
8
+ setup { get "/foos" }
9
+
10
+ asserts_response.exists
11
+ asserts_response(:status).equals(200)
12
+ asserts_response(:body).matches(/Bar baz/)
13
+ end # FoosController
14
+
15
+ * Added GET support for ActionController testing, a la Rack testing methods. [jaknowlden]
16
+
17
+ context MyController do
18
+ setup { get "/my/dashboard", "foo" => "bar" }
19
+ end # MyController
20
+
21
+ * Redoing support for ActionController. Dropped everything started from scratch. Also, moved the context-helper stuff into Riot and called it ContextMiddleware. Reimplemented helpers as middleware here. [jaknowlden]
22
+
23
+ *0.0.9.pre*
24
+
25
+ * To accommodate the last change, added concept of context helpers. It's so simple you can define your own. [jaknowlden]
26
+
27
+ module RiotRails
28
+ register_context_helper do
29
+ def prepare_context(original_description, context)
30
+ # ...
31
+ context.<do-something-before-the-context-runs-if-you-want-to>
32
+ # ...
33
+ end
34
+ end
35
+ end
36
+
37
+ * Well. Aren't we special? We don't have include ActiveRecord or ActionController code if we don't want to. But if you do: [jaknowlden]
38
+
39
+ # teststrap.rb
40
+ require 'riot/activerecord' # implicit riot/rails require
41
+ require 'riot/actioncontroller' # also an implicit riot/rails require
42
+
43
+ * Added asserts_request and asserts_response context macros. They also take a possible method to call.
44
+ [jaknowlden]
45
+
46
+ rails_context UsersController do
47
+ hookup { get :show, :id => 1 }
48
+
49
+ asserts_response.kind_of(ActionController::TestResponse)
50
+ asserts_response(:body).kind_of(String)
51
+
52
+ asserts_request.kind_of(ActionController::TestRequest)
53
+ asserts_request(:cookies).kind_of(Hash)
54
+ end
55
+
56
+ * Controller assertions now no longer assume the actual value is a controller instance. Instead, the actual
57
+ value is something that should quack like a response. [jaknowlden]
58
+
59
+ * Added asserts_assigned context macro [jaknowlden]
60
+
61
+ rails_context UsersController do
62
+ hookup { get :show, :id => 1 }
63
+
64
+ asserts_assigned(:user).kind_of(User)
65
+ end
66
+
67
+ * rails_context now recognizes controllers [jaknowlden]
68
+
69
+ rails_context RoomsController do
70
+ hookup { get :index }
71
+ asserts_controller.reponse_status :ok
72
+ end
73
+
74
+ Plus, you can only use controlling within a rails_context now
75
+
76
+ rails_context "rooms" do
77
+ controlling :rooms
78
+ hookup { get :index }
79
+ asserts_controller.reponse_status :ok
80
+ end
81
+
82
+ * controlling now lets you pass a controller class reference in [jaknowlden]
83
+
84
+ rails_context "rooms" do
85
+ controlling RoomsController
86
+ end
87
+
88
+ * Reflection macros now test the options [emschwar]
89
+
90
+ rails_context Room do
91
+ asserts_topic.has_one :floor, :class_name => "Surface"
92
+ asserts_topic.has_many :walls, :join_table => "foos"
93
+ end
94
+
95
+ * Added the has_one reflection macro [emschwar]
96
+
97
+ rails_context Room do
98
+ asserts_topic.has_one :floor
99
+ end
100
+
101
+ * Added the attribute_is_invalid validation macro [jaknowlden]
102
+
103
+ rails_context Room do
104
+ hookup { topic.location = nil }
105
+ asserts_topic.attribute_is_invalid(:location, "can't be blank")
106
+ end
107
+
108
+ * Added validates_length_of assertion macro (minimalistic) [jaknowlden]
109
+
110
+ rails_context Room do
111
+ asserts_topic.validates_length_of :name, (2..36)
112
+ end
113
+
114
+ * Added transactional support to RailsContext. Disabled by default [jaknowlden]
115
+
116
+ rails_context Room do
117
+ set :transactional, true
118
+ end
119
+
120
+ * Added option enabling to RailsContext [jaknowlden]
121
+
122
+ rails_context Room do
123
+ set :this, "that"
124
+ end
125
+
126
+ * Added initial #rails_context. Assumes ActiveRecord class for now [jaknowlden]
127
+
128
+ class Room < ActiveRecord::Base
129
+ # ...
130
+ end
131
+
132
+ # TEST
133
+
134
+ rails_context Room do
135
+ asserts_topic.belongs_to(:house)
136
+ asserts_topic.validates_presence_of(:house_id)
137
+ end
138
+
139
+ * Added the #belongs_to ActiveRecord assertion macro [jaknowlden]
140
+
141
+ context "a Room" do
142
+ setup { Room.new }
143
+ asserts_topic.belongs_to(:house)
144
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Justin Knowlden, Thumble Monks
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,31 @@
1
+ # Riot Rails
2
+
3
+ [Riot](http://github.com/thumblemonks/riot) macros for Rails application testing.
4
+
5
+ LOTS more to come ...
6
+
7
+ ## Installation
8
+
9
+ We're a gem! Install per the normal course of installing gems.
10
+
11
+ gem install riot_rails
12
+
13
+ ## Usage
14
+
15
+ Tons of documentation to come. Try looking at the [RDoc](http://rdoc.info/projects/thumblemonks/riot_rails) for now. As a note, you will likely put this in your `teststrap.rb` or `test_helper.rb`:
16
+
17
+ require 'riot/rails'
18
+
19
+ ### ActiveRecord
20
+
21
+ Awesome stuff in the works. Doc coming soon.
22
+
23
+ ### ActionController
24
+
25
+ Awesome stuff in the works. Doc coming soon.
26
+
27
+ ### ActionMailer
28
+
29
+ Awesome stuff coming soon. See [Shoulda Action Mailer](http://github.com/thumblemonks/shoulda_action_mailer) - which is also by us - in the meantime.
30
+
31
+ I told you we liked Shoulda.
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ task :default => [:test]
5
+
6
+ require 'rake/testtask'
7
+ Rake::TestTask.new(:test) do |test|
8
+ test.libs << 'lib' << 'test'
9
+ test.pattern = 'test/**/*_test.rb'
10
+ test.verbose = false
11
+ end
12
+
13
+ desc "Run Flog against library (except tests)"
14
+ task :flog do
15
+ puts %x[find ./lib -name *.rb | xargs flog]
16
+ end
17
+
18
+ desc "Run Roodi against library (except tests)"
19
+ task :roodi do
20
+ puts %x[find ./lib -name *.rb | xargs roodi]
21
+ end
22
+
23
+ #
24
+ # Some monks like diamonds. I like gems.
25
+
26
+ begin
27
+ require 'jeweler'
28
+ Jeweler::Tasks.new do |gem|
29
+ gem.name = "riot-rails"
30
+ gem.summary = "Riot specific test support for Rails apps"
31
+ gem.description = "Riot specific test support for Rails apps. Protest the slow app."
32
+ gem.email = "gus@gusg.us"
33
+ gem.homepage = "http://github.com/thumblemonks/riot-rails"
34
+ gem.authors = ["Justin 'Gus' Knowlden"]
35
+ gem.add_dependency("riot", ">= 0.11.0")
36
+ gem.add_dependency("rack-test", ">= 0.5.3")
37
+ gem.add_development_dependency("activerecord", ">= 3.0.0.beta3")
38
+ gem.add_development_dependency("actionpack", ">= 3.0.0.beta3")
39
+ end
40
+ Jeweler::GemcutterTasks.new
41
+ rescue LoadError
42
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
43
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.10.pre.3
@@ -0,0 +1,15 @@
1
+ module RiotRails
2
+ module ActionController
3
+ module AssertsResponse
4
+
5
+ def asserts_response(method_name=nil)
6
+ if method_name
7
+ asserts("response ##{method_name.to_s}") { response.send(method_name) }
8
+ else
9
+ asserts("response") { response }
10
+ end
11
+ end
12
+
13
+ end # AssertsResponse
14
+ end # ActionController
15
+ end # RiotRails
@@ -0,0 +1,45 @@
1
+ module RiotRails
2
+ class ActionControllerMiddleware < Riot::ContextMiddleware
3
+ register
4
+
5
+ def handle?(context)
6
+ description = context.description
7
+ description.kind_of?(Class) && description.ancestors.include?(::ActionController::Base)
8
+ end # handle?
9
+
10
+ def call(context)
11
+ setup_context_macros(context)
12
+ setup_situation(context)
13
+ end
14
+ private
15
+ def setup_context_macros(context)
16
+ context.class_eval do
17
+ include RiotRails::ActionController::AssertsResponse
18
+ end
19
+ end
20
+
21
+ def setup_situation(context)
22
+ context.helper(:app) { @app }
23
+ context.helper(:env) { @env }
24
+ context.helper(:controller) { env["action_controller.instance"] }
25
+
26
+ context.helper(:request) do
27
+ return controller.request if controller
28
+ raise Exception, "No request made yet"
29
+ end
30
+
31
+ context.helper(:response) do
32
+ return controller.response if controller
33
+ raise Exception, "No response since no request made yet"
34
+ end
35
+
36
+ context.setup(true) do
37
+ self.class_eval { include RiotRails::ActionController::HttpMethods }
38
+ @app = ::Rails.application
39
+ http_reset
40
+ context.description
41
+ end # context.setup(true)
42
+ end # call
43
+
44
+ end # ActionControllerMiddleware
45
+ end # RiotRails
@@ -0,0 +1,19 @@
1
+ module RiotRails
2
+ module ActionController
3
+ module HttpMethods
4
+ def http_reset; @env = {}; end
5
+ def get(uri, params={}) perform_request("GET", uri, params); end
6
+ def post(uri, params={}) perform_request("POST", uri, params); end
7
+ def put(uri, params={}); perform_request("PUT", uri, params); end
8
+ def delete(uri, params={}); perform_request("DELETE", uri, params); end
9
+ private
10
+ def perform_request(request_method, uri, params)
11
+ http_reset
12
+ params = params.inject({}) { |acc,(key,val)| acc[key] = val.to_s; acc }
13
+ @env = ::Rack::MockRequest.env_for(uri, {:params => params, :method => request_method}).merge(@env)
14
+ @env['action_dispatch.show_exceptions'] = false
15
+ @app.call(@env)
16
+ end
17
+ end # HttpMethods
18
+ end # ActionController
19
+ end # RiotRails
@@ -0,0 +1,4 @@
1
+ require 'riot/rails'
2
+ require 'riot/action_controller/context_middleware'
3
+ require 'riot/action_controller/context_macros/asserts_response'
4
+ require 'riot/action_controller/http_methods'
@@ -0,0 +1,3 @@
1
+ require 'riot/active_record/validation_macros'
2
+ require 'riot/active_record/reflection_macros'
3
+ require 'riot/active_record/database_macros'
@@ -0,0 +1,15 @@
1
+ module RiotRails
2
+ class ActiveRecordMiddleware < Riot::ContextMiddleware
3
+ register
4
+
5
+ def handle?(context)
6
+ description = context.description
7
+ description.kind_of?(Class) && description.ancestors.include?(::ActiveRecord::Base)
8
+ end
9
+
10
+ def call(context)
11
+ context.setup(true) { context.description.new }
12
+ end
13
+
14
+ end # ActiveRecordMiddleware
15
+ end # RiotRails
@@ -0,0 +1,58 @@
1
+ module RiotRails
2
+ module ActiveRecord
3
+
4
+ # An ActiveRecord assertion macro that looks for an index on a given set of attributes in the table
5
+ # used by the model under test (aka: topic).
6
+ #
7
+ # asserts_topic.has_database_index_on :name
8
+ # asserts_topic.has_database_index_on :email, :group_name
9
+ #
10
+ # In the form used above, the assertion will pass if any index is found with the attributes listed
11
+ # (unique or not). To be specific about uniqueness, provide the +:unique+ option.
12
+ #
13
+ # asserts_topic.has_database_index_on :email, :unique => true
14
+ # asserts_topic.has_database_index_on :name, :unique => false
15
+ #
16
+ # The last example will require that the index not be a unique one.
17
+ class HasDatabaseIndexOnMacro < Riot::AssertionMacro
18
+ register :has_database_index_on
19
+
20
+ def initialize(database_connection=nil) # Good for testing :)
21
+ @database_connection = database_connection
22
+ end
23
+
24
+ def evaluate(actual, *attributes_and_options)
25
+ attributes, options = extract_options_from(attributes_and_options)
26
+ unique = options[:unique]
27
+
28
+ index = find_index(actual, attributes, unique)
29
+
30
+ static_message = "#{unique ? "unique" : ""} index on #{attributes.inspect}".strip
31
+ index.nil? ? fail("expected #{static_message}") : pass("has #{static_message}")
32
+ end
33
+ private
34
+ def database_connection; @database_connection || ::ActiveRecord::Base.connection; end
35
+
36
+ def find_index(model, attributes, uniqueness, &block)
37
+ database_connection.indexes(model.class.table_name).find do |the_index|
38
+ unique_enough?(uniqueness, the_index) && attributes_match?(attributes, the_index)
39
+ end
40
+ end
41
+
42
+ def unique_enough?(uniqueness, index)
43
+ return true if uniqueness.nil?
44
+ index.unique == uniqueness
45
+ end
46
+
47
+ def attributes_match?(attributes, index)
48
+ Set.new(attributes.map(&:to_s)) == Set.new(index.columns)
49
+ end
50
+
51
+ def extract_options_from(attributes)
52
+ options = attributes.last.kind_of?(Hash) ? attributes.pop : {}
53
+ [attributes, options]
54
+ end
55
+ end
56
+
57
+ end # ActiveRecord
58
+ end # RiotRails
@@ -0,0 +1,106 @@
1
+ module RiotRails
2
+ module ActiveRecord
3
+ protected
4
+
5
+ class ReflectionAssertionMacro < Riot::AssertionMacro
6
+ private
7
+ def reflection_match?(expected, reflection)
8
+ !reflection.nil? && (expected == reflection.macro.to_s)
9
+ end
10
+
11
+ def options_match_for_reflection?(reflection, options)
12
+ options.all? { |k, v| reflection.options[k] == v }
13
+ end
14
+
15
+ def assert_reflection(expected, record, attribute, options=nil)
16
+ options ||= {}
17
+ reflection = record.class.reflect_on_association(attribute)
18
+ if reflection_match?(expected, reflection)
19
+ if options_match_for_reflection?(reflection, options)
20
+ pass new_message(attribute).is_a.push(expected).association
21
+ else
22
+ fail expected_message.push("#{expected} #{attribute.inspect}").with(options)
23
+ end
24
+ else
25
+ fail new_message(attribute).is_not_a.push(expected).association
26
+ end
27
+ end
28
+ end
29
+
30
+ public
31
+
32
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a +has_many+
33
+ # association. Will fail if an association is not defined for the attribute or if the association is
34
+ # not +has_many+.
35
+ #
36
+ # context "a Room" do
37
+ # setup { Room.new }
38
+ #
39
+ # asserts_topic.has_many(:doors)
40
+ # asserts_topic.has_many(:floors) # should probably fail given our current universe :)
41
+ # end
42
+ class HasManyMacro < ReflectionAssertionMacro
43
+ register :has_many
44
+
45
+ def evaluate(actual, *expectings)
46
+ attribute, options = *expectings
47
+ assert_reflection("has_many", actual, attribute, options)
48
+ end
49
+ end
50
+
51
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a
52
+ # +has_one+ association. Will fail if an association is not defined for the attribute or if the
53
+ # association is not +has_one+.
54
+ #
55
+ # context "a Room" do
56
+ # setup { Room.new }
57
+ #
58
+ # asserts_topic.has_one(:floor)
59
+ # end
60
+ class HasOneMacro < ReflectionAssertionMacro
61
+ register :has_one
62
+
63
+ def evaluate(actual, *expectings)
64
+ attribute, options = *expectings
65
+ assert_reflection("has_one", actual, attribute, options)
66
+ end
67
+ end
68
+
69
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a
70
+ # +belongs_to+ association. Will fail if an association is not defined for the attribute or if the
71
+ # association is not +belongs_to+.
72
+ #
73
+ # context "a Room" do
74
+ # setup { Room.new }
75
+ #
76
+ # asserts_topic.belongs_to(:house)
77
+ # end
78
+ class BelongsToMacro < ReflectionAssertionMacro
79
+ register :belongs_to
80
+
81
+ def evaluate(actual, *expectings)
82
+ attribute, options = *expectings
83
+ assert_reflection("belongs_to", actual, attribute, options)
84
+ end
85
+ end
86
+
87
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a
88
+ # +has_and_belongs_to_many+ association. Will fail if an association is not defined for the attribute or
89
+ # if the association is not +has_and_belongs_to_many+.
90
+ #
91
+ # context "a Room" do
92
+ # setup { Room.new }
93
+ #
94
+ # asserts_topic.has_and_belongs_to_many(:walls)
95
+ # end
96
+ class HasAndBelongsToManyMacro < ReflectionAssertionMacro
97
+ register :has_and_belongs_to_many
98
+
99
+ def evaluate(actual, *expectings)
100
+ attribute, options = *expectings
101
+ assert_reflection("has_and_belongs_to_many", actual, attribute, options)
102
+ end
103
+ end
104
+
105
+ end # ActiveRecord
106
+ end # RiotRails
@@ -0,0 +1,21 @@
1
+ module RiotRails
2
+ class TransactionalMiddleware < Riot::ContextMiddleware
3
+ register
4
+
5
+ def handle?(context)
6
+ context.option(:transactional) == true
7
+ end
8
+
9
+ def call(context)
10
+ context.class.class_eval do
11
+ alias_method :transactionless_local_run, :local_run
12
+ def local_run(*args)
13
+ ::ActiveRecord::Base.transaction do
14
+ transactionless_local_run(*args)
15
+ raise ::ActiveRecord::Rollback
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end # RiotRails