qa_server 7.6.0 → 7.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_fixme.yml +4 -0
  3. data/CHANGELOG.md +25 -0
  4. data/app/assets/stylesheets/qa_server/_check-status.scss +4 -0
  5. data/app/assets/stylesheets/qa_server/_monitor-status.scss +45 -0
  6. data/app/cache_processors/concerns/qa_server/cache_keys.rb +1 -0
  7. data/app/cache_processors/qa_server/scenario_history_cache.rb +19 -1
  8. data/app/controllers/qa_server/monitor_status_controller.rb +36 -6
  9. data/app/models/qa_server/scenario_run_history.rb +12 -3
  10. data/app/presenters/qa_server/check_status_presenter.rb +2 -1
  11. data/app/presenters/qa_server/monitor_status/history_presenter.rb +55 -4
  12. data/app/presenters/qa_server/monitor_status/history_up_down_presenter.rb +58 -0
  13. data/app/presenters/qa_server/monitor_status_presenter.rb +7 -2
  14. data/app/services/qa_server/history_up_down_service.rb +103 -0
  15. data/app/services/qa_server/time_service.rb +6 -0
  16. data/app/views/qa_server/check_status/index.html.erb +8 -6
  17. data/app/views/qa_server/monitor_status/_test_up_down_connection_history.html.erb +30 -0
  18. data/app/views/qa_server/monitor_status/index.html.erb +2 -1
  19. data/config/locales/qa_server.en.yml +7 -7
  20. data/lib/generators/qa_server/templates/config/authorities/linked_data/locvocabs_ld4l_cache.json +1 -1
  21. data/lib/generators/qa_server/templates/config/authorities/linked_data/mesh_nlm_ld4l_cache.json +1 -0
  22. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/cerl_ld4l_cache_validation.yml +2 -2
  23. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/mesh_nlm_ld4l_cache_validation.yml +1 -1
  24. data/lib/generators/qa_server/templates/config/initializers/qa_server.rb +8 -0
  25. data/lib/qa_server/configuration.rb +14 -0
  26. data/lib/qa_server/version.rb +1 -1
  27. data/spec/i18n_spec.rb +0 -1
  28. data/spec/lib/configuration_spec.rb +22 -0
  29. data/spec/presenters/qa_server/monitor_status/history_presenter_spec.rb +81 -0
  30. data/spec/services/qa_server/history_up_down_service_spec.rb +86 -0
  31. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76e06f7b15b4f8041797890dd3e4f7c362d73e73a31808060361cbcf16b4fe46
4
- data.tar.gz: efbd4527118c03ea42c1075a69196fd4aa6696a524d2c675085a280aa0838d5d
3
+ metadata.gz: ff06d85cd1c9fe8ce8b330c850856ebd43ec8859704158b9b845cf10d748a484
4
+ data.tar.gz: e6c72b49415833fba261d26603d2683f4dfa45c11f3ca7b4e238cea5080a6e7d
5
5
  SHA512:
6
- metadata.gz: d22de7e60cfb453837013bc4b0ca6df756bfbc9f180ea9d27f28734a6822e3606dd32f73cd1fd8eb9fd563985ce706c9e18fb4055129fa3c17a6cc11f6955deb
7
- data.tar.gz: c334da18f0a702644149e7228ecfb2407234feb3fe3e663388429f5513e3ca1a3f39ba9f364ce48213b17907b7bb0a27b4dbd53eb21d157047c7b254eeb8bacb
6
+ metadata.gz: 26a67a95946b201218cb764957ab50d050d13e5a051e51e1cec7a2dd83b69c7f2113f94519ae5597a1d35c79677feddd41f33b5fd24e777848c3ebc9b00524e8
7
+ data.tar.gz: 8499863e775be703faac4243a966faf58e66a5098878b1e19f3b95033a752abf7ab824aeab14dc3f7f8cf20a243dbbffd93ca7beccb7dfb09d28e87a263d5d03
data/.rubocop_fixme.yml CHANGED
@@ -15,3 +15,7 @@ Style/SpecialGlobalVars:
15
15
 
16
16
  Layout/AccessModifierIndentation:
17
17
  EnforcedStyle: outdent
18
+
19
+ Layout/HashAlignment:
20
+ Exclude:
21
+ - 'spec/presenters/qa_server/monitor_status/history_presenter_spec.rb'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ### 7.9.1 (2021-06-10)
2
+
3
+ * fix compare accuracy fails if either has pending tests
4
+
5
+ ### 7.9.0 (2021-04-16)
6
+
7
+ * Add chart showing simulated graph (in table) of the last 30 days of up-down connection data
8
+
9
+ ### 7.8.0 (2021-04-14)
10
+
11
+ * add geographic subauth for Mesh-NLM
12
+ * dogear expected and actual cells when accuracy test is pending
13
+
14
+ ### 7.7.1 (2021-04-14)
15
+
16
+ * fix background colors in historical uptime table
17
+
18
+ ### 7.7.0 (2021-04-13)
19
+
20
+ * remove unused translations
21
+ * show notification when refreshing starts on monitor status page
22
+ * hide data in Authority Connection History for non-active authorities
23
+ * loosen threshold for caution in historical uptime table
24
+ * minor tweaks missed in original sync
25
+
1
26
  ### 7.6.0 (2021-04-12)
2
27
 
3
28
  * update authority configs and test scenarios to use the new cache indexing with results stored as blobs in the index
@@ -88,6 +88,10 @@ td.bold-left-border {
88
88
  border-left: 2px solid black;
89
89
  }
90
90
 
