esp_sdk 2.7.0 → 2.8.0

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +36 -35
  3. data/.travis.yml +2 -1
  4. data/CHANGELOG.md +9 -0
  5. data/Gemfile.lock +63 -85
  6. data/Rakefile +2 -1
  7. data/esp_sdk.gemspec +3 -4
  8. data/lib/esp/aws_clients.rb +2 -2
  9. data/lib/esp/commands/add_external_account.rb +1 -1
  10. data/lib/esp/commands/commands_tasks.rb +2 -2
  11. data/lib/esp/extensions/active_resource/formats/json_api_format.rb +24 -16
  12. data/lib/esp/extensions/active_resource/validations.rb +1 -1
  13. data/lib/esp/resources/alert.rb +3 -2
  14. data/lib/esp/resources/custom_signature/definition.rb +2 -2
  15. data/lib/esp/resources/dashboard.rb +2 -5
  16. data/lib/esp/resources/resource.rb +14 -15
  17. data/lib/esp/resources/stat.rb +2 -5
  18. data/lib/esp/resources/suppression.rb +1 -1
  19. data/lib/esp/version.rb +1 -1
  20. data/test/esp/integration/custom_signature_definition_integration_test.rb +15 -1
  21. data/test/esp/integration/custom_signature_integration_test.rb +3 -1
  22. data/test/esp/integration/custom_signature_result_alert_integration_test.rb +4 -4
  23. data/test/esp/integration/custom_signature_result_integration_test.rb +2 -1
  24. data/test/esp/integration/json_api_format_integration_test.rb +1 -1
  25. data/test/esp/integration/suppression_integration_test.rb +1 -1
  26. data/test/esp/integration/suppression_unique_identifier_integration_test.rb +1 -1
  27. data/test/esp/resources/custom_signature/definition_test.rb +1 -1
  28. data/test/esp/resources/custom_signature_test.rb +1 -1
  29. data/test/esp/resources/external_account_test.rb +1 -1
  30. data/test/esp/resources/organization_test.rb +6 -6
  31. data/test/esp/resources/service_test.rb +1 -1
  32. data/test/esp/resources/sub_organization_test.rb +3 -3
  33. data/test/esp/resources/team_test.rb +2 -2
  34. data/test/factories/contact_requests.rb +1 -2
  35. data/test/factories/custom_signature/definitions.rb +1 -2
  36. data/test/factories/dashboards.rb +1 -2
  37. data/test/factories/external_accounts.rb +1 -2
  38. data/test/factories/organizations.rb +1 -2
  39. data/test/factories/reports.rb +1 -2
  40. data/test/factories/scan_intervals.rb +1 -2
  41. data/test/factories/stat_custom_signatures.rb +1 -2
  42. data/test/factories/stat_regions.rb +1 -2
  43. data/test/factories/stat_services.rb +1 -2
  44. data/test/factories/stat_signautures.rb +1 -2
  45. data/test/factories/stats.rb +1 -2
  46. data/test/factories/sub_organizations.rb +1 -2
  47. data/test/factories/suppression/regions.rb +2 -4
  48. data/test/factories/suppression/signatures.rb +2 -4
  49. data/test/factories/suppression/unique_identifiers.rb +2 -4
  50. data/test/factories/suppressions.rb +1 -2
  51. data/test/factories/users.rb +1 -2
  52. data/test/test_helper.rb +3 -10
  53. metadata +11 -28
  54. data/lib/tasks/testing.rake +0 -3
  55. data/test/parallel_reporter.rb +0 -93
data/Rakefile CHANGED
@@ -1,12 +1,13 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
  load 'lib/tasks/rubocop.rake'
4
- load 'lib/tasks/testing.rake'
5
4
  require 'rdoc/task'
6
5
 
7
6
  Rake::TestTask.new do |task|
8
7
  task.libs << 'test'
9
8
  task.test_files = FileList['test/*_test.rb', 'test/**/*_test.rb'] - FileList["test/esp/integration/**/*_test.rb"]
9
+ task.verbose = false
10
+ task.warning = false
10
11
  end
11
12
 
12
13
  namespace "test" do
@@ -3,7 +3,7 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'esp/version'
5
5
 
