Skip to content

Commit f3aa948

Browse files
authored
Merge pull request #238 from 3pillarlabs/develop
Merge PR #237. The UI automation failed while trying to click the 'Terminate' button on the AWS setup. The exact same command succeeded for the data center setup. Manually verified that AWS setup can be terminated successfully. Ignoring the false negative from the UI automation.
2 parents fe275b3 + 79b71ad commit f3aa948

35 files changed

+735
-107
lines changed

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
version: '3.2'
22
services:
33
web:
4-
image: "hailstorm3/hailstorm-web-client:1.7.10"
4+
image: "hailstorm3/hailstorm-web-client:1.8.10"
55
ports:
66
- "8080:80"
77
networks:
@@ -22,7 +22,7 @@ services:
2222
- "start.sh"
2323

2424
hailstorm-api:
25-
image: "hailstorm3/hailstorm-api:1.0.17"
25+
image: "hailstorm3/hailstorm-api:1.0.18"
2626
ports:
2727
- "4567:8080"
2828
environment:

hailstorm-api/app/api/jmeter_plans.rb

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@
1515

1616
# @type [Hailstorm::Support::Configuration]
1717
hailstorm_config = deep_decode(project_config.stringified_config)
18-
test_plans_attrs = (hailstorm_config.jmeter.test_plans || []).map { |e| { test_plan_name: e, jmx_file: true } }
18+
test_plans_attrs = hailstorm_config.jmeter.all_test_plans_attrs
1919
data_files_attrs = (hailstorm_config.jmeter.data_files || []).map { |e| { test_plan_name: e, jmx_file: false } }
2020
files_attrs = test_plans_attrs + data_files_attrs
21-
data_attrs = files_attrs.map { |partial_attrs| to_jmeter_attributes(hailstorm_config, project_id, partial_attrs) }
21+
data_attrs = files_attrs.map do |partial_attrs|
22+
deep_camelize_keys(to_jmeter_attributes(hailstorm_config, project_id, partial_attrs))
23+
end
24+
2225
JSON.dump(data_attrs)
2326
end
2427

2528
post '/projects/:project_id/jmeter_plans' do |project_id|
2629
found_project = Hailstorm::Model::Project.find(project_id)
2730
request.body.rewind
28-
jmeter_plan = configure_jmeter(found_project, request)
31+
jmeter_plan = deep_camelize_keys(configure_jmeter(found_project, request))
2932
JSON.dump(jmeter_plan)
3033
end
3134

@@ -40,16 +43,14 @@
4043
test_plan_name = hailstorm_config.jmeter.test_plans.find { |e| e.to_java_string.hash_code == id.to_i }
4144
return not_found unless test_plan_name
4245

43-
hailstorm_config.jmeter.properties(test_plan: test_plan_name) { |map| update_map(map, data) }
44-
project_config.update!(stringified_config: deep_encode(hailstorm_config))
46+
hailstorm_config
47+
.jmeter
48+
.properties(test_plan: test_plan_name) { |map| update_map(map, data) } unless data['properties'].blank?
4549

46-
path, name = test_plan_name.split('/')
47-
JSON.dump(
48-
id: test_plan_name.to_java_string.hash_code,
49-
name: "#{name}.jmx",
50-
path: path,
51-
properties: hailstorm_config.jmeter.properties(test_plan: test_plan_name).entries
52-
)
50+
handle_disabled(data, hailstorm_config, test_plan_name)
51+
project_config.update!(stringified_config: deep_encode(hailstorm_config))
52+
resp = deep_camelize_keys(build_patch_response(hailstorm_config, test_plan_name, project_id))
53+
JSON.dump(resp)
5354
end
5455

5556
delete '/projects/:project_id/jmeter_plans/:id' do |project_id, id|
@@ -58,8 +59,13 @@
5859

5960
hailstorm_config = deep_decode(project_config.stringified_config)
6061
test_plan_name = hailstorm_config.jmeter.test_plans.find { |e| e.to_java_string.hash_code == id.to_i }
61-
hailstorm_config.jmeter.test_plans.reject! { |e| e == test_plan_name } if test_plan_name
62-
if hailstorm_config.jmeter.data_files
62+
if test_plan_name
63+
return 402 if client_stats?(project_id, File.basename(test_plan_name))
64+
65+
hailstorm_config.jmeter.test_plans.reject! { |e| e == test_plan_name }
66+
end
67+
68+
unless hailstorm_config.jmeter.data_files.blank?
6369
data_file_name = hailstorm_config.jmeter.data_files.find { |e| e.to_java_string.hash_code == id.to_i }
6470
hailstorm_config.jmeter.data_files.reject! { |e| e == data_file_name } if data_file_name
6571
end

