tddium-preview 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +49 -0
- data/CHANGELOG +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +2 -0
- data/README.rdoc +62 -0
- data/Rakefile +2 -0
- data/bin/tddium +9 -0
- data/doc/aws-keypair-example.tiff +0 -0
- data/doc/aws-secgroup-example.tiff +0 -0
- data/lib/tddium/version.rb +3 -0
- data/lib/tddium.rb +229 -0
- data/spec/fixtures/get_suites_200.json +13 -0
- data/spec/fixtures/get_test_executions_200.json +13 -0
- data/spec/fixtures/get_test_executions_200_all_finished.json +13 -0
- data/spec/fixtures/post_register_test_executions_200.json +14 -0
- data/spec/fixtures/post_register_test_executions_200_json_status_1.json +14 -0
- data/spec/fixtures/post_sessions_201.json +13 -0
- data/spec/fixtures/post_start_test_executions_200.json +14 -0
- data/spec/fixtures/post_suites_201.json +13 -0
- data/spec/fixtures/post_suites_201_json_status_1.json +13 -0
- data/spec/fixtures/post_suites_409.json +13 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/tddium_spec.rb +489 -0
- data/tddium.gemspec +30 -0
- metadata +200 -0
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
|
4
|
+
# rdoc generated
|
5
|
+
rdoc
|
6
|
+
|
7
|
+
# yard generated
|
8
|
+
doc
|
9
|
+
.yardoc
|
10
|
+
|
11
|
+
# bundler
|
12
|
+
.bundle
|
13
|
+
|
14
|
+
# jeweler generated
|
15
|
+
pkg
|
16
|
+
|
17
|
+
spec_test_data
|
18
|
+
tmp
|
19
|
+
results
|
20
|
+
nbproject
|
21
|
+
.rvmrc
|
22
|
+
.tddium
|
23
|
+
|
24
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
25
|
+
#
|
26
|
+
# * Create a file at ~/.gitignore
|
27
|
+
# * Include files you want ignored
|
28
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
29
|
+
#
|
30
|
+
# After doing this, these files will be ignored in all your git projects,
|
31
|
+
# saving you from having to 'pollute' every project you touch with them
|
32
|
+
#
|
33
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
34
|
+
#
|
35
|
+
# For MacOS:
|
36
|
+
#
|
37
|
+
#.DS_Store
|
38
|
+
#
|
39
|
+
# For TextMate
|
40
|
+
#*.tmproj
|
41
|
+
#tmtags
|
42
|
+
#
|
43
|
+
# For emacs:
|
44
|
+
#*~
|
45
|
+
#\#*
|
46
|
+
#.\#*
|
47
|
+
#
|
48
|
+
# For vim:
|
49
|
+
*.swp
|
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.0.1 - Initial Version
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
= tddium
|
2
|
+
|
3
|
+
tddium takes the pain out of running Selenium testing in cloud.
|
4
|
+
|
5
|
+
It automatically starts private Amazon EC2 Instances, runs your test,
|
6
|
+
collect results, and cleans up the EC2 resources it used so you only pay for
|
7
|
+
what you use.
|
8
|
+
|
9
|
+
== Getting Started
|
10
|
+
|
11
|
+
1. Sign up for an EC2 account, by going here:
|
12
|
+
|
13
|
+
https://aws-portal.amazon.com/gp/aws/developer/registration/index.html
|
14
|
+
|
15
|
+
Note that if you have a personal Amazon account, you'll want to make a new one
|
16
|
+
for Amazon Web Services.
|
17
|
+
|
18
|
+
The registration process generates a variety of certificates and keys. Please
|
19
|
+
keep track of them.
|
20
|
+
|
21
|
+
2. Note your AWS Access Key and AWS Secret. You can find them by the following
|
22
|
+
steps:
|
23
|
+
|
24
|
+
- go to the AWS Management Console
|
25
|
+
- click on the "Account" link in the upper navigation menu
|
26
|
+
- click on "Security Credentials"
|
27
|
+
- scroll down to the first section "Access Credentials"
|
28
|
+
- the Access Key is a 20-character alphabetic string. The Secret is hidden. Click the 'show' link to reveal it.
|
29
|
+
|
30
|
+
3. In the AWS Management Console, create a selenium-grid security group, and
|
31
|
+
create rules for:
|
32
|
+
|
33
|
+
* allow dest port 4444 (Selenium-Grid)
|
34
|
+
* allow dest port 5900 (Windows Remote Desktop)
|
35
|
+
* allow http
|
36
|
+
* allow https
|
37
|
+
* allow vnc
|
38
|
+
* allow ssh
|
39
|
+
|
40
|
+
4. In the AWS Management Console, create a keypair called sg-keypair.
|
41
|
+
|
42
|
+
3. Subscribe to tddium:
|
43
|
+
|
44
|
+
Subscribe your Amazon EC2 account to tddium here:
|
45
|
+
|
46
|
+
https://aws-portal.amazon.com/gp/aws/user/subscription/index.html?ie=UTF8&offeringCode=FA429DE5
|
47
|
+
|
48
|
+
4. Start using tddium:
|
49
|
+
|
50
|
+
<tt>$ tddium config:init</tt>
|
51
|
+
|
52
|
+
# Asks you for the AWS Access Key and AWS Secret
|
53
|
+
|
54
|
+
<tt>$ tddium test:sequential</tt>
|
55
|
+
|
56
|
+
# Starts an EC2 Instance, sets SELENIUM_RC_HOST to it, and runs **/*_spec.rb in sequence
|
57
|
+
|
58
|
+
== Copyright
|
59
|
+
|
60
|
+
Copyright (c) 2010 Jay Moorthi. See LICENSE.txt for
|
61
|
+
further details.
|
62
|
+
|
data/Rakefile
ADDED
data/bin/tddium
ADDED
Binary file
|
Binary file
|
data/lib/tddium.rb
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2010 tddium.com All Rights Reserved
|
3
|
+
=end
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require "thor"
|
7
|
+
require "httparty"
|
8
|
+
require "json"
|
9
|
+
|
10
|
+
# Usage:
|
11
|
+
#
|
12
|
+
# tddium suite # Register the suite for this rails app, or manage its settings
|
13
|
+
# tddium spec # Run the test suite
|
14
|
+
# tddium status # Display information about this suite, and any open dev sessions
|
15
|
+
#
|
16
|
+
# tddium login # Log your unix user in to a tddium account
|
17
|
+
# tddium logout # Log out
|
18
|
+
#
|
19
|
+
# tddium account # View/Manage account information
|
20
|
+
#
|
21
|
+
# tddium dev # Enter "dev" mode, for single-test quick-turnaround debugging.
|
22
|
+
# tddium stopdev # Leave "dev" mode.
|
23
|
+
#
|
24
|
+
# tddium clean # Clean up test results, especially large objects like videos
|
25
|
+
#
|
26
|
+
# tddium help # Print this usage message
|
27
|
+
|
28
|
+
class Tddium < Thor
|
29
|
+
API_HOST = "https://api.tddium.com"
|
30
|
+
API_VERSION = "1"
|
31
|
+
API_KEY_HEADER = "X-tddium-api-key"
|
32
|
+
SUITES_PATH = "suites"
|
33
|
+
SESSIONS_PATH = "sessions"
|
34
|
+
TEST_EXECUTIONS_PATH = "test_executions"
|
35
|
+
REGISTER_TEST_EXECUTIONS_PATH = "#{TEST_EXECUTIONS_PATH}/register"
|
36
|
+
START_TEST_EXECUTIONS_PATH = "#{TEST_EXECUTIONS_PATH}/start"
|
37
|
+
REPORT_TEST_EXECUTIONS_PATH = "#{TEST_EXECUTIONS_PATH}/report"
|
38
|
+
GIT_REMOTE_NAME = "tddium"
|
39
|
+
GIT_REMOTE_SCHEME = "ssh"
|
40
|
+
GIT_REMOTE_USER = "git"
|
41
|
+
GIT_REMOTE_ABSOLUTE_PATH = "/home/git/repo"
|
42
|
+
SLEEP_TIME_BETWEEN_POLLS = 2
|
43
|
+
TERMINATE_PROCESS_INSTRUCTIONS = "Ctrl-C to terminate the process"
|
44
|
+
INTERRUPT_TEXT = "Interrupted"
|
45
|
+
NOT_INITIALIZED_ERROR = "tddium must be initialized. Try 'tddium login'"
|
46
|
+
INVALID_TDDIUM_FILE = ".tddium config file is corrupt. Try 'tddium login'"
|
47
|
+
|
48
|
+
desc "suite", "Register the suite for this rails app, or manage its settings"
|
49
|
+
method_option :ssh_key, :type => :string, :default => nil
|
50
|
+
method_option :test_pattern, :type => :string, :default => nil
|
51
|
+
method_option :name, :type => :string, :default => nil
|
52
|
+
def suite
|
53
|
+
return unless git_repo? && tddium_settings
|
54
|
+
|
55
|
+
# Inputs for API call
|
56
|
+
params = {}
|
57
|
+
|
58
|
+
default_ssh_file = "~/.ssh/id_rsa.pub"
|
59
|
+
ssh_file = options[:ssh_key] || ask("Enter your ssh key or press 'Return'. Using #{default_ssh_file} by default:")
|
60
|
+
ssh_file = default_ssh_file if ssh_file.empty?
|
61
|
+
params[:ssh_key] = File.open(File.expand_path(ssh_file)) {|file| file.read}
|
62
|
+
|
63
|
+
default_test_pattern = "**/*_spec.rb"
|
64
|
+
test_pattern = options[:test_pattern] || ask("Enter a test pattern or press 'Return'. Using #{default_test_pattern} by default:")
|
65
|
+
params[:test_pattern] = test_pattern.empty? ? default_test_pattern : test_pattern
|
66
|
+
|
67
|
+
default_suite_name = "#{File.basename(Dir.pwd)}/#{current_git_branch}"
|
68
|
+
suite_name = options[:name] || ask("Enter a suite name or press 'Return'. Using '#{default_suite_name}' by default:")
|
69
|
+
params[:suite_name] = suite_name.empty? ? default_suite_name : suite_name
|
70
|
+
|
71
|
+
params[:ruby_version] = `ruby -v`.match(/^ruby ([\d\.]+)/)[1]
|
72
|
+
|
73
|
+
call_api(:post, SUITES_PATH, {:suite => params}) do |api_response|
|
74
|
+
# Manage git
|
75
|
+
`git remote rm #{GIT_REMOTE_NAME}`
|
76
|
+
`git remote add #{GIT_REMOTE_NAME} #{tddium_git_repo_uri(params[:suite_name])}`
|
77
|
+
git_push
|
78
|
+
|
79
|
+
# Save the created suite
|
80
|
+
branches = tddium_settings["branches"] || {}
|
81
|
+
branches.merge!({current_git_branch => api_response["suite"]["id"]})
|
82
|
+
File.open(".tddium", "w") do |file|
|
83
|
+
file.write(tddium_settings.merge({:branches => branches}).to_json)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "spec", "Run the test suite"
|
89
|
+
def spec
|
90
|
+
start_time = Time.now
|
91
|
+
|
92
|
+
return unless git_repo? && tddium_settings
|
93
|
+
suite_id = tddium_settings["branches"][current_git_branch]
|
94
|
+
|
95
|
+
# Push the latest code to git
|
96
|
+
git_push
|
97
|
+
|
98
|
+
# Call the API to get the suite and its tests
|
99
|
+
call_api(:get, "#{SUITES_PATH}/#{suite_id}") do |api_response|
|
100
|
+
test_pattern = api_response["suite"]["test_pattern"]
|
101
|
+
test_files = Dir.glob(test_pattern).collect {|file_path| {:test_name => file_path}}
|
102
|
+
|
103
|
+
# Create a session
|
104
|
+
call_api(:post, SESSIONS_PATH) do |api_response|
|
105
|
+
session_id = api_response["session"]["id"]
|
106
|
+
|
107
|
+
# Call the API to register the tests
|
108
|
+
call_api(:post, "#{SESSIONS_PATH}/#{session_id}/#{REGISTER_TEST_EXECUTIONS_PATH}", {:suite_id => suite_id, :tests => test_files}) do |api_response|
|
109
|
+
# Start the tests
|
110
|
+
call_api(:post, "#{SESSIONS_PATH}/#{session_id}/#{START_TEST_EXECUTIONS_PATH}") do |api_response|
|
111
|
+
tests_not_finished_yet = true
|
112
|
+
finished_tests = {}
|
113
|
+
test_statuses = Hash.new(0)
|
114
|
+
api_call_successful = true
|
115
|
+
say TERMINATE_PROCESS_INSTRUCTIONS
|
116
|
+
while tests_not_finished_yet && api_call_successful do
|
117
|
+
# Poll the API to check the status
|
118
|
+
api_call_successful = call_api(:get, "#{SESSIONS_PATH}/#{session_id}/#{TEST_EXECUTIONS_PATH}") do |api_response|
|
119
|
+
Signal.trap(:INT) do
|
120
|
+
say INTERRUPT_TEXT
|
121
|
+
tests_not_finished_yet = false
|
122
|
+
end
|
123
|
+
# Print out the progress of running tests
|
124
|
+
api_response["tests"].each do |test_name, result_params|
|
125
|
+
test_status = result_params["status"]
|
126
|
+
if result_params["end_time"] && !finished_tests[test_name]
|
127
|
+
message = case test_status
|
128
|
+
when "passed" then [".", :green]
|
129
|
+
when "failed" then ["F", :red]
|
130
|
+
when "error" then ["E"]
|
131
|
+
when "pending" then ["*", :yellow]
|
132
|
+
end
|
133
|
+
finished_tests[test_name] = test_status
|
134
|
+
test_statuses[test_status] += 1
|
135
|
+
say message[0], message[1]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# If all tests finished, exit the loop else sleep
|
140
|
+
finished_tests.size == api_response["tests"].size ? tests_not_finished_yet = false : sleep(SLEEP_TIME_BETWEEN_POLLS)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Print out the result
|
145
|
+
say "Finished in #{Time.now - start_time} seconds"
|
146
|
+
say "#{finished_tests.size} examples, #{test_statuses["failed"]} failures, #{test_statuses["error"]} errors, #{test_statuses["pending"]} pending"
|
147
|
+
say "You can check out the test report details at #{api_response["report"]}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def call_api(method, api_path, params = {}, &block)
|
157
|
+
headers = { API_KEY_HEADER => tddium_settings(false)["api_key"] } if tddium_settings(false) && tddium_settings(false)["api_key"]
|
158
|
+
done = false
|
159
|
+
tries = 0
|
160
|
+
while tries < 5 && !done
|
161
|
+
begin
|
162
|
+
http = HTTParty.send(method, tddium_uri(api_path), :body => params, :headers => headers)
|
163
|
+
done = true
|
164
|
+
rescue Timeout::Error
|
165
|
+
ensure
|
166
|
+
tries += 5
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
response = JSON.parse(http.body) rescue {}
|
171
|
+
|
172
|
+
if http.success?
|
173
|
+
if response["status"] == 0
|
174
|
+
yield response
|
175
|
+
else
|
176
|
+
message = "An error occured: #{response["explanation"]}"
|
177
|
+
end
|
178
|
+
else
|
179
|
+
message = "An error occured: #{http.response.header.msg}"
|
180
|
+
message << " #{response["explanation"]}" if response["status"].to_i > 0
|
181
|
+
end
|
182
|
+
say message if message
|
183
|
+
message.nil?
|
184
|
+
end
|
185
|
+
|
186
|
+
def git_push
|
187
|
+
`git push #{GIT_REMOTE_NAME} #{current_git_branch}`
|
188
|
+
end
|
189
|
+
|
190
|
+
def tddium_uri(path, api_version = API_VERSION)
|
191
|
+
URI.join(API_HOST, "#{api_version}/#{path}").to_s
|
192
|
+
end
|
193
|
+
|
194
|
+
def tddium_git_repo_uri(suite_name)
|
195
|
+
repo_name = suite_name.split("/").first
|
196
|
+
git_uri = URI.parse(API_HOST)
|
197
|
+
git_uri.scheme = GIT_REMOTE_SCHEME
|
198
|
+
git_uri.userinfo = GIT_REMOTE_USER
|
199
|
+
git_uri.path = "#{GIT_REMOTE_ABSOLUTE_PATH}/#{repo_name}"
|
200
|
+
git_uri.to_s
|
201
|
+
end
|
202
|
+
|
203
|
+
def current_git_branch
|
204
|
+
@current_git_branch ||= File.basename(`git symbolic-ref HEAD`.gsub("\n", ""))
|
205
|
+
end
|
206
|
+
|
207
|
+
def tddium_settings(fail_with_message = true)
|
208
|
+
unless @tddium_settings
|
209
|
+
if File.exists?(".tddium")
|
210
|
+
tddium_config = File.open(".tddium") do |file|
|
211
|
+
file.read
|
212
|
+
end
|
213
|
+
@tddium_settings = JSON.parse(tddium_config) rescue nil
|
214
|
+
say INVALID_TDDIUM_FILE if @tddium_settings.nil? && fail_with_message
|
215
|
+
else
|
216
|
+
say NOT_INITIALIZED_ERROR if fail_with_message
|
217
|
+
end
|
218
|
+
end
|
219
|
+
@tddium_settings
|
220
|
+
end
|
221
|
+
|
222
|
+
def git_repo?
|
223
|
+
unless File.exists?(".git")
|
224
|
+
message = "git repo must be initialized. Try 'git init'."
|
225
|
+
say message
|
226
|
+
end
|
227
|
+
message.nil?
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Fri, 04 Mar 2011 05:58:52 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
ETag: "6f1f75f9653abfa85b807ea9a90c91dc"
|
6
|
+
Cache-Control: max-age=0, private, must-revalidate
|
7
|
+
X-UA-Compatible: IE=Edge
|
8
|
+
X-Runtime: 0.051654
|
9
|
+
Status: 200
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Content-Type: application/json; charset=utf-8
|
12
|
+
|
13
|
+
{"suite":{"created_at":"2011-03-04T04:21:00Z","id":24,"ruby_version":"1.8.7","ssh_key":"ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3YO0EJKUJsdrrqptMqXWhDLetOIqwnhSw3NDEXDEs699JjIx7y7dEYVISWnmW63fdlD5piKfByhbn/dL1T9oPfAV+o56tFZMBHI4UdZu9cEWqnbEZDnCzoSj7K3M5m7pQmG9K9MHjlAnPNL1oD8hykBQOIkR0hzG6TvIte6arkhvDGEcm3SoS2yykIRY8s6t/3u97FmlIoUjJnsMmx6GP9aO7YeZi70gySeVJQW4xvhb+qVIyCvIknXtZcmSenmy0fStzz8UX6HLczX0/U9MvAMpPi97gT6CsiGAkVmUTdiAXDktqQ1OO+GiadqaNFnQlt9Kb/wVAV56tAqqvSNjlQ== dwilkie@gmail.com\n","suite_name":"tddium/demo","test_pattern":"**/*_spec.rb","updated_at":"2011-03-04T04:21:00Z","user_id":null},"status":0}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Fri, 04 Mar 2011 09:59:03 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
ETag: "e1ccfca6b97460395252d127788c81b8"
|
6
|
+
Cache-Control: max-age=0, private, must-revalidate
|
7
|
+
X-UA-Compatible: IE=Edge
|
8
|
+
X-Runtime: 0.167928
|
9
|
+
Status: 200
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Content-Type: application/json; charset=utf-8
|
12
|
+
|
13
|
+
{"status":0,"report":"http://api.tddium.com/1/sessions/7/test_executions/report","tests":{"spec/cat_spec.rb":{"end_time":"2011-03-04T07:05:12Z","id":1,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:05:06Z","status":"passed","test_script_id":24,"usage":null},"spec/dog_spec.rb":{"end_time":"2011-03-04T07:06:12Z","id":2,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:06:06Z","status":"failed","test_script_id":25,"usage":null},"spec/mouse_spec.rb":{"end_time":"2011-03-04T07:07:06Z","id":3,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:07:06Z","status":"pending","test_script_id":26,"usage":null},"spec/pig_spec.rb":{"end_time":null,"id":4,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:08:06Z","status":"started","test_script_id":27,"usage":null}}}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Fri, 04 Mar 2011 09:59:03 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
ETag: "e1ccfca6b97460395252d127788c81b8"
|
6
|
+
Cache-Control: max-age=0, private, must-revalidate
|
7
|
+
X-UA-Compatible: IE=Edge
|
8
|
+
X-Runtime: 0.167928
|
9
|
+
Status: 200
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Content-Type: application/json; charset=utf-8
|
12
|
+
|
13
|
+
{"status":0,"report":"http://api.tddium.com/1/sessions/7/test_executions/report","tests":{"spec/cat_spec.rb":{"end_time":"2011-03-04T07:05:12Z","id":1,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:05:06Z","status":"passed","test_script_id":24,"usage":null},"spec/dog_spec.rb":{"end_time":"2011-03-04T07:06:12Z","id":2,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:06:06Z","status":"failed","test_script_id":25,"usage":null},"spec/mouse_spec.rb":{"end_time":"2011-03-04T07:07:06Z","id":3,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:07:06Z","status":"pending","test_script_id":26,"usage":null},"spec/pig_spec.rb":{"end_time":"2011-03-04T07:07:06Z","id":4,"instance_id":null,"result":null,"session_id":7,"start_time":"2011-03-04T07:08:06Z","status":"error","test_script_id":27,"usage":null}}}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Fri, 04 Mar 2011 07:05:51 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
ETag: "e0faa4dfc5cc0b52cf191a834f9916c2"
|
6
|
+
Cache-Control: max-age=0, private, must-revalidate
|
7
|
+
X-UA-Compatible: IE=Edge
|
8
|
+
X-Runtime: 0.120408
|
9
|
+
Set-Cookie: _tddium_site_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlOWFjNmU1MGZlMzBlYmIzZThjMTc1YjgzZjYxOWEwNGI%3D--0b52bea072b1a268dd9cbd74cc9d0e07b98f7164; path=/; HttpOnly
|
10
|
+
Status: 200
|
11
|
+
Transfer-Encoding: chunked
|
12
|
+
Content-Type: application/json; charset=utf-8
|
13
|
+
|
14
|
+
{"added":0,"errors":0,"existing":1,"status":0}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Fri, 04 Mar 2011 06:52:49 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
ETag: "c0ef16e8f392ddfc29848e2dd3818743"
|
6
|
+
Cache-Control: max-age=0, private, must-revalidate
|
7
|
+
X-UA-Compatible: IE=Edge
|
8
|
+
X-Runtime: 0.078775
|
9
|
+
Set-Cookie: _tddium_site_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlYzA5MjAzMTU5ZGZlYTZmYjljNzczNWFkNmMwYTQ2ZDk%3D--cd9dca3bf0a93adac3a9ac5e1ea60bd2cd66cba4; path=/; HttpOnly
|
10
|
+
Status: 200
|
11
|
+
Transfer-Encoding: chunked
|
12
|
+
Content-Type: application/json; charset=utf-8
|
13
|
+
|
14
|
+
{"added":0,"errors":3,"existing":0,"explanation":["Unrecognized script: cat_spec.rb","Unrecognized script: dog_spec.rb","Unrecognized script: mouse_spec.rb"],"status":1}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
HTTP/1.1 201 Created
|
2
|
+
Date: Fri, 04 Mar 2011 06:09:57 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
Cache-Control: no-cache
|
6
|
+
X-UA-Compatible: IE=Edge
|
7
|
+
X-Runtime: 0.087735
|
8
|
+
Set-Cookie: _tddium_site_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlYTZiYzQ5MzgzZDBhZmIxZWVjMzQ2ZjNiYmU3NmE2YTM%3D--82fb82e23ef118be5afd44f6634de96fc4425eb9; path=/; HttpOnly
|
9
|
+
Status: 201
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Content-Type: application/json; charset=utf-8
|
12
|
+
|
13
|
+
{"session":{"created_at":"2011-03-04T06:09:57Z","id":7,"updated_at":"2011-03-04T06:09:57Z","user_id":null},"status":0}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Fri, 04 Mar 2011 09:58:45 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
ETag: "0b921ebe813ca52c08b8f852cb81c0ed"
|
6
|
+
Cache-Control: max-age=0, private, must-revalidate
|
7
|
+
X-UA-Compatible: IE=Edge
|
8
|
+
X-Runtime: 0.146704
|
9
|
+
Set-Cookie: _tddium_site_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlYzk2OTZkYjI4OWViNTE4MDIxMDg1OTIyNmY1NTVjZTA%3D--f05ccc3fd61da38d791ad69dc635c9bbb1c0eeac; path=/; HttpOnly
|
10
|
+
Status: 200
|
11
|
+
Transfer-Encoding: chunked
|
12
|
+
Content-Type: application/json; charset=utf-8
|
13
|
+
|
14
|
+
{"started":1,"status":0}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
HTTP/1.1 201 Created
|
2
|
+
Date: Thu, 03 Mar 2011 10:25:28 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
Cache-Control: no-cache
|
6
|
+
X-UA-Compatible: IE=Edge
|
7
|
+
X-Runtime: 0.096863
|
8
|
+
Set-Cookie: _tddium_site_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlYjk0YTMzNmZmNzQ1NTI1NDk5YmE2NTc4Y2IxNTYxNGU%3D--d841305e17bd1b770b2b99ae74a03bc7aa5eddd5; path=/; HttpOnly
|
9
|
+
Status: 201
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Content-Type: application/json; charset=utf-8
|
12
|
+
|
13
|
+
{"suite":{"created_at":"2011-03-03T10:25:28Z","id":19,"ruby_version":"1.9.2","ssh_key":"ssh-rsa blah","suite_name":"tddium/demo","test_pattern":"**/*_spec.rb","updated_at":"2011-03-03T10:25:28Z","user_id":null},"status":0}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
HTTP/1.1 269 Duplicated
|
2
|
+
Date: Thu, 03 Mar 2011 10:00:32 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
Cache-Control: no-cache
|
6
|
+
X-UA-Compatible: IE=Edge
|
7
|
+
X-Runtime: 0.045332
|
8
|
+
Set-Cookie: _tddium_site_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlOGZiMTgxZjM0ZGIwMmJlMzI2ZWQwMmFkYzUwZGEzZDU%3D--287be8a7de3c8535fc4d958d2fc036110e64e5b6; path=/; HttpOnly
|
9
|
+
Status: 269
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Content-Type: application/json; charset=utf-8
|
12
|
+
|
13
|
+
{"status":1,"explanation":"{:suite_name=>[\"has already been taken\"]}"}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
HTTP/1.1 409 Conflict
|
2
|
+
Date: Thu, 03 Mar 2011 10:00:32 GMT
|
3
|
+
Server: Apache
|
4
|
+
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.3
|
5
|
+
Cache-Control: no-cache
|
6
|
+
X-UA-Compatible: IE=Edge
|
7
|
+
X-Runtime: 0.045332
|
8
|
+
Set-Cookie: _tddium_site_session=BAh7BkkiD3Nlc3Npb25faWQGOgZFRiIlOGZiMTgxZjM0ZGIwMmJlMzI2ZWQwMmFkYzUwZGEzZDU%3D--287be8a7de3c8535fc4d958d2fc036110e64e5b6; path=/; HttpOnly
|
9
|
+
Status: 409
|
10
|
+
Transfer-Encoding: chunked
|
11
|
+
Content-Type: application/json; charset=utf-8
|
12
|
+
|
13
|
+
{"status":1,"explanation":"{:suite_name=>[\"has already been taken\"]}"}
|
data/spec/spec_helper.rb
ADDED
data/spec/tddium_spec.rb
ADDED
@@ -0,0 +1,489 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# TODO: Test what happens if an error occurs in the POST and GET requests
|
4
|
+
describe Tddium do
|
5
|
+
include FakeFS::SpecHelpers
|
6
|
+
|
7
|
+
SSH_KEY_PROMPT = "Enter your ssh key or press 'Return'. Using ~/.ssh/id_rsa.pub by default:"
|
8
|
+
TEST_PATTERN_PROMPT = "Enter a test pattern or press 'Return'. Using **/*_spec.rb by default:"
|
9
|
+
DEFAULT_APP_NAME = "tddelicious"
|
10
|
+
DEFAULT_BRANCH_NAME = "test"
|
11
|
+
DEFAULT_SUITE_ID = "66"
|
12
|
+
DEFAULT_API_KEY = "afb12412bdafe124124asfasfabebafeabwbawf1312342erbfasbb"
|
13
|
+
|
14
|
+
def run(tddium)
|
15
|
+
send("run_#{example.example_group.ancestors.map(&:description)[-2][1..-1]}", tddium)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_suite(tddium)
|
19
|
+
tddium.suite
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_spec(tddium)
|
23
|
+
tddium.spec
|
24
|
+
end
|
25
|
+
|
26
|
+
def suite_name_prompt(default = default_suite_name)
|
27
|
+
"Enter a suite name or press 'Return'. Using '#{default}' by default:"
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_suite_name
|
31
|
+
"#{DEFAULT_APP_NAME}/#{DEFAULT_BRANCH_NAME}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def stub_default_suite_name(tddium, default_app_name = DEFAULT_APP_NAME, default_branch_name = DEFAULT_BRANCH_NAME)
|
35
|
+
Dir.stub(:pwd).and_return(default_app_name)
|
36
|
+
stub_git_branch(tddium, default_branch_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def stub_ruby_version(tddium, ruby_version = "1.9.2")
|
40
|
+
tddium.stub(:`).with("ruby -v").and_return("ruby #{ruby_version} (2010-08-16 patchlevel 302) [i686-darwin10.5.0]")
|
41
|
+
end
|
42
|
+
|
43
|
+
def stub_git_branch(tddium, default_branch_name = DEFAULT_BRANCH_NAME)
|
44
|
+
tddium.stub(:`).with("git symbolic-ref HEAD").and_return(default_branch_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_request_params
|
48
|
+
Rack::Utils.parse_nested_query(FakeWeb.last_request.body)
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_file(path, content = "blah")
|
52
|
+
FileUtils.mkdir_p(File.dirname(path))
|
53
|
+
File.open(path, 'w') do |f|
|
54
|
+
f.write(content)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def register_uri_options(options = {})
|
59
|
+
if options.is_a?(Array)
|
60
|
+
options_array = []
|
61
|
+
options.each do |sub_options|
|
62
|
+
options_array << register_uri_options(sub_options)
|
63
|
+
end
|
64
|
+
options_array
|
65
|
+
else
|
66
|
+
options_for_fake_web = {:body => options[:body], :status => options[:status]}
|
67
|
+
if options[:response]
|
68
|
+
FakeFS.deactivate!
|
69
|
+
response = File.open(options[:response]) { |f| f.read }
|
70
|
+
FakeFS.activate!
|
71
|
+
options_for_fake_web.merge!(:response => response)
|
72
|
+
end
|
73
|
+
options_for_fake_web
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def stub_http_response(method, path, options = {})
|
78
|
+
FakeWeb.register_uri(method, URI.join(Tddium::API_HOST, "#{Tddium::API_VERSION}/#{path}").to_s, register_uri_options(options))
|
79
|
+
end
|
80
|
+
|
81
|
+
def stub_defaults
|
82
|
+
FakeWeb.clean_registry
|
83
|
+
tddium.stub(:say)
|
84
|
+
stub_git_branch(tddium)
|
85
|
+
create_file(".git/something", "something")
|
86
|
+
create_file(".tddium", {:branches => {DEFAULT_BRANCH_NAME => DEFAULT_SUITE_ID}, :api_key => DEFAULT_API_KEY}.to_json)
|
87
|
+
end
|
88
|
+
|
89
|
+
def stub_git_push(tddium)
|
90
|
+
tddium.stub(:`).with(/^git push/)
|
91
|
+
end
|
92
|
+
|
93
|
+
def stub_sleep(tddium)
|
94
|
+
tddium.stub(:sleep).with(Tddium::SLEEP_TIME_BETWEEN_POLLS)
|
95
|
+
end
|
96
|
+
|
97
|
+
let(:tddium) { Tddium.new }
|
98
|
+
|
99
|
+
shared_examples_for "git repo has not been initialized" do
|
100
|
+
context "git repo has not been initialized" do
|
101
|
+
before do
|
102
|
+
FileUtils.rm_rf(".git")
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should return git is uninitialized" do
|
106
|
+
tddium.should_receive(:say).with("git repo must be initialized. Try 'git init'.")
|
107
|
+
run(tddium)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
shared_examples_for ".tddium file is missing or corrupt" do
|
113
|
+
context ".tddium file is missing" do
|
114
|
+
before do
|
115
|
+
FileUtils.rm_rf(".tddium")
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should tell the user '#{Tddium::NOT_INITIALIZED_ERROR}'" do
|
119
|
+
tddium.should_receive(:say).with(Tddium::NOT_INITIALIZED_ERROR)
|
120
|
+
run(tddium)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context ".tddium file is corrupt" do
|
125
|
+
before do
|
126
|
+
create_file(".tddium", "corrupt file")
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should tell the user '#{Tddium::NOT_INITIALIZED_ERROR}'" do
|
130
|
+
tddium.should_receive(:say).with(Tddium::INVALID_TDDIUM_FILE)
|
131
|
+
run(tddium)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
shared_examples_for "sending the api key" do
|
137
|
+
it "should include the api key in the headers" do
|
138
|
+
run(tddium)
|
139
|
+
FakeWeb.last_request[Tddium::API_KEY_HEADER].should == DEFAULT_API_KEY
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "#suite" do
|
144
|
+
before do
|
145
|
+
stub_defaults
|
146
|
+
tddium.stub(:ask).and_return("")
|
147
|
+
stub_http_response(:post, Tddium::SUITES_PATH)
|
148
|
+
stub_ruby_version(tddium)
|
149
|
+
create_file("~/.ssh/id_rsa.pub", "ssh-rsa blah")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should ask the user for their ssh key" do
|
153
|
+
tddium.should_receive(:ask).with(SSH_KEY_PROMPT)
|
154
|
+
run_suite(tddium)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should ask for a suite name" do
|
158
|
+
stub_default_suite_name(tddium)
|
159
|
+
tddium.should_receive(:ask).with(suite_name_prompt)
|
160
|
+
run_suite(tddium)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should ask for a test file pattern" do
|
164
|
+
tddium.should_receive(:ask).with(TEST_PATTERN_PROMPT)
|
165
|
+
run_suite(tddium)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should send a 'POST' request to '#{Tddium::SUITES_PATH}'" do
|
169
|
+
run_suite(tddium)
|
170
|
+
FakeWeb.last_request.method.should == "POST"
|
171
|
+
FakeWeb.last_request.path.should =~ /\/#{Tddium::SUITES_PATH}$/
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should post the current ruby version to the API" do
|
175
|
+
stub_ruby_version(tddium, "1.9.2")
|
176
|
+
run_suite(tddium)
|
177
|
+
parse_request_params["suite"].should include("ruby_version" => "1.9.2")
|
178
|
+
end
|
179
|
+
|
180
|
+
it_should_behave_like "sending the api key"
|
181
|
+
|
182
|
+
it_should_behave_like "git repo has not been initialized"
|
183
|
+
it_should_behave_like ".tddium file is missing or corrupt"
|
184
|
+
|
185
|
+
context "using defaults" do
|
186
|
+
before do
|
187
|
+
stub_default_suite_name(tddium)
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should POST the default values to the API" do
|
191
|
+
run_suite(tddium)
|
192
|
+
parse_request_params["suite"].should include("ssh_key" => "ssh-rsa blah", "suite_name" => default_suite_name,
|
193
|
+
"test_pattern" => "**/*_spec.rb")
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
context "passing arguments" do
|
199
|
+
before do
|
200
|
+
ssh_key_file = "~/.ssh/blah.txt"
|
201
|
+
tddium.stub(:options).and_return(
|
202
|
+
:ssh_key => ssh_key_file,
|
203
|
+
:name => "my_suite_name",
|
204
|
+
:test_pattern => "**/*_test.rb"
|
205
|
+
)
|
206
|
+
create_file(ssh_key_file, "ssh-rsa 1234")
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should POST the passed in values to the API" do
|
210
|
+
run_suite(tddium)
|
211
|
+
parse_request_params["suite"].should include("ssh_key" => "ssh-rsa 1234", "suite_name" => "my_suite_name",
|
212
|
+
"test_pattern" => "**/*_test.rb")
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
context "interactive mode" do
|
218
|
+
before do
|
219
|
+
ssh_key_file = "~/.ssh/foo.txt"
|
220
|
+
tddium.stub(:ask).with(SSH_KEY_PROMPT).and_return(ssh_key_file)
|
221
|
+
tddium.stub(:ask).with(TEST_PATTERN_PROMPT).and_return("**/*_selenium.rb")
|
222
|
+
tddium.stub(:ask).with(suite_name_prompt).and_return("foobar")
|
223
|
+
stub_default_suite_name(tddium)
|
224
|
+
create_file(ssh_key_file, "ssh-rsa 65431")
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should POST the passed in values to the API" do
|
228
|
+
run_suite(tddium)
|
229
|
+
parse_request_params["suite"].should include("ssh_key" => "ssh-rsa 65431", "suite_name" => "foobar",
|
230
|
+
"test_pattern" => "**/*_selenium.rb")
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "API response successful" do
|
235
|
+
before do
|
236
|
+
stub_http_response(:post, Tddium::SUITES_PATH, :response => fixture_path("post_suites_201.json"))
|
237
|
+
tddium.stub(:`).with(/^git remote/)
|
238
|
+
stub_git_push(tddium)
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should remove any existing remotes named 'tddium'" do
|
242
|
+
tddium.should_receive(:`).with("git remote rm tddium")
|
243
|
+
run_suite(tddium)
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should add a new remote called 'tddium'" do
|
247
|
+
stub_default_suite_name(tddium)
|
248
|
+
tddium.should_receive(:`).with("git remote add tddium ssh://git@api.tddium.com/home/git/repo/#{DEFAULT_APP_NAME}")
|
249
|
+
run_suite(tddium)
|
250
|
+
end
|
251
|
+
|
252
|
+
context "in the branch 'oaktree'" do
|
253
|
+
before do
|
254
|
+
tddium.stub(:current_git_branch).and_return("oaktree")
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should push the current git branch to tddium oaktree" do
|
258
|
+
tddium.should_receive(:`).with("git push tddium oaktree")
|
259
|
+
run_suite(tddium)
|
260
|
+
end
|
261
|
+
|
262
|
+
it "should create '.tddium' and write the suite_id and branch name" do
|
263
|
+
run_suite(tddium)
|
264
|
+
tddium_file = File.open(".tddium") { |file| file.read }
|
265
|
+
JSON.parse(tddium_file)["branches"]["oaktree"].should == 19 # From response
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context "API response successful but JSON status not 0" do
|
271
|
+
before do
|
272
|
+
stub_http_response(:post, Tddium::SUITES_PATH, :response => fixture_path("post_suites_201_json_status_1.json"))
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should do show the explaination" do
|
276
|
+
tddium.should_receive(:say).with("An error occured: {:suite_name=>[\"has already been taken\"]}")
|
277
|
+
run_suite(tddium)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context "API response unsuccessful" do
|
282
|
+
before do
|
283
|
+
stub_http_response(:post, Tddium::SUITES_PATH, :status => ["501", "Internal Server Error"])
|
284
|
+
end
|
285
|
+
|
286
|
+
it "should show that there was an error" do
|
287
|
+
tddium.should_receive(:say).with(/^An error occured: /)
|
288
|
+
run_suite(tddium)
|
289
|
+
end
|
290
|
+
|
291
|
+
context "API status code != 0" do
|
292
|
+
before do
|
293
|
+
stub_http_response(:post, Tddium::SUITES_PATH, :response => fixture_path("post_suites_409.json"))
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should show the error message" do
|
297
|
+
tddium.should_receive(:say).with(/Conflict \{\:suite_name\=\>\[\"has already been taken\"\]\}$/)
|
298
|
+
run_suite(tddium)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context "501 Error" do
|
303
|
+
before do
|
304
|
+
stub_http_response(:post, Tddium::SUITES_PATH, :status => ["501", "Internal Server Error"])
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should show the HTTP error message" do
|
308
|
+
tddium.should_receive(:say).with(/Internal Server Error$/)
|
309
|
+
run_suite(tddium)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
describe "#spec" do
|
316
|
+
before do
|
317
|
+
stub_defaults
|
318
|
+
stub_git_push(tddium)
|
319
|
+
stub_http_response(:get, "#{Tddium::SUITES_PATH}/#{DEFAULT_SUITE_ID}")
|
320
|
+
end
|
321
|
+
|
322
|
+
it_should_behave_like "git repo has not been initialized"
|
323
|
+
it_should_behave_like ".tddium file is missing or corrupt"
|
324
|
+
|
325
|
+
it "should push the latest code to tddium" do
|
326
|
+
tddium.should_receive(:`).with("git push #{Tddium::GIT_REMOTE_NAME} #{DEFAULT_BRANCH_NAME}")
|
327
|
+
run_spec(tddium)
|
328
|
+
end
|
329
|
+
|
330
|
+
it "should send a 'GET' request to '#{Tddium::SUITES_PATH}/#{DEFAULT_SUITE_ID}'" do
|
331
|
+
run_spec(tddium)
|
332
|
+
FakeWeb.last_request.method.should == "GET"
|
333
|
+
FakeWeb.last_request.path.should =~ /#{Tddium::SUITES_PATH}\/#{DEFAULT_SUITE_ID}$/
|
334
|
+
end
|
335
|
+
|
336
|
+
it_should_behave_like "sending the api key"
|
337
|
+
|
338
|
+
context "'GET #{Tddium::SUITES_PATH}/#{DEFAULT_SUITE_ID}' is successful" do
|
339
|
+
before do
|
340
|
+
stub_http_response(:get, "#{Tddium::SUITES_PATH}/#{DEFAULT_SUITE_ID}", :response => fixture_path("get_suites_200.json"))
|
341
|
+
stub_http_response(:post, Tddium::SESSIONS_PATH)
|
342
|
+
create_file("spec/mouse_spec.rb")
|
343
|
+
create_file("spec/cat_spec.rb")
|
344
|
+
create_file("spec/dog_spec.rb")
|
345
|
+
end
|
346
|
+
|
347
|
+
it "should send a 'POST' request to '#{Tddium::SESSIONS_PATH}'" do
|
348
|
+
run_spec(tddium)
|
349
|
+
FakeWeb.last_request.method.should == "POST"
|
350
|
+
FakeWeb.last_request.path.should =~ /#{Tddium::SESSIONS_PATH}$/
|
351
|
+
end
|
352
|
+
|
353
|
+
it_should_behave_like "sending the api key"
|
354
|
+
|
355
|
+
context "'POST #{Tddium::SESSIONS_PATH}' is successful" do
|
356
|
+
let(:session_id) {7} # from the fixture 'post_sessions_201.json'
|
357
|
+
before do
|
358
|
+
stub_http_response(:post, "#{Tddium::SESSIONS_PATH}", :response => fixture_path("post_sessions_201.json"))
|
359
|
+
stub_http_response(:post, "#{Tddium::SESSIONS_PATH}/#{session_id}/#{Tddium::REGISTER_TEST_EXECUTIONS_PATH}")
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should send a 'POST' request to '#{Tddium::REGISTER_TEST_EXECUTIONS_PATH}'" do
|
363
|
+
run_spec(tddium)
|
364
|
+
FakeWeb.last_request.method.should == "POST"
|
365
|
+
FakeWeb.last_request.path.should =~ /#{Tddium::REGISTER_TEST_EXECUTIONS_PATH}$/
|
366
|
+
end
|
367
|
+
|
368
|
+
it_should_behave_like "sending the api key"
|
369
|
+
|
370
|
+
it "should POST the names of the file names extracted from the suite's test_pattern" do
|
371
|
+
run_spec(tddium)
|
372
|
+
request_params = parse_request_params
|
373
|
+
request_params.should include({"suite_id" => DEFAULT_SUITE_ID})
|
374
|
+
request_params["tests"][0]["test_name"].should =~ /spec\/cat_spec.rb$/
|
375
|
+
request_params["tests"][1]["test_name"].should =~ /spec\/dog_spec.rb$/
|
376
|
+
request_params["tests"][2]["test_name"].should =~ /spec\/mouse_spec.rb$/
|
377
|
+
request_params["tests"].size.should == 3
|
378
|
+
end
|
379
|
+
|
380
|
+
context "'POST #{Tddium::REGISTER_TEST_EXECUTIONS_PATH}' is successful" do
|
381
|
+
before do
|
382
|
+
stub_http_response(:post, "#{Tddium::SESSIONS_PATH}/#{session_id}/#{Tddium::REGISTER_TEST_EXECUTIONS_PATH}", :response => fixture_path("post_register_test_executions_200.json"))
|
383
|
+
stub_http_response(:post, "#{Tddium::SESSIONS_PATH}/#{session_id}/#{Tddium::START_TEST_EXECUTIONS_PATH}")
|
384
|
+
end
|
385
|
+
|
386
|
+
it "should send a 'POST' request to '#{Tddium::START_TEST_EXECUTIONS_PATH}'" do
|
387
|
+
run_spec(tddium)
|
388
|
+
FakeWeb.last_request.method.should == "POST"
|
389
|
+
FakeWeb.last_request.path.should =~ /#{Tddium::START_TEST_EXECUTIONS_PATH}$/
|
390
|
+
end
|
391
|
+
|
392
|
+
it_should_behave_like "sending the api key"
|
393
|
+
|
394
|
+
context "'POST #{Tddium::START_TEST_EXECUTIONS_PATH}' is successful" do
|
395
|
+
before do
|
396
|
+
stub_http_response(:post, "#{Tddium::SESSIONS_PATH}/#{session_id}/#{Tddium::START_TEST_EXECUTIONS_PATH}", :response => fixture_path("post_start_test_executions_200.json"))
|
397
|
+
stub_http_response(:get, "#{Tddium::SESSIONS_PATH}/#{session_id}/#{Tddium::TEST_EXECUTIONS_PATH}")
|
398
|
+
end
|
399
|
+
|
400
|
+
it "should tell the user to '#{Tddium::TERMINATE_PROCESS_INSTRUCTIONS}'" do
|
401
|
+
tddium.should_receive(:say).with(Tddium::TERMINATE_PROCESS_INSTRUCTIONS)
|
402
|
+
run_spec(tddium)
|
403
|
+
end
|
404
|
+
|
405
|
+
it "should send a 'GET' request to '#{Tddium::TEST_EXECUTIONS_PATH}'" do
|
406
|
+
run_spec(tddium)
|
407
|
+
FakeWeb.last_request.method.should == "GET"
|
408
|
+
FakeWeb.last_request.path.should =~ /#{Tddium::TEST_EXECUTIONS_PATH}$/
|
409
|
+
end
|
410
|
+
|
411
|
+
it_should_behave_like "sending the api key"
|
412
|
+
|
413
|
+
shared_examples_for("test output summary") do
|
414
|
+
it "should display a link to the report" do
|
415
|
+
tddium.should_receive(:say).with("You can check out the test report details at http://api.tddium.com/1/sessions/7/test_executions/report")
|
416
|
+
run_spec(tddium)
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should display the time taken" do
|
420
|
+
tddium.should_receive(:say).with(/^Finished in [\d\.]+ seconds$/)
|
421
|
+
run_spec(tddium)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
context "user presses 'Ctrl-C' during the process" do
|
426
|
+
before do
|
427
|
+
stub_http_response(:get, "#{Tddium::SESSIONS_PATH}/#{session_id}/#{Tddium::TEST_EXECUTIONS_PATH}", :response => fixture_path("get_test_executions_200.json"))
|
428
|
+
Signal.stub(:trap).with(:INT).and_yield
|
429
|
+
stub_sleep(tddium)
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should display '#{Tddium::INTERRUPT_TEXT}'" do
|
433
|
+
tddium.should_receive(:say).with(Tddium::INTERRUPT_TEXT)
|
434
|
+
run_spec(tddium)
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should display a summary of all the tests" do
|
438
|
+
tddium.should_receive(:say).with("3 examples, 1 failures, 0 errors, 1 pending")
|
439
|
+
run_spec(tddium)
|
440
|
+
end
|
441
|
+
|
442
|
+
it_should_behave_like("test output summary")
|
443
|
+
end
|
444
|
+
|
445
|
+
context "'GET #{Tddium::TEST_EXECUTIONS_PATH}' is successful" do
|
446
|
+
before do
|
447
|
+
stub_http_response(:get, "#{Tddium::SESSIONS_PATH}/#{session_id}/#{Tddium::TEST_EXECUTIONS_PATH}", [{:response => fixture_path("get_test_executions_200.json")}, {:response => fixture_path("get_test_executions_200_all_finished.json")}])
|
448
|
+
stub_sleep(tddium)
|
449
|
+
end
|
450
|
+
|
451
|
+
it "should sleep for #{Tddium::SLEEP_TIME_BETWEEN_POLLS} seconds" do
|
452
|
+
tddium.should_receive(:sleep).exactly(1).times.with(Tddium::SLEEP_TIME_BETWEEN_POLLS)
|
453
|
+
run_spec(tddium)
|
454
|
+
end
|
455
|
+
|
456
|
+
it "should display a green '.'" do
|
457
|
+
tddium.should_receive(:say).with(".", :green)
|
458
|
+
run_spec(tddium)
|
459
|
+
end
|
460
|
+
|
461
|
+
it "should display a red 'F'" do
|
462
|
+
tddium.should_receive(:say).with("F", :red)
|
463
|
+
run_spec(tddium)
|
464
|
+
end
|
465
|
+
|
466
|
+
it "should display a yellow '*'" do
|
467
|
+
tddium.should_receive(:say).with("*", :yellow)
|
468
|
+
run_spec(tddium)
|
469
|
+
end
|
470
|
+
|
471
|
+
it "should display 'E' with no color" do
|
472
|
+
tddium.should_receive(:say).with("E", nil)
|
473
|
+
run_spec(tddium)
|
474
|
+
end
|
475
|
+
|
476
|
+
it "should display a summary of all the tests" do
|
477
|
+
tddium.should_receive(:say).with("4 examples, 1 failures, 1 errors, 1 pending")
|
478
|
+
run_spec(tddium)
|
479
|
+
end
|
480
|
+
|
481
|
+
it_should_behave_like("test output summary")
|
482
|
+
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
data/tddium.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "tddium/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "tddium-preview"
|
7
|
+
s.version = Tddium::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jay Moorthi"]
|
10
|
+
s.email = ["info@tddium.com"]
|
11
|
+
s.homepage = "http://www.tddium.com/"
|
12
|
+
s.summary = %q{tddium Cloud Test Runner}
|
13
|
+
s.description = %q{tddium gets your rspec+selenium tests into the cloud by running them on your VMs}
|
14
|
+
|
15
|
+
s.rubyforge_project = "tddium"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency("thor")
|
23
|
+
s.add_runtime_dependency("httparty")
|
24
|
+
s.add_runtime_dependency("json")
|
25
|
+
|
26
|
+
s.add_development_dependency("rspec")
|
27
|
+
s.add_development_dependency("fakeweb")
|
28
|
+
s.add_development_dependency("rspec")
|
29
|
+
s.add_development_dependency("rack-test")
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tddium-preview
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jay Moorthi
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-08 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: thor
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: httparty
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: json
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rspec
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: fakeweb
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :development
|
90
|
+
version_requirements: *id005
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rspec
|
93
|
+
prerelease: false
|
94
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
type: :development
|
104
|
+
version_requirements: *id006
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: rack-test
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
type: :development
|
118
|
+
version_requirements: *id007
|
119
|
+
description: tddium gets your rspec+selenium tests into the cloud by running them on your VMs
|
120
|
+
email:
|
121
|
+
- info@tddium.com
|
122
|
+
executables:
|
123
|
+
- tddium
|
124
|
+
extensions: []
|
125
|
+
|
126
|
+
extra_rdoc_files: []
|
127
|
+
|
128
|
+
files:
|
129
|
+
- .document
|
130
|
+
- .gitignore
|
131
|
+
- CHANGELOG
|
132
|
+
- Gemfile
|
133
|
+
- LICENSE.txt
|
134
|
+
- README.rdoc
|
135
|
+
- Rakefile
|
136
|
+
- bin/tddium
|
137
|
+
- doc/aws-keypair-example.tiff
|
138
|
+
- doc/aws-secgroup-example.tiff
|
139
|
+
- lib/tddium.rb
|
140
|
+
- lib/tddium/version.rb
|
141
|
+
- spec/fixtures/get_suites_200.json
|
142
|
+
- spec/fixtures/get_test_executions_200.json
|
143
|
+
- spec/fixtures/get_test_executions_200_all_finished.json
|
144
|
+
- spec/fixtures/post_register_test_executions_200.json
|
145
|
+
- spec/fixtures/post_register_test_executions_200_json_status_1.json
|
146
|
+
- spec/fixtures/post_sessions_201.json
|
147
|
+
- spec/fixtures/post_start_test_executions_200.json
|
148
|
+
- spec/fixtures/post_suites_201.json
|
149
|
+
- spec/fixtures/post_suites_201_json_status_1.json
|
150
|
+
- spec/fixtures/post_suites_409.json
|
151
|
+
- spec/spec_helper.rb
|
152
|
+
- spec/tddium_spec.rb
|
153
|
+
- tddium.gemspec
|
154
|
+
has_rdoc: true
|
155
|
+
homepage: http://www.tddium.com/
|
156
|
+
licenses: []
|
157
|
+
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
|
161
|
+
require_paths:
|
162
|
+
- lib
|
163
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
164
|
+
none: false
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
hash: 3
|
169
|
+
segments:
|
170
|
+
- 0
|
171
|
+
version: "0"
|
172
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
hash: 3
|
178
|
+
segments:
|
179
|
+
- 0
|
180
|
+
version: "0"
|
181
|
+
requirements: []
|
182
|
+
|
183
|
+
rubyforge_project: tddium
|
184
|
+
rubygems_version: 1.5.2
|
185
|
+
signing_key:
|
186
|
+
specification_version: 3
|
187
|
+
summary: tddium Cloud Test Runner
|
188
|
+
test_files:
|
189
|
+
- spec/fixtures/get_suites_200.json
|
190
|
+
- spec/fixtures/get_test_executions_200.json
|
191
|
+
- spec/fixtures/get_test_executions_200_all_finished.json
|
192
|
+
- spec/fixtures/post_register_test_executions_200.json
|
193
|
+
- spec/fixtures/post_register_test_executions_200_json_status_1.json
|
194
|
+
- spec/fixtures/post_sessions_201.json
|
195
|
+
- spec/fixtures/post_start_test_executions_200.json
|
196
|
+
- spec/fixtures/post_suites_201.json
|
197
|
+
- spec/fixtures/post_suites_201_json_status_1.json
|
198
|
+
- spec/fixtures/post_suites_409.json
|
199
|
+
- spec/spec_helper.rb
|
200
|
+
- spec/tddium_spec.rb
|