Skip to content

Commit

Permalink
Merge pull request #238 from 3pillarlabs/develop
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sayantam committed Apr 26, 2021
2 parents fe275b3 + 79b71ad commit f3aa948
Show file tree
Hide file tree
Showing 35 changed files with 735 additions and 107 deletions.
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3.2'
services:
web:
image: "hailstorm3/hailstorm-web-client:1.7.10"
image: "hailstorm3/hailstorm-web-client:1.8.10"
ports:
- "8080:80"
networks:
Expand All @@ -22,7 +22,7 @@ services:
- "start.sh"

hailstorm-api:
image: "hailstorm3/hailstorm-api:1.0.17"
image: "hailstorm3/hailstorm-api:1.0.18"
ports:
- "4567:8080"
environment:
Expand Down
34 changes: 20 additions & 14 deletions hailstorm-api/app/api/jmeter_plans.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@

# @type [Hailstorm::Support::Configuration]
hailstorm_config = deep_decode(project_config.stringified_config)
test_plans_attrs = (hailstorm_config.jmeter.test_plans || []).map { |e| { test_plan_name: e, jmx_file: true } }
test_plans_attrs = hailstorm_config.jmeter.all_test_plans_attrs
data_files_attrs = (hailstorm_config.jmeter.data_files || []).map { |e| { test_plan_name: e, jmx_file: false } }
files_attrs = test_plans_attrs + data_files_attrs
data_attrs = files_attrs.map { |partial_attrs| to_jmeter_attributes(hailstorm_config, project_id, partial_attrs) }
data_attrs = files_attrs.map do |partial_attrs|
deep_camelize_keys(to_jmeter_attributes(hailstorm_config, project_id, partial_attrs))
end

JSON.dump(data_attrs)
end

post '/projects/:project_id/jmeter_plans' do |project_id|
found_project = Hailstorm::Model::Project.find(project_id)
request.body.rewind
jmeter_plan = configure_jmeter(found_project, request)
jmeter_plan = deep_camelize_keys(configure_jmeter(found_project, request))
JSON.dump(jmeter_plan)
end

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

hailstorm_config.jmeter.properties(test_plan: test_plan_name) { |map| update_map(map, data) }
project_config.update!(stringified_config: deep_encode(hailstorm_config))
hailstorm_config
.jmeter
.properties(test_plan: test_plan_name) { |map| update_map(map, data) } unless data['properties'].blank?

path, name = test_plan_name.split('/')
JSON.dump(
id: test_plan_name.to_java_string.hash_code,
name: "#{name}.jmx",
path: path,
properties: hailstorm_config.jmeter.properties(test_plan: test_plan_name).entries
)
handle_disabled(data, hailstorm_config, test_plan_name)
project_config.update!(stringified_config: deep_encode(hailstorm_config))
resp = deep_camelize_keys(build_patch_response(hailstorm_config, test_plan_name, project_id))
JSON.dump(resp)
end

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

hailstorm_config = deep_decode(project_config.stringified_config)
test_plan_name = hailstorm_config.jmeter.test_plans.find { |e| e.to_java_string.hash_code == id.to_i }
hailstorm_config.jmeter.test_plans.reject! { |e| e == test_plan_name } if test_plan_name
if hailstorm_config.jmeter.data_files
if test_plan_name
return 402 if client_stats?(project_id, File.basename(test_plan_name))

hailstorm_config.jmeter.test_plans.reject! { |e| e == test_plan_name }
end

unless hailstorm_config.jmeter.data_files.blank?
data_file_name = hailstorm_config.jmeter.data_files.find { |e| e.to_java_string.hash_code == id.to_i }
hailstorm_config.jmeter.data_files.reject! { |e| e == data_file_name } if data_file_name
end
Expand Down
2 changes: 1 addition & 1 deletion hailstorm-api/app/helpers/api_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def deep_encode(obj)
Base64.encode64(Marshal.dump(obj))
end