6
- Gem::Specification.new do |spec|
6
+ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
7
7
  spec.name = 'esp_sdk'
8
8
  spec.version = ESP::VERSION
9
9
  spec.authors = ['Evident.io']
@@ -26,19 +26,18 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency 'guard-minitest'
27
27
  spec.add_development_dependency 'guard-rubocop'
28
28
  spec.add_development_dependency 'minitest'
29
- spec.add_development_dependency 'minitest-reporters'
30
29
  spec.add_development_dependency 'shoulda'
31
30
  spec.add_development_dependency 'mocha'
32
31
  spec.add_development_dependency 'bourne'
33
32
  spec.add_development_dependency 'webmock'
34
- spec.add_development_dependency 'coveralls'
35
33
  spec.add_development_dependency 'factory_girl'
36
34
  spec.add_development_dependency 'yard'
37
35
  spec.add_development_dependency 'awesome_print'
38
36
  spec.add_development_dependency 'aws-sdk'
39
37
  spec.add_development_dependency 'rdiscount'
38
+ spec.add_development_dependency 'bundler-audit'
40
39
 
41
40
  spec.add_dependency 'activeresource', '~> 4.0.0'
42
- spec.add_dependency 'api-auth', '~> 2.0.0'
41
+ spec.add_dependency 'api-auth', '~> 2.0'
43
42
  spec.add_dependency 'rack'
44
43
  end
@@ -35,8 +35,8 @@ module ESP
35
35
  ESP_OWNER_ID.fetch(ESP.env, "762160981991")
36
36
  end
37
37
 