91
+ .status-dogear {
92
+ background: linear-gradient(135deg, #333 0%, #333 10%, transparent 10%, transparent 100%);
93
+ }
94
+
91
95
  .status-good {
92
96
  text-align: center;
93
97
  background-color: #ccffcc;
@@ -53,3 +53,48 @@ div#performance-by-the-day {
53
53
  div#performance-by-the-month {
54
54
  display: none;
55
55
  }
56
+
57
+ table.up-down-history {
58
+ border: none;
59
+ }
60
+
61
+ td.up-down-history {
62
+ font-size: .8em;
63
+ font-style: italic;
64
+ border: none;
65
+ }
66
+ th.up-down-history {
67
+ width: 20px;
68
+ border: none;
69
+ }
70
+
71
+ td.connection-up-down {
72
+ border-right: 8px white solid;
73
+ border-left: 8px white solid;
74
+ border-top: none;
75
+ border-bottom: 3px white solid;
76
+ }
77
+
78
+ td.connection-no-data {
79
+ background-color: white;
80
+ }
81
+
82
+ td.connection-fully-up {
83
+ background-color: #19AE19;
84
+ }
85
+
86
+ td.connection-mostly-up {
87
+ background-color: #19AEA7;
88
+ }
89
+
90
+ td.connection-timeouts {
91
+ background-color: #EDF908;
92
+ }
93
+
94
+ td.connection-barely-up {
95
+ background-color: #AE7619;
96
+ }
97
+
98
+ td.connection-down {
99
+ background-color: #CE0303;
100
+ }
@@ -5,6 +5,7 @@ module QaServer
5
5
  SCENARIO_RUN_SUMMARY_DATA_CACHE_KEY = "QaServer--CacheKeys--scenario_run_summary_data"
6
6
  SCENARIO_RUN_FAILURE_DATA_CACHE_KEY = "QaServer--CacheKeys--scenario_run_failure_data"
7
7
  SCENARIO_RUN_HISTORY_DATA_CACHE_KEY = "QaServer--CacheKeys--scenario_run_history_data"
8
+ SCENARIO_RUN_HISTORY_UP_DOWN_DATA_CACHE_KEY = "QaServer--CacheKeys--history_up_down_data"
8
9
 
9
10
  PERFORMANCE_DATATABLE_DATA_CACHE_KEY = "QaServer--Cache--performance_datatable_data"
10
11
  end
@@ -2,8 +2,9 @@
2
2
  # Maintain a cache of data for Authority Connection History table displayed on Monitor Status page
3
3
  module QaServer
4
4
  class ScenarioHistoryCache
5
- class_attribute :scenario_history_class
5
+ class_attribute :scenario_history_class, :scenario_up_down_class
6
6
  self.scenario_history_class = QaServer::ScenarioRunHistory
7
+ self.scenario_up_down_class = QaServer::HistoryUpDownService
7
8
 
8
9
  class << self
9
10
  include QaServer::CacheKeys
@@ -21,12 +22,29 @@ module QaServer
21
22
  end
22
23
  end
23
24
 
25
+ # Get a status for each of the last 30 days for queries that succeeded or failed.
26
+ # @param force [Boolean] if true, run the tests even if the cache hasn't expired; otherwise, use cache if not expired
27
+ # @returns [Hash<Array>] status for the last 30 days for each authority
28
+ # @example { auth_name => [:fully_up, :fully_up, :down, :mostly_up, ... ], ... }
29
+ # { 'agrovoc' => [ :fully_up, :fully_up, :down, :mostly_up, ...],
30
+ # 'geonames_ld4l_cache' => [ :fully_up, :mostly_up, :down, :fully_up, :timeouts, ...] }
31
+ def historical_up_down_data(force: false)
32
+ Rails.cache.fetch(cache_key_for_historical_up_down_data, expires_in: next_expiry, race_condition_ttl: 30.seconds, force: force) do
33
+ QaServer.config.monitor_logger.debug("(QaServer::ScenarioHistoryCache) - CALCULATING UP-DOWN STATUS HISTORY of scenario runs (force: #{force})")
34
+ scenario_up_down_class.new.last_30_days
35
+ end
36
+ end
37
+
24
38
  private
25
39
 
26
40
  def cache_key_for_historical_data
27
41
  SCENARIO_RUN_HISTORY_DATA_CACHE_KEY
28
42
  end
29
43
 
44
+ def cache_key_for_historical_up_down_data
45
+ SCENARIO_RUN_HISTORY_UP_DOWN_DATA_CACHE_KEY
46
+ end
47
+
30
48
  def next_expiry
31
49
  QaServer::CacheExpiryService.cache_expiry
32
50
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  # Controller for Monitor Status header menu item
3
3
  module QaServer
4
- class MonitorStatusController < ApplicationController
4
+ class MonitorStatusController < ApplicationController # rubocop:disable Metrics/ClassLength
5
5
  layout 'qa_server'
6
6
 
7
7
  include QaServer::AuthorityValidationBehavior
@@ -19,6 +19,7 @@ module QaServer
19
19
  @presenter = presenter_class.new(current_summary: latest_summary,
20
20
  current_failure_data: latest_failures,
21
21
  historical_summary_data: historical_data,
22
+ historical_up_down_data: historical_up_down_data,
22
23
  performance_data: performance_table_data)
23
24
  QaServer.config.monitor_logger.debug("~~~~~~~~ DONE rendering monitor status")
24
25
  render 'index', status: :internal_server_error if latest_summary&.failing_authority_count&.positive?
@@ -59,6 +60,13 @@ module QaServer
59
60
  @historical_data ||= QaServer::ScenarioHistoryCache.historical_summary(force: refresh_history?)
60
61
  end
61
62
 
63
+ # Get a summary level of historical data
64
+ # @returns [Array<Hash>] summary of passing/failing tests for each authority
65
+ # @see QaServer::ScenarioRunHistory#historical_summary for structure of output
66
+ def historical_up_down_data
67
+ @historical_up_down_data ||= QaServer::ScenarioHistoryCache.historical_up_down_data(force: refresh_history?)
68
+ end
69
+
62
70
  def update_historical_graph
63
71
  return unless QaServer.config.display_historical_graph?
64
72
  QaServer::ScenarioHistoryGraphCache.generate_graph(data: historical_data, force: refresh_history?)
@@ -77,7 +85,8 @@ module QaServer
77
85
  end
78
86
 
79
87
  def refresh?
80
- params.key?(:refresh) && validate_auth_reload_token("refresh status")
88
+ return @refresh unless @refresh.nil?
89
+ @refresh ||= params.key?(:refresh) && validate_auth_reload_token("refresh status")
81
90
  end
82
91
 
83
92
  def refresh_all?
@@ -86,15 +95,36 @@ module QaServer
86
95
  end
87
96
 
88
97
  def refresh_tests?
89
- refresh? ? (refresh_all? || params[:refresh].casecmp?('tests')) : false
98
+ return @refresh_tests unless @refresh_tests.nil?
99
+ @refresh_tests = refresh? && (refresh_all? || params[:refresh].casecmp?('tests'))
100
+ if @refresh_tests
101
+ msg = I18n.t('qa_server.monitor_status.refreshing_tests')
102
+ logger.info msg
103
+ flash.now[:success] = msg
104
+ end
105
+ @refresh_tests
90
106
  end
91
107
 
92
108
  def refresh_history?
93
- refresh? ? (refresh_all? || params[:refresh].casecmp?('history')) : false
109
+ return @refresh_history unless @refresh_history.nil?
110
+ @refresh_history = refresh? && (refresh_all? || params[:refresh].casecmp?('history'))
111
+ if @refresh_history
112
+ msg = I18n.t('qa_server.monitor_status.refreshing_history')
113
+ logger.info msg
114
+ flash.now[:success] = msg
115
+ end
116
+ @refresh_history
94
117
  end
95
118
 
96
119
  def refresh_performance?
97
- refresh? ? (refresh_all? || params[:refresh].casecmp?('performance')) : false
120
+ return @refresh_performance unless @refresh_performance.nil?
121
+ @refresh_performance = refresh? && (refresh_all? || params[:refresh].casecmp?('performance'))
122
+ if @refresh_performance
123
+ msg = I18n.t('qa_server.monitor_status.refreshing_performance')
124
+ logger.info msg
125
+ flash.now[:success] = msg
126
+ end
127
+ @refresh_performance
98
128
  end
99
129
 
100
130
  def refresh_performance_table?
@@ -117,7 +147,7 @@ module QaServer
117
147
  token = params.key?(:auth_token) ? params[:auth_token] : nil
118
148
  valid = Qa.config.valid_authority_reload_token?(token)
119
149
  return true if valid
120
- msg = "Permission denied. Unable to #{action}."
150
+ msg = I18n.t('qa_server.monitor_status.permission_denied', action: action)
121
151
  logger.warn msg
122
152
  flash.now[:error] = msg
123
153
  false
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  # Provide access to the scenario_run_history database table which tracks scenario runs over time.
3
3
  module QaServer
4
- class ScenarioRunHistory < ApplicationRecord
4
+ class ScenarioRunHistory < ApplicationRecord # rubocop:disable Metrics/ClassLength
5
5
  self.table_name = 'scenario_run_history'
6
6
  belongs_to :scenario_run_registry
7
7
  enum scenario_type: { connection: 0, accuracy: 1, performance: 2 }, _suffix: :type
@@ -11,9 +11,9 @@ module QaServer
11
11
  BAD_MARKER = 'X'
12
12
  UNKNOWN_MARKER = '?'
13
13
 
14
- class_attribute :summary_class
15
-
14
+ class_attribute :summary_class, :authority_lister_class
16
15
  self.summary_class = QaServer::ScenarioRunSummary
16
+ self.authority_lister_class = QaServer::AuthorityListerService
17
17
 
18
18
  class << self
19
19
  # Save a scenario result
@@ -92,12 +92,21 @@ module QaServer
92
92
  days_unknown = count_days(:unknown)
93
93
  keys = (days_good.keys + days_bad.keys + days_unknown.keys).uniq.sort
94
94
  keys.each_with_object({}) do |auth, hash|
95
+ next unless active_authority? auth
95
96
  hash[auth] = { good: day_count(auth, days_good), bad: day_count(auth, days_bad) + day_count(auth, days_unknown) }
96
97
  end
97
98
  end
98
99
 
99
100
  private
100
101
 
102
+ def active_authority?(auth)
103
+ active_authorities.include? auth.to_sym
104
+ end
105
+
106
+ def active_authorities
107
+ @active_authorities = authority_lister_class.authorities_list
108
+ end
109
+
101
110
  def day_count(auth, days)
102
111
  days&.key?(auth) ? days[auth] : 0
103
112
  end
@@ -81,7 +81,8 @@ module QaServer
81
81
 
82
82
  # @return [String] the name of the css style class to use for the status cell based on the status of the scenario test.
83
83
  def status_style_class(status)
84
- "status-#{status}"
84
+ return "status-#{status}" if status.is_a? Symbol
85
+ status[:pending] ? "status-dogear status-#{status[:status]}" : "status-#{status[:status]}"
85
86
  end
86
87
 
87
88
  # @return [String] the name of the css style class to use for the status cell based on the status of the scenario test.
@@ -2,8 +2,16 @@
2
2
  # This presenter class provides historical testing data needed by the view that monitors status of authorities.
3
3
  module QaServer::MonitorStatus
4
4
  class HistoryPresenter
5
+ CAUTION_THRESHOLD = 0.05
6
+ WARNING_THRESHOLD = 0.1
7
+
5
8
  # @param parent [QaServer::MonitorStatusPresenter] parent presenter
6
9
  # @param historical_summary_data [Array<Hash>] summary of past failuring runs per authority to drive chart
10
+ # @example historical_summary_data
11
+ # {
12
+ # "AGROVOC_DIRECT"=>{:good=>4, :bad=>0},
13
+ # "AGROVOC_LD4L_CACHE"=>{:good=>4, :bad=>0}
14
+ # }
7
15
  def initialize(parent:, historical_summary_data:)
8
16
  @parent = parent
9
17
  @historical_summary_data = historical_summary_data
@@ -11,8 +19,10 @@ module QaServer::MonitorStatus
11
19
 
12
20
  # @return [Array<Hash>] historical test data to be displayed (authname, failing, passing)
13
21
  # @example
14
- # [ [ 'agrovoc', 0, 24 ],
15
- # [ 'geonames_ld4l_cache', 2, 22 ] ... ]
22
+ # {
23
+ # "AGROVOC_DIRECT"=>{:good=>4, :bad=>0},
24
+ # "AGROVOC_LD4L_CACHE"=>{:good=>4, :bad=>0}
25
+ # }
16
26
  def historical_summary
17
27
  @historical_summary_data
18
28
  end
@@ -69,47 +79,88 @@ module QaServer::MonitorStatus
69
79
  end
70
80
  end
71
81
 
82
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
83
+ # @return [String] name of the authority (e.g. 'AUTH_NAME')
84
+ # @example historical_entry
85
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
72
86
  def historical_data_authority_name(historical_entry)
73
87
  historical_entry[0]
74
88
  end
75
89
 
90
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
91
+ # @return [Integer] number of days with passing tests (e.g. 949)
92
+ # @example historical_entry
93
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
76
94
  def days_authority_passing(historical_entry)
77
95
  historical_entry[1][:good]
78
96
  end
79
97
 
98
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
99
+ # @return [Integer] number of days with failing tests (e.g. 51)
100
+ # @example historical_entry
101
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
80
102
  def days_authority_failing(historical_entry)
81
103
  historical_entry[1][:bad]
82
104
  end
83
105
 
106
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
107
+ # @return [Integer] number of days tested (e.g. 1000)
108
+ # @example historical_entry
109
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
84
110
  def days_authority_tested(historical_entry)
85
111
  days_authority_passing(historical_entry) + days_authority_failing(historical_entry)
86
112
  end
87
113
 
114
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
115
+ # @return [Float] percent of failing to passing tests (e.g. 0.05374 )
116
+ # @example historical_entry
117
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
88
118
  def percent_authority_failing(historical_entry)
89
119
  days_authority_failing(historical_entry).to_f / days_authority_tested(historical_entry)
90
120
  end
91
121
 
122
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
123
+ # @return [String] percent of failing to passing tests (e.g. '5.4%')
124
+ # @example historical_entry
125
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
92
126
  def percent_authority_failing_str(historical_entry)
93
127
  ActiveSupport::NumberHelper.number_to_percentage(percent_authority_failing(historical_entry) * 100, precision: 1)
94
128
  end
95
129
 
130
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
131
+ # @return [String] css class for background in Days Failing and Percent Failing columns (e.g. 'status-neutral', 'status-unknown', 'status-bad')
132
+ # @example historical_entry
133
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
96
134
  def failure_style_class(historical_entry)
97
- return "status-neutral" if days_authority_failing(historical_entry) <= 0
98
- percent_authority_failing(historical_entry) < 0.10 ? "status-unknown" : "status-bad"
135
+ case percent_authority_failing(historical_entry)
136
+ when 0.0...CAUTION_THRESHOLD
137
+ "status-neutral"
138
+ when CAUTION_THRESHOLD...WARNING_THRESHOLD
139
+ "status-unknown"
140
+ else
141
+ "status-bad"
142
+ end
99
143
  end
100
144
 
145
+ # @param historical_entry [Array<String,Hash>] data for a single authority including name, # passing tests (good), # failing tests (bad)
146
+ # @return [String] css class for background in Days Passing column (e.g. 'status-good', 'status-bad')
147
+ # @example historical_entry
148
+ # [ 'AUTH_NAME', { good: 949, bad: 51 } ]
101
149
  def passing_style_class(historical_entry)
102
150
  days_authority_passing(historical_entry) <= 0 ? "status-bad" : "status-good"
103
151
  end
104
152
 
153
+ # @return [Boolean] true if historical section should be visible; otherwise false
105
154
  def display_history_details?
106
155
  display_historical_graph? || display_historical_datatable?
107
156
  end
108
157
 
158
+ # @return [Boolean] true if historical graph should be visible; otherwise false
109
159
  def display_historical_graph?
110
160
  QaServer.config.display_historical_graph? && QaServer::HistoryGraphingService.history_graph_image_exists?
111
161
  end
112
162
 
163
+ # @return [Boolean] true if historical datatable should be visible; otherwise false
113
164
  def display_historical_datatable?
114
165
  QaServer.config.display_historical_datatable?
115
166
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+ # This presenter class provides historical testing data needed by the view that monitors status of authorities.
3
+ module QaServer::MonitorStatus
4
+ class HistoryUpDownPresenter
5
+ attr_reader :historical_up_down_data
6
+
7
+ # @param parent [QaServer::MonitorStatusPresenter] parent presenter
8
+ # @param historical_up_down_data [Hash<Array>] recent connection status of queries (typically last 30 days)
9
+ # @example historical_up_down_data
10
+ # { 'AGROVOC' = [
11
+ # :FULLY_UP, # 0 - today
12
+ # :MOSTLY_UP, # 1 - yesterday
13
+ # :MOSTLY_UP, # 2 - two days ago
14
+ # :FULLY_UP, # 3 - three days ago
15
+ # :DOWN, # 4 - four days ago
16
+ # ... # etc.
17
+ # ],
18
+ # 'CERL' = [ ... ]
19
+ # }
20
+ def initialize(parent:, historical_up_down_data:)
21
+ @parent = parent
22
+ @historical_up_down_data = historical_up_down_data
23
+ end
24
+
25
+ # Return the last date of data represented in the history graph and data table
26
+ # @return [ActiveSupport::TimeWithZone] date time stamp
27
+ def up_down_start
28
+ QaServer::TimeService.pretty_date(up_down_end_dt - 29.days)
29
+ end
30
+
31
+ def up_down_end
32
+ QaServer::TimeService.pretty_date(up_down_end_dt)
33
+ end
34
+
35
+ def up_down_end_dt
36
+ @parent.last_updated_dt
37
+ end
38
+
39
+ # @param status [Symbol] :fully_up, :mostly_up, :timeouts, :barely_up, :down
40
+ # @param day [Integer] retrieve the status for this day
41
+ # @return [String] name of the css class for the status
42
+ def historical_up_down_status_class(status, day) # rubocop:disable Metrics/CyclomaticComplexity
43
+ case status[day]
44
+ when :no_date then 'connection-no-date'
45
+ when :fully_up then 'connection-fully-up'
46
+ when :mostly_up then 'connection-mostly-up'
47
+ when :timeouts then 'connection-timeouts'
48
+ when :barely_up then 'connection-barely-up'
49
+ when :down then 'connection-down'
50
+ end
51
+ end
52
+
53
+ # @return [Boolean] true if historical datatable should be visible; otherwise false
54
+ def display_historical_up_down?
55
+ QaServer.config.display_historical_datatable? && @historical_up_down_data.present?
56
+ end
57
+ end
58
+ end
@@ -5,12 +5,14 @@ module QaServer
5
5
  extend Forwardable
6
6
 
7
7
  # @param current_summary [ScenarioRunSummary] summary status of the latest run of test scenarios
8
- # @param current_data [Array<Hash>] current set of failures for the latest test run, if any
8
+ # @param current_failure_data [Array<Hash>] current set of failures for the latest test run, if any
9
9
  # @param historical_summary_data [Array<Hash>] summary of past failuring runs per authority to drive chart
10
+ # @param historical_up_down_data [Hash<Array>] status of queries for the last 30 days
10
11
  # @param performance_data [Hash<Hash>] performance datatable data
11
- def initialize(current_summary:, current_failure_data:, historical_summary_data:, performance_data:)
12
+ def initialize(current_summary:, current_failure_data:, historical_summary_data:, historical_up_down_data:, performance_data:)
12
13
  @current_status_presenter = QaServer::MonitorStatus::CurrentStatusPresenter.new(parent: self, current_summary: current_summary, current_failure_data: current_failure_data)
13
14
  @history_presenter = QaServer::MonitorStatus::HistoryPresenter.new(parent: self, historical_summary_data: historical_summary_data)
15
+ @history_up_down_presenter = QaServer::MonitorStatus::HistoryUpDownPresenter.new(parent: self, historical_up_down_data: historical_up_down_data)
14
16
  @performance_presenter = QaServer::MonitorStatus::PerformancePresenter.new(parent: self, performance_data: performance_data)
15
17
  end
16
18
 
@@ -23,6 +25,9 @@ module QaServer
23
25
  :percent_authority_failing, :percent_authority_failing_str, :failure_style_class, :passing_style_class,
24
26
  :display_history_details?, :display_historical_graph?, :display_historical_datatable?, :history_start, :history_end
25
27
 
28
+ def_delegators :@history_up_down_presenter, :historical_up_down_data, :display_historical_up_down?, :historical_up_down_status_class,
29
+ :up_down_start, :up_down_end
30
+
26
31
  def_delegators :@performance_presenter, :performance_data, :performance_data?, :display_performance?, :display_performance_graph?,
27
32
  :display_performance_datatable?, :performance_data_authority_name, :performance_for_day_graph, :performance_for_month_graph,
28
33
  :performance_for_year_graph, :datatable_search_stats, :datatable_fetch_stats, :datatable_all_actions_stats,
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+ # This class determines the state (e.g. fully_up, mostly_up, barely_up, down)of an authority during the last 30 days.
3
+ module QaServer
4
+ class HistoryUpDownService
5
+ NO_DATA = :no_data
6
+ FULLY_UP = :fully_up
7
+ MOSTLY_UP = :mostly_up
8
+ EXCESSIVE_TIMEOUTS = :timeouts
9
+ BARELY_UP = :barely_up
10
+ DOWN = :down
11
+
12
+ MOSTLY_UP_THRESHOLD = QaServer.config.up_down_data_mostly_up_threshold
13
+ TIMEOUT_THRESHOLD = QaServer.config.up_down_data_timeouts_max_threshold
14
+
15
+ class_attribute :authority_lister, :scenario_history_class, :time_service
16
+ self.authority_lister = QaServer::AuthorityListerService
17
+ self.scenario_history_class = QaServer::ScenarioRunHistory
18
+ self.time_service = QaServer::TimeService
19
+
20
+ def last_30_days
21
+ data = {}
22
+ authorities_list.each { |authority| data[authority] = last_30_days_for(authority.to_s) }
23
+ data
24
+ end
25
+
26
+ private
27
+
28
+ # @returns [Hash <Array<Hash>>] data for an authority for each of the last 30 days
29
+ # @example
30
+ # { 'AGROVOC' = [
31
+ # :FULLY_UP, # 0 - today
32
+ # :MOSTLY_UP, # 1 - yesterday
33
+ # :MOSTLY_UP, # 2 - two days ago
34
+ # :FULLY_UP, # 3 - three days ago
35
+ # :DOWN, # 4 - four days ago
36
+ # ... # etc.
37
+ # ]
38
+ # }
39
+ def last_30_days_for(authority)
40
+ auth_data = []
41
+ 0.upto(29) { |offset| auth_data[offset] = day_status(authority, offset) }
42
+ auth_data
43
+ end
44
+
45
+ # @returns [Symbol] status for a given day for an authority
46
+ def day_status(authority, offset)
47
+ day = offset_day(offset)
48
+ good_count = count_good(authority, day)
49
+ unknown_count = count_unknown(authority, day)
50
+ bad_count = count_bad(authority, day)
51
+ timeout_count = count_timeouts(authority, day)
52
+ status_determination(good_count, unknown_count, bad_count, timeout_count)
53
+ end
54
+
55
+ def status_determination(good_count, unknown_count, bad_count, timeout_count) # rubocop:disable Metrics/CyclomaticComplexity
56
+ total_count = good_count + unknown_count + bad_count
57
+ return NO_DATA if total_count.zero?
58
+ return FULLY_UP if good_count == total_count
59
+ return DOWN if bad_count == total_count
60
+ return BARELY_UP if unknown_count == total_count
61
+ return EXCESSIVE_TIMEOUTS if (timeout_count.to_f / total_count) > TIMEOUT_THRESHOLD
62
+ return MOSTLY_UP if (bad_count.to_f / total_count) < (1 - MOSTLY_UP_THRESHOLD)
63
+ BARELY_UP
64
+ end
65
+
66
+ def authorities_list
67
+ @authorities_list ||= authority_lister.authorities_list
68
+ end
69
+
70
+ def offset_day(offset)
71
+ @today ||= time_service.current_time
72
+ time_service.pretty_query_date(@today - offset.days)
73
+ end
74
+
75
+ def count_good(authority, day)
76
+ scenario_history_class.where(authority_name: authority)
77
+ .where(date: day)
78
+ .where(status: :good)
79
+ .count(:id)
80
+ end
81
+
82
+ def count_unknown(authority, day)
83
+ scenario_history_class.where(authority_name: authority)
84
+ .where(date: day)
85
+ .where(status: :unknown)
86
+ .count(:id)
87
+ end
88
+
89
+ def count_bad(authority, day)
90
+ scenario_history_class.where(authority_name: authority)
91
+ .where(date: day)
92
+ .where(status: :bad)
93
+ .count(:id)
94
+ end
95
+
96
+ def count_timeouts(authority, day)
97
+ scenario_history_class.where(authority_name: authority)
98
+ .where(date: day)
99
+ .where('err_message LIKE ?', "%timeout%")
100
+ .count(:id)
101
+ end
102
+ end
103
+ end
@@ -24,6 +24,12 @@ module QaServer
24
24
  def pretty_date(dt)
25
25
  dt.in_time_zone(QaServer.config.preferred_time_zone_name).strftime("%m/%d/%Y")
26
26
  end
27
+
28
+ # @param dt [ActiveSupport::TimeWithZone] date time stamp
29
+ # @return [String] string version of date formatted with just date (e.g. "2020-02-01")
30
+ def pretty_query_date(dt)
31
+ dt.in_time_zone(QaServer.config.preferred_time_zone_name).strftime("%Y-%m-%d")
32
+ end
27
33
  end
28
34
  end
29
35
  end
@@ -44,8 +44,6 @@
44
44
  <select name="authority" id="authority" class="string optional form-control form-control" value="" aria-labelledby="authority" onchange="hide_data()">
45
45
  <option value=""><%= t('qa_server.check_status.select_authority') %></option>
46
46
  <option disabled>──────────</option>
47
- <option value="<%= @presenter.value_all_collections %>"><%= t('qa_server.check_status.show_all') %></option>
48
- <option disabled>──────────</option>
49
47
  <% @authorities_list.each do |auth_name| %>
50
48
  <option value="<%= auth_name %>"<%= " selected" if auth_name == selected_authority %>><%= auth_name.upcase %></option>
51
49
  <% end %>
@@ -77,7 +75,10 @@
77
75
  <% end %>
78
76
 
79
77
 
80
- <div id="status-loading-message" class="wait-message"><%= t('qa_server.check_status.wait_message') %></div>
78
+ <div id="status-loading-message" class="wait-message">
79
+ <%= t('qa_server.check_status.wait_message_ln1') %><br>
80
+ <%= t('qa_server.check_status.wait_message_ln2') %>
81
+ </div>
81
82
 
82
83
  <% if @presenter.connection_status_data? %>
83
84
  <div id="connection-status-section" class="status-section">
@@ -100,7 +101,7 @@
100
101
  </tr>
101
102
  <% end %>
102
103
  <tr>
103
- <td class="<%= @presenter.status_style_class(status[:status]) %>"><%= @presenter.status_label(status[:status]) %></td>
104
+ <td class="<%= @presenter.status_style_class(status) %>"><%= @presenter.status_label(status[:status]) %></td>
104
105
  <td><%= status[:subauthority_name] %></td>
105
106
  <td><%= status[:service] %></td>
106
107
  <td><%= status[:action] %></td>
@@ -137,8 +138,8 @@
137
138
  </tr>
138
139
  <% end %>
139
140
  <tr>
140
- <td class="position <%= @presenter.status_style_class(status[:status]) %>"><%= status[:expected] %></td>
141
- <td class="position <%= @presenter.status_style_class(status[:status]) %>"><%= status[:actual] %></td>
141
+ <td class="position <%= @presenter.status_style_class(status) %>"><%= status[:expected] %></td>
142
+ <td class="position <%= @presenter.status_style_class(status) %>"><%= status[:actual] %></td>
142
143
  <td><%= status[:request_data] %></td>
143
144
  <td><a href="<%= status[:target] %>"><%= status[:target] %></a></td>
144
145
  <td><%= status[:authority_name] %></td>
@@ -150,6 +151,7 @@
150
151
  </tr>
151
152
  <% end %>
152
153
  </table>
154
+ <p><i>NOTE: Dogear means that the test is a known failure marked as pending.</i></p>
153
155
  </div>
154
156
  <% end %>
155
157
 
@@ -0,0 +1,30 @@
1
+ <% if @presenter.history? && @presenter.display_history_details?%>
2
+ <div id="availability-history" class="status-section">
3
+ <h3><%= t('qa_server.monitor_status.history.title') %></h3>
4
+ <% if @presenter.display_historical_graph? %>
5
+ <p class="status-update-dtstamp"><%= t('qa_server.monitor_status.history.range', from: @presenter.history_start, to: @presenter.history_end) %></p>
6
+ <%= image_tag(@presenter.historical_graph, alt: 'History Graph Unavailable') %>
7
+ <% end %>
8
+
9
+ <% if @presenter.display_historical_up_down? %>
10
+ <p class="status-update-dtstamp"><%= t('qa_server.monitor_status.history.range', from: @presenter.up_down_start, to: @presenter.up_down_end) %></p>
11
+ <table class="up-down-history">
12
+ <tr>
13
+ <th class="up-down-history"><%= t('qa_server.monitor_status.history.authority') %></th>
14
+ <% 0.upto(29) do %>
15
+ <th class='up-down-history'></th>
16
+ <% end %>
17
+ <td class='up-down-history'>Most Recent</td>
18
+ </tr>
19
+ <% @presenter.historical_up_down_data.each do |authority_name, status| %>
20
+ <tr>
21
+ <td class="connection-up-down"><%= authority_name %></td>
22
+ <% 29.downto(0) do |day| %>
23
+ <td class="connection-up-down <%= @presenter.historical_up_down_status_class(status, day) %>"></td>
24
+ <% end %>
25
+ </tr>
26
+ <% end %>
27
+ </table>
28
+ <% end %>
29
+ </div>
30
+ <% end %>
@@ -5,7 +5,8 @@
5
5
  <h2><%= t('qa_server.monitor_status.title') %></h2>
6
6
 
7
7
  <%= render 'test_summary' %>
8
- <%= render 'test_history' %>
8
+ <%= render 'test_up_down_connection_history' %>
9
+ <% # = render 'test_history' %>
9
10
  <%= render 'performance' %>
10
11
 
11
12
  </div>
@@ -25,12 +25,12 @@ en:
25
25
  check_status:
26
26
  title: Check Status
27
27
  select_authority: Select authority...
28
- show_all: ALL Authorities (SLOW)
29
28
  connections: Check Connection Status only
30
29
  accuracy: Check Accuracy only
31
30
  comparison: Compare Accuracy
32
31
  all_checks: Run all checks
33
- wait_message: "Please wait while the status is verified. This will be slow if you selected ALL Authorities."
32
+ wait_message_ln1: "Please wait while the status is verified."
33
+ wait_message_ln2: "This may be slow for large authorities or ones with a lot of tests."
34
34
  connection_checks: Connection Checks
35
35
  accuracy_checks: Accuracy Checks for Search Results
36
36
  comparison_checks: Comparison of Accuracy Checks
@@ -58,7 +58,11 @@ en:
58
58
  wait_message: Fetching term...
59
59
  term_results: Results
60
60
  monitor_status:
61
- title: Monitor Status
61
+ title: Monitor Status
62
+ permission_denied: 'Permission denied. Unable to %{action}.'
63
+ refreshing_tests: Refreshing tests.
64
+ refreshing_history: Refreshing connection history.
65
+ refreshing_performance: Refreshing performance metrics.
62
66
  summary:
63
67
  title: Latest Monitored Status
64
68
  summary_table: Summary
@@ -72,7 +76,6 @@ en:
72
76
  failures:
73
77
  title: Failures During Latest Status Update
74
78
  status: Status
75
- authority: Authority
76
79
  subauthority: Subauthority
77
80
  service: Service
78
81
  action: Action
@@ -80,7 +83,6 @@ en:
80
83
  errmsg: Errors
81
84
  history:
82
85
  title: Authority Connection History
83
- since: 'Since %{date} ET'
84
86
  range: 'From %{from} to %{to}'
85
87
  authority: Authority
86
88
  days_failing: Days Failing
@@ -101,7 +103,6 @@ en:
101
103
  graph_load_time_ms: Graph Load Time (ms)
102
104
  normalization_time_ms: Normalization Time (ms)
103
105
  retrieve_time_ms: Retrieve Time (ms)
104
- full_request_time_ms: Full Request Time (ms)
105
106
  x_axis_hour: Hour
106
107
  x_axis_day: Day
107
108
  x_axis_month: Month
@@ -114,7 +115,6 @@ en:
114
115
  datatable_month_desc: 'Month'
115
116
  datatable_year_desc: 'Year'
116
117
  datarange: 'From %{from} to %{to}'
117
- authority: Authority
118
118
  average_times: Average (ms)
119
119
  fastest_times: 10th percentile (ms)
120
120
  slowest_times: 90th percentile (ms)
@@ -12,7 +12,7 @@
12
12
  "url": {
13
13
  "@context": "http://www.w3.org/ns/hydra/context.jsonld",
14
14
  "@type": "IriTemplate",
15
- "template": "http://wintermute.info-science.uiowa.edu:8081/ld4l_services/loc_vocab_batch.jsp?{?query}&{?entity}&{?maxRecords}&{?startRecord}&{?lang}",
15
+ "template": "http://services.ld4l.org/ld4l_services/loc_vocab_batch.jsp?{?query}&{?entity}&{?maxRecords}&{?startRecord}&{?lang}",
16
16
  "variableRepresentation": "BasicRepresentation",
17
17
  "mapping": [
18
18
  {
@@ -206,6 +206,7 @@
206
206
  ]
207
207
  },
208
208
  "subauthorities": {
209
+ "geographic": "Geographic",
209
210
  "subject": "Subject",
210
211
  "publication_type": "FormOfWork"
211
212
  }
@@ -64,10 +64,10 @@ search:
64
64
  -
65
65
  query: Plantin
66
66
  subauth: imprint
67
- position: 18
67
+ position: 1
68
68
  subject_uri: "http://thesaurus.cerl.org/record/cni00007649"
69
69
  replacements:
70
- maxRecords: '30'
70
+ maxRecords: '8'
71
71
  term:
72
72
  -
73
73
  identifier: "http://thesaurus.cerl.org/record/cnp00895966"
@@ -45,7 +45,7 @@ search:
45
45
  -
46
46
  pending: true
47
47
  query: Spain
48
- subauth: subject
48
+ subauth: geographic
49
49
  subject_uri: "http://id.nlm.nih.gov/mesh/D013030"
50
50
  position: 3
51
51
  replacements:
@@ -24,6 +24,14 @@ QaServer.config do |config|
24
24
  # @param [Symbol] time period for calculating historical pass/fail (i.e., one of :month, :year, or :all)
25
25
  # config.historical_datatable_default_time_period = :year
26
26
 
27
+ # Threshold for percentage of queries that timed out after which it gets marked in the Authority Connection up-down History
28
+ # @param [Float] percentage of queries that are ok to timeout
29
+ # config.up_down_data_timeouts_max_threshold = 0.3
30
+
31
+ # Threshold for percentage of queries that are passing, below which are marked in the Authority Connection up-down History as barely_up
32
+ # @param [Float] required percentage of queries passing to be considered mostly-up when there are some failures
33
+ # config.up_down_data_mostly_up_threshold = 0.95
34
+
27
35
  # Displays a graph of performance test data when true
28
36
  # @param [Boolean] display performance graph when true
29
37
  # config.display_performance_graph = false
@@ -73,6 +73,20 @@ module QaServer
73
73
  @historical_datatable_default_time_period ||= :year
74
74
  end
75
75
 
76
+ # Threshold for percentage of queries that timed out after which it gets marked in the Authority Connection up-down History
77
+ # @param [Float] percentage of queries that are ok to timeout
78
+ attr_writer :up_down_data_timeouts_max_threshold
79
+ def up_down_data_timeouts_max_threshold
80
+ @up_down_data_timeouts_max_threshold ||= 0.3
81
+ end
82
+
83
+ # Threshold for percentage of queries that are passing, below which are marked in the Authority Connection up-down History as barely_up
84
+ # @param [Float] required percentage of queries passing to be considered mostly-up when there are some failures
85
+ attr_writer :up_down_data_mostly_up_threshold
86
+ def up_down_data_mostly_up_threshold
87
+ @up_down_data_mostly_up_threshold ||= 0.95
88
+ end
89
+
76
90
  # Displays a graph of performance test data when true
77
91
  # @param [Boolean] display performance graph when true
78
92
  attr_writer :display_performance_graph
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module QaServer
3
- VERSION = '7.6.0'
3
+ VERSION = '7.9.1'
4
4
  end
data/spec/i18n_spec.rb CHANGED
@@ -14,7 +14,6 @@ RSpec.describe I18n do
14
14
  end
15
15
 
16
16
  it 'does not have unused keys' do
17
- pending 'need to explore unused keys further'
18
17
  expect(unused_keys).to be_empty,
19
18
  "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
20
19
  end
@@ -86,6 +86,28 @@ RSpec.describe QaServer::Configuration do
86
86
  end
87
87
  end
88
88
 
89
+ describe '#up_down_data_timeouts_max_threshold' do
90
+ it 'return default as 0.3 (e.g. 30%)' do
91
+ expect(config.up_down_data_timeouts_max_threshold).to eq 0.3
92
+ end
93
+
94
+ it 'returns set value' do
95
+ config.up_down_data_timeouts_max_threshold = 0.25
96
+ expect(config.up_down_data_timeouts_max_threshold).to eq 0.25
97
+ end
98
+ end
99
+
100
+ describe '#up_down_data_mostly_up_threshold' do
101
+ it 'return default as 0.95 (e.g. 95%)' do
102
+ expect(config.up_down_data_mostly_up_threshold).to eq 0.95
103
+ end
104
+
105
+ it 'returns set value' do
106
+ config.up_down_data_mostly_up_threshold = 0.98
107
+ expect(config.up_down_data_mostly_up_threshold).to eq 0.98
108
+ end
109
+ end
110
+
89
111
  describe '#display_performance_graph?' do
90
112
  it 'return default as false' do
91
113
  expect(config.display_performance_graph?).to eq false
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe QaServer::MonitorStatus::HistoryPresenter do
5
+ let(:presenter) { described_class.new(parent: nil, historical_summary_data: historical_summary_data) }
6
+ # rubocop:disable Layout/ExtraSpacing
7
+ let(:historical_summary_data) do
8
+ # { 'auth_name' => { good: count_of_passing_tests, bad: count_of_failing_tests } }
9
+ {
10
+ 'GOOD_AUTH' => { good: 0, bad: 1000 },
11
+ 'BARELY_GOOD' => { good: 49, bad: 951 },
12
+ 'OK_AUTH' => { good: 50, bad: 950 },
13
+ 'STILL_OK' => { good: 51, bad: 949 },
14
+ 'BARELY_OK' => { good: 99, bad: 901 },
15
+ 'BAD_AUTH' => { good: 100, bad: 900 },
16
+ 'STILL_BAD' => { good: 101, bad: 899 },
17
+ 'REALLY_BAD' => { good: 500, bad: 500 },
18
+ 'HORRIBLE' => { good: 1000, bad: 0 }
19
+ }
20
+ # rubocop:enable Layout/ExtraSpacing
21
+ end
22
+
23
+ describe '.failure_style_class' do
24
+ context 'returns NEUTRAL style' do
25
+ let(:expected_css_style) { "status-neutral" }
26
+ let(:zero_failure_entry) { ['GOOD_AUTH', { good: 1000, bad: 0 }] } # rubocop:disable Layout/ExtraSpacing
27
+ let(:just_below_caution) { ['BARELY_GOOD', { good: 951, bad: 49 }] } # rubocop:disable Layout/ExtraSpacing
28
+
29
+ it 'when no failures' do
30
+ expect(presenter.failure_style_class(zero_failure_entry)).to eq expected_css_style
31
+ end
32
+
33
+ it 'when percent of failures is just below the CAUTION_THRESHOLD' do
34
+ expect(presenter.failure_style_class(just_below_caution)).to eq expected_css_style
35
+ end
36
+ end
37
+
38
+ context 'returns CAUTION style' do
39
+ let(:expected_css_style) { "status-unknown" }
40
+ let(:equal_caution) { ['OK_AUTH', { good: 950, bad: 50 }] }
41
+ let(:just_above_caution) { ['STILL_OK', { good: 949, bad: 51 }] }
42
+ let(:just_below_warning) { ['BARELY_OK', { good: 901, bad: 99 }] }
43
+
44
+ it 'when percent of failures is equal to CAUTION_THRESHOLD' do
45
+ expect(presenter.failure_style_class(equal_caution)).to eq expected_css_style
46
+ end
47
+
48
+ it 'when percent of failures is just above CAUTION_THRESHOLD' do
49
+ expect(presenter.failure_style_class(just_above_caution)).to eq expected_css_style
50
+ end
51
+
52
+ it 'when percent of failures is just below WARNING_THRESHOLD' do
53
+ expect(presenter.failure_style_class(just_below_warning)).to eq expected_css_style
54
+ end
55
+ end
56
+
57
+ context 'returns WARNING style' do
58
+ let(:expected_css_style) { "status-bad" }
59
+ let(:equal_warning) { ['BAD_AUTH', { good: 900, bad: 100 }] }
60
+ let(:just_above_warning) { ['STILL_BAD', { good: 899, bad: 101 }] }
61
+ let(:well_above_warning) { ['REALLY_BAD', { good: 500, bad: 500 }] }
62
+ let(:all_failures) { ['HORRIBLE', { good: 0, bad: 1000 }] }
63
+
64
+ it 'when percent of failures is equal to WARNING_THRESHOLD' do
65
+ expect(presenter.failure_style_class(equal_warning)).to eq expected_css_style
66
+ end
67
+
68
+ it 'when percent of failures is just above WARNING_THRESHOLD' do
69
+ expect(presenter.failure_style_class(just_above_warning)).to eq expected_css_style
70
+ end
71
+
72
+ it 'when percent of failures is well above WARNING_THRESHOLD' do
73
+ expect(presenter.failure_style_class(well_above_warning)).to eq expected_css_style
74
+ end
75
+
76
+ it 'when percent of failures is 100%' do
77
+ expect(presenter.failure_style_class(all_failures)).to eq expected_css_style
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe QaServer::HistoryUpDownService do
5
+ let(:service) { described_class.new }
6
+
7
+ context 'when total_count is 0' do
8
+ let(:good_count) { 0 }
9
+ let(:unknown_count) { 0 }
10
+ let(:bad_count) { 0 }
11
+ let(:timeout_count) { 0 }
12
+ it 'returns :no_data' do
13
+ status = service.send(:status_determination, good_count, unknown_count, bad_count, timeout_count)
14
+ expect(status).to eq :no_data
15
+ end
16
+ end
17
+
18
+ context 'when all queries failed' do
19
+ let(:good_count) { 0 }
20
+ let(:unknown_count) { 0 }
21
+ let(:bad_count) { 5 }
22
+ let(:timeout_count) { 0 }
23
+ it 'returns :down' do
24
+ status = service.send(:status_determination, good_count, unknown_count, bad_count, timeout_count)
25
+ expect(status).to eq :down
26
+ end
27
+ end
28
+
29
+ context 'when all queries passed' do
30
+ let(:good_count) { 5 }
31
+ let(:unknown_count) { 0 }
32
+ let(:bad_count) { 0 }
33
+ let(:timeout_count) { 0 }
34
+ it 'returns :fully_up' do
35
+ status = service.send(:status_determination, good_count, unknown_count, bad_count, timeout_count)
36
+ expect(status).to eq :fully_up
37
+ end
38
+ end
39
+
40
+ context 'when all queries are unknown' do
41
+ let(:good_count) { 0 }
42
+ let(:unknown_count) { 5 }
43
+ let(:bad_count) { 0 }
44
+ let(:timeout_count) { 0 }
45
+ it 'returns :barely_up' do
46
+ status = service.send(:status_determination, good_count, unknown_count, bad_count, timeout_count)
47
+ expect(status).to eq :barely_up
48
+ end
49
+ end
50
+
51
+ context 'when too many queries timed out' do
52
+ let(:threshold) { 0.5 }
53
+ let(:good_count) { 100 - bad_count }
54
+ let(:unknown_count) { 0 }
55
+ let(:bad_count) { timeout_count + 2 }
56
+ let(:timeout_count) { threshold * 100 + 1 }
57
+ it 'returns :good' do
58
+ status = service.send(:status_determination, good_count, unknown_count, bad_count, timeout_count)
59
+ expect(status).to eq :timeouts
60
+ end
61
+ end
62
+
63
+ context 'when almost all queries pass' do
64
+ let(:threshold) { 0.95 }
65
+ let(:good_count) { threshold * 100 + 1 }
66
+ let(:unknown_count) { 0 }
67
+ let(:bad_count) { 100 - good_count }
68
+ let(:timeout_count) { 0 }
69
+ it 'returns :good' do
70
+ status = service.send(:status_determination, good_count, unknown_count, bad_count, timeout_count)
71
+ expect(status).to eq :mostly_up
72
+ end
73
+ end
74
+
75
+ context 'when too many queries fail' do
76
+ let(:threshold) { 0.95 }
77
+ let(:good_count) { threshold * 100 - 1 }
78
+ let(:unknown_count) { 0 }
79
+ let(:bad_count) { 100 - good_count }
80
+ let(:timeout_count) { 0 }
81
+ it 'returns :good' do
82
+ status = service.send(:status_determination, good_count, unknown_count, bad_count, timeout_count)
83
+ expect(status).to eq :barely_up
84
+ end
85
+ end
86
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qa_server
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.6.0
4
+ version: 7.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - E. Lynette Rayle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-12 00:00:00.000000000 Z
11
+ date: 2021-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -418,6 +418,7 @@ files:
418
418
  - app/presenters/qa_server/fetch_presenter.rb
419
419
  - app/presenters/qa_server/monitor_status/current_status_presenter.rb
420
420
  - app/presenters/qa_server/monitor_status/history_presenter.rb
421
+ - app/presenters/qa_server/monitor_status/history_up_down_presenter.rb
421
422
  - app/presenters/qa_server/monitor_status/performance_presenter.rb
422
423
  - app/presenters/qa_server/monitor_status_presenter.rb
423
424
  - app/presenters/qa_server/navmenu_presenter.rb
@@ -427,6 +428,7 @@ files:
427
428
  - app/services/qa_server/authority_validator_service.rb
428
429
  - app/services/qa_server/database_migrator.rb
429
430
  - app/services/qa_server/history_graphing_service.rb
431
+ - app/services/qa_server/history_up_down_service.rb
430
432
  - app/services/qa_server/performance_calculator_service.rb
431
433
  - app/services/qa_server/performance_datatable_service.rb
432
434
  - app/services/qa_server/performance_graph_data_service.rb
@@ -447,6 +449,7 @@ files:
447
449
  - app/views/qa_server/monitor_status/_performance.html.erb
448
450
  - app/views/qa_server/monitor_status/_test_history.html.erb
449
451
  - app/views/qa_server/monitor_status/_test_summary.html.erb
452
+ - app/views/qa_server/monitor_status/_test_up_down_connection_history.html.erb
450
453
  - app/views/qa_server/monitor_status/index.html.erb
451
454
  - app/views/qa_server/usage/index.html.erb
452
455
  - app/views/shared/_footer.html.erb
@@ -546,7 +549,9 @@ files:
546
549
  - spec/i18n_spec.rb
547
550
  - spec/lib/configuration_spec.rb
548
551
  - spec/lib/qa_server_spec.rb
552
+ - spec/presenters/qa_server/monitor_status/history_presenter_spec.rb
549
553
  - spec/rails_helper.rb
554
+ - spec/services/qa_server/history_up_down_service_spec.rb
550
555
  - spec/services/qa_server/time_period_service_spec.rb
551
556
  - spec/services/qa_server/time_service_spec.rb
552
557
  - spec/spec_helper.rb
@@ -584,7 +589,9 @@ test_files:
584
589
  - spec/i18n_spec.rb
585
590
  - spec/lib/configuration_spec.rb
586
591
  - spec/lib/qa_server_spec.rb
592
+ - spec/presenters/qa_server/monitor_status/history_presenter_spec.rb
587
593
  - spec/rails_helper.rb
594
+ - spec/services/qa_server/history_up_down_service_spec.rb
588
595
  - spec/services/qa_server/time_period_service_spec.rb
589
596
  - spec/services/qa_server/time_service_spec.rb
590
597
  - spec/spec_helper.rb