riot-rails 0.0.10.pre.3

Sign up to get free protection for your applications and to get access to all the features.
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