38
- def trust_policy(external_account_id) # rubocop:disable Metrics/MethodLength
39
- <<TRUST_POLICY
38
+ def trust_policy(external_account_id)
39
+ <<-TRUST_POLICY.gsub(/^\s*/, '')
40
40
  {
41
41
  "Version": "2012-10-17",
42
42
  "Statement": [
@@ -1,6 +1,6 @@
1
1
  require 'optparse'
2
2
 
3
- ARGV.clone.options do |opts|
3
+ ARGV.clone.options do |opts| # rubocop:disable Metrics/BlockLength
4
4
  opts.banner = "Usage: esp add_external_account"
5
5
 
6
6
  opts.separator ""
@@ -8,7 +8,7 @@ module ESP
8
8
  class CommandsTasks
9
9
  attr_reader :argv
10
10
 
11
- HELP_MESSAGE = <<-EOT
11
+ HELP_MESSAGE = <<-EOT.freeze
12
12
  Usage: esp COMMAND [environment] [ARGS]
13
13
 
14
14
  The ESP commands are:
@@ -18,7 +18,7 @@ The ESP commands are:
18
18
  All commands can be run with -h (or --help) for more information.
19
19
  EOT
20
20
 
21
- COMMAND_WHITELIST = %w(console add_external_account version help)
21
+ COMMAND_WHITELIST = %w(console add_external_account version help).freeze
22
22
 
23
23
  def initialize(argv)
24
24
  @argv = argv
@@ -4,9 +4,9 @@ module ActiveResource
4
4
  # @private
5
5
  class ConnectionError
6
6
  def initialize(response)
7
- @response = if response.respond_to?(:response)
8
- message = decoded_errors(response.response.body)
9
- Struct.new(:body, :code, :message).new(response.response.body, response.code, message)
7
+ @response = if response.respond_to?(:body)
8
+ message = decoded_errors(response.body)
9
+ Struct.new(:body, :code, :message).new(response.body, response.code, message)
10
10
  else
11
11
  response
12
12
  end
@@ -15,7 +15,7 @@ module ActiveResource
15
15
  private
16
16
 
17
17
  def decoded_errors(json)
18
- Array((Hash(ActiveSupport::JSON.decode(json)))['errors'].map { |e| e['title'] }).join(" ")
18
+ Array(Hash(ActiveSupport::JSON.decode(json))['errors'].map { |e| e['title'] }).join(" ")
19
19
  rescue
20
20
  []
21
21
  end
@@ -43,9 +43,7 @@ module ActiveResource
43
43
  Formats.remove_root(parse_json_api(ActiveSupport::JSON.decode(json)))
44
44
  end
45
45
 
46
- private
47
-
48
- def self.parse_json_api(elements)
46
+ def parse_json_api(elements)
49
47
  included = elements.delete('included')
50
48
  elements.tap do |e|
51
49
  Array.wrap(e.fetch('data', {})).each do |object|
@@ -53,16 +51,18 @@ module ActiveResource
53
51
  end
54
52
  end
55
53
  end
54
+ private_class_method :parse_json_api
56
55
 
57
- def self.parse_object!(object, included = nil)
56
+ def parse_object!(object, included = nil)
58
57
  return object unless object.respond_to?(:each)
59
58
  merge_attributes!(object)
60
59
  parse_elements(object)
61
60
  parse_relationships!(object, included)
62
61
  object
63
62
  end
63
+ private_class_method :parse_object!
64
64
 
65
- def self.parse_elements(object)
65
+ def parse_elements(object)
66
66
  object.each_value do |value|
67
67
  if value.is_a? Hash
68
68
  parse_object!(value)
@@ -71,20 +71,23 @@ module ActiveResource
71
71
  end
72
72
  end
73
73
  end
74
+ private_class_method :parse_elements
74
75
 
75
- def self.parse_relationships!(object, included)
76
+ def parse_relationships!(object, included)
76
77
  object.fetch('relationships', {}).each do |assoc, details|
77
78
  extract_foreign_keys!(object, assoc, details)
78
79
  merge_included_objects!(object, assoc, details['data'], included)
79
80
  end
80
81
  end
82
+ private_class_method :parse_relationships!
81
83
 
82
- def self.merge_attributes!(object)
84
+ def merge_attributes!(object)
83
85
  return unless object.is_a? Hash
84
86
  object.merge! object.delete('attributes') unless object['attributes'].blank?
85
87
  end
88
+ private_class_method :merge_attributes!
86
89
 
87
- def self.extract_foreign_keys!(object, assoc, assoc_details)
90
+ def extract_foreign_keys!(object, assoc, assoc_details)
88
91
  data = assoc_details['data']
89
92
  related_link = assoc_details.fetch('links', {}).fetch('related', {})
90
93
  if data.present?
@@ -93,16 +96,18 @@ module ActiveResource
93
96
  parse_related_link(object, assoc, related_link)
94
97
  end
95
98
  end
99
+ private_class_method :extract_foreign_keys!
96
100
 
97
- def self.parse_data(object, assoc, data)
101
+ def parse_data(object, assoc, data)
98
102
  if data.is_a? Array
99
103
  object["#{assoc.singularize}_ids"] = data.map { |d| d['id'] }
100
104
  else
101
105
  object["#{assoc}_id"] = data['id']
102
106
  end
103
107
  end
108
+ private_class_method :parse_data
104
109
 
105
- def self.parse_related_link(object, assoc, related_link)
110
+ def parse_related_link(object, assoc, related_link)
106
111
  # parse the url to get the id if the data node is not returned
107
112
  related_link.scan(%r{/(\d+)\.json$}) do |id|
108
113
  object["#{assoc}_id"] = id.first
@@ -111,8 +116,9 @@ module ActiveResource
111
116
  uri = URI.parse(related_link)
112
117
  object["#{assoc.singularize}_ids"] = Rack::Utils.parse_nested_query(CGI.unescape(uri.query)).fetch('filter', {}).fetch('id_in', []) if uri.query.present?
113
118
  end
119
+ private_class_method :parse_related_link
114
120
 
115
- def self.merge_included_objects!(object, assoc, data, included)
121
+ def merge_included_objects!(object, assoc, data, included)
116
122
  return if included.blank?
117
123
  object[assoc] = case data
118
124
  when Array
@@ -121,14 +127,16 @@ module ActiveResource
121
127
  merge_nested_included_objects(object, [data], included).first
122
128
  end
123
129
  end
130
+ private_class_method :merge_included_objects!
124
131
 
125
- def self.merge_nested_included_objects(object, data, included)
132
+ def merge_nested_included_objects(object, data, included)
126
133
  assocs = included.compact.select { |i| data.include?(i.slice('type', 'id')) }
127
134
  # Remove the object from the included array to prevent an infinite loop if one of it's associations relates back to itself.
128
135
  assoc_included = included.dup
129
136
  assoc_included.delete(object)
130
137
  assocs.map { |i| parse_object!(i, assoc_included) }
131
138
  end
139
+ private_class_method :merge_nested_included_objects
132
140
  end
133
141
  end
134
142
  end
@@ -27,7 +27,7 @@ module ActiveResource
27
27
  private
28
28
 
29
29
  def decoded_errors(json)
30
- Array((Hash(ActiveSupport::JSON.decode(json)))['errors'])
30
+ Array(Hash(ActiveSupport::JSON.decode(json))['errors'])
31
31
  rescue
32
32
  []
33
33
  end
@@ -133,12 +133,13 @@ module ESP
133
133
  suppress(Suppression::UniqueIdentifier, reason)
134
134
  end
135
135
 
136
- private
137
-
138
136
  # Overridden because alerts does not use ransack for searching
139
137
  def self.filters(params)
140
138
  { filter: params }
141
139
  end
140
+ private_class_method :filters
141
+
142
+ private
142
143
 
143
144
  def suppress(klass, reason)
144
145
  fail ArgumentError, "You must specify the reason.".freeze unless reason.present?
@@ -11,7 +11,7 @@ module ESP
11
11
  patch(:activate).tap do |response|
12
12
  load_attributes_from_response(response)
13
13
  end
14
- rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess => error
14
+ rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess, ActiveResource::ForbiddenAccess => error
15
15
  load_remote_errors(error, true)
16
16
  self.code = error.response.code
17
17
  false
@@ -22,7 +22,7 @@ module ESP
22
22
  patch(:archive).tap do |response|
23
23
  load_attributes_from_response(response)
24
24
  end
25
- rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess => error
25
+ rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess, ActiveResource::ForbiddenAccess => error
26
26
  load_remote_errors(error, true)
27
27
  self.code = error.response.code
28
28
  false
@@ -16,11 +16,8 @@ module ESP
16
16
  # @return [void]
17
17
  def self.where(attrs)
18
18
  # when calling `recent.next_page` it will come into here
19
- if attrs[:from].to_s.include?('recent')
20
- super
21
- else
22
- fail ESP::NotImplementedError, 'Regular ARELlike methods are disabled. Use the .recent method.'
23
- end
19
+ return super if attrs[:from].to_s.include?('recent')
20
+ fail ESP::NotImplementedError, 'Regular ARELlike methods are disabled. Use the .recent method.'
24
21
  end
25
22
 
26
23
  # Not Implemented. You cannot create or update a Dashboard.
@@ -1,30 +1,30 @@
1
1
  module ESP
2
2
  # @private
3
3
  class Resource < ActiveResource::Base
4
- self.site = ESP.site
5
- self.proxy = ESP.http_proxy
4
+ self.site = ESP.site
5
+ self.proxy = ESP.http_proxy
6
6
  self.format = ActiveResource::Formats::JsonAPIFormat
7
7
  with_api_auth(ESP.access_key_id, ESP.secret_access_key)
8
8
  headers["Content-Type"] = format.mime_type
9
- headers["User-Agent"] = "Ruby SDK #{ESP::VERSION}"
9
+ headers["User-Agent"] = "Ruby SDK #{ESP::VERSION}"
10
10
 
11
11
  self.collection_parser = ActiveResource::PaginatedCollection
12
12
 
13
13
  # List of predicates that can be used for searching
14
- PREDICATES = %w(sorts m eq eq_any eq_all not_eq not_eq_any not_eq_all matches matches_any matches_all does_not_match does_not_match_any does_not_match_all lt lt_any lt_all lteq lteq_any lteq_all gt gt_any gt_all gteq gteq_any gteq_all in in_any in_all not_in not_in_any not_in_all cont cont_any cont_all not_cont not_cont_any not_cont_all start start_any start_all not_start not_start_any not_start_all end end_any end_all not_end not_end_any not_end_all true false present blank null not_null).join('|').freeze
14
+ PREDICATES = %w(sorts m eq eq_any eq_all not_eq not_eq_any not_eq_all matches matches_any matches_all does_not_match does_not_match_any does_not_match_all lt lt_any lt_all lteq lteq_any lteq_all gt gt_any gt_all gteq gteq_any gteq_all in in_any in_all not_in not_in_any not_in_all cont cont_any cont_all not_cont not_cont_any not_cont_all start start_any start_all not_start not_start_any not_start_all end end_any end_all not_end not_end_any not_end_all true false present blank null not_null).join('|').freeze
15
15
 
16
16
  # Pass a json api compliant hash to the api.
17
17
  def serializable_hash(*)
18
- h = attributes.extract!('included')
19
- h['data'] = { 'type' => self.class.to_s.underscore.sub('esp/', '').pluralize,
20
- 'attributes' => changed_attributes.except('id', 'type', 'created_at', 'updated_at', 'relationships') }
18
+ h = attributes.extract!('included')
19
+ h['data'] = { 'type' => self.class.to_s.underscore.sub('esp/', '').pluralize,
20
+ 'attributes' => changed_attributes.except('id', 'type', 'created_at', 'updated_at', 'relationships') }
21
21
  h['data']['id'] = id if id.present?
22
22
  h
23
23
  end
24
24
 
25
25
  def self.where(clauses = {})
26
26
  fail ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash
27
- from = clauses.delete(:from) || "#{prefix}#{name.demodulize.pluralize.underscore}"
27
+ from = clauses.delete(:from) || "#{prefix}#{name.demodulize.pluralize.underscore}"
28
28
  clauses = { params: clauses }.with_indifferent_access
29
29
  arrange_options(clauses)
30
30
  prefix_options, query_options = split_options(clauses)
@@ -34,7 +34,7 @@ module ESP
34
34
  end
35
35
 
36
36
  def self.find(*arguments)
37
- scope = arguments.slice!(0)
37
+ scope = arguments.slice!(0)
38
38
  options = (arguments.slice!(0) || {}).with_indifferent_access
39
39
  arrange_options(options)
40
40
  super(scope, options).tap do |object|
@@ -63,22 +63,21 @@ module ESP
63
63
  return object unless object.is_a? ActiveResource::PaginatedCollection
64
64
  # Need to set from so paginated collection can use it for page calls.
65
65
  object.tap do |collection|
66
- collection.from = options['from']
66
+ collection.from = options['from']
67
67
  collection.original_params = options.fetch('params', {})
68
68
  end
69
69
  end
70
70
 
71
71
  def self.arrange_options(options)
72
72
  if options[:params].present?
73
- page = options[:params][:page] ? { page: options[:params].delete(:page) } : {}
73
+ page = options[:params][:page] ? { page: options[:params].delete(:page) } : {}
74
74
  include = options[:params][:include] ? { include: options[:params].delete(:include) } : {}
75
75
  options[:params].merge!(options[:params].delete(:filter)) if options[:params][:filter]
76
76
  options[:params] = filters(options[:params]).merge!(page).merge!(include)
77
77
  end
78
- if options[:include].present?
79
- options[:params] ||= {}
80
- options[:params].merge!(options.extract!(:include))
81
- end
78
+ return unless options[:include].present?
79
+ options[:params] ||= {}
80
+ options[:params].merge!(options.extract!(:include))
82
81
  end
83
82
  end
84
83
  end
@@ -32,11 +32,8 @@ module ESP
32
32
  # @return [void]
33
33
  def self.where(attrs)
34
34
  # when calling `latest_for_teams.next_page` it will come into here
35
- if attrs[:from].to_s.include?('latest_for_teams')
36
- super
37
- else
38
- fail ESP::NotImplementedError
39
- end
35
+ return super if attrs[:from].to_s.include?('latest_for_teams')
36
+ fail ESP::NotImplementedError
40
37
  end
41
38
 
42
39
  # Not Implemented. You cannot search for a Stat.
@@ -98,7 +98,7 @@ module ESP
98
98
  patch(:deactivate).tap do |response|
99
99
  load_attributes_from_response(response)
100
100
  end
101
- rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess => error
101
+ rescue ActiveResource::BadRequest, ActiveResource::ResourceInvalid, ActiveResource::UnauthorizedAccess, ActiveResource::ForbiddenAccess => error
102
102
  load_remote_errors(error, true)
103
103
  self.code = error.response.code
104
104
  false
@@ -1,3 +1,3 @@
1
1
  module ESP
2
- VERSION = '2.7.0'.freeze
2
+ VERSION = '2.8.0'.freeze
3
3
  end
@@ -53,7 +53,11 @@ module ESP::Integration
53
53
  should 'activate definition' do
54
54
  custom_signature = ESP::CustomSignature.last
55
55
  fail 'Missing custom signature' if custom_signature.blank?
56
- definition = ESP::CustomSignature::Definition.create(custom_signature_id: custom_signature.id)
56
+ definition = custom_signature.definitions.last
57
+
58
+ if definition.blank? || definition.status != 'editable'
59
+ definition = ESP::CustomSignature::Definition.create(custom_signature_id: custom_signature.id)
60
+ end
57
61
 
58
62
  assert_equal 'editable', definition.status
59
63
 
@@ -67,6 +71,16 @@ module ESP::Integration
67
71
  should 'be able to create, update and destroy' do
68
72
  custom_signature = ESP::CustomSignature.last
69
73
  fail 'Missing custom signature' if custom_signature.blank?
74
+ old_definition = custom_signature.definitions.last
75
+
76
+ if old_definition.present? && old_definition.status == 'editable'
77
+ old_definition.destroy
78
+
79
+ assert_raises ActiveResource::ResourceNotFound do
80
+ ESP::CustomSignature::Definition.find(old_definition.id)
81
+ end
82
+ end
83
+
70
84
  definition = ESP::CustomSignature::Definition.new(custom_signature_id: custom_signature.id)
71
85
 
72
86
  assert_predicate definition, :new?
@@ -36,7 +36,9 @@ module ESP::Integration
36
36
 
37
37
  context '#CRUD' do
38
38
  should 'be able to create, update and destroy' do
39
- custom_signature = ESP::CustomSignature.new(@custom_signature.attributes)
39
+ team = ESP::Team.last
40
+ assert_predicate team, :present?
41
+ custom_signature = ESP::CustomSignature.new(@custom_signature.attributes.merge(team_ids: [team.id]))
40
42
 
41
43
  assert_predicate custom_signature, :new?
42
44
 
@@ -6,7 +6,7 @@ module ESP::Integration
6
6
  context 'live calls' do
7
7
  context '#for_result' do
8
8
  should 'return alerts' do
9
- result = ESP::CustomSignature::Result.first
9
+ result = ESP::CustomSignature::Result.first(params: { sorts: 'id' })
10
10
  fail 'Missing result' if result.blank?
11
11
 
12
12
  alerts = ESP::CustomSignature::Result::Alert.for_result(result.id)
@@ -17,7 +17,7 @@ module ESP::Integration
17
17
 
18
18
  context '#custom_signature' do
19
19
  should 'return a custom_signature' do
20
- result = ESP::CustomSignature::Result.first
20
+ result = ESP::CustomSignature::Result.first(params: { sorts: 'id' })
21
21
  fail 'Missing result' if result.blank?
22
22
  alert = ESP::CustomSignature::Result::Alert.for_result(result.id).first
23
23
 
@@ -30,7 +30,7 @@ module ESP::Integration
30
30
 
31
31
  context '#external_account' do
32
32
  should 'return a external_account' do
33
- result = ESP::CustomSignature::Result.first
33
+ result = ESP::CustomSignature::Result.first(params: { sorts: 'id' })
34
34
  fail 'Missing result' if result.blank?
35
35
  alert = ESP::CustomSignature::Result::Alert.for_result(result.id).first
36
36
 
@@ -43,7 +43,7 @@ module ESP::Integration
43
43
 
44
44
  context '#region' do
45
45
  should 'return a region' do
46
- result = ESP::CustomSignature::Result.first
46
+ result = ESP::CustomSignature::Result.first(params: { sorts: 'id' })
47
47
  fail 'Missing result' if result.blank?
48
48
  alert = ESP::CustomSignature::Result::Alert.for_result(result.id).first
49
49