stratagem 0.1.7
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.
- data/Manifest +99 -0
- data/Rakefile +17 -0
- data/bin/stratagem +10 -0
- data/init.rb +2 -0
- data/lib/bootstrap.rb +31 -0
- data/lib/stratagem/authentication.rb +64 -0
- data/lib/stratagem/auto_mock/aquifer.rb +86 -0
- data/lib/stratagem/auto_mock/factory.rb +213 -0
- data/lib/stratagem/auto_mock/value_generator.rb +174 -0
- data/lib/stratagem/auto_mock.rb +6 -0
- data/lib/stratagem/blocker.rb +16 -0
- data/lib/stratagem/client.rb +32 -0
- data/lib/stratagem/command.rb +13 -0
- data/lib/stratagem/commands/analyze.rb +22 -0
- data/lib/stratagem/commands/base.rb +11 -0
- data/lib/stratagem/commands/devel_crawl.rb +27 -0
- data/lib/stratagem/commands/devel_mock.rb +10 -0
- data/lib/stratagem/commands.rb +7 -0
- data/lib/stratagem/crawler/authentication.rb +109 -0
- data/lib/stratagem/crawler/form.rb +101 -0
- data/lib/stratagem/crawler/html_utils.rb +92 -0
- data/lib/stratagem/crawler/session.rb +296 -0
- data/lib/stratagem/crawler/site_model.rb +138 -0
- data/lib/stratagem/crawler/trace_utils.rb +10 -0
- data/lib/stratagem/crawler.rb +9 -0
- data/lib/stratagem/extensions/class.rb +9 -0
- data/lib/stratagem/extensions/hash.rb +16 -0
- data/lib/stratagem/extensions/module.rb +11 -0
- data/lib/stratagem/extensions/object.rb +15 -0
- data/lib/stratagem/extensions/red_parse.rb +86 -0
- data/lib/stratagem/extensions/string.rb +20 -0
- data/lib/stratagem/extensions.rb +6 -0
- data/lib/stratagem/framework_extensions/controllers/action_controller.rb +10 -0
- data/lib/stratagem/framework_extensions/controllers/action_mailer.rb +12 -0
- data/lib/stratagem/framework_extensions/controllers.rb +5 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb +7 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb +35 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb +103 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb +50 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb +11 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb +10 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb +30 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/adapters/common/authentication_metadata.rb +21 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb +13 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb +19 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb +30 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/annotations.rb +79 -0
- data/lib/stratagem/framework_extensions/models/detect.rb +7 -0
- data/lib/stratagem/framework_extensions/models/metadata.rb +85 -0
- data/lib/stratagem/framework_extensions/models/mocking.rb +23 -0
- data/lib/stratagem/framework_extensions/models/tracing.rb +71 -0
- data/lib/stratagem/framework_extensions/models.rb +21 -0
- data/lib/stratagem/framework_extensions/rails.rb +8 -0
- data/lib/stratagem/framework_extensions.rb +6 -0
- data/lib/stratagem/interface/browser.rb +37 -0
- data/lib/stratagem/interface/public/images/backgrounds/content.png +0 -0
- data/lib/stratagem/interface/public/images/backgrounds/shadow.png +0 -0
- data/lib/stratagem/interface/public/javascripts/jquery-1.4.2.min.js +154 -0
- data/lib/stratagem/interface/public/javascripts/stratagem.js +27 -0
- data/lib/stratagem/interface/public/javascripts/stratagem_debug.js +53 -0
- data/lib/stratagem/interface/public/stylesheets/960.css +1 -0
- data/lib/stratagem/interface/public/stylesheets/reset.css +10 -0
- data/lib/stratagem/interface/public/stylesheets/stratagem.css +20 -0
- data/lib/stratagem/interface/public/stylesheets/stratagem_debug.css +20 -0
- data/lib/stratagem/interface/views/debug.haml +43 -0
- data/lib/stratagem/interface/views/index.haml +35 -0
- data/lib/stratagem/labs/auto_mock.rb +7 -0
- data/lib/stratagem/labs/crawler.rb +0 -0
- data/lib/stratagem/logger.rb +46 -0
- data/lib/stratagem/model/application.rb +157 -0
- data/lib/stratagem/model/components/base.rb +55 -0
- data/lib/stratagem/model/components/controller.rb +118 -0
- data/lib/stratagem/model/components/model.rb +170 -0
- data/lib/stratagem/model/components/reference.rb +30 -0
- data/lib/stratagem/model/components/route.rb +53 -0
- data/lib/stratagem/model/components/static_file.rb +18 -0
- data/lib/stratagem/model/components/view.rb +186 -0
- data/lib/stratagem/model/parse_util.rb +61 -0
- data/lib/stratagem/model.rb +12 -0
- data/lib/stratagem/model_builder.rb +146 -0
- data/lib/stratagem/recipes/deploy.rb +30 -0
- data/lib/stratagem/scan/checks/capistrano/secure_deploy.rb +43 -0
- data/lib/stratagem/scan/checks/email_address.rb +15 -0
- data/lib/stratagem/scan/checks/error_pages.rb +25 -0
- data/lib/stratagem/scan/checks/filter_parameter_logging.rb +6 -0
- data/lib/stratagem/scan/checks/mongo_mapper/base.rb +19 -0
- data/lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb +32 -0
- data/lib/stratagem/scan/checks/routes.rb +16 -0
- data/lib/stratagem/scan/checks/ssl/secure_login_page.rb +19 -0
- data/lib/stratagem/scan/checks/ssl/secure_login_submit.rb +18 -0
- data/lib/stratagem/scan/result.rb +45 -0
- data/lib/stratagem/scan.rb +19 -0
- data/lib/stratagem/scanner.rb +32 -0
- data/lib/stratagem/site_crawler.rb +47 -0
- data/lib/stratagem/snapshot.rb +33 -0
- data/lib/stratagem.rb +77 -0
- data/lib/tasks/_old_stratagem.rake +99 -0
- data/stratagem.gemspec +56 -0
- metadata +380 -0
data/Manifest
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Manifest
|
|
2
|
+
Rakefile
|
|
3
|
+
bin/stratagem
|
|
4
|
+
init.rb
|
|
5
|
+
lib/bootstrap.rb
|
|
6
|
+
lib/stratagem.rb
|
|
7
|
+
lib/stratagem/authentication.rb
|
|
8
|
+
lib/stratagem/auto_mock.rb
|
|
9
|
+
lib/stratagem/auto_mock/aquifer.rb
|
|
10
|
+
lib/stratagem/auto_mock/factory.rb
|
|
11
|
+
lib/stratagem/auto_mock/value_generator.rb
|
|
12
|
+
lib/stratagem/blocker.rb
|
|
13
|
+
lib/stratagem/client.rb
|
|
14
|
+
lib/stratagem/command.rb
|
|
15
|
+
lib/stratagem/commands.rb
|
|
16
|
+
lib/stratagem/commands/analyze.rb
|
|
17
|
+
lib/stratagem/commands/base.rb
|
|
18
|
+
lib/stratagem/commands/devel_crawl.rb
|
|
19
|
+
lib/stratagem/commands/devel_mock.rb
|
|
20
|
+
lib/stratagem/crawler.rb
|
|
21
|
+
lib/stratagem/crawler/authentication.rb
|
|
22
|
+
lib/stratagem/crawler/form.rb
|
|
23
|
+
lib/stratagem/crawler/html_utils.rb
|
|
24
|
+
lib/stratagem/crawler/session.rb
|
|
25
|
+
lib/stratagem/crawler/site_model.rb
|
|
26
|
+
lib/stratagem/crawler/trace_utils.rb
|
|
27
|
+
lib/stratagem/extensions.rb
|
|
28
|
+
lib/stratagem/extensions/class.rb
|
|
29
|
+
lib/stratagem/extensions/hash.rb
|
|
30
|
+
lib/stratagem/extensions/module.rb
|
|
31
|
+
lib/stratagem/extensions/object.rb
|
|
32
|
+
lib/stratagem/extensions/red_parse.rb
|
|
33
|
+
lib/stratagem/extensions/string.rb
|
|
34
|
+
lib/stratagem/framework_extensions.rb
|
|
35
|
+
lib/stratagem/framework_extensions/controllers.rb
|
|
36
|
+
lib/stratagem/framework_extensions/controllers/action_controller.rb
|
|
37
|
+
lib/stratagem/framework_extensions/controllers/action_mailer.rb
|
|
38
|
+
lib/stratagem/framework_extensions/models.rb
|
|
39
|
+
lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb
|
|
40
|
+
lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb
|
|
41
|
+
lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb
|
|
42
|
+
lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb
|
|
43
|
+
lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb
|
|
44
|
+
lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb
|
|
45
|
+
lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb
|
|
46
|
+
lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb
|
|
47
|
+
lib/stratagem/framework_extensions/models/adapters/common/authentication_metadata.rb
|
|
48
|
+
lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb
|
|
49
|
+
lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb
|
|
50
|
+
lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb
|
|
51
|
+
lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb
|
|
52
|
+
lib/stratagem/framework_extensions/models/annotations.rb
|
|
53
|
+
lib/stratagem/framework_extensions/models/detect.rb
|
|
54
|
+
lib/stratagem/framework_extensions/models/metadata.rb
|
|
55
|
+
lib/stratagem/framework_extensions/models/mocking.rb
|
|
56
|
+
lib/stratagem/framework_extensions/models/tracing.rb
|
|
57
|
+
lib/stratagem/framework_extensions/rails.rb
|
|
58
|
+
lib/stratagem/interface/browser.rb
|
|
59
|
+
lib/stratagem/interface/public/images/backgrounds/content.png
|
|
60
|
+
lib/stratagem/interface/public/images/backgrounds/shadow.png
|
|
61
|
+
lib/stratagem/interface/public/javascripts/jquery-1.4.2.min.js
|
|
62
|
+
lib/stratagem/interface/public/javascripts/stratagem.js
|
|
63
|
+
lib/stratagem/interface/public/javascripts/stratagem_debug.js
|
|
64
|
+
lib/stratagem/interface/public/stylesheets/960.css
|
|
65
|
+
lib/stratagem/interface/public/stylesheets/reset.css
|
|
66
|
+
lib/stratagem/interface/public/stylesheets/stratagem.css
|
|
67
|
+
lib/stratagem/interface/public/stylesheets/stratagem_debug.css
|
|
68
|
+
lib/stratagem/interface/views/debug.haml
|
|
69
|
+
lib/stratagem/interface/views/index.haml
|
|
70
|
+
lib/stratagem/labs/auto_mock.rb
|
|
71
|
+
lib/stratagem/labs/crawler.rb
|
|
72
|
+
lib/stratagem/logger.rb
|
|
73
|
+
lib/stratagem/model.rb
|
|
74
|
+
lib/stratagem/model/application.rb
|
|
75
|
+
lib/stratagem/model/components/base.rb
|
|
76
|
+
lib/stratagem/model/components/controller.rb
|
|
77
|
+
lib/stratagem/model/components/model.rb
|
|
78
|
+
lib/stratagem/model/components/reference.rb
|
|
79
|
+
lib/stratagem/model/components/route.rb
|
|
80
|
+
lib/stratagem/model/components/static_file.rb
|
|
81
|
+
lib/stratagem/model/components/view.rb
|
|
82
|
+
lib/stratagem/model/parse_util.rb
|
|
83
|
+
lib/stratagem/model_builder.rb
|
|
84
|
+
lib/stratagem/recipes/deploy.rb
|
|
85
|
+
lib/stratagem/scan.rb
|
|
86
|
+
lib/stratagem/scan/checks/capistrano/secure_deploy.rb
|
|
87
|
+
lib/stratagem/scan/checks/email_address.rb
|
|
88
|
+
lib/stratagem/scan/checks/error_pages.rb
|
|
89
|
+
lib/stratagem/scan/checks/filter_parameter_logging.rb
|
|
90
|
+
lib/stratagem/scan/checks/mongo_mapper/base.rb
|
|
91
|
+
lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb
|
|
92
|
+
lib/stratagem/scan/checks/routes.rb
|
|
93
|
+
lib/stratagem/scan/checks/ssl/secure_login_page.rb
|
|
94
|
+
lib/stratagem/scan/checks/ssl/secure_login_submit.rb
|
|
95
|
+
lib/stratagem/scan/result.rb
|
|
96
|
+
lib/stratagem/scanner.rb
|
|
97
|
+
lib/stratagem/site_crawler.rb
|
|
98
|
+
lib/stratagem/snapshot.rb
|
|
99
|
+
lib/tasks/_old_stratagem.rake
|
data/Rakefile
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'echoe'
|
|
4
|
+
|
|
5
|
+
Echoe.new('stratagem', '0.1.7') do |p|
|
|
6
|
+
p.description = "Intuitive security analysis of your Rails applications"
|
|
7
|
+
p.url = "http://github.com/stratagem/stratagem"
|
|
8
|
+
p.author = "Charles Grimes"
|
|
9
|
+
p.email = "cj@stratagemapp.com"
|
|
10
|
+
p.executable_pattern = ['bin/*']
|
|
11
|
+
p.ignore_pattern = ["tmp/*", "script/*", "spec/*", "webapp/*"]
|
|
12
|
+
p.runtime_dependencies = ["launchy >=0.3.5", "redparse >=0.8.4", "haml >=3.0.0"]
|
|
13
|
+
p.development_dependencies = ["launchy >=0.3.5", "redparse >=0.8.4", "sinatra =1.0", "haml >=3.0.0", "webrat >=0.4.3"]
|
|
14
|
+
# p.requirements ["Install the stratagem-ui gem for the web browser interface."]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/bin/stratagem
ADDED
data/init.rb
ADDED
data/lib/bootstrap.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
# inject environment helpers into Rails class to be used during bootstrapping
|
|
3
|
+
Rails.class_eval do
|
|
4
|
+
def self.stratagem_production_env
|
|
5
|
+
@stratagem_initial_env ||= @_env
|
|
6
|
+
@_env = ActiveSupport::StringInquirer.new('production')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.stratagem_restore_env
|
|
10
|
+
@_env = @stratagem_initial_env
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Inject env change into Rails initializer prior to loading controllers
|
|
16
|
+
# but after framework is initialized. This is to force SSL to work as
|
|
17
|
+
# if the app were running in production mode
|
|
18
|
+
Rails::Initializer.class_eval do
|
|
19
|
+
alias_method :rails_load_application_classes, :load_application_classes
|
|
20
|
+
|
|
21
|
+
def load_application_classes
|
|
22
|
+
puts "LOADING APPLICATION CLASSES"
|
|
23
|
+
# load stratagem
|
|
24
|
+
require 'stratagem'
|
|
25
|
+
|
|
26
|
+
Rails.stratagem_production_env
|
|
27
|
+
rails_load_application_classes
|
|
28
|
+
Rails.stratagem_restore_env
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
require 'singleton'
|
|
3
|
+
|
|
4
|
+
module Stratagem
|
|
5
|
+
class Authentication
|
|
6
|
+
include Singleton
|
|
7
|
+
|
|
8
|
+
FILENAME = '.stratagem'
|
|
9
|
+
|
|
10
|
+
attr_reader :token, :project
|
|
11
|
+
|
|
12
|
+
def store_credentials(account, token, project)
|
|
13
|
+
File.open(FILENAME, 'w') do |f|
|
|
14
|
+
f.puts account
|
|
15
|
+
f.puts token
|
|
16
|
+
f.puts project
|
|
17
|
+
end
|
|
18
|
+
reload_credentials
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def url
|
|
22
|
+
if (credentials.nil?)
|
|
23
|
+
base_url+"/project_links/new"
|
|
24
|
+
else
|
|
25
|
+
base_url+"/project_links/validate/#{credentials[:token]}/#{credentials[:project]}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def base_url
|
|
30
|
+
subdomain = credentials ? credentials[:account] : 'www'
|
|
31
|
+
"http://#{subdomain}.#{Stratagem.domain}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def project_url
|
|
35
|
+
"#{base_url}/projects/#{credentials[:project]}?api_key=#{credentials[:token]}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def credentials
|
|
39
|
+
@credentials ||= load_credentials
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
def reload_credentials
|
|
44
|
+
@credentials = nil
|
|
45
|
+
credentials
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def load_credentials
|
|
49
|
+
if File.exists?(FILENAME)
|
|
50
|
+
credentials = nil
|
|
51
|
+
File.open(FILENAME) do |f|
|
|
52
|
+
credentials = {
|
|
53
|
+
:account => f.readline.strip,
|
|
54
|
+
:token => f.readline.strip,
|
|
55
|
+
:project => f.readline.strip
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
return credentials
|
|
59
|
+
else
|
|
60
|
+
return nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require 'timeout'
|
|
2
|
+
|
|
3
|
+
module Stratagem::AutoMock
|
|
4
|
+
class Aquifer
|
|
5
|
+
include Singleton
|
|
6
|
+
include Factory
|
|
7
|
+
|
|
8
|
+
attr_accessor :application
|
|
9
|
+
|
|
10
|
+
def self.init(application)
|
|
11
|
+
at_exit { self.instance.destroy }
|
|
12
|
+
self.instance.application = application
|
|
13
|
+
self.instance
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def destroy
|
|
17
|
+
objects = self.repo.values.inject([]) {|memo,obj| memo += obj.compact }
|
|
18
|
+
i = 0
|
|
19
|
+
while (objects.size > 0 && ((i+=1) < objects.size))
|
|
20
|
+
objects = objects.select do |instance|
|
|
21
|
+
puts "deleting #{instance.class.name}"
|
|
22
|
+
begin
|
|
23
|
+
instance.destroy
|
|
24
|
+
rescue
|
|
25
|
+
begin
|
|
26
|
+
instance.delete
|
|
27
|
+
rescue
|
|
28
|
+
puts "Unable to delete object: #{instance.class.name} - #{$!.message}"
|
|
29
|
+
puts $!.backtrace
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
!instance.frozen?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def instances_of(model_klass)
|
|
38
|
+
repo[model_klass].clone
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def random_instance(model_klass)
|
|
42
|
+
objects = repo[model_klass]
|
|
43
|
+
puts "found #{objects.size} instances in well"
|
|
44
|
+
instance = objects[rand objects.size]
|
|
45
|
+
instance
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def fill
|
|
49
|
+
Stratagem.logger.phase "mocking_models"
|
|
50
|
+
application.models.each do |meta_model|
|
|
51
|
+
models = mock_model(meta_model.klass) if (meta_model.stratagem?)
|
|
52
|
+
end
|
|
53
|
+
puts "aquifer full"
|
|
54
|
+
application.models.each do |meta_model|
|
|
55
|
+
puts "#{meta_model.klass.name}"
|
|
56
|
+
(repo[meta_model.klass] || []).each do |instance|
|
|
57
|
+
puts "\t#{instance.id}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def mock_model(klass, count=2)
|
|
63
|
+
count.times do |i|
|
|
64
|
+
instance,valid = mock(klass)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
protected
|
|
69
|
+
|
|
70
|
+
def instance_call(instance, method, *args)
|
|
71
|
+
begin
|
|
72
|
+
timeout(3) do
|
|
73
|
+
instance.send(method, *args)
|
|
74
|
+
end
|
|
75
|
+
rescue Timeout::Error
|
|
76
|
+
e = StratagemError.new("unable to call method #{method} on model #{instance.class.name}, timed out") # log it
|
|
77
|
+
puts e.message
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def repo
|
|
83
|
+
mocked
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
module Stratagem::AutoMock
|
|
2
|
+
@@bank = {} # key is model class, value is an array of mocked instances
|
|
3
|
+
|
|
4
|
+
# todo - move these to a generic automock file
|
|
5
|
+
class MockError < StratagemError; end
|
|
6
|
+
class UnsupportedModelError < MockError; end
|
|
7
|
+
class UnsupportedColumnTypeError < MockError; end
|
|
8
|
+
class ColumnValidationError < MockError; end
|
|
9
|
+
class ParentsExhaustedError < MockError; end
|
|
10
|
+
|
|
11
|
+
module Factory
|
|
12
|
+
include ValueGenerator
|
|
13
|
+
|
|
14
|
+
def mock(model,mock_chain=[], belongs_to=nil)
|
|
15
|
+
return if mock_chain.select {|m| m == model }.size > 1
|
|
16
|
+
mock_chain << model
|
|
17
|
+
|
|
18
|
+
object,valid = model.new, nil
|
|
19
|
+
begin
|
|
20
|
+
10.times do |i|
|
|
21
|
+
log "mocking model #{model.name}"
|
|
22
|
+
populate_attributes(object, mock_chain)
|
|
23
|
+
correct_invalid_columns(object, mock_chain)
|
|
24
|
+
|
|
25
|
+
valid = populate_relations(object, mock_chain, belongs_to)
|
|
26
|
+
|
|
27
|
+
# allow relationship attributes to be invalid
|
|
28
|
+
valid = save_object(object) if (valid)
|
|
29
|
+
|
|
30
|
+
if valid
|
|
31
|
+
add_mocked(object)
|
|
32
|
+
break
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
rescue
|
|
36
|
+
puts $!.class.name
|
|
37
|
+
puts $!.message
|
|
38
|
+
puts $!.backtrace
|
|
39
|
+
e = MockError.new("Unexpected error mocking model #{model.name}")
|
|
40
|
+
e.target = $!
|
|
41
|
+
# raise e
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
return [object,valid] if valid
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
protected
|
|
48
|
+
|
|
49
|
+
def add_mocked(instance)
|
|
50
|
+
(mocked[instance.class] ||= []) << instance
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def mocked(model=nil)
|
|
54
|
+
@mocked ||= {}
|
|
55
|
+
if (model)
|
|
56
|
+
@mocked[model] ||= []
|
|
57
|
+
else
|
|
58
|
+
@mocked
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def populate_attributes(object, mock_chain)
|
|
63
|
+
exclude = [:created_at, :updated_at] + object.stratagem.relation_names + object.stratagem.foreign_keys + object.stratagem.internal_attributes
|
|
64
|
+
|
|
65
|
+
# exclude the confirmation attributes, as they will be set automatically
|
|
66
|
+
exclude += object.stratagem.validations(nil, :validates_confirmation_of).map {|v| (v.field.to_s+"_confirmation").to_sym }
|
|
67
|
+
exclude += object.stratagem.exclude_attributes_for_mocking
|
|
68
|
+
exclude += object.stratagem.internal_attributes
|
|
69
|
+
|
|
70
|
+
exclude_regex = [/^photo/, /picture/]
|
|
71
|
+
names = object.stratagem.attribute_names.select {|n| n !~ /_id$/ } - exclude
|
|
72
|
+
puts "mocking names: #{names.inspect}"
|
|
73
|
+
puts "excluded: #{exclude.inspect}"
|
|
74
|
+
puts "internal: #{object.stratagem.internal_attributes.inspect}"
|
|
75
|
+
names.each do |attr_name|
|
|
76
|
+
next if exclude_regex.find {|r| attr_name =~ r }
|
|
77
|
+
set_attribute_value(object, attr_name, mock_chain)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# check for fields that should be forced into specific values
|
|
81
|
+
object.stratagem.validations.select {|v| [:validates_acceptance_of, :validates_presence_of].include?(v.validation) }.each do |validator|
|
|
82
|
+
value = validator.args[:with] || validator.args[:accept]
|
|
83
|
+
set_attribute_value(object, validator.field, mock_chain, value) if (value && value.kind_of?(String))
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
(object.stratagem.invalid_columns(object)).size == 0
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def populate_relations(object, mock_chain, belongs_to=nil)
|
|
90
|
+
# belongs_to relations
|
|
91
|
+
object.stratagem.relation_names(:belongs_to).each do |attr_name|
|
|
92
|
+
relation = object.stratagem.relation(attr_name)
|
|
93
|
+
if (belongs_to && belongs_to.class == relation.klass)
|
|
94
|
+
object.send(attr_name.to_s+"=", belongs_to)
|
|
95
|
+
else
|
|
96
|
+
instances = mocked(relation.klass)
|
|
97
|
+
if (instances.size > 1)
|
|
98
|
+
object.send(attr_name.to_s+"=", instances[rand instances.size])
|
|
99
|
+
else
|
|
100
|
+
set_attribute_value(object, attr_name, mock_chain)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# has_many relations
|
|
106
|
+
# object.stratagem.relation_names(:has_many).each do |attr_name|
|
|
107
|
+
# relation = object.stratagem.relation(attr_name)
|
|
108
|
+
# # select the instances that don't have an existing belongs_to for this relation type
|
|
109
|
+
# instances = mocked(relation.klass).select {|i|
|
|
110
|
+
# br = i.stratagem.relations.find {|r| r.klass == i.class }
|
|
111
|
+
# if (br && !i.send(br.name).nil?)
|
|
112
|
+
# false
|
|
113
|
+
# else
|
|
114
|
+
# true
|
|
115
|
+
# end
|
|
116
|
+
# }
|
|
117
|
+
# if (instances.size > 2)
|
|
118
|
+
# collection = object.send(attr_name.to_s)
|
|
119
|
+
# collection << instances[rand instances.size]
|
|
120
|
+
# else
|
|
121
|
+
# mock(relation.klass, mock_chain, object)
|
|
122
|
+
# end
|
|
123
|
+
# end
|
|
124
|
+
|
|
125
|
+
(object.stratagem.invalid_columns(object)).size == 0
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def correct_invalid_columns(object, mock_chain)
|
|
129
|
+
exclude = [:created_at, :updated_at] + object.stratagem.relation_names + object.stratagem.foreign_keys
|
|
130
|
+
|
|
131
|
+
# randomize over incorrect values and try to correct
|
|
132
|
+
(object.stratagem.invalid_columns(object) - exclude).each do |error_column|
|
|
133
|
+
puts "\t\terrors for #{error_column}: #{object.errors[error_column].inspect}"
|
|
134
|
+
|
|
135
|
+
i = 0
|
|
136
|
+
begin
|
|
137
|
+
value = set_attribute_value(object, error_column, mock_chain)
|
|
138
|
+
print_value = value || 'nil'
|
|
139
|
+
#log "\tattempting value #{print_value} for column #{error_column} to correct error - invalid columns: #{object.stratagem.invalid_columns(object)}"
|
|
140
|
+
i += 1
|
|
141
|
+
end while object.stratagem.invalid_columns(object).include?(error_column) && (i < 200)
|
|
142
|
+
|
|
143
|
+
if object.stratagem.invalid_columns(object).include?(error_column)
|
|
144
|
+
raise Stratagem::AutoMock::ColumnValidationError.new("Column #{error_column} cannot be resolved")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
object
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def save_object(object)
|
|
151
|
+
valid = true
|
|
152
|
+
retries = 0
|
|
153
|
+
begin
|
|
154
|
+
object.save!
|
|
155
|
+
rescue Stratagem::AutoMock::MockError
|
|
156
|
+
puts "Error mocking #{klass.name}"
|
|
157
|
+
valid = false
|
|
158
|
+
rescue
|
|
159
|
+
puts object.stratagem.mock_attributes.inspect
|
|
160
|
+
puts "Error mocking #{object.class.name} - #{$!.message}"
|
|
161
|
+
error_column = object.stratagem.column_from_error($!)
|
|
162
|
+
puts "\terror column: #{error_column}"
|
|
163
|
+
if error_column && !object.stratagem.foreign_keys.include?(error_column)
|
|
164
|
+
if retries < 100
|
|
165
|
+
puts "\tretrying to correct field #{error_column} that was missing validation"
|
|
166
|
+
set_attribute_value(object, error_column, [])
|
|
167
|
+
retries += 1
|
|
168
|
+
retry
|
|
169
|
+
else
|
|
170
|
+
valid = false
|
|
171
|
+
end
|
|
172
|
+
else
|
|
173
|
+
valid = false
|
|
174
|
+
end
|
|
175
|
+
puts $!.backtrace unless valid
|
|
176
|
+
end
|
|
177
|
+
valid
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def set_attribute_value(object, attr_name, mock_chain=[], value=nil)
|
|
181
|
+
return if attr_name.to_s =~ /_confirmation$/ # ignore confirmation fields
|
|
182
|
+
|
|
183
|
+
unless value
|
|
184
|
+
if (object.stratagem.relation_names.include?(attr_name))
|
|
185
|
+
puts "\tmocking relation #{attr_name} for #{object.class.name}"
|
|
186
|
+
relation = object.stratagem.relation(attr_name)
|
|
187
|
+
value,valid = mock(relation.klass, mock_chain+[object.class])
|
|
188
|
+
value = nil unless valid
|
|
189
|
+
else
|
|
190
|
+
value = generate_value(attr_name, object.stratagem.attribute_type(attr_name))
|
|
191
|
+
#puts "#{attr_name} -> #{value}"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
object.send("#{attr_name}=", value)
|
|
196
|
+
object.stratagem.write_mock_attribute(attr_name, value)
|
|
197
|
+
|
|
198
|
+
confirmation_writer = "#{attr_name}_confirmation="
|
|
199
|
+
if object.methods_include?(confirmation_writer) || (object.stratagem.validations(attr_name, :validates_confirmation_of).size > 0)
|
|
200
|
+
puts "setting confirmation field for #{attr_name}"
|
|
201
|
+
object.send(confirmation_writer, value)
|
|
202
|
+
object.stratagem.write_mock_attribute("#{attr_name}_confirmation".to_sym, value)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
value
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def log(msg)
|
|
210
|
+
Stratagem.logger.debug msg
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|