# @param [Sting] serz
# @param [String] serz
# @return [Object]
def deep_decode(serz)
Marshal.load(Base64.decode64(serz)) # rubocop:disable Security/MarshalLoad
Expand Down
56 changes: 52 additions & 4 deletions hailstorm-api/app/helpers/jmeter_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# frozen_string_literal: true

require 'hailstorm/model/project'
require 'hailstorm/model/jmeter_plan'
require 'hailstorm/model/client_stat'

# Helper for JMeter API
module JMeterHelper

Expand All @@ -16,14 +20,13 @@ def to_jmeter_attributes(hailstorm_config, project_id, partial_attrs)
obj[:projectId] = project_id
obj[:path] = File.dirname(partial_attrs[:test_plan_name])
if partial_attrs[:jmx_file]
obj[:id] = compute_test_plan_id(partial_attrs[:test_plan_name])
obj[:name] = "#{File.basename(partial_attrs[:test_plan_name])}.jmx"
properties = hailstorm_config.jmeter.properties(test_plan: partial_attrs[:test_plan_name])
obj[:properties] = properties.entries
add_jmx_attributes(obj, partial_attrs, project_id)
else
obj[:id] = compute_data_file_id(partial_attrs[:test_plan_name])
obj[:name] = File.basename(partial_attrs[:test_plan_name])
obj[:dataFile] = true
obj[:data_file] = true
end

obj
Expand Down Expand Up @@ -59,6 +62,44 @@ def compute_data_file_id(data_file_name)
data_file_name.to_java_string.hash_code
end

# @param [Hash] data
# @param [Hailstorm::Support::Configuration] hailstorm_config
# @param [String] test_plan_name
def handle_disabled(data, hailstorm_config, test_plan_name)
return if data['disabled'].nil?

if data['disabled']
already_disabled = hailstorm_config.jmeter.disabled_test_plans.include?(test_plan_name)
hailstorm_config.jmeter.disabled_test_plans.push(test_plan_name) unless already_disabled
else
hailstorm_config.jmeter.disabled_test_plans.reject! { |e| e == test_plan_name }
end
end

# @param [Hailstorm::Support::Configuration] hailstorm_config
# @param [String] test_plan_name
# @param [Integer] project_id
# @return [Hash]
def build_patch_response(hailstorm_config, test_plan_name, project_id)
path, name = test_plan_name.split('/')
resp = { id: test_plan_name.to_java_string.hash_code,
name: "#{name}.jmx",
path: path,
properties: hailstorm_config.jmeter.properties(test_plan: test_plan_name).entries,
plan_executed_before: client_stats?(project_id, name) }

resp[:disabled] = true if hailstorm_config.jmeter.disabled_test_plans.include?(test_plan_name)
resp
end

# @param [Integer] project_id
# @param [String] test_plan_name
def client_stats?(project_id, test_plan_name)
project = Hailstorm::Model::Project.find(project_id)
test_plan = project.jmeter_plans.where(test_plan_name: test_plan_name).first
test_plan && Hailstorm::Model::ClientStat.where(jmeter_plan_id: test_plan.id).count.positive?
end

private

def jmeter_attributes(data, file_id, found_project)
Expand All @@ -70,7 +111,7 @@ def jmeter_attributes(data, file_id, found_project)
}

jmeter_plan[:properties] = data['properties'] if data['properties']
jmeter_plan[:dataFile] = true if data['dataFile']
jmeter_plan[:data_file] = true if data['dataFile']
jmeter_plan
end

Expand Down Expand Up @@ -107,4 +148,11 @@ def validate_jmeter_plan(jmeter_plan, local_file_path, response_data)
end
end
end