hailstorm-api/app/helpers/api_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def deep_encode(obj)
1515
Base64.encode64(Marshal.dump(obj))
1616
end
1717

18-
# @param [Sting] serz
18+
# @param [String] serz
1919
# @return [Object]
2020
def deep_decode(serz)
2121
Marshal.load(Base64.decode64(serz)) # rubocop:disable Security/MarshalLoad

hailstorm-api/app/helpers/jmeter_helper.rb

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# frozen_string_literal: true
22

3+
require 'hailstorm/model/project'
4+
require 'hailstorm/model/jmeter_plan'
5+
require 'hailstorm/model/client_stat'
6+
37
# Helper for JMeter API
48
module JMeterHelper
59

@@ -16,14 +20,13 @@ def to_jmeter_attributes(hailstorm_config, project_id, partial_attrs)
1620
obj[:projectId] = project_id
1721
obj[:path] = File.dirname(partial_attrs[:test_plan_name])
1822
if partial_attrs[:jmx_file]
19-
obj[:id] = compute_test_plan_id(partial_attrs[:test_plan_name])
20-
obj[:name] = "#{File.basename(partial_attrs[:test_plan_name])}.jmx"
2123
properties = hailstorm_config.jmeter.properties(test_plan: partial_attrs[:test_plan_name])
2224
obj[:properties] = properties.entries
25+
add_jmx_attributes(obj, partial_attrs, project_id)
2326
else
2427
obj[:id] = compute_data_file_id(partial_attrs[:test_plan_name])
2528
obj[:name] = File.basename(partial_attrs[:test_plan_name])
26-
obj[:dataFile] = true
29+
obj[:data_file] = true
2730
end
2831

2932
obj
@@ -59,6 +62,44 @@ def compute_data_file_id(data_file_name)
5962
data_file_name.to_java_string.hash_code
6063
end
6164

65+
# @param [Hash] data
66+
# @param [Hailstorm::Support::Configuration] hailstorm_config
67+
# @param [String] test_plan_name
68+
def handle_disabled(data, hailstorm_config, test_plan_name)
69+
return if data['disabled'].nil?
70+
71+
if data['disabled']
72+
already_disabled = hailstorm_config.jmeter.disabled_test_plans.include?(test_plan_name)
73+
hailstorm_config.jmeter.disabled_test_plans.push(test_plan_name) unless already_disabled
74+
else
75+
hailstorm_config.jmeter.disabled_test_plans.reject! { |e| e == test_plan_name }
76+
end
77+
end
78+
79+
# @param [Hailstorm::Support::Configuration] hailstorm_config
80+
# @param [String] test_plan_name
81+
# @param [Integer] project_id
82+
# @return [Hash]
83+
def build_patch_response(hailstorm_config, test_plan_name, project_id)
84+
path, name = test_plan_name.split('/')
85+
resp = { id: test_plan_name.to_java_string.hash_code,
86+
name: "#{name}.jmx",
87+
path: path,
88+
properties: hailstorm_config.jmeter.properties(test_plan: test_plan_name).entries,
89+
plan_executed_before: client_stats?(project_id, name) }
90+
91+
resp[:disabled] = true if hailstorm_config.jmeter.disabled_test_plans.include?(test_plan_name)
92+
resp
93+
end
94+
95+
# @param [Integer] project_id
96+
# @param [String] test_plan_name
97+
def client_stats?(project_id, test_plan_name)
98+
project = Hailstorm::Model::Project.find(project_id)
99+
test_plan = project.jmeter_plans.where(test_plan_name: test_plan_name).first
100+
test_plan && Hailstorm::Model::ClientStat.where(jmeter_plan_id: test_plan.id).count.positive?
101+
end
102+
62103
private
63104

64105
def jmeter_attributes(data, file_id, found_project)
@@ -70,7 +111,7 @@ def jmeter_attributes(data, file_id, found_project)
70111
}
71112

72113
jmeter_plan[:properties] = data['properties'] if data['properties']
73-
jmeter_plan[:dataFile] = true if data['dataFile']
114+
jmeter_plan[:data_file] = true if data['dataFile']
74115
jmeter_plan
75116
end
76117

