tddium-preview 0.0.1
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/.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
|