def add_jmx_attributes(obj, partial_attrs, project_id)
obj[:id] = compute_test_plan_id(partial_attrs[:test_plan_name])
obj[:name] = "#{File.basename(partial_attrs[:test_plan_name])}.jmx"
obj[:disabled] = partial_attrs[:disabled] if partial_attrs.key?(:disabled)
obj[:plan_executed_before] = client_stats?(project_id, File.basename(partial_attrs[:test_plan_name]))
end
end
2 changes: 1 addition & 1 deletion hailstorm-api/app/helpers/projects_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def add_incomplete_attribute(project, project_attrs)
project_config = ProjectConfiguration.where(project_id: project.id).first
if project_config
hailstorm_config = deep_decode(project_config.stringified_config)
if hailstorm_config.jmeter.test_plans.empty? ||
if hailstorm_config.jmeter.enabled_test_plans.empty? ||
hailstorm_config.clusters.select { |e| e.active || e.active.nil? }.empty?
project_attrs[:incomplete] = true
end
Expand Down
1 change: 1 addition & 0 deletions hailstorm-api/app/initializer/api_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require 'hailstorm/initializer/java_classpath'
require 'initializer/db_config'
require 'initializer/migrations'
require 'initializer/configuration_ext'
require 'web_file_store'
require 'version'

Expand Down
43 changes: 43 additions & 0 deletions hailstorm-api/app/initializer/configuration_ext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

require 'hailstorm/support/configuration'

class Hailstorm::Support::Configuration
# JMeter extension
class JMeter

def disabled_test_plans
# For backward compatibility. Existing marshalled representations do not have this field, and unmarshalling
# does not invoke the constructor.
@disabled_test_plans ||= []
end

attr_writer :disabled_test_plans

alias original_initialize initialize
def initialize
original_initialize
self.disabled_test_plans = []
end

# @return [Array<Hash>] hash hash.keys = [:test_plan_name, :jmx_file, :disabled]
# test_plan_name: String
# jmx_file: Boolean, true
# disabled: Boolean
def all_test_plans_attrs
return [] if self.test_plans.nil?

self.test_plans.map do |plan|
attrs = { test_plan_name: plan, jmx_file: true }
attrs[:disabled] = true if self.disabled_test_plans.include?(plan)
attrs
end
end

# All test plans that are not disabled. Does not include data files
# @return [Array<String>]
def enabled_test_plans
self.test_plans.reject { |plan| self.disabled_test_plans.include?(plan) }
end
end
end
2 changes: 1 addition & 1 deletion hailstorm-api/app/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
# Version
module Hailstorm
module Api
VERSION = '1.0.17'
VERSION = '1.0.18'
end
end
118 changes: 117 additions & 1 deletion hailstorm-api/spec/api/jmeter_plans_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,42 @@
expect(data_file[:path]).to eq('234')
expect(data_file[:dataFile]).to be true
end

it 'should include disabled plans in list' do
project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
hailstorm_config = Hailstorm::Support::Configuration.new
hailstorm_config.jmeter do |jmeter|
jmeter.add_test_plan('123/a.jmx')
jmeter.properties(test_plan: '123/a.jmx') do |map|
map['NumUsers'] = '100'
end

jmeter.add_test_plan('124/b.jmx')
jmeter.properties(test_plan: '124/b.jmx') do |map|
map['NumUsers'] = '20'
end

jmeter.disabled_test_plans.push('124/b')
jmeter.data_files = %w[234/foo.csv]
end

ProjectConfiguration.create!(project_id: project.id, stringified_config: deep_encode(hailstorm_config))

@browser.get("/projects/#{project.id}/jmeter_plans")
expect(@browser.last_response).to be_ok
# @type [Array<Hash>] res
res = JSON.parse(@browser.last_response.body)
expect(res.size).to eq(3)
expect(res.first.symbolize_keys[:name]).to eq('a.jmx')

jmeter_plan = res[1].symbolize_keys
expect(jmeter_plan[:name]).to eq('b.jmx')
expect(jmeter_plan[:disabled]).to be == true

data_file = res[2].symbolize_keys
expect(data_file[:name]).to eq('foo.csv')
expect(data_file[:dataFile]).to be true
end
end