@@ -107,4 +148,11 @@ def validate_jmeter_plan(jmeter_plan, local_file_path, response_data)
107148
end
108149
end
109150
end
151+
152+
def add_jmx_attributes(obj, partial_attrs, project_id)
153+
obj[:id] = compute_test_plan_id(partial_attrs[:test_plan_name])
154+
obj[:name] = "#{File.basename(partial_attrs[:test_plan_name])}.jmx"
155+
obj[:disabled] = partial_attrs[:disabled] if partial_attrs.key?(:disabled)
156+
obj[:plan_executed_before] = client_stats?(project_id, File.basename(partial_attrs[:test_plan_name]))
157+
end
110158
end

hailstorm-api/app/helpers/projects_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def add_incomplete_attribute(project, project_attrs)
7979
project_config = ProjectConfiguration.where(project_id: project.id).first
8080
if project_config
8181
hailstorm_config = deep_decode(project_config.stringified_config)
82-
if hailstorm_config.jmeter.test_plans.empty? ||
82+
if hailstorm_config.jmeter.enabled_test_plans.empty? ||
8383
hailstorm_config.clusters.select { |e| e.active || e.active.nil? }.empty?
8484
project_attrs[:incomplete] = true
8585
end

hailstorm-api/app/initializer/api_config.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require 'hailstorm/initializer/java_classpath'
99
require 'initializer/db_config'
1010
require 'initializer/migrations'
11+
require 'initializer/configuration_ext'
1112
require 'web_file_store'
1213
require 'version'
1314

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# frozen_string_literal: true
2+
3+
require 'hailstorm/support/configuration'
4+
5+
class Hailstorm::Support::Configuration
6+
# JMeter extension
7+
class JMeter
8+
9+
def disabled_test_plans
10+
# For backward compatibility. Existing marshalled representations do not have this field, and unmarshalling
11+
# does not invoke the constructor.
12+
@disabled_test_plans ||= []
13+
end
14+
15+
attr_writer :disabled_test_plans
16+
17+
alias original_initialize initialize
18+
def initialize
19+
original_initialize
20+
self.disabled_test_plans = []
21+
end
22+
23+
# @return [Array<Hash>] hash hash.keys = [:test_plan_name, :jmx_file, :disabled]
24+
# test_plan_name: String
25+
# jmx_file: Boolean, true
26+
# disabled: Boolean
27+
def all_test_plans_attrs
28+
return [] if self.test_plans.nil?
29+
30+
self.test_plans.map do |plan|
31+
attrs = { test_plan_name: plan, jmx_file: true }
32+
attrs[:disabled] = true if self.disabled_test_plans.include?(plan)
33+
attrs
34+
end
35+
end
36+
37+
# All test plans that are not disabled. Does not include data files
38+
# @return [Array<String>]
39+
def enabled_test_plans
40+
self.test_plans.reject { |plan| self.disabled_test_plans.include?(plan) }
41+
end
42+
end
43+
end

hailstorm-api/app/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
# Version
44
module Hailstorm
55
module Api
6-
VERSION = '1.0.17'
6+
VERSION = '1.0.18'
77
end
88
end

hailstorm-api/spec/api/jmeter_plans_spec.rb

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,42 @@
7878
expect(data_file[:path]).to eq('234')
7979
expect(data_file[:dataFile]).to be true
8080
end
81+
82+
it 'should include disabled plans in list' do
83+
project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
84+
hailstorm_config = Hailstorm::Support::Configuration.new
85+
hailstorm_config.jmeter do |jmeter|
86+
jmeter.add_test_plan('123/a.jmx')
87+
jmeter.properties(test_plan: '123/a.jmx') do |map|
88+
map['NumUsers'] = '100'
89+
end
90+
91+
jmeter.add_test_plan('124/b.jmx')
92+
jmeter.properties(test_plan: '124/b.jmx') do |map|
93+
map['NumUsers'] = '20'
94+
end
95+
96+
jmeter.disabled_test_plans.push('124/b')
97+
jmeter.data_files = %w[234/foo.csv]
98+
end
99+
100+
ProjectConfiguration.create!(project_id: project.id, stringified_config: deep_encode(hailstorm_config))
101+
102+
@browser.get("/projects/#{project.id}/jmeter_plans")
103+
expect(@browser.last_response).to be_ok
104+
# @type [Array<Hash>] res
105+
res = JSON.parse(@browser.last_response.body)
106+
expect(res.size).to eq(3)
107+
expect(res.first.symbolize_keys[:name]).to eq('a.jmx')
108+
109+
jmeter_plan = res[1].symbolize_keys
110+
expect(jmeter_plan[:name]).to eq('b.jmx')
111+
expect(jmeter_plan[:disabled]).to be == true
112+
113+
data_file = res[2].symbolize_keys
114+
expect(data_file[:name]).to eq('foo.csv')
115+
expect(data_file[:dataFile]).to be true
116+
end
81117
end
82118