context 'PATCH /projects/:project_id/jmeter_plans/:id' do
Expand Down Expand Up @@ -110,10 +146,63 @@
@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump(patch_params))
expect(@browser.last_response).to be_ok
patch_res = JSON.parse(@browser.last_response.body).symbolize_keys
expect(patch_res.keys.sort).to eq(%i[name path properties id].sort)
expect(patch_res.keys.sort).to eq(%i[name path properties id planExecutedBefore].sort)
expect(patch_res[:id]).to eq(post_res[:id])
expect(patch_res[:properties].to_h).to eq(patch_params[:properties].to_h)
end

it 'should disable a test plan' do
project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
params = {
name: 'hailstorm.jmx',
path: '1234',
properties: [
%w[NumUsers 10],
%w[RampUp 30],
%w[Duration 180],
%w[ServerName 152.36.34.28]
]
}

@browser.post("/projects/#{project.id}/jmeter_plans", JSON.dump(params))
expect(@browser.last_response).to be_ok
post_res = JSON.parse(@browser.last_response.body).symbolize_keys

patch_params = { disabled: true }
@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump(patch_params))
expect(@browser.last_response).to be_ok
patch_res = JSON.parse(@browser.last_response.body).symbolize_keys
expect(patch_res.keys.sort).to eq(%i[name path properties id disabled planExecutedBefore].sort)
expect(patch_res[:id]).to eq(post_res[:id])
expect(patch_res[:properties].to_h).to eq(params[:properties].to_h)
expect(patch_res[:disabled]).to eql(true)
end

it 'should enable a previously disabled test plan' do
project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
params = {
name: 'hailstorm.jmx',
path: '1234',
properties: [
%w[NumUsers 10],
%w[RampUp 30],
%w[Duration 180],
%w[ServerName 152.36.34.28]
]
}

@browser.post("/projects/#{project.id}/jmeter_plans", JSON.dump(params))
expect(@browser.last_response).to be_ok
post_res = JSON.parse(@browser.last_response.body).symbolize_keys
@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump({ disabled: true }))
expect(@browser.last_response).to be_ok

@browser.patch("/projects/#{project.id}/jmeter_plans/#{post_res[:id]}", JSON.dump({ disabled: false }))
expect(@browser.last_response).to be_ok
patch_res = JSON.parse(@browser.last_response.body).symbolize_keys
expect(patch_res[:disabled]).to be_nil
expect(patch_res[:properties].to_h).to eq(params[:properties].to_h)
end
end

context 'DELETE /projects/:project_id/jmeter_plans/:id' do
Expand Down Expand Up @@ -177,5 +266,32 @@
expect(updated_hailstorm_config.jmeter.test_plans.size).to eq(1)
expect(updated_hailstorm_config.jmeter.data_files.size).to eq(0)
end

it 'should not delete a test plan if it has been used in a previous test run' do
allow(Hailstorm::Model::ClientStat).to receive_message_chain(:where, :count).and_return(1)
mock_test_plan = double(Hailstorm::Model::JmeterPlan, id: 12)
allow_any_instance_of(Hailstorm::Model::Project).to receive_message_chain(:jmeter_plans,
:where).and_return([mock_test_plan])

project = Hailstorm::Model::Project.create!(project_code: 'api_jmeter_plans_spec')
hailstorm_config = Hailstorm::Support::Configuration.new
hailstorm_config.jmeter do |jmeter|
jmeter.add_test_plan('1/a.jmx')
jmeter.properties(test_plan: '1/a.jmx') do |map|
map['NumUsers'] = 100
end

jmeter.data_files.push('2/a.csv')
end

ProjectConfiguration.create!(
project_id: project.id,
stringified_config: deep_encode(hailstorm_config)
)

id = '1/a'.to_java_string.hash_code
@browser.delete("/projects/#{project.id}/jmeter_plans/#{id}")
expect(@browser.last_response).to_not be_successful
end
end
end
Loading

0 comments on commit f3aa948

Please sign in to comment.