83119
context 'PATCH /projects/:project_id/jmeter_plans/:id' do
@@ -110,10 +146,63 @@
110146
@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump(patch_params))
111147
expect(@browser.last_response).to be_ok
112148
patch_res = JSON.parse(@browser.last_response.body).symbolize_keys
113-
expect(patch_res.keys.sort).to eq(%i[name path properties id].sort)
149+
expect(patch_res.keys.sort).to eq(%i[name path properties id planExecutedBefore].sort)
114150
expect(patch_res[:id]).to eq(post_res[:id])
115151
expect(patch_res[:properties].to_h).to eq(patch_params[:properties].to_h)
116152
end
153+
154+
it 'should disable a test plan' do
155+
project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
156+
params = {
157+
name: 'hailstorm.jmx',
158+
path: '1234',
159+
properties: [
160+
%w[NumUsers 10],
161+
%w[RampUp 30],
162+
%w[Duration 180],
163+
%w[ServerName 152.36.34.28]
164+
]
165+
}
166+
167+
@browser.post("/projects/#{project.id}/jmeter_plans", JSON.dump(params))
168+
expect(@browser.last_response).to be_ok
169+
post_res = JSON.parse(@browser.last_response.body).symbolize_keys
170+
171+
patch_params = { disabled: true }
172+
@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump(patch_params))
173+
expect(@browser.last_response).to be_ok
174+
patch_res = JSON.parse(@browser.last_response.body).symbolize_keys
175+
expect(patch_res.keys.sort).to eq(%i[name path properties id disabled planExecutedBefore].sort)
176+
expect(patch_res[:id]).to eq(post_res[:id])
177+
expect(patch_res[:properties].to_h).to eq(params[:properties].to_h)
178+
expect(patch_res[:disabled]).to eql(true)
179+
end
180+
181+
it 'should enable a previously disabled test plan' do
182+
project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
183+
params = {
184+
name: 'hailstorm.jmx',
185+
path: '1234',
186+
properties: [
187+
%w[NumUsers 10],
188+
%w[RampUp 30],
189+
%w[Duration 180],
190+
%w[ServerName 152.36.34.28]
191+
]
192+
}
193+
194+
@browser.post("/projects/#{project.id}/jmeter_plans", JSON.dump(params))
195+
expect(@browser.last_response).to be_ok
196+
post_res = JSON.parse(@browser.last_response.body).symbolize_keys
197+
@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump({ disabled: true }))
198+
expect(@browser.last_response).to be_ok
199+
200+
@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump({ disabled: false }))
201+
expect(@browser.last_response).to be_ok
202+
patch_res = JSON.parse(@browser.last_response.body).symbolize_keys
203+
expect(patch_res[:disabled]).to be_nil
204+
expect(patch_res[:properties].to_h).to eq(params[:properties].to_h)
205+
end
117206
end
118207

119208
context 'DELETE /projects/:project_id/jmeter_plans/:id' do
@@ -177,5 +266,32 @@
177266
expect(updated_hailstorm_config.jmeter.test_plans.size).to eq(1)
178267
expect(updated_hailstorm_config.jmeter.data_files.size).to eq(0)
179268
end
269+
270+
it 'should not delete a test plan if it has been used in a previous test run' do
271+
allow(Hailstorm::Model::ClientStat).to receive_message_chain(:where, :count).and_return(1)
272+
mock_test_plan = double(Hailstorm::Model::JmeterPlan, id: 12)
273+
allow_any_instance_of(Hailstorm::Model::Project).to receive_message_chain(:jmeter_plans,
274+
:where).and_return([mock_test_plan])
275+
276+
project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
277+
hailstorm_config = Hailstorm::Support::Configuration.new
278+
hailstorm_config.jmeter do |jmeter|
279+
jmeter.add_test_plan('1/a.jmx')
280+
jmeter.properties(test_plan: '1/a.jmx') do |map|
281+
map['NumUsers'] = 100
282+
end
283+
284+
jmeter.data_files.push('2/a.csv')
285+
end
286+
287+
ProjectConfiguration.create!(
288+
project_id: project.id,
289+
stringified_config: deep_encode(hailstorm_config)
290+
)
291+
292+
id = '1/a'.to_java_string.hash_code
293+
@browser.delete("/projects/#{project.id}/jmeter_plans/#{id}")
294+
expect(@browser.last_response).to_not be_successful
295+
end
180296
end
181297
end

0 commit comments

Comments
 (0)