diff options
author | Luke Bennett <lukeeeebennettplus@gmail.com> | 2018-04-09 16:22:44 +0100 |
---|---|---|
committer | Luke Bennett <lukeeeebennettplus@gmail.com> | 2018-04-09 16:22:44 +0100 |
commit | 2d0bec150c8ff5dbc01cb49af1b54b2d2a3d8e9c (patch) | |
tree | a7a7b45c027eea1877a5b6b2e17d47a746056c29 /spec | |
parent | bff59c8cb0b0778ebe85ee6d74babda262ef5562 (diff) | |
parent | de3f55b226b4c5bb28b207b422582c6e2bfd417f (diff) | |
download | gitlab-ce-2d0bec150c8ff5dbc01cb49af1b54b2d2a3d8e9c.tar.gz |
Merge remote-tracking branch 'origin/master' into deprecation-warning-for-dynamic-milestones
Diffstat (limited to 'spec')
160 files changed, 12544 insertions, 9070 deletions
diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index 97c2c3fb940..3458d679107 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -11,9 +11,11 @@ describe DashboardController do describe 'GET issues' do it_behaves_like 'issuables list meta-data', :issue, :issues + it_behaves_like 'issuables requiring filter', :issues end describe 'GET merge requests' do it_behaves_like 'issuables list meta-data', :merge_request, :merge_requests + it_behaves_like 'issuables requiring filter', :merge_requests end end diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb index 891485406c6..de6ef919221 100644 --- a/spec/controllers/profiles_controller_spec.rb +++ b/spec/controllers/profiles_controller_spec.rb @@ -84,6 +84,28 @@ describe ProfilesController, :request_store do expect(user.username).to eq(new_username) end + it 'updates a username using JSON request' do + sign_in(user) + + put :update_username, + user: { username: new_username }, + format: :json + + expect(response.status).to eq(200) + expect(json_response['message']).to eq('Username successfully changed') + end + + it 'renders an error message when the username was not updated' do + sign_in(user) + + put :update_username, + user: { username: 'invalid username.git' }, + format: :json + + expect(response.status).to eq(422) + expect(json_response['message']).to match(/Username change failed/) + end + it 'raises a correct error when the username is missing' do sign_in(user) diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb index 31046c202e6..b9a979044fe 100644 --- a/spec/controllers/projects/jobs_controller_spec.rb +++ b/spec/controllers/projects/jobs_controller_spec.rb @@ -513,13 +513,30 @@ describe Projects::JobsController do end end + context 'when job has a trace in database' do + let(:job) { create(:ci_build, pipeline: pipeline) } + + before do + job.update_column(:trace, 'Sample trace') + end + + it 'send a trace file' do + response = subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.content_type).to eq 'text/plain; charset=utf-8' + expect(response.body).to eq 'Sample trace' + end + end + context 'when job does not have a trace file' do let(:job) { create(:ci_build, pipeline: pipeline) } it 'returns not_found' do response = subject - expect(response).to have_gitlab_http_status(:not_found) + expect(response).to have_gitlab_http_status(:ok) + expect(response.body).to eq '' end end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index 04d16e98913..c3b71458e38 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -6,7 +6,7 @@ describe Projects::RepositoriesController do describe "GET archive" do context 'as a guest' do it 'responds with redirect in correct format' do - get :archive, namespace_id: project.namespace, project_id: project, format: "zip", ref: 'master' + get :archive, namespace_id: project.namespace, project_id: project, id: "master", format: "zip" expect(response.header["Content-Type"]).to start_with('text/html') expect(response).to be_redirect @@ -22,7 +22,20 @@ describe Projects::RepositoriesController do end it "uses Gitlab::Workhorse" do - get :archive, namespace_id: project.namespace, project_id: project, ref: "master", format: "zip" + get :archive, namespace_id: project.namespace, project_id: project, id: "master", format: "zip" + + expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") + end + + it 'responds with redirect to the short name archive if fully qualified' do + get :archive, namespace_id: project.namespace, project_id: project, id: "master/#{project.path}-master", format: "zip" + + expect(assigns(:ref)).to eq("master") + expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") + end + + it 'handles legacy queries with no ref' do + get :archive, namespace_id: project.namespace, project_id: project, format: "zip" expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:") end @@ -33,7 +46,7 @@ describe Projects::RepositoriesController do end it "renders Not Found" do - get :archive, namespace_id: project.namespace, project_id: project, ref: "master", format: "zip" + get :archive, namespace_id: project.namespace, project_id: project, id: "master", format: "zip" expect(response).to have_gitlab_http_status(404) end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index f6ba3a581ca..fdacbe6c3f1 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -62,6 +62,7 @@ FactoryBot.define do end trait :pending do + queued_at 'Di 29. Okt 09:50:59 CET 2013' status 'pending' end @@ -237,5 +238,10 @@ FactoryBot.define do trait :protected do protected true end + + trait :script_failure do + failed + failure_reason 1 + end end end diff --git a/spec/factories/deploy_tokens.rb b/spec/factories/deploy_tokens.rb new file mode 100644 index 00000000000..5fea4a9d5a6 --- /dev/null +++ b/spec/factories/deploy_tokens.rb @@ -0,0 +1,14 @@ +FactoryBot.define do + factory :deploy_token do + token { SecureRandom.hex(50) } + sequence(:name) { |n| "PDT #{n}" } + read_repository true + read_registry true + revoked false + expires_at { 5.days.from_now } + + trait :revoked do + revoked true + end + end +end diff --git a/spec/factories/project_deploy_tokens.rb b/spec/factories/project_deploy_tokens.rb new file mode 100644 index 00000000000..4866cb58d88 --- /dev/null +++ b/spec/factories/project_deploy_tokens.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :project_deploy_token do + project + deploy_token + end +end diff --git a/spec/factories/users_star_projects.rb b/spec/factories/users_star_projects.rb new file mode 100644 index 00000000000..6afd08a2084 --- /dev/null +++ b/spec/factories/users_star_projects.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :users_star_project do + project + user + end +end diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb index d673bac4995..fb6c71ce997 100644 --- a/spec/features/atom/dashboard_issues_spec.rb +++ b/spec/features/atom/dashboard_issues_spec.rb @@ -13,17 +13,26 @@ describe "Dashboard Issues Feed" do end describe "atom feed" do - it "renders atom feed via personal access token" do + it "returns 400 if no filter is used" do personal_access_token = create(:personal_access_token, user: user) visit issues_dashboard_path(:atom, private_token: personal_access_token.token) expect(response_headers['Content-Type']).to have_content('application/atom+xml') + expect(page.status_code).to eq(400) + end + + it "renders atom feed via personal access token" do + personal_access_token = create(:personal_access_token, user: user) + + visit issues_dashboard_path(:atom, private_token: personal_access_token.token, assignee_id: user.id) + + expect(response_headers['Content-Type']).to have_content('application/atom+xml') expect(body).to have_selector('title', text: "#{user.name} issues") end it "renders atom feed via RSS token" do - visit issues_dashboard_path(:atom, rss_token: user.rss_token) + visit issues_dashboard_path(:atom, rss_token: user.rss_token, assignee_id: user.id) expect(response_headers['Content-Type']).to have_content('application/atom+xml') expect(body).to have_selector('title', text: "#{user.name} issues") @@ -44,7 +53,7 @@ describe "Dashboard Issues Feed" do let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') } it "renders issue fields" do - visit issues_dashboard_path(:atom, rss_token: user.rss_token) + visit issues_dashboard_path(:atom, rss_token: user.rss_token, assignee_id: assignee.id) entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]") @@ -67,7 +76,7 @@ describe "Dashboard Issues Feed" do end it "renders issue label and milestone info" do - visit issues_dashboard_path(:atom, rss_token: user.rss_token) + visit issues_dashboard_path(:atom, rss_token: user.rss_token, assignee_id: assignee.id) entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]") diff --git a/spec/features/dashboard/issues_filter_spec.rb b/spec/features/dashboard/issues_filter_spec.rb index 029fc45c791..bab34ac9346 100644 --- a/spec/features/dashboard/issues_filter_spec.rb +++ b/spec/features/dashboard/issues_filter_spec.rb @@ -17,6 +17,12 @@ feature 'Dashboard Issues filtering', :js do visit_issues end + context 'without any filter' do + it 'shows error message' do + expect(page).to have_content 'Please select at least one filter to see results' + end + end + context 'filtering by milestone' do it 'shows all issues with no milestone' do show_milestone_dropdown @@ -27,15 +33,6 @@ feature 'Dashboard Issues filtering', :js do expect(page).to have_selector('.issue', count: 1) end - it 'shows all issues with any milestone' do - show_milestone_dropdown - - click_link 'Any Milestone' - - expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2) - expect(page).to have_selector('.issue', count: 2) - end - it 'shows all issues with the selected milestone' do show_milestone_dropdown @@ -68,13 +65,6 @@ feature 'Dashboard Issues filtering', :js do let(:label) { create(:label, project: project) } let!(:label_link) { create(:label_link, label: label, target: issue) } - it 'shows all issues without filter' do - page.within 'ul.content-list' do - expect(page).to have_content issue.title - expect(page).to have_content issue2.title - end - end - it 'shows all issues with the selected label' do page.within '.labels-filter' do find('.dropdown').click @@ -89,9 +79,13 @@ feature 'Dashboard Issues filtering', :js do end context 'sorting' do - it 'shows sorted issues' do + before do + visit_issues(assignee_id: user.id) + end + + it 'remembers last sorting value' do sort_by('Created date') - visit_issues + visit_issues(assignee_id: user.id) expect(find('.issues-filters')).to have_content('Created date') end diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb index 8d1d5a51750..e41a2e4ce09 100644 --- a/spec/features/dashboard/issues_spec.rb +++ b/spec/features/dashboard/issues_spec.rb @@ -51,15 +51,6 @@ RSpec.describe 'Dashboard Issues' do expect(page).not_to have_content(other_issue.title) end - it 'shows all issues' do - click_link('Reset filters') - - expect(page).to have_content(authored_issue.title) - expect(page).to have_content(authored_issue_on_public_project.title) - expect(page).to have_content(assigned_issue.title) - expect(page).to have_content(other_issue.title) - end - it 'state filter tabs work' do find('#state-closed').click expect(page).to have_current_path(issues_dashboard_url(assignee_id: current_user.id, state: 'closed'), url: true) diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb index 4a9344115d2..0965b745c03 100644 --- a/spec/features/dashboard/merge_requests_spec.rb +++ b/spec/features/dashboard/merge_requests_spec.rb @@ -103,15 +103,11 @@ feature 'Dashboard Merge Requests' do expect(page).not_to have_content(other_merge_request.title) end - it 'shows all merge requests', :js do + it 'shows error message without filter', :js do filter_item_select('Any Assignee', '.js-assignee-search') filter_item_select('Any Author', '.js-author-search') - expect(page).to have_content(authored_merge_request.title) - expect(page).to have_content(authored_merge_request_from_fork.title) - expect(page).to have_content(assigned_merge_request.title) - expect(page).to have_content(assigned_merge_request_from_fork.title) - expect(page).to have_content(other_merge_request.title) + expect(page).to have_content('Please select at least one filter to see results') end it 'shows sorted merge requests' do diff --git a/spec/features/groups/settings/group_badges_spec.rb b/spec/features/groups/settings/group_badges_spec.rb new file mode 100644 index 00000000000..92217294446 --- /dev/null +++ b/spec/features/groups/settings/group_badges_spec.rb @@ -0,0 +1,124 @@ +require 'spec_helper' + +feature 'Group Badges' do + include WaitForRequests + + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:badge_link_url) { 'https://gitlab.com/gitlab-org/gitlab-ee/commits/master'} + let(:badge_image_url) { 'https://gitlab.com/gitlab-org/gitlab-ee/badges/master/build.svg'} + let!(:badge_1) { create(:group_badge, group: group) } + let!(:badge_2) { create(:group_badge, group: group) } + + before do + group.add_owner(user) + sign_in(user) + + visit(group_settings_badges_path(group)) + end + + it 'shows a list of badges', :js do + page.within '.badge-settings' do + wait_for_requests + + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + expect(rows[0]).to have_content badge_1.link_url + expect(rows[1]).to have_content badge_2.link_url + end + end + + context 'adding a badge', :js do + it 'user can preview a badge' do + page.within '.badge-settings form' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url + within '#badge-preview' do + expect(find('a')[:href]).to eq badge_link_url + expect(find('a img')[:src]).to eq badge_image_url + end + end + end + + it do + page.within '.badge-settings' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url + + click_button 'Add badge' + wait_for_requests + + within '.panel-body' do + expect(find('a')[:href]).to eq badge_link_url + expect(find('a img')[:src]).to eq badge_image_url + end + end + end + end + + context 'editing a badge', :js do + it 'form is shown when clicking edit button in list' do + page.within '.badge-settings' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + rows[1].find('[aria-label="Edit"]').click + + within 'form' do + expect(find('#badge-link-url').value).to eq badge_2.link_url + expect(find('#badge-image-url').value).to eq badge_2.image_url + end + end + end + + it 'updates a badge when submitting the edit form' do + page.within '.badge-settings' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + rows[1].find('[aria-label="Edit"]').click + within 'form' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url + + click_button 'Save changes' + wait_for_requests + end + + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + expect(rows[1]).to have_content badge_link_url + end + end + end + + context 'deleting a badge', :js do + def click_delete_button(badge_row) + badge_row.find('[aria-label="Delete"]').click + end + + it 'shows a modal when deleting a badge' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + + click_delete_button(rows[1]) + + expect(find('.modal .modal-title')).to have_content 'Delete badge?' + end + + it 'deletes a badge when confirming the modal' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + click_delete_button(rows[1]) + + find('.modal .btn-danger').click + wait_for_requests + + rows = all('.panel-body > div') + expect(rows.length).to eq 1 + expect(rows[0]).to have_content badge_1.link_url + end + end +end diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb index a75ca1d42b3..73022afbda2 100644 --- a/spec/features/issues/spam_issues_spec.rb +++ b/spec/features/issues/spam_issues_spec.rb @@ -34,9 +34,6 @@ describe 'New issue', :js do click_button 'Submit issue' - # reCAPTCHA alerts when it can't contact the server, so just accept it and move on - page.driver.browser.switch_to.alert.accept - # it is impossible to test recaptcha automatically and there is no possibility to fill in recaptcha # recaptcha verification is skipped in test environment and it always returns true expect(page).not_to have_content('issue title') diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 99e1fb30d5b..3e05e7b7f38 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -115,17 +115,17 @@ feature 'Labels Hierarchy', :js, :nested_groups do it 'filters by descendant group labels' do wait_for_requests - if board - pending("Waiting for https://gitlab.com/gitlab-org/gitlab-ce/issues/44270") + select_label_on_dropdown(group_label_3.title) - select_label_on_dropdown(group_label_3.title) + if board + expect(page).to have_selector('.card-title') do |card| + expect(card).not_to have_selector('a', text: labeled_issue_2.title) + end expect(page).to have_selector('.card-title') do |card| expect(card).to have_selector('a', text: labeled_issue_3.title) end else - select_label_on_dropdown(group_label_3.title) - expect_issues_list_count(1) expect(page).to have_selector('span.issue-title-text', text: labeled_issue_3.title) end diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb index a43ba05c64c..fd1629746ef 100644 --- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb +++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb @@ -9,6 +9,7 @@ describe 'Merge request < User sees mini pipeline graph', :js do before do build.run + build.trace.set('hello') sign_in(user) visit_merge_request end @@ -26,15 +27,15 @@ describe 'Merge request < User sees mini pipeline graph', :js do let(:artifacts_file2) { fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') } before do - create(:ci_build, pipeline: pipeline, legacy_artifacts_file: artifacts_file1) - create(:ci_build, pipeline: pipeline, when: 'manual') + create(:ci_build, :success, :trace_artifact, pipeline: pipeline, legacy_artifacts_file: artifacts_file1) + create(:ci_build, :manual, pipeline: pipeline, when: 'manual') end it 'avoids repeated database queries' do before = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') } - create(:ci_build, pipeline: pipeline, legacy_artifacts_file: artifacts_file2) - create(:ci_build, pipeline: pipeline, when: 'manual') + create(:ci_build, :success, :trace_artifact, pipeline: pipeline, legacy_artifacts_file: artifacts_file2) + create(:ci_build, :manual, pipeline: pipeline, when: 'manual') after = ActiveRecord::QueryRecorder.new { visit_merge_request(format: :json, serializer: 'widget') } diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 0848857ed1e..15dcb30cbdd 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -97,9 +97,13 @@ describe 'Profile account page', :js do end it 'changes my username' do - fill_in 'user_username', with: 'new-username' + fill_in 'username-change-input', with: 'new-username' - click_button('Update username') + page.find('[data-target="#username-change-confirmation-modal"]').click + + page.within('.modal') do + find('.js-modal-primary-action').click + end expect(page).to have_content('new-username') end diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb index e8eb0d17ca4..215b658eb7b 100644 --- a/spec/features/profiles/account_spec.rb +++ b/spec/features/profiles/account_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -feature 'Profile > Account' do +feature 'Profile > Account', :js do given(:user) { create(:user, username: 'foo') } before do @@ -59,6 +59,12 @@ end def update_username(new_username) allow(user.namespace).to receive(:move_dir) visit profile_account_path - fill_in 'user_username', with: new_username - click_button 'Update username' + + fill_in 'username-change-input', with: new_username + + page.find('[data-target="#username-change-confirmation-modal"]').click + + page.within('.modal') do + find('.js-modal-primary-action').click + end end diff --git a/spec/features/projects/files/template_selector_menu_spec.rb b/spec/features/projects/files/template_selector_menu_spec.rb new file mode 100644 index 00000000000..b549a69ddf3 --- /dev/null +++ b/spec/features/projects/files/template_selector_menu_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +feature 'Template selector menu', :js do + let(:project) { create(:project, :repository) } + let(:user) { create(:user) } + + before do + project.add_master(user) + sign_in user + end + + context 'editing a non-matching file' do + before do + create_and_edit_file('README.md') + end + + scenario 'is not displayed' do + check_template_selector_menu_display(false) + end + + context 'user toggles preview' do + before do + click_link 'Preview' + end + + scenario 'template selector menu is not displayed' do + check_template_selector_menu_display(false) + click_link 'Write' + check_template_selector_menu_display(false) + end + end + end + + context 'editing a matching file' do + before do + visit project_edit_blob_path(project, File.join(project.default_branch, 'LICENSE')) + end + + scenario 'is displayed' do + check_template_selector_menu_display(true) + end + + context 'user toggles preview' do + before do + click_link 'Preview' + end + + scenario 'template selector menu is hidden and shown correctly' do + check_template_selector_menu_display(false) + click_link 'Write' + check_template_selector_menu_display(true) + end + end + end +end + +def check_template_selector_menu_display(is_visible) + count = is_visible ? 1 : 0 + expect(page).to have_css('.template-selectors-menu', count: count) +end + +def create_and_edit_file(file_name) + visit project_new_blob_path(project, 'master', file_name: file_name) + click_button "Commit changes" + visit project_edit_blob_path(project, File.join(project.default_branch, file_name)) +end diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 4c49cff30d4..bff5bbe99af 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -1,16 +1,15 @@ require 'spec_helper' describe 'User browses a job', :js do - let!(:build) { create(:ci_build, :running, :coverage, pipeline: pipeline) } - let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } - let(:project) { create(:project, :repository, namespace: user.namespace) } let(:user) { create(:user) } + let(:user_access_level) { :developer } + let(:project) { create(:project, :repository, namespace: user.namespace) } + let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } + let!(:build) { create(:ci_build, :success, :trace_artifact, :coverage, pipeline: pipeline) } before do project.add_master(user) project.enable_ci - build.success - build.trace.set('job trace') sign_in(user) @@ -21,7 +20,9 @@ describe 'User browses a job', :js do expect(page).to have_content("Job ##{build.id}") expect(page).to have_css('#build-trace') - accept_confirm { click_link('Erase') } + # scroll to the top of the page first + execute_script "window.scrollTo(0,0)" + accept_confirm { find('.js-erase-link').click } expect(page).to have_no_css('.artifacts') expect(build).not_to have_trace @@ -34,4 +35,26 @@ describe 'User browses a job', :js do expect(build.project.running_or_pending_build_count).to eq(build.project.builds.running_or_pending.count(:all)) end + + context 'with a failed job' do + let!(:build) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) } + + it 'displays the failure reason' do + within('.builds-container') do + build_link = first('.build-job > a') + expect(build_link['data-title']).to eq('test - failed <br> (unknown failure)') + end + end + end + + context 'when a failed job has been retried' do + let!(:build) { create(:ci_build, :failed, :retried, :trace_artifact, pipeline: pipeline) } + + it 'displays the failure reason and retried label' do + within('.builds-container') do + build_link = first('.build-job > a') + expect(build_link['data-title']).to eq('test - failed <br> (unknown failure) (retried)') + end + end + end end diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb index 767777f3bf9..36ebbeadd4a 100644 --- a/spec/features/projects/jobs/user_browses_jobs_spec.rb +++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb @@ -29,4 +29,15 @@ describe 'User browses jobs' do expect(ci_lint_tool_link[:href]).to end_with(ci_lint_path) end end + + context 'with a failed job' do + let!(:build) { create(:ci_build, :coverage, :failed, pipeline: pipeline) } + + it 'displays a tooltip with the failure reason' do + page.within('.ci-table') do + failed_job_link = page.find('.ci-failed') + expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)') + end + end + end end diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 5d311f2dde3..749a1b81872 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -113,7 +113,7 @@ feature 'Jobs' do describe "GET /:project/jobs/:id" do context "Job from project" do - let(:job) { create(:ci_build, :success, pipeline: pipeline) } + let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline) } before do visit project_job_path(project, job) @@ -136,7 +136,7 @@ feature 'Jobs' do end context 'when job is not running', :js do - let(:job) { create(:ci_build, :success, pipeline: pipeline) } + let(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) } before do visit project_job_path(project, job) @@ -153,7 +153,7 @@ feature 'Jobs' do end context 'if job failed' do - let(:job) { create(:ci_build, :failed, pipeline: pipeline) } + let(:job) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) } before do visit project_job_path(project, job) @@ -339,7 +339,7 @@ feature 'Jobs' do context 'job is successfull and has deployment' do let(:deployment) { create(:deployment) } - let(:job) { create(:ci_build, :success, environment: environment.name, deployments: [deployment], pipeline: pipeline) } + let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, deployments: [deployment], pipeline: pipeline) } it 'shows a link for the job' do visit project_job_path(project, job) @@ -349,7 +349,7 @@ feature 'Jobs' do end context 'job is complete and not successful' do - let(:job) { create(:ci_build, :failed, environment: environment.name, pipeline: pipeline) } + let(:job) { create(:ci_build, :failed, :trace_artifact, environment: environment.name, pipeline: pipeline) } it 'shows a link for the job' do visit project_job_path(project, job) @@ -360,7 +360,7 @@ feature 'Jobs' do context 'job creates a new deployment' do let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) } - let(:job) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) } + let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, pipeline: pipeline) } it 'shows a link to latest deployment' do visit project_job_path(project, job) @@ -379,6 +379,7 @@ feature 'Jobs' do end it 'shows manual action empty state' do + expect(page).to have_content(job.detailed_status(user).illustration[:title]) expect(page).to have_content('This job requires a manual action') expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments') expect(page).to have_link('Trigger this manual action') @@ -402,6 +403,7 @@ feature 'Jobs' do end it 'shows empty state' do + expect(page).to have_content(job.detailed_status(user).illustration[:title]) expect(page).to have_content('This job has not been triggered yet') expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered') end @@ -415,10 +417,53 @@ feature 'Jobs' do end it 'shows pending empty state' do + expect(page).to have_content(job.detailed_status(user).illustration[:title]) expect(page).to have_content('This job has not started yet') expect(page).to have_content('This job is in pending state and is waiting to be picked by a runner') end end + + context 'Canceled job' do + context 'with log' do + let(:job) { create(:ci_build, :canceled, :trace_artifact, pipeline: pipeline) } + + before do + visit project_job_path(project, job) + end + + it 'renders job log' do + expect(page).to have_selector('.js-build-output') + end + end + + context 'without log' do + let(:job) { create(:ci_build, :canceled, pipeline: pipeline) } + + before do + visit project_job_path(project, job) + end + + it 'renders empty state' do + expect(page).to have_content(job.detailed_status(user).illustration[:title]) + expect(page).not_to have_selector('.js-build-output') + expect(page).to have_content('This job has been canceled') + end + end + end + + context 'Skipped job' do + let(:job) { create(:ci_build, :skipped, pipeline: pipeline) } + + before do + visit project_job_path(project, job) + end + + it 'renders empty state' do + expect(page).to have_content(job.detailed_status(user).illustration[:title]) + expect(page).not_to have_selector('.js-build-output') + expect(page).to have_content('This job has been skipped') + end + end end describe "POST /:project/jobs/:id/cancel", :js do diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb index 266ef693d0b..990e5c4d9df 100644 --- a/spec/features/projects/pipelines/pipeline_spec.rb +++ b/spec/features/projects/pipelines/pipeline_spec.rb @@ -115,6 +115,13 @@ describe 'Pipeline', :js do expect(page).not_to have_content('Retry job') end + + it 'should include the failure reason' do + page.within('#ci-badge-test') do + build_link = page.find('.js-pipeline-graph-job-link') + expect(build_link['data-original-title']).to eq('test - failed <br> (unknown failure)') + end + end end context 'when pipeline has manual jobs' do @@ -289,6 +296,15 @@ describe 'Pipeline', :js do it { expect(build_manual.reload).to be_pending } end + + context 'failed jobs' do + it 'displays a tooltip with the failure reason' do + page.within('.ci-table') do + failed_job_link = page.find('.ci-failed') + expect(failed_job_link[:title]).to eq('Failed <br> (unknown failure)') + end + end + end end describe 'GET /:project/pipelines/:id/failures' do diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 0e81c6c629a..6e63e0f0b49 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -394,6 +394,23 @@ describe 'Pipelines', :js do expect(build.reload).to be_canceled end end + + context 'for a failed pipeline' do + let!(:build) do + create(:ci_build, :failed, pipeline: pipeline, + stage: 'build', + name: 'build') + end + + it 'should display the failure reason' do + find('.js-builds-dropdown-button').click + + within('.js-builds-dropdown-list') do + build_element = page.find('.mini-pipeline-graph-dropdown-item') + expect(build_element['data-title']).to eq('build - failed <br> (unknown failure)') + end + end + end end context 'with pagination' do diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb new file mode 100644 index 00000000000..cc3551a4c21 --- /dev/null +++ b/spec/features/projects/settings/project_badges_spec.rb @@ -0,0 +1,125 @@ +require 'spec_helper' + +feature 'Project Badges' do + include WaitForRequests + + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, namespace: group) } + let(:badge_link_url) { 'https://gitlab.com/gitlab-org/gitlab-ee/commits/master'} + let(:badge_image_url) { 'https://gitlab.com/gitlab-org/gitlab-ee/badges/master/build.svg'} + let!(:project_badge) { create(:project_badge, project: project) } + let!(:group_badge) { create(:group_badge, group: group) } + + before do + group.add_master(user) + sign_in(user) + + visit(project_settings_badges_path(project)) + end + + it 'shows a list of badges', :js do + page.within '.badge-settings' do + wait_for_requests + + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + expect(rows[0]).to have_content group_badge.link_url + expect(rows[1]).to have_content project_badge.link_url + end + end + + context 'adding a badge', :js do + it 'user can preview a badge' do + page.within '.badge-settings form' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url + within '#badge-preview' do + expect(find('a')[:href]).to eq badge_link_url + expect(find('a img')[:src]).to eq badge_image_url + end + end + end + + it do + page.within '.badge-settings' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url + + click_button 'Add badge' + wait_for_requests + + within '.panel-body' do + expect(find('a')[:href]).to eq badge_link_url + expect(find('a img')[:src]).to eq badge_image_url + end + end + end + end + + context 'editing a badge', :js do + it 'form is shown when clicking edit button in list' do + page.within '.badge-settings' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + rows[1].find('[aria-label="Edit"]').click + + within 'form' do + expect(find('#badge-link-url').value).to eq project_badge.link_url + expect(find('#badge-image-url').value).to eq project_badge.image_url + end + end + end + + it 'updates a badge when submitting the edit form' do + page.within '.badge-settings' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + rows[1].find('[aria-label="Edit"]').click + within 'form' do + fill_in 'badge-link-url', with: badge_link_url + fill_in 'badge-image-url', with: badge_image_url + + click_button 'Save changes' + wait_for_requests + end + + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + expect(rows[1]).to have_content badge_link_url + end + end + end + + context 'deleting a badge', :js do + def click_delete_button(badge_row) + badge_row.find('[aria-label="Delete"]').click + end + + it 'shows a modal when deleting a badge' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + + click_delete_button(rows[1]) + + expect(find('.modal .modal-title')).to have_content 'Delete badge?' + end + + it 'deletes a badge when confirming the modal' do + wait_for_requests + rows = all('.panel-body > div') + expect(rows.length).to eq 2 + click_delete_button(rows[1]) + + find('.modal .btn-danger').click + wait_for_requests + + rows = all('.panel-body > div') + expect(rows.length).to eq 1 + expect(rows[0]).to have_content group_badge.link_url + end + end +end diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index 14670e91006..f2c371b7df5 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -88,5 +88,32 @@ feature 'Repository settings' do expect(page).not_to have_content(private_deploy_key.title) end end + + context 'Deploy tokens' do + let!(:deploy_token) { create(:deploy_token, projects: [project]) } + + before do + stub_container_registry_config(enabled: true) + visit project_settings_repository_path(project) + end + + scenario 'view deploy tokens' do + within('.deploy-tokens') do + expect(page).to have_content(deploy_token.name) + expect(page).to have_content('read_repository') + expect(page).to have_content('read_registry') + end + end + + scenario 'add a new deploy token' do + fill_in 'deploy_token_name', with: 'new_deploy_key' + fill_in 'deploy_token_expires_at', with: (Date.today + 1.month).to_s + check 'deploy_token_read_repository' + check 'deploy_token_read_registry' + click_button 'Create deploy token' + + expect(page).to have_content('Your new project deploy token has been created') + end + end end end diff --git a/spec/features/search/user_uses_header_search_field_spec.rb b/spec/features/search/user_uses_header_search_field_spec.rb index 5ddea36add5..a9128104b87 100644 --- a/spec/features/search/user_uses_header_search_field_spec.rb +++ b/spec/features/search/user_uses_header_search_field_spec.rb @@ -9,49 +9,25 @@ describe 'User uses header search field' do before do project.add_reporter(user) sign_in(user) - - visit(project_path(project)) - end - - it 'starts searching by pressing the enter key', :js do - fill_in('search', with: 'gitlab') - find('#search').native.send_keys(:enter) - - page.within('.breadcrumbs-sub-title') do - expect(page).to have_content('Search') - end end - it 'contains location badge' do - expect(page).to have_selector('.has-location-badge') - end - - context 'when clicking the search field', :js do + context 'when user is in a global scope', :js do before do + visit(root_path) page.find('#search').click end - it 'shows category search dropdown' do - expect(page).to have_selector('.dropdown-header', text: /#{project.name}/i) - end - context 'when clicking issues' do - let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) } - it 'shows assigned issues' do - find('.dropdown-menu').click_link('Issues assigned to me') + find('.search-input-container .dropdown-menu').click_link('Issues assigned to me') - expect(page).to have_selector('.filtered-search') - expect_tokens([assignee_token(user.name)]) - expect_filtered_search_input_empty + expect(find('.js-assignee-search')).to have_content(user.name) end it 'shows created issues' do - find('.dropdown-menu').click_link("Issues I've created") + find('.search-input-container .dropdown-menu').click_link("Issues I've created") - expect(page).to have_selector('.filtered-search') - expect_tokens([author_token(user.name)]) - expect_filtered_search_input_empty + expect(find('.js-author-search')).to have_content(user.name) end end @@ -59,32 +35,97 @@ describe 'User uses header search field' do let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignee: user) } it 'shows assigned merge requests' do - find('.dropdown-menu').click_link('Merge requests assigned to me') + find('.search-input-container .dropdown-menu').click_link('Merge requests assigned to me') - expect(page).to have_selector('.merge-requests-holder') - expect_tokens([assignee_token(user.name)]) - expect_filtered_search_input_empty + expect(find('.js-assignee-search')).to have_content(user.name) end it 'shows created merge requests' do - find('.dropdown-menu').click_link("Merge requests I've created") + find('.search-input-container .dropdown-menu').click_link("Merge requests I've created") - expect(page).to have_selector('.merge-requests-holder') - expect_tokens([author_token(user.name)]) - expect_filtered_search_input_empty + expect(find('.js-author-search')).to have_content(user.name) end end end - context 'when entering text into the search field', :js do + context 'when user is in a project scope' do before do - page.within('.search-input-wrap') do - fill_in('search', with: project.name[0..3]) + visit(project_path(project)) + end + + it 'starts searching by pressing the enter key', :js do + fill_in('search', with: 'gitlab') + find('#search').native.send_keys(:enter) + + page.within('.breadcrumbs-sub-title') do + expect(page).to have_content('Search') end end - it 'does not display the category search dropdown' do - expect(page).not_to have_selector('.dropdown-header', text: /#{project.name}/i) + it 'contains location badge' do + expect(page).to have_selector('.has-location-badge') + end + + context 'when clicking the search field', :js do + before do + page.find('#search').click + end + + it 'shows category search dropdown' do + expect(page).to have_selector('.dropdown-header', text: /#{project.name}/i) + end + + context 'when clicking issues' do + let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) } + + it 'shows assigned issues' do + find('.dropdown-menu').click_link('Issues assigned to me') + + expect(page).to have_selector('.filtered-search') + expect_tokens([assignee_token(user.name)]) + expect_filtered_search_input_empty + end + + it 'shows created issues' do + find('.dropdown-menu').click_link("Issues I've created") + + expect(page).to have_selector('.filtered-search') + expect_tokens([author_token(user.name)]) + expect_filtered_search_input_empty + end + end + + context 'when clicking merge requests' do + let!(:merge_request) { create(:merge_request, source_project: project, author: user, assignee: user) } + + it 'shows assigned merge requests' do + find('.dropdown-menu').click_link('Merge requests assigned to me') + + expect(page).to have_selector('.merge-requests-holder') + expect_tokens([assignee_token(user.name)]) + expect_filtered_search_input_empty + end + + it 'shows created merge requests' do + find('.dropdown-menu').click_link("Merge requests I've created") + + expect(page).to have_selector('.merge-requests-holder') + expect_tokens([author_token(user.name)]) + expect_filtered_search_input_empty + end + end + end + + context 'when entering text into the search field', :js do + before do + page.within('.search-input-wrap') do + fill_in('search', with: project.name[0..3]) + end + end + + it 'does not display the category search dropdown' do + expect(page).not_to have_selector('.dropdown-header', text: /#{project.name}/i) + end end end end diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json index b579e32c9aa..8833825e3fb 100644 --- a/spec/fixtures/api/schemas/issue.json +++ b/spec/fixtures/api/schemas/issue.json @@ -15,6 +15,8 @@ "relative_position": { "type": "integer" }, "issue_sidebar_endpoint": { "type": "string" }, "toggle_subscription_endpoint": { "type": "string" }, + "reference_path": { "type": "string" }, + "real_path": { "type": "string" }, "project": { "id": { "type": "integer" }, "path": { "type": "string" } diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index 2fecd1a3d27..4224cea4652 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -40,22 +40,22 @@ describe IssuablesHelper do end it 'returns "Open" when state is :opened' do - expect(helper.issuables_state_counter_text(:issues, :opened)) + expect(helper.issuables_state_counter_text(:issues, :opened, true)) .to eq('<span>Open</span> <span class="badge">42</span>') end it 'returns "Closed" when state is :closed' do - expect(helper.issuables_state_counter_text(:issues, :closed)) + expect(helper.issuables_state_counter_text(:issues, :closed, true)) .to eq('<span>Closed</span> <span class="badge">42</span>') end it 'returns "Merged" when state is :merged' do - expect(helper.issuables_state_counter_text(:merge_requests, :merged)) + expect(helper.issuables_state_counter_text(:merge_requests, :merged, true)) .to eq('<span>Merged</span> <span class="badge">42</span>') end it 'returns "All" when state is :all' do - expect(helper.issuables_state_counter_text(:merge_requests, :all)) + expect(helper.issuables_state_counter_text(:merge_requests, :all, true)) .to eq('<span>All</span> <span class="badge">42</span>') end end @@ -101,27 +101,6 @@ describe IssuablesHelper do end end - describe '#issuable_filter_present?' do - it 'returns true when any key is present' do - allow(helper).to receive(:params).and_return( - ActionController::Parameters.new(milestone_title: 'Velit consectetur asperiores natus delectus.', - project_id: 'gitlabhq', - scope: 'all') - ) - - expect(helper.issuable_filter_present?).to be_truthy - end - - it 'returns false when no key is present' do - allow(helper).to receive(:params).and_return( - ActionController::Parameters.new(project_id: 'gitlabhq', - scope: 'all') - ) - - expect(helper.issuable_filter_present?).to be_falsey - end - end - describe '#updated_at_by' do let(:user) { create(:user) } let(:unedited_issuable) { create(:issue) } diff --git a/spec/initializers/artifacts_direct_upload_support_spec.rb b/spec/initializers/artifacts_direct_upload_support_spec.rb new file mode 100644 index 00000000000..bfb71da3388 --- /dev/null +++ b/spec/initializers/artifacts_direct_upload_support_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe 'Artifacts direct upload support' do + subject do + load Rails.root.join('config/initializers/artifacts_direct_upload_support.rb') + end + + let(:connection) do + { provider: provider } + end + + before do + stub_artifacts_setting( + object_store: { + enabled: enabled, + direct_upload: direct_upload, + connection: connection + }) + end + + context 'when object storage is enabled' do + let(:enabled) { true } + + context 'when direct upload is enabled' do + let(:direct_upload) { true } + + context 'when provider is Google' do + let(:provider) { 'Google' } + + it 'succeeds' do + expect { subject }.not_to raise_error + end + end + + context 'when connection is empty' do + let(:connection) { nil } + + it 'raises an error' do + expect { subject }.to raise_error /object storage provider when 'direct_upload' of artifacts is used/ + end + end + + context 'when other provider is used' do + let(:provider) { 'AWS' } + + it 'raises an error' do + expect { subject }.to raise_error /object storage provider when 'direct_upload' of artifacts is used/ + end + end + end + + context 'when direct upload is disabled' do + let(:direct_upload) { false } + let(:provider) { 'AWS' } + + it 'succeeds' do + expect { subject }.not_to raise_error + end + end + end + + context 'when object storage is disabled' do + let(:enabled) { false } + let(:direct_upload) { false } + let(:provider) { 'AWS' } + + it 'succeeds' do + expect { subject }.not_to raise_error + end + end +end diff --git a/spec/javascripts/badges/components/badge_form_spec.js b/spec/javascripts/badges/components/badge_form_spec.js new file mode 100644 index 00000000000..dd21ec279cb --- /dev/null +++ b/spec/javascripts/badges/components/badge_form_spec.js @@ -0,0 +1,171 @@ +import Vue from 'vue'; +import store from '~/badges/store'; +import BadgeForm from '~/badges/components/badge_form.vue'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createDummyBadge } from '../dummy_badge'; + +describe('BadgeForm component', () => { + const Component = Vue.extend(BadgeForm); + let vm; + + beforeEach(() => { + setFixtures(` + <div id="dummy-element"></div> + `); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('methods', () => { + beforeEach(() => { + vm = mountComponentWithStore(Component, { + el: '#dummy-element', + store, + props: { + isEditing: false, + }, + }); + }); + + describe('onCancel', () => { + it('calls stopEditing', () => { + spyOn(vm, 'stopEditing'); + + vm.onCancel(); + + expect(vm.stopEditing).toHaveBeenCalled(); + }); + }); + + describe('onSubmit', () => { + describe('if isEditing is true', () => { + beforeEach(() => { + spyOn(vm, 'saveBadge').and.returnValue(Promise.resolve()); + store.replaceState({ + ...store.state, + isSaving: false, + badgeInEditForm: createDummyBadge(), + }); + vm.isEditing = true; + }); + + it('returns immediately if imageUrl is empty', () => { + store.state.badgeInEditForm.imageUrl = ''; + + vm.onSubmit(); + + expect(vm.saveBadge).not.toHaveBeenCalled(); + }); + + it('returns immediately if linkUrl is empty', () => { + store.state.badgeInEditForm.linkUrl = ''; + + vm.onSubmit(); + + expect(vm.saveBadge).not.toHaveBeenCalled(); + }); + + it('returns immediately if isSaving is true', () => { + store.state.isSaving = true; + + vm.onSubmit(); + + expect(vm.saveBadge).not.toHaveBeenCalled(); + }); + + it('calls saveBadge', () => { + vm.onSubmit(); + + expect(vm.saveBadge).toHaveBeenCalled(); + }); + }); + + describe('if isEditing is false', () => { + beforeEach(() => { + spyOn(vm, 'addBadge').and.returnValue(Promise.resolve()); + store.replaceState({ + ...store.state, + isSaving: false, + badgeInAddForm: createDummyBadge(), + }); + vm.isEditing = false; + }); + + it('returns immediately if imageUrl is empty', () => { + store.state.badgeInAddForm.imageUrl = ''; + + vm.onSubmit(); + + expect(vm.addBadge).not.toHaveBeenCalled(); + }); + + it('returns immediately if linkUrl is empty', () => { + store.state.badgeInAddForm.linkUrl = ''; + + vm.onSubmit(); + + expect(vm.addBadge).not.toHaveBeenCalled(); + }); + + it('returns immediately if isSaving is true', () => { + store.state.isSaving = true; + + vm.onSubmit(); + + expect(vm.addBadge).not.toHaveBeenCalled(); + }); + + it('calls addBadge', () => { + vm.onSubmit(); + + expect(vm.addBadge).toHaveBeenCalled(); + }); + }); + }); + }); + + describe('if isEditing is false', () => { + beforeEach(() => { + vm = mountComponentWithStore(Component, { + el: '#dummy-element', + store, + props: { + isEditing: false, + }, + }); + }); + + it('renders one button', () => { + const buttons = vm.$el.querySelectorAll('.row-content-block button'); + expect(buttons.length).toBe(1); + const buttonAddElement = buttons[0]; + expect(buttonAddElement).toBeVisible(); + expect(buttonAddElement).toHaveText('Add badge'); + }); + }); + + describe('if isEditing is true', () => { + beforeEach(() => { + vm = mountComponentWithStore(Component, { + el: '#dummy-element', + store, + props: { + isEditing: true, + }, + }); + }); + + it('renders two buttons', () => { + const buttons = vm.$el.querySelectorAll('.row-content-block button'); + expect(buttons.length).toBe(2); + const buttonSaveElement = buttons[0]; + expect(buttonSaveElement).toBeVisible(); + expect(buttonSaveElement).toHaveText('Save changes'); + const buttonCancelElement = buttons[1]; + expect(buttonCancelElement).toBeVisible(); + expect(buttonCancelElement).toHaveText('Cancel'); + }); + }); +}); diff --git a/spec/javascripts/badges/components/badge_list_row_spec.js b/spec/javascripts/badges/components/badge_list_row_spec.js new file mode 100644 index 00000000000..21bd00d82f0 --- /dev/null +++ b/spec/javascripts/badges/components/badge_list_row_spec.js @@ -0,0 +1,97 @@ +import $ from 'jquery'; +import Vue from 'vue'; +import { GROUP_BADGE, PROJECT_BADGE } from '~/badges/constants'; +import store from '~/badges/store'; +import BadgeListRow from '~/badges/components/badge_list_row.vue'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createDummyBadge } from '../dummy_badge'; + +describe('BadgeListRow component', () => { + const Component = Vue.extend(BadgeListRow); + let badge; + let vm; + + beforeEach(() => { + setFixtures(` + <div id="delete-badge-modal" class="modal"></div> + <div id="dummy-element"></div> + `); + store.replaceState({ + ...store.state, + kind: PROJECT_BADGE, + }); + badge = createDummyBadge(); + vm = mountComponentWithStore(Component, { + el: '#dummy-element', + store, + props: { badge }, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders the badge', () => { + const badgeElement = vm.$el.querySelector('.project-badge'); + expect(badgeElement).not.toBeNull(); + expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl); + }); + + it('renders the badge link', () => { + expect(vm.$el).toContainText(badge.linkUrl); + }); + + it('renders the badge kind', () => { + expect(vm.$el).toContainText('Project Badge'); + }); + + it('shows edit and delete buttons', () => { + const buttons = vm.$el.querySelectorAll('.table-button-footer button'); + expect(buttons).toHaveLength(2); + const buttonEditElement = buttons[0]; + expect(buttonEditElement).toBeVisible(); + expect(buttonEditElement).toHaveSpriteIcon('pencil'); + const buttonDeleteElement = buttons[1]; + expect(buttonDeleteElement).toBeVisible(); + expect(buttonDeleteElement).toHaveSpriteIcon('remove'); + }); + + it('calls editBadge when clicking then edit button', () => { + spyOn(vm, 'editBadge'); + + const editButton = vm.$el.querySelector('.table-button-footer button:first-of-type'); + editButton.click(); + + expect(vm.editBadge).toHaveBeenCalled(); + }); + + it('calls updateBadgeInModal and shows modal when clicking then delete button', done => { + spyOn(vm, 'updateBadgeInModal'); + $('#delete-badge-modal').on('shown.bs.modal', () => done()); + + const deleteButton = vm.$el.querySelector('.table-button-footer button:last-of-type'); + deleteButton.click(); + + expect(vm.updateBadgeInModal).toHaveBeenCalled(); + }); + + describe('for a group badge', () => { + beforeEach(done => { + badge.kind = GROUP_BADGE; + + Vue.nextTick() + .then(done) + .catch(done.fail); + }); + + it('renders the badge kind', () => { + expect(vm.$el).toContainText('Group Badge'); + }); + + it('hides edit and delete buttons', () => { + const buttons = vm.$el.querySelectorAll('.table-button-footer button'); + expect(buttons).toHaveLength(0); + }); + }); +}); diff --git a/spec/javascripts/badges/components/badge_list_spec.js b/spec/javascripts/badges/components/badge_list_spec.js new file mode 100644 index 00000000000..9439c578973 --- /dev/null +++ b/spec/javascripts/badges/components/badge_list_spec.js @@ -0,0 +1,88 @@ +import Vue from 'vue'; +import { GROUP_BADGE, PROJECT_BADGE } from '~/badges/constants'; +import store from '~/badges/store'; +import BadgeList from '~/badges/components/badge_list.vue'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createDummyBadge } from '../dummy_badge'; + +describe('BadgeList component', () => { + const Component = Vue.extend(BadgeList); + const numberOfDummyBadges = 3; + let vm; + + beforeEach(() => { + setFixtures('<div id="dummy-element"></div>'); + const badges = []; + for (let id = 0; id < numberOfDummyBadges; id += 1) { + badges.push({ id, ...createDummyBadge() }); + } + store.replaceState({ + ...store.state, + badges, + kind: PROJECT_BADGE, + isLoading: false, + }); + vm = mountComponentWithStore(Component, { + el: '#dummy-element', + store, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders a header with the badge count', () => { + const header = vm.$el.querySelector('.panel-heading'); + expect(header).toHaveText(new RegExp(`Your badges\\s+${numberOfDummyBadges}`)); + }); + + it('renders a row for each badge', () => { + const rows = vm.$el.querySelectorAll('.gl-responsive-table-row'); + expect(rows).toHaveLength(numberOfDummyBadges); + }); + + it('renders a message if no badges exist', done => { + store.state.badges = []; + + Vue.nextTick() + .then(() => { + expect(vm.$el).toContainText('This project has no badges'); + }) + .then(done) + .catch(done.fail); + }); + + it('shows a loading icon when loading', done => { + store.state.isLoading = true; + + Vue.nextTick() + .then(() => { + const loadingIcon = vm.$el.querySelector('.fa-spinner'); + expect(loadingIcon).toBeVisible(); + }) + .then(done) + .catch(done.fail); + }); + + describe('for group badges', () => { + beforeEach(done => { + store.state.kind = GROUP_BADGE; + + Vue.nextTick() + .then(done) + .catch(done.fail); + }); + + it('renders a message if no badges exist', done => { + store.state.badges = []; + + Vue.nextTick() + .then(() => { + expect(vm.$el).toContainText('This group has no badges'); + }) + .then(done) + .catch(done.fail); + }); + }); +}); diff --git a/spec/javascripts/badges/components/badge_settings_spec.js b/spec/javascripts/badges/components/badge_settings_spec.js new file mode 100644 index 00000000000..3db02982ad4 --- /dev/null +++ b/spec/javascripts/badges/components/badge_settings_spec.js @@ -0,0 +1,109 @@ +import $ from 'jquery'; +import Vue from 'vue'; +import store from '~/badges/store'; +import BadgeSettings from '~/badges/components/badge_settings.vue'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { createDummyBadge } from '../dummy_badge'; + +describe('BadgeSettings component', () => { + const Component = Vue.extend(BadgeSettings); + let vm; + + beforeEach(() => { + setFixtures(` + <div id="dummy-element"></div> + <button + id="dummy-modal-button" + type="button" + data-toggle="modal" + data-target="#delete-badge-modal" + >Show modal</button> + `); + vm = mountComponentWithStore(Component, { + el: '#dummy-element', + store, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('displays modal if button is clicked', done => { + const badge = createDummyBadge(); + store.state.badgeInModal = badge; + const modal = vm.$el.querySelector('#delete-badge-modal'); + const button = document.getElementById('dummy-modal-button'); + + $(modal).on('shown.bs.modal', () => { + expect(modal).toContainText('Delete badge?'); + const badgeElement = modal.querySelector('img.project-badge'); + expect(badgeElement).not.toBe(null); + expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl); + + done(); + }); + + Vue.nextTick() + .then(() => { + button.click(); + }) + .catch(done.fail); + }); + + it('displays a form to add a badge', () => { + const form = vm.$el.querySelector('form:nth-of-type(2)'); + expect(form).not.toBe(null); + const button = form.querySelector('.btn-success'); + expect(button).not.toBe(null); + expect(button).toHaveText(/Add badge/); + }); + + it('displays badge list', () => { + const badgeListElement = vm.$el.querySelector('.panel'); + expect(badgeListElement).not.toBe(null); + expect(badgeListElement).toBeVisible(); + expect(badgeListElement).toContainText('Your badges'); + }); + + describe('when editing', () => { + beforeEach(done => { + store.state.isEditing = true; + + Vue.nextTick() + .then(done) + .catch(done.fail); + }); + + it('displays a form to edit a badge', () => { + const form = vm.$el.querySelector('form:nth-of-type(1)'); + expect(form).not.toBe(null); + const submitButton = form.querySelector('.btn-success'); + expect(submitButton).not.toBe(null); + expect(submitButton).toHaveText(/Save changes/); + const cancelButton = form.querySelector('.btn-cancel'); + expect(cancelButton).not.toBe(null); + expect(cancelButton).toHaveText(/Cancel/); + }); + + it('displays no badge list', () => { + const badgeListElement = vm.$el.querySelector('.panel'); + expect(badgeListElement).toBeHidden(); + }); + }); + + describe('methods', () => { + describe('onSubmitModal', () => { + it('triggers ', () => { + spyOn(vm, 'deleteBadge').and.callFake(() => Promise.resolve()); + const modal = vm.$el.querySelector('#delete-badge-modal'); + const deleteButton = modal.querySelector('.btn-danger'); + + deleteButton.click(); + + const badge = store.state.badgeInModal; + expect(vm.deleteBadge).toHaveBeenCalledWith(badge); + }); + }); + }); +}); diff --git a/spec/javascripts/badges/components/badge_spec.js b/spec/javascripts/badges/components/badge_spec.js new file mode 100644 index 00000000000..fd1ecc9cdd8 --- /dev/null +++ b/spec/javascripts/badges/components/badge_spec.js @@ -0,0 +1,147 @@ +import Vue from 'vue'; +import Badge from '~/badges/components/badge.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import { DUMMY_IMAGE_URL, TEST_HOST } from 'spec/test_constants'; + +describe('Badge component', () => { + const Component = Vue.extend(Badge); + const dummyProps = { + imageUrl: DUMMY_IMAGE_URL, + linkUrl: `${TEST_HOST}/badge/link/url`, + }; + let vm; + + const findElements = () => { + const buttons = vm.$el.querySelectorAll('button'); + return { + badgeImage: vm.$el.querySelector('img.project-badge'), + loadingIcon: vm.$el.querySelector('.fa-spinner'), + reloadButton: buttons[buttons.length - 1], + }; + }; + + const createComponent = (props, el = null) => { + vm = mountComponent(Component, props, el); + const { badgeImage } = findElements(); + return new Promise(resolve => badgeImage.addEventListener('load', resolve)).then(() => + Vue.nextTick(), + ); + }; + + afterEach(() => { + vm.$destroy(); + }); + + describe('watchers', () => { + describe('imageUrl', () => { + it('sets isLoading and resets numRetries and hasError', done => { + const props = { ...dummyProps }; + createComponent(props) + .then(() => { + expect(vm.isLoading).toBe(false); + vm.hasError = true; + vm.numRetries = 42; + + vm.imageUrl = `${props.imageUrl}#something/else`; + + return Vue.nextTick(); + }) + .then(() => { + expect(vm.isLoading).toBe(true); + expect(vm.numRetries).toBe(0); + expect(vm.hasError).toBe(false); + }) + .then(done) + .catch(done.fail); + }); + }); + }); + + describe('methods', () => { + beforeEach(done => { + createComponent({ ...dummyProps }) + .then(done) + .catch(done.fail); + }); + + it('onError resets isLoading and sets hasError', () => { + vm.hasError = false; + vm.isLoading = true; + + vm.onError(); + + expect(vm.hasError).toBe(true); + expect(vm.isLoading).toBe(false); + }); + + it('onLoad sets isLoading', () => { + vm.isLoading = true; + + vm.onLoad(); + + expect(vm.isLoading).toBe(false); + }); + + it('reloadImage resets isLoading and hasError and increases numRetries', () => { + vm.hasError = true; + vm.isLoading = false; + vm.numRetries = 0; + + vm.reloadImage(); + + expect(vm.hasError).toBe(false); + expect(vm.isLoading).toBe(true); + expect(vm.numRetries).toBe(1); + }); + }); + + describe('behavior', () => { + beforeEach(done => { + setFixtures('<div id="dummy-element"></div>'); + createComponent({ ...dummyProps }, '#dummy-element') + .then(done) + .catch(done.fail); + }); + + it('shows a badge image after loading', () => { + expect(vm.isLoading).toBe(false); + expect(vm.hasError).toBe(false); + const { badgeImage, loadingIcon, reloadButton } = findElements(); + expect(badgeImage).toBeVisible(); + expect(loadingIcon).toBeHidden(); + expect(reloadButton).toBeHidden(); + expect(vm.$el.innerText).toBe(''); + }); + + it('shows a loading icon when loading', done => { + vm.isLoading = true; + + Vue.nextTick() + .then(() => { + const { badgeImage, loadingIcon, reloadButton } = findElements(); + expect(badgeImage).toBeHidden(); + expect(loadingIcon).toBeVisible(); + expect(reloadButton).toBeHidden(); + expect(vm.$el.innerText).toBe(''); + }) + .then(done) + .catch(done.fail); + }); + + it('shows an error and reload button if loading failed', done => { + vm.hasError = true; + + Vue.nextTick() + .then(() => { + const { badgeImage, loadingIcon, reloadButton } = findElements(); + expect(badgeImage).toBeHidden(); + expect(loadingIcon).toBeHidden(); + expect(reloadButton).toBeVisible(); + expect(reloadButton).toHaveSpriteIcon('retry'); + expect(vm.$el.innerText.trim()).toBe('No badge image'); + }) + .then(done) + .catch(done.fail); + }); + }); +}); diff --git a/spec/javascripts/badges/dummy_badge.js b/spec/javascripts/badges/dummy_badge.js new file mode 100644 index 00000000000..6aaff21c503 --- /dev/null +++ b/spec/javascripts/badges/dummy_badge.js @@ -0,0 +1,23 @@ +import { PROJECT_BADGE } from '~/badges/constants'; +import { DUMMY_IMAGE_URL, TEST_HOST } from 'spec/test_constants'; + +export const createDummyBadge = () => { + const id = Math.floor(1000 * Math.random()); + return { + id, + imageUrl: `${TEST_HOST}/badges/${id}/image/url`, + isDeleting: false, + linkUrl: `${TEST_HOST}/badges/${id}/link/url`, + kind: PROJECT_BADGE, + renderedImageUrl: `${DUMMY_IMAGE_URL}?id=${id}`, + renderedLinkUrl: `${TEST_HOST}/badges/${id}/rendered/link/url`, + }; +}; + +export const createDummyBadgeResponse = () => ({ + image_url: `${TEST_HOST}/badge/image/url`, + link_url: `${TEST_HOST}/badge/link/url`, + kind: PROJECT_BADGE, + rendered_image_url: DUMMY_IMAGE_URL, + rendered_link_url: `${TEST_HOST}/rendered/badge/link/url`, +}); diff --git a/spec/javascripts/badges/store/actions_spec.js b/spec/javascripts/badges/store/actions_spec.js new file mode 100644 index 00000000000..bb6263c6de4 --- /dev/null +++ b/spec/javascripts/badges/store/actions_spec.js @@ -0,0 +1,607 @@ +import axios from '~/lib/utils/axios_utils'; +import MockAdapter from 'axios-mock-adapter'; +import actions, { transformBackendBadge } from '~/badges/store/actions'; +import mutationTypes from '~/badges/store/mutation_types'; +import createState from '~/badges/store/state'; +import { TEST_HOST } from 'spec/test_constants'; +import testAction from 'spec/helpers/vuex_action_helper'; +import { createDummyBadge, createDummyBadgeResponse } from '../dummy_badge'; + +describe('Badges store actions', () => { + const dummyEndpointUrl = `${TEST_HOST}/badges/endpoint`; + const dummyBadges = [{ ...createDummyBadge(), id: 5 }, { ...createDummyBadge(), id: 6 }]; + + let axiosMock; + let badgeId; + let state; + + beforeEach(() => { + axiosMock = new MockAdapter(axios); + state = { + ...createState(), + apiEndpointUrl: dummyEndpointUrl, + badges: dummyBadges, + }; + badgeId = state.badges[0].id; + }); + + afterEach(() => { + axiosMock.restore(); + }); + + describe('requestNewBadge', () => { + it('commits REQUEST_NEW_BADGE', done => { + testAction( + actions.requestNewBadge, + null, + state, + [{ type: mutationTypes.REQUEST_NEW_BADGE }], + [], + done, + ); + }); + }); + + describe('receiveNewBadge', () => { + it('commits RECEIVE_NEW_BADGE', done => { + const newBadge = createDummyBadge(); + testAction( + actions.receiveNewBadge, + newBadge, + state, + [{ type: mutationTypes.RECEIVE_NEW_BADGE, payload: newBadge }], + [], + done, + ); + }); + }); + + describe('receiveNewBadgeError', () => { + it('commits RECEIVE_NEW_BADGE_ERROR', done => { + testAction( + actions.receiveNewBadgeError, + null, + state, + [{ type: mutationTypes.RECEIVE_NEW_BADGE_ERROR }], + [], + done, + ); + }); + }); + + describe('addBadge', () => { + let badgeInAddForm; + let dispatch; + let endpointMock; + + beforeEach(() => { + endpointMock = axiosMock.onPost(dummyEndpointUrl); + dispatch = jasmine.createSpy('dispatch'); + badgeInAddForm = createDummyBadge(); + state = { + ...state, + badgeInAddForm, + }; + }); + + it('dispatches requestNewBadge and receiveNewBadge for successful response', done => { + const dummyResponse = createDummyBadgeResponse(); + + endpointMock.replyOnce(req => { + expect(req.data).toBe( + JSON.stringify({ + image_url: badgeInAddForm.imageUrl, + link_url: badgeInAddForm.linkUrl, + }), + ); + expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]); + dispatch.calls.reset(); + return [200, dummyResponse]; + }); + + const dummyBadge = transformBackendBadge(dummyResponse); + actions + .addBadge({ state, dispatch }) + .then(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveNewBadge', dummyBadge]]); + }) + .then(done) + .catch(done.fail); + }); + + it('dispatches requestNewBadge and receiveNewBadgeError for error response', done => { + endpointMock.replyOnce(req => { + expect(req.data).toBe( + JSON.stringify({ + image_url: badgeInAddForm.imageUrl, + link_url: badgeInAddForm.linkUrl, + }), + ); + expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]); + dispatch.calls.reset(); + return [500, '']; + }); + + actions + .addBadge({ state, dispatch }) + .then(() => done.fail('Expected Ajax call to fail!')) + .catch(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveNewBadgeError']]); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('requestDeleteBadge', () => { + it('commits REQUEST_DELETE_BADGE', done => { + testAction( + actions.requestDeleteBadge, + badgeId, + state, + [{ type: mutationTypes.REQUEST_DELETE_BADGE, payload: badgeId }], + [], + done, + ); + }); + }); + + describe('receiveDeleteBadge', () => { + it('commits RECEIVE_DELETE_BADGE', done => { + testAction( + actions.receiveDeleteBadge, + badgeId, + state, + [{ type: mutationTypes.RECEIVE_DELETE_BADGE, payload: badgeId }], + [], + done, + ); + }); + }); + + describe('receiveDeleteBadgeError', () => { + it('commits RECEIVE_DELETE_BADGE_ERROR', done => { + testAction( + actions.receiveDeleteBadgeError, + badgeId, + state, + [{ type: mutationTypes.RECEIVE_DELETE_BADGE_ERROR, payload: badgeId }], + [], + done, + ); + }); + }); + + describe('deleteBadge', () => { + let dispatch; + let endpointMock; + + beforeEach(() => { + endpointMock = axiosMock.onDelete(`${dummyEndpointUrl}/${badgeId}`); + dispatch = jasmine.createSpy('dispatch'); + }); + + it('dispatches requestDeleteBadge and receiveDeleteBadge for successful response', done => { + endpointMock.replyOnce(() => { + expect(dispatch.calls.allArgs()).toEqual([['requestDeleteBadge', badgeId]]); + dispatch.calls.reset(); + return [200, '']; + }); + + actions + .deleteBadge({ state, dispatch }, { id: badgeId }) + .then(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveDeleteBadge', badgeId]]); + }) + .then(done) + .catch(done.fail); + }); + + it('dispatches requestDeleteBadge and receiveDeleteBadgeError for error response', done => { + endpointMock.replyOnce(() => { + expect(dispatch.calls.allArgs()).toEqual([['requestDeleteBadge', badgeId]]); + dispatch.calls.reset(); + return [500, '']; + }); + + actions + .deleteBadge({ state, dispatch }, { id: badgeId }) + .then(() => done.fail('Expected Ajax call to fail!')) + .catch(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveDeleteBadgeError', badgeId]]); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('editBadge', () => { + it('commits START_EDITING', done => { + const dummyBadge = createDummyBadge(); + testAction( + actions.editBadge, + dummyBadge, + state, + [{ type: mutationTypes.START_EDITING, payload: dummyBadge }], + [], + done, + ); + }); + }); + + describe('requestLoadBadges', () => { + it('commits REQUEST_LOAD_BADGES', done => { + const dummyData = 'this is not real data'; + testAction( + actions.requestLoadBadges, + dummyData, + state, + [{ type: mutationTypes.REQUEST_LOAD_BADGES, payload: dummyData }], + [], + done, + ); + }); + }); + + describe('receiveLoadBadges', () => { + it('commits RECEIVE_LOAD_BADGES', done => { + const badges = dummyBadges; + testAction( + actions.receiveLoadBadges, + badges, + state, + [{ type: mutationTypes.RECEIVE_LOAD_BADGES, payload: badges }], + [], + done, + ); + }); + }); + + describe('receiveLoadBadgesError', () => { + it('commits RECEIVE_LOAD_BADGES_ERROR', done => { + testAction( + actions.receiveLoadBadgesError, + null, + state, + [{ type: mutationTypes.RECEIVE_LOAD_BADGES_ERROR }], + [], + done, + ); + }); + }); + + describe('loadBadges', () => { + let dispatch; + let endpointMock; + + beforeEach(() => { + endpointMock = axiosMock.onGet(dummyEndpointUrl); + dispatch = jasmine.createSpy('dispatch'); + }); + + it('dispatches requestLoadBadges and receiveLoadBadges for successful response', done => { + const dummyData = 'this is just some data'; + const dummyReponse = [ + createDummyBadgeResponse(), + createDummyBadgeResponse(), + createDummyBadgeResponse(), + ]; + endpointMock.replyOnce(() => { + expect(dispatch.calls.allArgs()).toEqual([['requestLoadBadges', dummyData]]); + dispatch.calls.reset(); + return [200, dummyReponse]; + }); + + actions + .loadBadges({ state, dispatch }, dummyData) + .then(() => { + const badges = dummyReponse.map(transformBackendBadge); + expect(dispatch.calls.allArgs()).toEqual([['receiveLoadBadges', badges]]); + }) + .then(done) + .catch(done.fail); + }); + + it('dispatches requestLoadBadges and receiveLoadBadgesError for error response', done => { + const dummyData = 'this is just some data'; + endpointMock.replyOnce(() => { + expect(dispatch.calls.allArgs()).toEqual([['requestLoadBadges', dummyData]]); + dispatch.calls.reset(); + return [500, '']; + }); + + actions + .loadBadges({ state, dispatch }, dummyData) + .then(() => done.fail('Expected Ajax call to fail!')) + .catch(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveLoadBadgesError']]); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('requestRenderedBadge', () => { + it('commits REQUEST_RENDERED_BADGE', done => { + testAction( + actions.requestRenderedBadge, + null, + state, + [{ type: mutationTypes.REQUEST_RENDERED_BADGE }], + [], + done, + ); + }); + }); + + describe('receiveRenderedBadge', () => { + it('commits RECEIVE_RENDERED_BADGE', done => { + const dummyBadge = createDummyBadge(); + testAction( + actions.receiveRenderedBadge, + dummyBadge, + state, + [{ type: mutationTypes.RECEIVE_RENDERED_BADGE, payload: dummyBadge }], + [], + done, + ); + }); + }); + + describe('receiveRenderedBadgeError', () => { + it('commits RECEIVE_RENDERED_BADGE_ERROR', done => { + testAction( + actions.receiveRenderedBadgeError, + null, + state, + [{ type: mutationTypes.RECEIVE_RENDERED_BADGE_ERROR }], + [], + done, + ); + }); + }); + + describe('renderBadge', () => { + let dispatch; + let endpointMock; + let badgeInForm; + + beforeEach(() => { + badgeInForm = createDummyBadge(); + state = { + ...state, + badgeInAddForm: badgeInForm, + }; + const urlParameters = [ + `link_url=${encodeURIComponent(badgeInForm.linkUrl)}`, + `image_url=${encodeURIComponent(badgeInForm.imageUrl)}`, + ].join('&'); + endpointMock = axiosMock.onGet(`${dummyEndpointUrl}/render?${urlParameters}`); + dispatch = jasmine.createSpy('dispatch'); + }); + + it('returns immediately if imageUrl is empty', done => { + spyOn(axios, 'get'); + badgeInForm.imageUrl = ''; + + actions + .renderBadge({ state, dispatch }) + .then(() => { + expect(axios.get).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + + it('returns immediately if linkUrl is empty', done => { + spyOn(axios, 'get'); + badgeInForm.linkUrl = ''; + + actions + .renderBadge({ state, dispatch }) + .then(() => { + expect(axios.get).not.toHaveBeenCalled(); + }) + .then(done) + .catch(done.fail); + }); + + it('escapes user input', done => { + spyOn(axios, 'get').and.callFake(() => Promise.resolve({ data: createDummyBadgeResponse() })); + badgeInForm.imageUrl = '&make-sandwhich=true'; + badgeInForm.linkUrl = '<script>I am dangerous!</script>'; + + actions + .renderBadge({ state, dispatch }) + .then(() => { + expect(axios.get.calls.count()).toBe(1); + const url = axios.get.calls.argsFor(0)[0]; + expect(url).toMatch(`^${dummyEndpointUrl}/render?`); + expect(url).toMatch('\\?link_url=%3Cscript%3EI%20am%20dangerous!%3C%2Fscript%3E&'); + expect(url).toMatch('&image_url=%26make-sandwhich%3Dtrue$'); + }) + .then(done) + .catch(done.fail); + }); + + it('dispatches requestRenderedBadge and receiveRenderedBadge for successful response', done => { + const dummyReponse = createDummyBadgeResponse(); + endpointMock.replyOnce(() => { + expect(dispatch.calls.allArgs()).toEqual([['requestRenderedBadge']]); + dispatch.calls.reset(); + return [200, dummyReponse]; + }); + + actions + .renderBadge({ state, dispatch }) + .then(() => { + const renderedBadge = transformBackendBadge(dummyReponse); + expect(dispatch.calls.allArgs()).toEqual([['receiveRenderedBadge', renderedBadge]]); + }) + .then(done) + .catch(done.fail); + }); + + it('dispatches requestRenderedBadge and receiveRenderedBadgeError for error response', done => { + endpointMock.replyOnce(() => { + expect(dispatch.calls.allArgs()).toEqual([['requestRenderedBadge']]); + dispatch.calls.reset(); + return [500, '']; + }); + + actions + .renderBadge({ state, dispatch }) + .then(() => done.fail('Expected Ajax call to fail!')) + .catch(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveRenderedBadgeError']]); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('requestUpdatedBadge', () => { + it('commits REQUEST_UPDATED_BADGE', done => { + testAction( + actions.requestUpdatedBadge, + null, + state, + [{ type: mutationTypes.REQUEST_UPDATED_BADGE }], + [], + done, + ); + }); + }); + + describe('receiveUpdatedBadge', () => { + it('commits RECEIVE_UPDATED_BADGE', done => { + const updatedBadge = createDummyBadge(); + testAction( + actions.receiveUpdatedBadge, + updatedBadge, + state, + [{ type: mutationTypes.RECEIVE_UPDATED_BADGE, payload: updatedBadge }], + [], + done, + ); + }); + }); + + describe('receiveUpdatedBadgeError', () => { + it('commits RECEIVE_UPDATED_BADGE_ERROR', done => { + testAction( + actions.receiveUpdatedBadgeError, + null, + state, + [{ type: mutationTypes.RECEIVE_UPDATED_BADGE_ERROR }], + [], + done, + ); + }); + }); + + describe('saveBadge', () => { + let badgeInEditForm; + let dispatch; + let endpointMock; + + beforeEach(() => { + badgeInEditForm = createDummyBadge(); + state = { + ...state, + badgeInEditForm, + }; + endpointMock = axiosMock.onPut(`${dummyEndpointUrl}/${badgeInEditForm.id}`); + dispatch = jasmine.createSpy('dispatch'); + }); + + it('dispatches requestUpdatedBadge and receiveUpdatedBadge for successful response', done => { + const dummyResponse = createDummyBadgeResponse(); + + endpointMock.replyOnce(req => { + expect(req.data).toBe( + JSON.stringify({ + image_url: badgeInEditForm.imageUrl, + link_url: badgeInEditForm.linkUrl, + }), + ); + expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]); + dispatch.calls.reset(); + return [200, dummyResponse]; + }); + + const updatedBadge = transformBackendBadge(dummyResponse); + actions + .saveBadge({ state, dispatch }) + .then(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveUpdatedBadge', updatedBadge]]); + }) + .then(done) + .catch(done.fail); + }); + + it('dispatches requestUpdatedBadge and receiveUpdatedBadgeError for error response', done => { + endpointMock.replyOnce(req => { + expect(req.data).toBe( + JSON.stringify({ + image_url: badgeInEditForm.imageUrl, + link_url: badgeInEditForm.linkUrl, + }), + ); + expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]); + dispatch.calls.reset(); + return [500, '']; + }); + + actions + .saveBadge({ state, dispatch }) + .then(() => done.fail('Expected Ajax call to fail!')) + .catch(() => { + expect(dispatch.calls.allArgs()).toEqual([['receiveUpdatedBadgeError']]); + }) + .then(done) + .catch(done.fail); + }); + }); + + describe('stopEditing', () => { + it('commits STOP_EDITING', done => { + testAction( + actions.stopEditing, + null, + state, + [{ type: mutationTypes.STOP_EDITING }], + [], + done, + ); + }); + }); + + describe('updateBadgeInForm', () => { + it('commits UPDATE_BADGE_IN_FORM', done => { + const dummyBadge = createDummyBadge(); + testAction( + actions.updateBadgeInForm, + dummyBadge, + state, + [{ type: mutationTypes.UPDATE_BADGE_IN_FORM, payload: dummyBadge }], + [], + done, + ); + }); + + describe('updateBadgeInModal', () => { + it('commits UPDATE_BADGE_IN_MODAL', done => { + const dummyBadge = createDummyBadge(); + testAction( + actions.updateBadgeInModal, + dummyBadge, + state, + [{ type: mutationTypes.UPDATE_BADGE_IN_MODAL, payload: dummyBadge }], + [], + done, + ); + }); + }); + }); +}); diff --git a/spec/javascripts/badges/store/mutations_spec.js b/spec/javascripts/badges/store/mutations_spec.js new file mode 100644 index 00000000000..8d26f83339d --- /dev/null +++ b/spec/javascripts/badges/store/mutations_spec.js @@ -0,0 +1,418 @@ +import { GROUP_BADGE, PROJECT_BADGE } from '~/badges/constants'; +import store from '~/badges/store'; +import types from '~/badges/store/mutation_types'; +import createState from '~/badges/store/state'; +import { createDummyBadge } from '../dummy_badge'; + +describe('Badges store mutations', () => { + let dummyBadge; + + beforeEach(() => { + dummyBadge = createDummyBadge(); + store.replaceState(createState()); + }); + + describe('RECEIVE_DELETE_BADGE', () => { + beforeEach(() => { + const badges = [ + { ...dummyBadge, id: dummyBadge.id - 1 }, + dummyBadge, + { ...dummyBadge, id: dummyBadge.id + 1 }, + ]; + + store.replaceState({ + ...store.state, + badges, + }); + }); + + it('removes deleted badge', () => { + const badgeCount = store.state.badges.length; + + store.commit(types.RECEIVE_DELETE_BADGE, dummyBadge.id); + + expect(store.state.badges.length).toBe(badgeCount - 1); + expect(store.state.badges.indexOf(dummyBadge)).toBe(-1); + }); + }); + + describe('RECEIVE_DELETE_BADGE_ERROR', () => { + beforeEach(() => { + const badges = [ + { ...dummyBadge, id: dummyBadge.id - 1, isDeleting: false }, + { ...dummyBadge, isDeleting: true }, + { ...dummyBadge, id: dummyBadge.id + 1, isDeleting: true }, + ]; + + store.replaceState({ + ...store.state, + badges, + }); + }); + + it('sets isDeleting to false', () => { + const badgeCount = store.state.badges.length; + + store.commit(types.RECEIVE_DELETE_BADGE_ERROR, dummyBadge.id); + + expect(store.state.badges.length).toBe(badgeCount); + expect(store.state.badges[0].isDeleting).toBe(false); + expect(store.state.badges[1].isDeleting).toBe(false); + expect(store.state.badges[2].isDeleting).toBe(true); + }); + }); + + describe('RECEIVE_LOAD_BADGES', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isLoading: 'not false', + }); + }); + + it('sets badges and isLoading to false', () => { + const badges = [createDummyBadge()]; + store.commit(types.RECEIVE_LOAD_BADGES, badges); + + expect(store.state.isLoading).toBe(false); + expect(store.state.badges).toBe(badges); + }); + }); + + describe('RECEIVE_LOAD_BADGES_ERROR', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isLoading: 'not false', + }); + }); + + it('sets isLoading to false', () => { + store.commit(types.RECEIVE_LOAD_BADGES_ERROR); + + expect(store.state.isLoading).toBe(false); + }); + }); + + describe('RECEIVE_NEW_BADGE', () => { + beforeEach(() => { + const badges = [ + { ...dummyBadge, id: dummyBadge.id - 1, kind: GROUP_BADGE }, + { ...dummyBadge, id: dummyBadge.id + 1, kind: GROUP_BADGE }, + { ...dummyBadge, id: dummyBadge.id - 1, kind: PROJECT_BADGE }, + { ...dummyBadge, id: dummyBadge.id + 1, kind: PROJECT_BADGE }, + ]; + store.replaceState({ + ...store.state, + badgeInAddForm: createDummyBadge(), + badges, + isSaving: 'dummy value', + renderedBadge: createDummyBadge(), + }); + }); + + it('resets the add form', () => { + store.commit(types.RECEIVE_NEW_BADGE, dummyBadge); + + expect(store.state.badgeInAddForm).toBe(null); + expect(store.state.isSaving).toBe(false); + expect(store.state.renderedBadge).toBe(null); + }); + + it('inserts group badge at correct position', () => { + const badgeCount = store.state.badges.length; + dummyBadge = { ...dummyBadge, kind: GROUP_BADGE }; + + store.commit(types.RECEIVE_NEW_BADGE, dummyBadge); + + expect(store.state.badges.length).toBe(badgeCount + 1); + expect(store.state.badges.indexOf(dummyBadge)).toBe(1); + }); + + it('inserts project badge at correct position', () => { + const badgeCount = store.state.badges.length; + dummyBadge = { ...dummyBadge, kind: PROJECT_BADGE }; + + store.commit(types.RECEIVE_NEW_BADGE, dummyBadge); + + expect(store.state.badges.length).toBe(badgeCount + 1); + expect(store.state.badges.indexOf(dummyBadge)).toBe(3); + }); + }); + + describe('RECEIVE_NEW_BADGE_ERROR', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isSaving: 'dummy value', + }); + }); + + it('sets isSaving to false', () => { + store.commit(types.RECEIVE_NEW_BADGE_ERROR); + + expect(store.state.isSaving).toBe(false); + }); + }); + + describe('RECEIVE_RENDERED_BADGE', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isRendering: 'dummy value', + renderedBadge: 'dummy value', + }); + }); + + it('sets renderedBadge', () => { + store.commit(types.RECEIVE_RENDERED_BADGE, dummyBadge); + + expect(store.state.isRendering).toBe(false); + expect(store.state.renderedBadge).toBe(dummyBadge); + }); + }); + + describe('RECEIVE_RENDERED_BADGE_ERROR', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isRendering: 'dummy value', + }); + }); + + it('sets isRendering to false', () => { + store.commit(types.RECEIVE_RENDERED_BADGE_ERROR); + + expect(store.state.isRendering).toBe(false); + }); + }); + + describe('RECEIVE_UPDATED_BADGE', () => { + beforeEach(() => { + const badges = [ + { ...dummyBadge, id: dummyBadge.id - 1 }, + dummyBadge, + { ...dummyBadge, id: dummyBadge.id + 1 }, + ]; + store.replaceState({ + ...store.state, + badgeInEditForm: createDummyBadge(), + badges, + isEditing: 'dummy value', + isSaving: 'dummy value', + renderedBadge: createDummyBadge(), + }); + }); + + it('resets the edit form', () => { + store.commit(types.RECEIVE_UPDATED_BADGE, dummyBadge); + + expect(store.state.badgeInAddForm).toBe(null); + expect(store.state.isSaving).toBe(false); + expect(store.state.renderedBadge).toBe(null); + }); + + it('replaces the updated badge', () => { + const badgeCount = store.state.badges.length; + const badgeIndex = store.state.badges.indexOf(dummyBadge); + const newBadge = { id: dummyBadge.id, dummy: 'value' }; + + store.commit(types.RECEIVE_UPDATED_BADGE, newBadge); + + expect(store.state.badges.length).toBe(badgeCount); + expect(store.state.badges[badgeIndex]).toBe(newBadge); + }); + }); + + describe('RECEIVE_UPDATED_BADGE_ERROR', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isSaving: 'dummy value', + }); + }); + + it('sets isSaving to false', () => { + store.commit(types.RECEIVE_NEW_BADGE_ERROR); + + expect(store.state.isSaving).toBe(false); + }); + }); + + describe('REQUEST_DELETE_BADGE', () => { + beforeEach(() => { + const badges = [ + { ...dummyBadge, id: dummyBadge.id - 1, isDeleting: false }, + { ...dummyBadge, isDeleting: false }, + { ...dummyBadge, id: dummyBadge.id + 1, isDeleting: true }, + ]; + + store.replaceState({ + ...store.state, + badges, + }); + }); + + it('sets isDeleting to true', () => { + const badgeCount = store.state.badges.length; + + store.commit(types.REQUEST_DELETE_BADGE, dummyBadge.id); + + expect(store.state.badges.length).toBe(badgeCount); + expect(store.state.badges[0].isDeleting).toBe(false); + expect(store.state.badges[1].isDeleting).toBe(true); + expect(store.state.badges[2].isDeleting).toBe(true); + }); + }); + + describe('REQUEST_LOAD_BADGES', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + apiEndpointUrl: 'some endpoint', + docsUrl: 'some url', + isLoading: 'dummy value', + kind: 'some kind', + }); + }); + + it('sets isLoading to true and initializes the store', () => { + const dummyData = { + apiEndpointUrl: 'dummy endpoint', + docsUrl: 'dummy url', + kind: 'dummy kind', + }; + + store.commit(types.REQUEST_LOAD_BADGES, dummyData); + + expect(store.state.isLoading).toBe(true); + expect(store.state.apiEndpointUrl).toBe(dummyData.apiEndpointUrl); + expect(store.state.docsUrl).toBe(dummyData.docsUrl); + expect(store.state.kind).toBe(dummyData.kind); + }); + }); + + describe('REQUEST_NEW_BADGE', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isSaving: 'dummy value', + }); + }); + + it('sets isSaving to true', () => { + store.commit(types.REQUEST_NEW_BADGE); + + expect(store.state.isSaving).toBe(true); + }); + }); + + describe('REQUEST_RENDERED_BADGE', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isRendering: 'dummy value', + }); + }); + + it('sets isRendering to true', () => { + store.commit(types.REQUEST_RENDERED_BADGE); + + expect(store.state.isRendering).toBe(true); + }); + }); + + describe('REQUEST_UPDATED_BADGE', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + isSaving: 'dummy value', + }); + }); + + it('sets isSaving to true', () => { + store.commit(types.REQUEST_NEW_BADGE); + + expect(store.state.isSaving).toBe(true); + }); + }); + + describe('START_EDITING', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + badgeInEditForm: 'dummy value', + isEditing: 'dummy value', + renderedBadge: 'dummy value', + }); + }); + + it('initializes the edit form', () => { + store.commit(types.START_EDITING, dummyBadge); + + expect(store.state.isEditing).toBe(true); + expect(store.state.badgeInEditForm).toEqual(dummyBadge); + expect(store.state.renderedBadge).toEqual(dummyBadge); + }); + }); + + describe('STOP_EDITING', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + badgeInEditForm: 'dummy value', + isEditing: 'dummy value', + renderedBadge: 'dummy value', + }); + }); + + it('resets the edit form', () => { + store.commit(types.STOP_EDITING); + + expect(store.state.isEditing).toBe(false); + expect(store.state.badgeInEditForm).toBe(null); + expect(store.state.renderedBadge).toBe(null); + }); + }); + + describe('UPDATE_BADGE_IN_FORM', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + badgeInAddForm: 'dummy value', + badgeInEditForm: 'dummy value', + }); + }); + + it('sets badgeInEditForm if isEditing is true', () => { + store.state.isEditing = true; + + store.commit(types.UPDATE_BADGE_IN_FORM, dummyBadge); + + expect(store.state.badgeInEditForm).toBe(dummyBadge); + }); + + it('sets badgeInAddForm if isEditing is false', () => { + store.state.isEditing = false; + + store.commit(types.UPDATE_BADGE_IN_FORM, dummyBadge); + + expect(store.state.badgeInAddForm).toBe(dummyBadge); + }); + }); + + describe('UPDATE_BADGE_IN_MODAL', () => { + beforeEach(() => { + store.replaceState({ + ...store.state, + badgeInModal: 'dummy value', + }); + }); + + it('sets badgeInModal', () => { + store.commit(types.UPDATE_BADGE_IN_MODAL, dummyBadge); + + expect(store.state.badgeInModal).toBe(dummyBadge); + }); + }); +}); diff --git a/spec/javascripts/boards/board_blank_state_spec.js b/spec/javascripts/boards/board_blank_state_spec.js index f757dadfada..664ea202e93 100644 --- a/spec/javascripts/boards/board_blank_state_spec.js +++ b/spec/javascripts/boards/board_blank_state_spec.js @@ -1,7 +1,7 @@ /* global BoardService */ import Vue from 'vue'; import '~/boards/stores/boards_store'; -import boardBlankState from '~/boards/components/board_blank_state'; +import BoardBlankState from '~/boards/components/board_blank_state.vue'; import { mockBoardService } from './mock_data'; describe('Boards blank state', () => { @@ -9,7 +9,7 @@ describe('Boards blank state', () => { let fail = false; beforeEach((done) => { - const Comp = Vue.extend(boardBlankState); + const Comp = Vue.extend(BoardBlankState); gl.issueBoards.BoardsStore.create(); gl.boardService = mockBoardService(); diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js index 37088a6421c..be1ea0b57b4 100644 --- a/spec/javascripts/boards/issue_card_spec.js +++ b/spec/javascripts/boards/issue_card_spec.js @@ -41,6 +41,8 @@ describe('Issue card component', () => { confidential: false, labels: [list.label], assignees: [], + reference_path: '#1', + real_path: '/test/1', }); component = new Vue({ diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js index e9d77f035e3..797693a21aa 100644 --- a/spec/javascripts/boards/modal_store_spec.js +++ b/spec/javascripts/boards/modal_store_spec.js @@ -4,12 +4,11 @@ import '~/vue_shared/models/label'; import '~/boards/models/issue'; import '~/boards/models/list'; import '~/boards/models/assignee'; -import '~/boards/stores/modal_store'; +import Store from '~/boards/stores/modal_store'; describe('Modal store', () => { let issue; let issue2; - const Store = gl.issueBoards.ModalStore; beforeEach(() => { // Setup default state diff --git a/spec/javascripts/fixtures/one_white_pixel.png b/spec/javascripts/fixtures/one_white_pixel.png Binary files differnew file mode 100644 index 00000000000..073fcf40a18 --- /dev/null +++ b/spec/javascripts/fixtures/one_white_pixel.png diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js index 34acdfbfba9..effacbcff4e 100644 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -3,6 +3,12 @@ export const createComponentWithStore = (Component, store, propsData = {}) => ne propsData, }); +export const mountComponentWithStore = (Component, { el, props, store }) => + new Component({ + store, + propsData: props || { }, + }).$mount(el); + export default (Component, props = {}, el = null) => new Component({ propsData: props, }).$mount(el); diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js index e57a55fa71a..ae00fb76714 100644 --- a/spec/javascripts/lib/utils/text_utility_spec.js +++ b/spec/javascripts/lib/utils/text_utility_spec.js @@ -65,11 +65,15 @@ describe('text_utility', () => { describe('stripHtml', () => { it('replaces html tag with the default replacement', () => { - expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual('This is a text with html.'); + expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual( + 'This is a text with html.', + ); }); it('replaces html tags with the provided replacement', () => { - expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .'); + expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual( + 'This is a text with html .', + ); }); }); @@ -78,4 +82,10 @@ describe('text_utility', () => { expect(textUtils.convertToCamelCase('snake_case')).toBe('snakeCase'); }); }); + + describe('convertToSentenceCase', () => { + it('converts Sentence Case to Sentence case', () => { + expect(textUtils.convertToSentenceCase('Hello World')).toBe('Hello world'); + }); + }); }); diff --git a/spec/javascripts/matchers.js b/spec/javascripts/matchers.js new file mode 100644 index 00000000000..7cc5e753c22 --- /dev/null +++ b/spec/javascripts/matchers.js @@ -0,0 +1,35 @@ +export default { + toHaveSpriteIcon: () => ({ + compare(element, iconName) { + if (!iconName) { + throw new Error('toHaveSpriteIcon is missing iconName argument!'); + } + + if (!(element instanceof HTMLElement)) { + throw new Error(`${element} is not a DOM element!`); + } + + const iconReferences = [].slice.apply(element.querySelectorAll('svg use')); + const matchingIcon = iconReferences.find(reference => reference.getAttribute('xlink:href').endsWith(`#${iconName}`)); + const result = { + pass: !!matchingIcon, + }; + + if (result.pass) { + result.message = `${element.outerHTML} contains the sprite icon "${iconName}"!`; + } else { + result.message = `${element.outerHTML} does not contain the sprite icon "${iconName}"!`; + + const existingIcons = iconReferences.map((reference) => { + const iconUrl = reference.getAttribute('xlink:href'); + return `"${iconUrl.replace(/^.+#/, '')}"`; + }); + if (existingIcons.length > 0) { + result.message += ` (only found ${existingIcons.join(',')})`; + } + } + + return result; + }, + }), +}; diff --git a/spec/javascripts/monitoring/graph/axis_spec.js b/spec/javascripts/monitoring/graph/axis_spec.js new file mode 100644 index 00000000000..c7adba00637 --- /dev/null +++ b/spec/javascripts/monitoring/graph/axis_spec.js @@ -0,0 +1,65 @@ +import Vue from 'vue'; +import GraphAxis from '~/monitoring/components/graph/axis.vue'; +import measurements from '~/monitoring/utils/measurements'; + +const createComponent = propsData => { + const Component = Vue.extend(GraphAxis); + + return new Component({ + propsData, + }).$mount(); +}; + +const defaultValuesComponent = { + graphWidth: 500, + graphHeight: 300, + graphHeightOffset: 120, + margin: measurements.large.margin, + measurements: measurements.large, + yAxisLabel: 'Values', + unitOfDisplay: 'MB', +}; + +function getTextFromNode(component, selector) { + return component.$el.querySelector(selector).firstChild.nodeValue.trim(); +} + +describe('Axis', () => { + describe('Computed props', () => { + it('textTransform', () => { + const component = createComponent(defaultValuesComponent); + + expect(component.textTransform).toContain('translate(15, 120) rotate(-90)'); + }); + + it('xPosition', () => { + const component = createComponent(defaultValuesComponent); + + expect(component.xPosition).toEqual(180); + }); + + it('yPosition', () => { + const component = createComponent(defaultValuesComponent); + + expect(component.yPosition).toEqual(240); + }); + + it('rectTransform', () => { + const component = createComponent(defaultValuesComponent); + + expect(component.rectTransform).toContain('translate(0, 120) rotate(-90)'); + }); + }); + + it('has 2 rect-axis-text rect svg elements', () => { + const component = createComponent(defaultValuesComponent); + + expect(component.$el.querySelectorAll('.rect-axis-text').length).toEqual(2); + }); + + it('contains text to signal the usage, title and time with multiple time series', () => { + const component = createComponent(defaultValuesComponent); + + expect(getTextFromNode(component, '.y-label-text')).toEqual('Values (MB)'); + }); +}); diff --git a/spec/javascripts/monitoring/graph/legend_spec.js b/spec/javascripts/monitoring/graph/legend_spec.js index 145c8db28d5..abcc51aa077 100644 --- a/spec/javascripts/monitoring/graph/legend_spec.js +++ b/spec/javascripts/monitoring/graph/legend_spec.js @@ -1,106 +1,44 @@ import Vue from 'vue'; import GraphLegend from '~/monitoring/components/graph/legend.vue'; -import measurements from '~/monitoring/utils/measurements'; import createTimeSeries from '~/monitoring/utils/multiple_time_series'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data'; -const createComponent = (propsData) => { - const Component = Vue.extend(GraphLegend); - - return new Component({ - propsData, - }).$mount(); -}; - const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); -const defaultValuesComponent = { - graphWidth: 500, - graphHeight: 300, - graphHeightOffset: 120, - margin: measurements.large.margin, - measurements: measurements.large, - areaColorRgb: '#f0f0f0', - legendTitle: 'Title', - yAxisLabel: 'Values', - metricUsage: 'Value', - unitOfDisplay: 'Req/Sec', - currentDataIndex: 0, -}; +const defaultValuesComponent = {}; -const timeSeries = createTimeSeries(convertedMetrics[0].queries, - defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight, - defaultValuesComponent.graphHeightOffset); +const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120); defaultValuesComponent.timeSeries = timeSeries; -function getTextFromNode(component, selector) { - return component.$el.querySelector(selector).firstChild.nodeValue.trim(); -} - -describe('GraphLegend', () => { - describe('Computed props', () => { - it('textTransform', () => { - const component = createComponent(defaultValuesComponent); - - expect(component.textTransform).toContain('translate(15, 120) rotate(-90)'); - }); - - it('xPosition', () => { - const component = createComponent(defaultValuesComponent); - - expect(component.xPosition).toEqual(180); - }); - - it('yPosition', () => { - const component = createComponent(defaultValuesComponent); - - expect(component.yPosition).toEqual(240); - }); - - it('rectTransform', () => { - const component = createComponent(defaultValuesComponent); +describe('Legend Component', () => { + let vm; + let Legend; - expect(component.rectTransform).toContain('translate(0, 120) rotate(-90)'); - }); + beforeEach(() => { + Legend = Vue.extend(GraphLegend); }); - describe('methods', () => { - it('translateLegendGroup should only change Y direction', () => { - const component = createComponent(defaultValuesComponent); - - const translatedCoordinate = component.translateLegendGroup(1); - expect(translatedCoordinate.indexOf('translate(0, ')).not.toEqual(-1); + describe('View', () => { + beforeEach(() => { + vm = mountComponent(Legend, { + legendTitle: 'legend', + timeSeries, + currentDataIndex: 0, + unitOfDisplay: 'Req/Sec', + }); }); - it('formatMetricUsage should contain the unit of display and the current value selected via "currentDataIndex"', () => { - const component = createComponent(defaultValuesComponent); + it('should render the usage, title and time with multiple time series', () => { + const titles = vm.$el.querySelectorAll('.legend-metric-title'); - const formattedMetricUsage = component.formatMetricUsage(timeSeries[0]); - const valueFromSeries = timeSeries[0].values[component.currentDataIndex].value; - expect(formattedMetricUsage.indexOf(component.unitOfDisplay)).not.toEqual(-1); - expect(formattedMetricUsage.indexOf(valueFromSeries)).not.toEqual(-1); + expect(titles[0].textContent.indexOf('1xx')).not.toEqual(-1); + expect(titles[1].textContent.indexOf('2xx')).not.toEqual(-1); }); - }); - - it('has 2 rect-axis-text rect svg elements', () => { - const component = createComponent(defaultValuesComponent); - - expect(component.$el.querySelectorAll('.rect-axis-text').length).toEqual(2); - }); - it('contains text to signal the usage, title and time with multiple time series', () => { - const component = createComponent(defaultValuesComponent); - const titles = component.$el.querySelectorAll('.legend-metric-title'); - - expect(titles[0].textContent.indexOf('1xx')).not.toEqual(-1); - expect(titles[1].textContent.indexOf('2xx')).not.toEqual(-1); - expect(getTextFromNode(component, '.y-label-text')).toEqual(component.yAxisLabel); - }); - - it('should contain the same number of legend groups as the timeSeries length', () => { - const component = createComponent(defaultValuesComponent); - - expect(component.$el.querySelectorAll('.legend-group').length).toEqual(component.timeSeries.length); + it('should container the same number of rows in the table as time series', () => { + expect(vm.$el.querySelectorAll('.prometheus-table tr').length).toEqual(vm.timeSeries.length); + }); }); }); diff --git a/spec/javascripts/monitoring/graph/track_info_spec.js b/spec/javascripts/monitoring/graph/track_info_spec.js new file mode 100644 index 00000000000..d3121d553f9 --- /dev/null +++ b/spec/javascripts/monitoring/graph/track_info_spec.js @@ -0,0 +1,44 @@ +import Vue from 'vue'; +import TrackInfo from '~/monitoring/components/graph/track_info.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import createTimeSeries from '~/monitoring/utils/multiple_time_series'; +import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data'; + +const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); +const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120); + +describe('TrackInfo component', () => { + let vm; + let Component; + + beforeEach(() => { + Component = Vue.extend(TrackInfo); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('Computed props', () => { + beforeEach(() => { + vm = mountComponent(Component, { track: timeSeries[0] }); + }); + + it('summaryMetrics', () => { + expect(vm.summaryMetrics).toEqual('Avg: 0.000 · Max: 0.000'); + }); + }); + + describe('Rendered output', () => { + beforeEach(() => { + vm = mountComponent(Component, { track: timeSeries[0] }); + }); + + it('contains metric tag and the summary metrics', () => { + const metricTag = vm.$el.querySelector('strong'); + + expect(metricTag.textContent.trim()).toEqual(vm.track.metricTag); + expect(vm.$el.textContent).toContain('Avg: 0.000 · Max: 0.000'); + }); + }); +}); diff --git a/spec/javascripts/monitoring/graph/track_line_spec.js b/spec/javascripts/monitoring/graph/track_line_spec.js new file mode 100644 index 00000000000..45106830a67 --- /dev/null +++ b/spec/javascripts/monitoring/graph/track_line_spec.js @@ -0,0 +1,52 @@ +import Vue from 'vue'; +import TrackLine from '~/monitoring/components/graph/track_line.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import createTimeSeries from '~/monitoring/utils/multiple_time_series'; +import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data'; + +const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); +const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120); + +describe('TrackLine component', () => { + let vm; + let Component; + + beforeEach(() => { + Component = Vue.extend(TrackLine); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('Computed props', () => { + it('stylizedLine for dashed lineStyles', () => { + vm = mountComponent(Component, { track: { ...timeSeries[0], lineStyle: 'dashed' } }); + + expect(vm.stylizedLine).toEqual('6, 3'); + }); + + it('stylizedLine for dotted lineStyles', () => { + vm = mountComponent(Component, { track: { ...timeSeries[0], lineStyle: 'dotted' } }); + + expect(vm.stylizedLine).toEqual('3, 3'); + }); + }); + + describe('Rendered output', () => { + it('has an svg with a line', () => { + vm = mountComponent(Component, { track: { ...timeSeries[0] } }); + const svgEl = vm.$el.querySelector('svg'); + const lineEl = vm.$el.querySelector('svg line'); + + expect(svgEl.getAttribute('width')).toEqual('15'); + expect(svgEl.getAttribute('height')).toEqual('6'); + + expect(lineEl.getAttribute('stroke-width')).toEqual('4'); + expect(lineEl.getAttribute('x1')).toEqual('0'); + expect(lineEl.getAttribute('x2')).toEqual('15'); + expect(lineEl.getAttribute('y1')).toEqual('2'); + expect(lineEl.getAttribute('y2')).toEqual('2'); + }); + }); +}); diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js index b1d69752bad..1213c80ba3a 100644 --- a/spec/javascripts/monitoring/graph_spec.js +++ b/spec/javascripts/monitoring/graph_spec.js @@ -2,11 +2,15 @@ import Vue from 'vue'; import Graph from '~/monitoring/components/graph.vue'; import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins'; import eventHub from '~/monitoring/event_hub'; -import { deploymentData, convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from './mock_data'; +import { + deploymentData, + convertDatesMultipleSeries, + singleRowMetricsMultipleSeries, +} from './mock_data'; const tagsPath = 'http://test.host/frontend-fixtures/environments-project/tags'; const projectPath = 'http://test.host/frontend-fixtures/environments-project'; -const createComponent = (propsData) => { +const createComponent = propsData => { const Component = Vue.extend(Graph); return new Component({ @@ -14,7 +18,9 @@ const createComponent = (propsData) => { }).$mount(); }; -const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); +const convertedMetrics = convertDatesMultipleSeries( + singleRowMetricsMultipleSeries, +); describe('Graph', () => { beforeEach(() => { @@ -31,7 +37,9 @@ describe('Graph', () => { projectPath, }); - expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(component.graphData.title); + expect(component.$el.querySelector('.text-center').innerText.trim()).toBe( + component.graphData.title, + ); }); describe('Computed props', () => { @@ -46,8 +54,9 @@ describe('Graph', () => { }); const transformedHeight = `${component.graphHeight - 100}`; - expect(component.axisTransform.indexOf(transformedHeight)) - .not.toEqual(-1); + expect(component.axisTransform.indexOf(transformedHeight)).not.toEqual( + -1, + ); }); it('outerViewBox gets a width and height property based on the DOM size of the element', () => { @@ -63,11 +72,11 @@ describe('Graph', () => { const viewBoxArray = component.outerViewBox.split(' '); expect(typeof component.outerViewBox).toEqual('string'); expect(viewBoxArray[2]).toEqual(component.graphWidth.toString()); - expect(viewBoxArray[3]).toEqual(component.graphHeight.toString()); + expect(viewBoxArray[3]).toEqual((component.graphHeight - 50).toString()); }); }); - it('sends an event to the eventhub when it has finished resizing', (done) => { + it('sends an event to the eventhub when it has finished resizing', done => { const component = createComponent({ graphData: convertedMetrics[1], classType: 'col-md-6', diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index f30208b27b6..50da6da2e07 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -3,2426 +3,645 @@ export const mockApiEndpoint = `${gl.TEST_HOST}/monitoring/mock`; export const metricsGroupsAPIResponse = { - 'success': true, - 'data': [ + success: true, + data: [ { - 'group': 'Kubernetes', - 'priority': 1, - 'metrics': [ - { - 'title': 'Memory usage', - 'weight': 1, - 'queries': [ + group: 'Kubernetes', + priority: 1, + metrics: [ + { + title: 'Memory usage', + weight: 1, + queries: [ + { + query_range: 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20', + y_label: 'Memory', + unit: 'MiB', + result: [ { - 'query_range': 'avg(container_memory_usage_bytes{%{environment_filter}}) / 2^20', - 'y_label': 'Memory', - 'unit': 'MiB', - 'result': [ - { - 'metric': {}, - 'values': [ - [ - 1495700554.925, - '8.0390625' - ], - [ - 1495700614.925, - '8.0390625' - ], - [ - 1495700674.925, - '8.0390625' - ], - [ - 1495700734.925, - '8.0390625' - ], - [ - 1495700794.925, - '8.0390625' - ], - [ - 1495700854.925, - '8.0390625' - ], - [ - 1495700914.925, - '8.0390625' - ], - [ - 1495700974.925, - '8.0390625' - ], - [ - 1495701034.925, - '8.0390625' - ], - [ - 1495701094.925, - '8.0390625' - ], - [ - 1495701154.925, - '8.0390625' - ], - [ - 1495701214.925, - '8.0390625' - ], - [ - 1495701274.925, - '8.0390625' - ], - [ - 1495701334.925, - '8.0390625' - ], - [ - 1495701394.925, - '8.0390625' - ], - [ - 1495701454.925, - '8.0390625' - ], - [ - 1495701514.925, - '8.0390625' - ], - [ - 1495701574.925, - '8.0390625' - ], - [ - 1495701634.925, - '8.0390625' - ], - [ - 1495701694.925, - '8.0390625' - ], - [ - 1495701754.925, - '8.0390625' - ], - [ - 1495701814.925, - '8.0390625' - ], - [ - 1495701874.925, - '8.0390625' - ], - [ - 1495701934.925, - '8.0390625' - ], - [ - 1495701994.925, - '8.0390625' - ], - [ - 1495702054.925, - '8.0390625' - ], - [ - 1495702114.925, - '8.0390625' - ], - [ - 1495702174.925, - '8.0390625' - ], - [ - 1495702234.925, - '8.0390625' - ], - [ - 1495702294.925, - '8.0390625' - ], - [ - 1495702354.925, - '8.0390625' - ], - [ - 1495702414.925, - '8.0390625' - ], - [ - 1495702474.925, - '8.0390625' - ], - [ - 1495702534.925, - '8.0390625' - ], - [ - 1495702594.925, - '8.0390625' - ], - [ - 1495702654.925, - '8.0390625' - ], - [ - 1495702714.925, - '8.0390625' - ], - [ - 1495702774.925, - '8.0390625' - ], - [ - 1495702834.925, - '8.0390625' - ], - [ - 1495702894.925, - '8.0390625' - ], - [ - 1495702954.925, - '8.0390625' - ], - [ - 1495703014.925, - '8.0390625' - ], - [ - 1495703074.925, - '8.0390625' - ], - [ - 1495703134.925, - '8.0390625' - ], - [ - 1495703194.925, - '8.0390625' - ], - [ - 1495703254.925, - '8.03515625' - ], - [ - 1495703314.925, - '8.03515625' - ], - [ - 1495703374.925, - '8.03515625' - ], - [ - 1495703434.925, - '8.03515625' - ], - [ - 1495703494.925, - '8.03515625' - ], - [ - 1495703554.925, - '8.03515625' - ], - [ - 1495703614.925, - '8.03515625' - ], - [ - 1495703674.925, - '8.03515625' - ], - [ - 1495703734.925, - '8.03515625' - ], - [ - 1495703794.925, - '8.03515625' - ], - [ - 1495703854.925, - '8.03515625' - ], - [ - 1495703914.925, - '8.03515625' - ], - [ - 1495703974.925, - '8.03515625' - ], - [ - 1495704034.925, - '8.03515625' - ], - [ - 1495704094.925, - '8.03515625' - ], - [ - 1495704154.925, - '8.03515625' - ], - [ - 1495704214.925, - '7.9296875' - ], - [ - 1495704274.925, - '7.9296875' - ], - [ - 1495704334.925, - '7.9296875' - ], - [ - 1495704394.925, - '7.9296875' - ], - [ - 1495704454.925, - '7.9296875' - ], - [ - 1495704514.925, - '7.9296875' - ], - [ - 1495704574.925, - '7.9296875' - ], - [ - 1495704634.925, - '7.9296875' - ], - [ - 1495704694.925, - '7.9296875' - ], - [ - 1495704754.925, - '7.9296875' - ], - [ - 1495704814.925, - '7.9296875' - ], - [ - 1495704874.925, - '7.9296875' - ], - [ - 1495704934.925, - '7.9296875' - ], - [ - 1495704994.925, - '7.9296875' - ], - [ - 1495705054.925, - '7.9296875' - ], - [ - 1495705114.925, - '7.9296875' - ], - [ - 1495705174.925, - '7.9296875' - ], - [ - 1495705234.925, - '7.9296875' - ], - [ - 1495705294.925, - '7.9296875' - ], - [ - 1495705354.925, - '7.9296875' - ], - [ - 1495705414.925, - '7.9296875' - ], - [ - 1495705474.925, - '7.9296875' - ], - [ - 1495705534.925, - '7.9296875' - ], - [ - 1495705594.925, - '7.9296875' - ], - [ - 1495705654.925, - '7.9296875' - ], - [ - 1495705714.925, - '7.9296875' - ], - [ - 1495705774.925, - '7.9296875' - ], - [ - 1495705834.925, - '7.9296875' - ], - [ - 1495705894.925, - '7.9296875' - ], - [ - 1495705954.925, - '7.9296875' - ], - [ - 1495706014.925, - '7.9296875' - ], - [ - 1495706074.925, - '7.9296875' - ], - [ - 1495706134.925, - '7.9296875' - ], - [ - 1495706194.925, - '7.9296875' - ], - [ - 1495706254.925, - '7.9296875' - ], - [ - 1495706314.925, - '7.9296875' - ], - [ - 1495706374.925, - '7.9296875' - ], - [ - 1495706434.925, - '7.9296875' - ], - [ - 1495706494.925, - '7.9296875' - ], - [ - 1495706554.925, - '7.9296875' - ], - [ - 1495706614.925, - '7.9296875' - ], - [ - 1495706674.925, - '7.9296875' - ], - [ - 1495706734.925, - '7.9296875' - ], - [ - 1495706794.925, - '7.9296875' - ], - [ - 1495706854.925, - '7.9296875' - ], - [ - 1495706914.925, - '7.9296875' - ], - [ - 1495706974.925, - '7.9296875' - ], - [ - 1495707034.925, - '7.9296875' - ], - [ - 1495707094.925, - '7.9296875' - ], - [ - 1495707154.925, - '7.9296875' - ], - [ - 1495707214.925, - '7.9296875' - ], - [ - 1495707274.925, - '7.9296875' - ], - [ - 1495707334.925, - '7.9296875' - ], - [ - 1495707394.925, - '7.9296875' - ], - [ - 1495707454.925, - '7.9296875' - ], - [ - 1495707514.925, - '7.9296875' - ], - [ - 1495707574.925, - '7.9296875' - ], - [ - 1495707634.925, - '7.9296875' - ], - [ - 1495707694.925, - '7.9296875' - ], - [ - 1495707754.925, - '7.9296875' - ], - [ - 1495707814.925, - '7.9296875' - ], - [ - 1495707874.925, - '7.9296875' - ], - [ - 1495707934.925, - '7.9296875' - ], - [ - 1495707994.925, - '7.9296875' - ], - [ - 1495708054.925, - '7.9296875' - ], - [ - 1495708114.925, - '7.9296875' - ], - [ - 1495708174.925, - '7.9296875' - ], - [ - 1495708234.925, - '7.9296875' - ], - [ - 1495708294.925, - '7.9296875' - ], - [ - 1495708354.925, - '7.9296875' - ], - [ - 1495708414.925, - '7.9296875' - ], - [ - 1495708474.925, - '7.9296875' - ], - [ - 1495708534.925, - '7.9296875' - ], - [ - 1495708594.925, - '7.9296875' - ], - [ - 1495708654.925, - '7.9296875' - ], - [ - 1495708714.925, - '7.9296875' - ], - [ - 1495708774.925, - '7.9296875' - ], - [ - 1495708834.925, - '7.9296875' - ], - [ - 1495708894.925, - '7.9296875' - ], - [ - 1495708954.925, - '7.8984375' - ], - [ - 1495709014.925, - '7.8984375' - ], - [ - 1495709074.925, - '7.8984375' - ], - [ - 1495709134.925, - '7.8984375' - ], - [ - 1495709194.925, - '7.8984375' - ], - [ - 1495709254.925, - '7.89453125' - ], - [ - 1495709314.925, - '7.89453125' - ], - [ - 1495709374.925, - '7.89453125' - ], - [ - 1495709434.925, - '7.89453125' - ], - [ - 1495709494.925, - '7.89453125' - ], - [ - 1495709554.925, - '7.89453125' - ], - [ - 1495709614.925, - '7.89453125' - ], - [ - 1495709674.925, - '7.89453125' - ], - [ - 1495709734.925, - '7.89453125' - ], - [ - 1495709794.925, - '7.89453125' - ], - [ - 1495709854.925, - '7.89453125' - ], - [ - 1495709914.925, - '7.89453125' - ], - [ - 1495709974.925, - '7.89453125' - ], - [ - 1495710034.925, - '7.89453125' - ], - [ - 1495710094.925, - '7.89453125' - ], - [ - 1495710154.925, - '7.89453125' - ], - [ - 1495710214.925, - '7.89453125' - ], - [ - 1495710274.925, - '7.89453125' - ], - [ - 1495710334.925, - '7.89453125' - ], - [ - 1495710394.925, - '7.89453125' - ], - [ - 1495710454.925, - '7.89453125' - ], - [ - 1495710514.925, - '7.89453125' - ], - [ - 1495710574.925, - '7.89453125' - ], - [ - 1495710634.925, - '7.89453125' - ], - [ - 1495710694.925, - '7.89453125' - ], - [ - 1495710754.925, - '7.89453125' - ], - [ - 1495710814.925, - '7.89453125' - ], - [ - 1495710874.925, - '7.89453125' - ], - [ - 1495710934.925, - '7.89453125' - ], - [ - 1495710994.925, - '7.89453125' - ], - [ - 1495711054.925, - '7.89453125' - ], - [ - 1495711114.925, - '7.89453125' - ], - [ - 1495711174.925, - '7.8515625' - ], - [ - 1495711234.925, - '7.8515625' - ], - [ - 1495711294.925, - '7.8515625' - ], - [ - 1495711354.925, - '7.8515625' - ], - [ - 1495711414.925, - '7.8515625' - ], - [ - 1495711474.925, - '7.8515625' - ], - [ - 1495711534.925, - '7.8515625' - ], - [ - 1495711594.925, - '7.8515625' - ], - [ - 1495711654.925, - '7.8515625' - ], - [ - 1495711714.925, - '7.8515625' - ], - [ - 1495711774.925, - '7.8515625' - ], - [ - 1495711834.925, - '7.8515625' - ], - [ - 1495711894.925, - '7.8515625' - ], - [ - 1495711954.925, - '7.8515625' - ], - [ - 1495712014.925, - '7.8515625' - ], - [ - 1495712074.925, - '7.8515625' - ], - [ - 1495712134.925, - '7.8515625' - ], - [ - 1495712194.925, - '7.8515625' - ], - [ - 1495712254.925, - '7.8515625' - ], - [ - 1495712314.925, - '7.8515625' - ], - [ - 1495712374.925, - '7.8515625' - ], - [ - 1495712434.925, - '7.83203125' - ], - [ - 1495712494.925, - '7.83203125' - ], - [ - 1495712554.925, - '7.83203125' - ], - [ - 1495712614.925, - '7.83203125' - ], - [ - 1495712674.925, - '7.83203125' - ], - [ - 1495712734.925, - '7.83203125' - ], - [ - 1495712794.925, - '7.83203125' - ], - [ - 1495712854.925, - '7.83203125' - ], - [ - 1495712914.925, - '7.83203125' - ], - [ - 1495712974.925, - '7.83203125' - ], - [ - 1495713034.925, - '7.83203125' - ], - [ - 1495713094.925, - '7.83203125' - ], - [ - 1495713154.925, - '7.83203125' - ], - [ - 1495713214.925, - '7.83203125' - ], - [ - 1495713274.925, - '7.83203125' - ], - [ - 1495713334.925, - '7.83203125' - ], - [ - 1495713394.925, - '7.8125' - ], - [ - 1495713454.925, - '7.8125' - ], - [ - 1495713514.925, - '7.8125' - ], - [ - 1495713574.925, - '7.8125' - ], - [ - 1495713634.925, - '7.8125' - ], - [ - 1495713694.925, - '7.8125' - ], - [ - 1495713754.925, - '7.8125' - ], - [ - 1495713814.925, - '7.8125' - ], - [ - 1495713874.925, - '7.8125' - ], - [ - 1495713934.925, - '7.8125' - ], - [ - 1495713994.925, - '7.8125' - ], - [ - 1495714054.925, - '7.8125' - ], - [ - 1495714114.925, - '7.8125' - ], - [ - 1495714174.925, - '7.8125' - ], - [ - 1495714234.925, - '7.8125' - ], - [ - 1495714294.925, - '7.8125' - ], - [ - 1495714354.925, - '7.80859375' - ], - [ - 1495714414.925, - '7.80859375' - ], - [ - 1495714474.925, - '7.80859375' - ], - [ - 1495714534.925, - '7.80859375' - ], - [ - 1495714594.925, - '7.80859375' - ], - [ - 1495714654.925, - '7.80859375' - ], - [ - 1495714714.925, - '7.80859375' - ], - [ - 1495714774.925, - '7.80859375' - ], - [ - 1495714834.925, - '7.80859375' - ], - [ - 1495714894.925, - '7.80859375' - ], - [ - 1495714954.925, - '7.80859375' - ], - [ - 1495715014.925, - '7.80859375' - ], - [ - 1495715074.925, - '7.80859375' - ], - [ - 1495715134.925, - '7.80859375' - ], - [ - 1495715194.925, - '7.80859375' - ], - [ - 1495715254.925, - '7.80859375' - ], - [ - 1495715314.925, - '7.80859375' - ], - [ - 1495715374.925, - '7.80859375' - ], - [ - 1495715434.925, - '7.80859375' - ], - [ - 1495715494.925, - '7.80859375' - ], - [ - 1495715554.925, - '7.80859375' - ], - [ - 1495715614.925, - '7.80859375' - ], - [ - 1495715674.925, - '7.80859375' - ], - [ - 1495715734.925, - '7.80859375' - ], - [ - 1495715794.925, - '7.80859375' - ], - [ - 1495715854.925, - '7.80859375' - ], - [ - 1495715914.925, - '7.80078125' - ], - [ - 1495715974.925, - '7.80078125' - ], - [ - 1495716034.925, - '7.80078125' - ], - [ - 1495716094.925, - '7.80078125' - ], - [ - 1495716154.925, - '7.80078125' - ], - [ - 1495716214.925, - '7.796875' - ], - [ - 1495716274.925, - '7.796875' - ], - [ - 1495716334.925, - '7.796875' - ], - [ - 1495716394.925, - '7.796875' - ], - [ - 1495716454.925, - '7.796875' - ], - [ - 1495716514.925, - '7.796875' - ], - [ - 1495716574.925, - '7.796875' - ], - [ - 1495716634.925, - '7.796875' - ], - [ - 1495716694.925, - '7.796875' - ], - [ - 1495716754.925, - '7.796875' - ], - [ - 1495716814.925, - '7.796875' - ], - [ - 1495716874.925, - '7.79296875' - ], - [ - 1495716934.925, - '7.79296875' - ], - [ - 1495716994.925, - '7.79296875' - ], - [ - 1495717054.925, - '7.79296875' - ], - [ - 1495717114.925, - '7.79296875' - ], - [ - 1495717174.925, - '7.7890625' - ], - [ - 1495717234.925, - '7.7890625' - ], - [ - 1495717294.925, - '7.7890625' - ], - [ - 1495717354.925, - '7.7890625' - ], - [ - 1495717414.925, - '7.7890625' - ], - [ - 1495717474.925, - '7.7890625' - ], - [ - 1495717534.925, - '7.7890625' - ], - [ - 1495717594.925, - '7.7890625' - ], - [ - 1495717654.925, - '7.7890625' - ], - [ - 1495717714.925, - '7.7890625' - ], - [ - 1495717774.925, - '7.7890625' - ], - [ - 1495717834.925, - '7.77734375' - ], - [ - 1495717894.925, - '7.77734375' - ], - [ - 1495717954.925, - '7.77734375' - ], - [ - 1495718014.925, - '7.77734375' - ], - [ - 1495718074.925, - '7.77734375' - ], - [ - 1495718134.925, - '7.7421875' - ], - [ - 1495718194.925, - '7.7421875' - ], - [ - 1495718254.925, - '7.7421875' - ], - [ - 1495718314.925, - '7.7421875' - ] - ] - } - ] - } - ] + metric: {}, + values: [ + [1495700554.925, '8.0390625'], + [1495700614.925, '8.0390625'], + [1495700674.925, '8.0390625'], + [1495700734.925, '8.0390625'], + [1495700794.925, '8.0390625'], + [1495700854.925, '8.0390625'], + [1495700914.925, '8.0390625'], + [1495700974.925, '8.0390625'], + [1495701034.925, '8.0390625'], + [1495701094.925, '8.0390625'], + [1495701154.925, '8.0390625'], + [1495701214.925, '8.0390625'], + [1495701274.925, '8.0390625'], + [1495701334.925, '8.0390625'], + [1495701394.925, '8.0390625'], + [1495701454.925, '8.0390625'], + [1495701514.925, '8.0390625'], + [1495701574.925, '8.0390625'], + [1495701634.925, '8.0390625'], + [1495701694.925, '8.0390625'], + [1495701754.925, '8.0390625'], + [1495701814.925, '8.0390625'], + [1495701874.925, '8.0390625'], + [1495701934.925, '8.0390625'], + [1495701994.925, '8.0390625'], + [1495702054.925, '8.0390625'], + [1495702114.925, '8.0390625'], + [1495702174.925, '8.0390625'], + [1495702234.925, '8.0390625'], + [1495702294.925, '8.0390625'], + [1495702354.925, '8.0390625'], + [1495702414.925, '8.0390625'], + [1495702474.925, '8.0390625'], + [1495702534.925, '8.0390625'], + [1495702594.925, '8.0390625'], + [1495702654.925, '8.0390625'], + [1495702714.925, '8.0390625'], + [1495702774.925, '8.0390625'], + [1495702834.925, '8.0390625'], + [1495702894.925, '8.0390625'], + [1495702954.925, '8.0390625'], + [1495703014.925, '8.0390625'], + [1495703074.925, '8.0390625'], + [1495703134.925, '8.0390625'], + [1495703194.925, '8.0390625'], + [1495703254.925, '8.03515625'], + [1495703314.925, '8.03515625'], + [1495703374.925, '8.03515625'], + [1495703434.925, '8.03515625'], + [1495703494.925, '8.03515625'], + [1495703554.925, '8.03515625'], + [1495703614.925, '8.03515625'], + [1495703674.925, '8.03515625'], + [1495703734.925, '8.03515625'], + [1495703794.925, '8.03515625'], + [1495703854.925, '8.03515625'], + [1495703914.925, '8.03515625'], + [1495703974.925, '8.03515625'], + [1495704034.925, '8.03515625'], + [1495704094.925, '8.03515625'], + [1495704154.925, '8.03515625'], + [1495704214.925, '7.9296875'], + [1495704274.925, '7.9296875'], + [1495704334.925, '7.9296875'], + [1495704394.925, '7.9296875'], + [1495704454.925, '7.9296875'], + [1495704514.925, '7.9296875'], + [1495704574.925, '7.9296875'], + [1495704634.925, '7.9296875'], + [1495704694.925, '7.9296875'], + [1495704754.925, '7.9296875'], + [1495704814.925, '7.9296875'], + [1495704874.925, '7.9296875'], + [1495704934.925, '7.9296875'], + [1495704994.925, '7.9296875'], + [1495705054.925, '7.9296875'], + [1495705114.925, '7.9296875'], + [1495705174.925, '7.9296875'], + [1495705234.925, '7.9296875'], + [1495705294.925, '7.9296875'], + [1495705354.925, '7.9296875'], + [1495705414.925, '7.9296875'], + [1495705474.925, '7.9296875'], + [1495705534.925, '7.9296875'], + [1495705594.925, '7.9296875'], + [1495705654.925, '7.9296875'], + [1495705714.925, '7.9296875'], + [1495705774.925, '7.9296875'], + [1495705834.925, '7.9296875'], + [1495705894.925, '7.9296875'], + [1495705954.925, '7.9296875'], + [1495706014.925, '7.9296875'], + [1495706074.925, '7.9296875'], + [1495706134.925, '7.9296875'], + [1495706194.925, '7.9296875'], + [1495706254.925, '7.9296875'], + [1495706314.925, '7.9296875'], + [1495706374.925, '7.9296875'], + [1495706434.925, '7.9296875'], + [1495706494.925, '7.9296875'], + [1495706554.925, '7.9296875'], + [1495706614.925, '7.9296875'], + [1495706674.925, '7.9296875'], + [1495706734.925, '7.9296875'], + [1495706794.925, '7.9296875'], + [1495706854.925, '7.9296875'], + [1495706914.925, '7.9296875'], + [1495706974.925, '7.9296875'], + [1495707034.925, '7.9296875'], + [1495707094.925, '7.9296875'], + [1495707154.925, '7.9296875'], + [1495707214.925, '7.9296875'], + [1495707274.925, '7.9296875'], + [1495707334.925, '7.9296875'], + [1495707394.925, '7.9296875'], + [1495707454.925, '7.9296875'], + [1495707514.925, '7.9296875'], + [1495707574.925, '7.9296875'], + [1495707634.925, '7.9296875'], + [1495707694.925, '7.9296875'], + [1495707754.925, '7.9296875'], + [1495707814.925, '7.9296875'], + [1495707874.925, '7.9296875'], + [1495707934.925, '7.9296875'], + [1495707994.925, '7.9296875'], + [1495708054.925, '7.9296875'], + [1495708114.925, '7.9296875'], + [1495708174.925, '7.9296875'], + [1495708234.925, '7.9296875'], + [1495708294.925, '7.9296875'], + [1495708354.925, '7.9296875'], + [1495708414.925, '7.9296875'], + [1495708474.925, '7.9296875'], + [1495708534.925, '7.9296875'], + [1495708594.925, '7.9296875'], + [1495708654.925, '7.9296875'], + [1495708714.925, '7.9296875'], + [1495708774.925, '7.9296875'], + [1495708834.925, '7.9296875'], + [1495708894.925, '7.9296875'], + [1495708954.925, '7.8984375'], + [1495709014.925, '7.8984375'], + [1495709074.925, '7.8984375'], + [1495709134.925, '7.8984375'], + [1495709194.925, '7.8984375'], + [1495709254.925, '7.89453125'], + [1495709314.925, '7.89453125'], + [1495709374.925, '7.89453125'], + [1495709434.925, '7.89453125'], + [1495709494.925, '7.89453125'], + [1495709554.925, '7.89453125'], + [1495709614.925, '7.89453125'], + [1495709674.925, '7.89453125'], + [1495709734.925, '7.89453125'], + [1495709794.925, '7.89453125'], + [1495709854.925, '7.89453125'], + [1495709914.925, '7.89453125'], + [1495709974.925, '7.89453125'], + [1495710034.925, '7.89453125'], + [1495710094.925, '7.89453125'], + [1495710154.925, '7.89453125'], + [1495710214.925, '7.89453125'], + [1495710274.925, '7.89453125'], + [1495710334.925, '7.89453125'], + [1495710394.925, '7.89453125'], + [1495710454.925, '7.89453125'], + [1495710514.925, '7.89453125'], + [1495710574.925, '7.89453125'], + [1495710634.925, '7.89453125'], + [1495710694.925, '7.89453125'], + [1495710754.925, '7.89453125'], + [1495710814.925, '7.89453125'], + [1495710874.925, '7.89453125'], + [1495710934.925, '7.89453125'], + [1495710994.925, '7.89453125'], + [1495711054.925, '7.89453125'], + [1495711114.925, '7.89453125'], + [1495711174.925, '7.8515625'], + [1495711234.925, '7.8515625'], + [1495711294.925, '7.8515625'], + [1495711354.925, '7.8515625'], + [1495711414.925, '7.8515625'], + [1495711474.925, '7.8515625'], + [1495711534.925, '7.8515625'], + [1495711594.925, '7.8515625'], + [1495711654.925, '7.8515625'], + [1495711714.925, '7.8515625'], + [1495711774.925, '7.8515625'], + [1495711834.925, '7.8515625'], + [1495711894.925, '7.8515625'], + [1495711954.925, '7.8515625'], + [1495712014.925, '7.8515625'], + [1495712074.925, '7.8515625'], + [1495712134.925, '7.8515625'], + [1495712194.925, '7.8515625'], + [1495712254.925, '7.8515625'], + [1495712314.925, '7.8515625'], + [1495712374.925, '7.8515625'], + [1495712434.925, '7.83203125'], + [1495712494.925, '7.83203125'], + [1495712554.925, '7.83203125'], + [1495712614.925, '7.83203125'], + [1495712674.925, '7.83203125'], + [1495712734.925, '7.83203125'], + [1495712794.925, '7.83203125'], + [1495712854.925, '7.83203125'], + [1495712914.925, '7.83203125'], + [1495712974.925, '7.83203125'], + [1495713034.925, '7.83203125'], + [1495713094.925, '7.83203125'], + [1495713154.925, '7.83203125'], + [1495713214.925, '7.83203125'], + [1495713274.925, '7.83203125'], + [1495713334.925, '7.83203125'], + [1495713394.925, '7.8125'], + [1495713454.925, '7.8125'], + [1495713514.925, '7.8125'], + [1495713574.925, '7.8125'], + [1495713634.925, '7.8125'], + [1495713694.925, '7.8125'], + [1495713754.925, '7.8125'], + [1495713814.925, '7.8125'], + [1495713874.925, '7.8125'], + [1495713934.925, '7.8125'], + [1495713994.925, '7.8125'], + [1495714054.925, '7.8125'], + [1495714114.925, '7.8125'], + [1495714174.925, '7.8125'], + [1495714234.925, '7.8125'], + [1495714294.925, '7.8125'], + [1495714354.925, '7.80859375'], + [1495714414.925, '7.80859375'], + [1495714474.925, '7.80859375'], + [1495714534.925, '7.80859375'], + [1495714594.925, '7.80859375'], + [1495714654.925, '7.80859375'], + [1495714714.925, '7.80859375'], + [1495714774.925, '7.80859375'], + [1495714834.925, '7.80859375'], + [1495714894.925, '7.80859375'], + [1495714954.925, '7.80859375'], + [1495715014.925, '7.80859375'], + [1495715074.925, '7.80859375'], + [1495715134.925, '7.80859375'], + [1495715194.925, '7.80859375'], + [1495715254.925, '7.80859375'], + [1495715314.925, '7.80859375'], + [1495715374.925, '7.80859375'], + [1495715434.925, '7.80859375'], + [1495715494.925, '7.80859375'], + [1495715554.925, '7.80859375'], + [1495715614.925, '7.80859375'], + [1495715674.925, '7.80859375'], + [1495715734.925, '7.80859375'], + [1495715794.925, '7.80859375'], + [1495715854.925, '7.80859375'], + [1495715914.925, '7.80078125'], + [1495715974.925, '7.80078125'], + [1495716034.925, '7.80078125'], + [1495716094.925, '7.80078125'], + [1495716154.925, '7.80078125'], + [1495716214.925, '7.796875'], + [1495716274.925, '7.796875'], + [1495716334.925, '7.796875'], + [1495716394.925, '7.796875'], + [1495716454.925, '7.796875'], + [1495716514.925, '7.796875'], + [1495716574.925, '7.796875'], + [1495716634.925, '7.796875'], + [1495716694.925, '7.796875'], + [1495716754.925, '7.796875'], + [1495716814.925, '7.796875'], + [1495716874.925, '7.79296875'], + [1495716934.925, '7.79296875'], + [1495716994.925, '7.79296875'], + [1495717054.925, '7.79296875'], + [1495717114.925, '7.79296875'], + [1495717174.925, '7.7890625'], + [1495717234.925, '7.7890625'], + [1495717294.925, '7.7890625'], + [1495717354.925, '7.7890625'], + [1495717414.925, '7.7890625'], + [1495717474.925, '7.7890625'], + [1495717534.925, '7.7890625'], + [1495717594.925, '7.7890625'], + [1495717654.925, '7.7890625'], + [1495717714.925, '7.7890625'], + [1495717774.925, '7.7890625'], + [1495717834.925, '7.77734375'], + [1495717894.925, '7.77734375'], + [1495717954.925, '7.77734375'], + [1495718014.925, '7.77734375'], + [1495718074.925, '7.77734375'], + [1495718134.925, '7.7421875'], + [1495718194.925, '7.7421875'], + [1495718254.925, '7.7421875'], + [1495718314.925, '7.7421875'], + ], + }, + ], + }, + ], }, { - 'title': 'CPU usage', - 'weight': 1, - 'queries': [ + title: 'CPU usage', + weight: 1, + queries: [ + { + query_range: + 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100', + result: [ { - 'query_range': 'avg(rate(container_cpu_usage_seconds_total{%{environment_filter}}[2m])) * 100', - 'result': [ - { - 'metric': {}, - 'values': [ - [ - 1495700554.925, - '0.0010794445585559514' - ], - [ - 1495700614.925, - '0.003927214935433527' - ], - [ - 1495700674.925, - '0.0053045219047619975' - ], - [ - 1495700734.925, - '0.0048892095238097155' - ], - [ - 1495700794.925, - '0.005827140952381137' - ], - [ - 1495700854.925, - '0.00569846906219937' - ], - [ - 1495700914.925, - '0.004972616802849382' - ], - [ - 1495700974.925, - '0.005117509523809902' - ], - [ - 1495701034.925, - '0.00512389061919564' - ], - [ - 1495701094.925, - '0.005199100501890691' - ], - [ - 1495701154.925, - '0.005415746394885837' - ], - [ - 1495701214.925, - '0.005607682788146286' - ], - [ - 1495701274.925, - '0.005641300000000118' - ], - [ - 1495701334.925, - '0.0071166279368766495' - ], - [ - 1495701394.925, - '0.0063242138095234044' - ], - [ - 1495701454.925, - '0.005793314698235304' - ], - [ - 1495701514.925, - '0.00703934942237556' - ], - [ - 1495701574.925, - '0.006357007076123191' - ], - [ - 1495701634.925, - '0.003753167300126738' - ], - [ - 1495701694.925, - '0.005018469678430698' - ], - [ - 1495701754.925, - '0.0045217153371887' - ], - [ - 1495701814.925, - '0.006140104285714119' - ], - [ - 1495701874.925, - '0.004818684285714102' - ], - [ - 1495701934.925, - '0.005079509718955242' - ], - [ - 1495701994.925, - '0.005059981142498263' - ], - [ - 1495702054.925, - '0.005269098389538773' - ], - [ - 1495702114.925, - '0.005269954285714175' - ], - [ - 1495702174.925, - '0.014199241435795856' - ], - [ - 1495702234.925, - '0.01511936843111017' - ], - [ - 1495702294.925, - '0.0060933692920682875' - ], - [ - 1495702354.925, - '0.004945682380952493' - ], - [ - 1495702414.925, - '0.005641266666666565' - ], - [ - 1495702474.925, - '0.005223752857142996' - ], - [ - 1495702534.925, - '0.005743098505699831' - ], - [ - 1495702594.925, - '0.00538493380952391' - ], - [ - 1495702654.925, - '0.005507793883751339' - ], - [ - 1495702714.925, - '0.005666705714285466' - ], - [ - 1495702774.925, - '0.006231530000000112' - ], - [ - 1495702834.925, - '0.006570768635394899' - ], - [ - 1495702894.925, - '0.005551146666666895' - ], - [ - 1495702954.925, - '0.005602604737098058' - ], - [ - 1495703014.925, - '0.00613993580402159' - ], - [ - 1495703074.925, - '0.004770258764368832' - ], - [ - 1495703134.925, - '0.005512376671364914' - ], - [ - 1495703194.925, - '0.005254436666666674' - ], - [ - 1495703254.925, - '0.0050109839141320505' - ], - [ - 1495703314.925, - '0.0049478019256960016' - ], - [ - 1495703374.925, - '0.0037666860965123463' - ], - [ - 1495703434.925, - '0.004813526061656314' - ], - [ - 1495703494.925, - '0.005047748095238278' - ], - [ - 1495703554.925, - '0.00386494081008772' - ], - [ - 1495703614.925, - '0.004304037408111405' - ], - [ - 1495703674.925, - '0.004999466661587168' - ], - [ - 1495703734.925, - '0.004689140476190834' - ], - [ - 1495703794.925, - '0.004746126153582475' - ], - [ - 1495703854.925, - '0.004482706382572302' - ], - [ - 1495703914.925, - '0.004032808931864524' - ], - [ - 1495703974.925, - '0.005728319047618988' - ], - [ - 1495704034.925, - '0.004436139179627006' - ], - [ - 1495704094.925, - '0.004553455714285617' - ], - [ - 1495704154.925, - '0.003455244285714341' - ], - [ - 1495704214.925, - '0.004742244761904621' - ], - [ - 1495704274.925, - '0.005366978571428422' - ], - [ - 1495704334.925, - '0.004257954837665058' - ], - [ - 1495704394.925, - '0.005431603259831257' - ], - [ - 1495704454.925, - '0.0052009214498621986' - ], - [ - 1495704514.925, - '0.004317201904761618' - ], - [ - 1495704574.925, - '0.004307384285714157' - ], - [ - 1495704634.925, - '0.004789801146644822' - ], - [ - 1495704694.925, - '0.0051429795906706485' - ], - [ - 1495704754.925, - '0.005322495714285479' - ], - [ - 1495704814.925, - '0.004512809333244233' - ], - [ - 1495704874.925, - '0.004953843582568726' - ], - [ - 1495704934.925, - '0.005812690120858119' - ], - [ - 1495704994.925, - '0.004997024285714838' - ], - [ - 1495705054.925, - '0.005246216154439592' - ], - [ - 1495705114.925, - '0.0063494966618726795' - ], - [ - 1495705174.925, - '0.005306004342898225' - ], - [ - 1495705234.925, - '0.005081412857142978' - ], - [ - 1495705294.925, - '0.00511409523809522' - ], - [ - 1495705354.925, - '0.0047861001481192' - ], - [ - 1495705414.925, - '0.005107688228042962' - ], - [ - 1495705474.925, - '0.005271929582294012' - ], - [ - 1495705534.925, - '0.004453254502681249' - ], - [ - 1495705594.925, - '0.005799134293959226' - ], - [ - 1495705654.925, - '0.005340865929502478' - ], - [ - 1495705714.925, - '0.004911654761904942' - ], - [ - 1495705774.925, - '0.005888234873953261' - ], - [ - 1495705834.925, - '0.005565283333332954' - ], - [ - 1495705894.925, - '0.005522869047618869' - ], - [ - 1495705954.925, - '0.005177549737621646' - ], - [ - 1495706014.925, - '0.0053145810232096465' - ], - [ - 1495706074.925, - '0.004751095238095275' - ], - [ - 1495706134.925, - '0.006242077142856976' - ], - [ - 1495706194.925, - '0.00621034406957871' - ], - [ - 1495706254.925, - '0.006887592738978596' - ], - [ - 1495706314.925, - '0.006328128779726213' - ], - [ - 1495706374.925, - '0.007488363809523927' - ], - [ - 1495706434.925, - '0.006193758571428157' - ], - [ - 1495706494.925, - '0.0068798371839706935' - ], - [ - 1495706554.925, - '0.005757034340423128' - ], - [ - 1495706614.925, - '0.004571388497294698' - ], - [ - 1495706674.925, - '0.00620283044923395' - ], - [ - 1495706734.925, - '0.005607562380952455' - ], - [ - 1495706794.925, - '0.005506969933620308' - ], - [ - 1495706854.925, - '0.005621118095238131' - ], - [ - 1495706914.925, - '0.004876606098698849' - ], - [ - 1495706974.925, - '0.0047871205988517206' - ], - [ - 1495707034.925, - '0.00526405939458784' - ], - [ - 1495707094.925, - '0.005716323800605852' - ], - [ - 1495707154.925, - '0.005301459523809575' - ], - [ - 1495707214.925, - '0.0051613042857144905' - ], - [ - 1495707274.925, - '0.005384792857142714' - ], - [ - 1495707334.925, - '0.005259719047619222' - ], - [ - 1495707394.925, - '0.00584101142857182' - ], - [ - 1495707454.925, - '0.0060066121920326326' - ], - [ - 1495707514.925, - '0.006359978571428453' - ], - [ - 1495707574.925, - '0.006315876322151109' - ], - [ - 1495707634.925, - '0.005590012517198831' - ], - [ - 1495707694.925, - '0.005517419877137072' - ], - [ - 1495707754.925, - '0.006089813430348506' - ], - [ - 1495707814.925, - '0.00466754476190479' - ], - [ - 1495707874.925, - '0.006059954380517721' - ], - [ - 1495707934.925, - '0.005085657142856972' - ], - [ - 1495707994.925, - '0.005897665238095296' - ], - [ - 1495708054.925, - '0.0062282023199555885' - ], - [ - 1495708114.925, - '0.00526214553236979' - ], - [ - 1495708174.925, - '0.0044803300000000644' - ], - [ - 1495708234.925, - '0.005421443333333592' - ], - [ - 1495708294.925, - '0.005694326244512144' - ], - [ - 1495708354.925, - '0.005527721904761457' - ], - [ - 1495708414.925, - '0.005988819523809819' - ], - [ - 1495708474.925, - '0.005484704285714448' - ], - [ - 1495708534.925, - '0.005041123649230085' - ], - [ - 1495708594.925, - '0.005717767639612059' - ], - [ - 1495708654.925, - '0.005412954417342863' - ], - [ - 1495708714.925, - '0.005833343333333254' - ], - [ - 1495708774.925, - '0.005448135238094969' - ], - [ - 1495708834.925, - '0.005117341428571432' - ], - [ - 1495708894.925, - '0.005888345825277833' - ], - [ - 1495708954.925, - '0.005398543809524135' - ], - [ - 1495709014.925, - '0.005325611428571416' - ], - [ - 1495709074.925, - '0.005848668571428527' - ], - [ - 1495709134.925, - '0.005135003105145044' - ], - [ - 1495709194.925, - '0.0054551400000003' - ], - [ - 1495709254.925, - '0.005319472937322171' - ], - [ - 1495709314.925, - '0.00585677857142792' - ], - [ - 1495709374.925, - '0.0062146261904759215' - ], - [ - 1495709434.925, - '0.0067105060904182265' - ], - [ - 1495709494.925, - '0.005829691904762108' - ], - [ - 1495709554.925, - '0.005719280952381261' - ], - [ - 1495709614.925, - '0.005682603793416407' - ], - [ - 1495709674.925, - '0.0055272846277326934' - ], - [ - 1495709734.925, - '0.0057123680952386735' - ], - [ - 1495709794.925, - '0.00520597958075818' - ], - [ - 1495709854.925, - '0.005584358957263837' - ], - [ - 1495709914.925, - '0.005601104275197466' - ], - [ - 1495709974.925, - '0.005991657142857066' - ], - [ - 1495710034.925, - '0.00553722238095218' - ], - [ - 1495710094.925, - '0.005127883122696293' - ], - [ - 1495710154.925, - '0.005498111927534584' - ], - [ - 1495710214.925, - '0.005609934069084202' - ], - [ - 1495710274.925, - '0.00459206285714307' - ], - [ - 1495710334.925, - '0.0047910828571428084' - ], - [ - 1495710394.925, - '0.0056014671288845685' - ], - [ - 1495710454.925, - '0.005686936791078528' - ], - [ - 1495710514.925, - '0.00444480476190448' - ], - [ - 1495710574.925, - '0.005780394696738921' - ], - [ - 1495710634.925, - '0.0053107227550210365' - ], - [ - 1495710694.925, - '0.005096031495761817' - ], - [ - 1495710754.925, - '0.005451377979091524' - ], - [ - 1495710814.925, - '0.005328136666667083' - ], - [ - 1495710874.925, - '0.006020612857143043' - ], - [ - 1495710934.925, - '0.0061063585714285365' - ], - [ - 1495710994.925, - '0.006018346015752312' - ], - [ - 1495711054.925, - '0.005069130952381193' - ], - [ - 1495711114.925, - '0.005458406190476052' - ], - [ - 1495711174.925, - '0.00577219190476179' - ], - [ - 1495711234.925, - '0.005760814645658314' - ], - [ - 1495711294.925, - '0.005371875716579101' - ], - [ - 1495711354.925, - '0.0064232666666665834' - ], - [ - 1495711414.925, - '0.009369806836906667' - ], - [ - 1495711474.925, - '0.008956864761904692' - ], - [ - 1495711534.925, - '0.005266849368559271' - ], - [ - 1495711594.925, - '0.005335111364934262' - ], - [ - 1495711654.925, - '0.006461778319586945' - ], - [ - 1495711714.925, - '0.004687939890762393' - ], - [ - 1495711774.925, - '0.004438831245760684' - ], - [ - 1495711834.925, - '0.005142786666666613' - ], - [ - 1495711894.925, - '0.007257734212054963' - ], - [ - 1495711954.925, - '0.005621991904761494' - ], - [ - 1495712014.925, - '0.007868689999999862' - ], - [ - 1495712074.925, - '0.00910970215275738' - ], - [ - 1495712134.925, - '0.006151004285714278' - ], - [ - 1495712194.925, - '0.005447120924961522' - ], - [ - 1495712254.925, - '0.005150705153929503' - ], - [ - 1495712314.925, - '0.006358108714969314' - ], - [ - 1495712374.925, - '0.0057725354795696475' - ], - [ - 1495712434.925, - '0.005232139047619015' - ], - [ - 1495712494.925, - '0.004932809617949037' - ], - [ - 1495712554.925, - '0.004511607508499662' - ], - [ - 1495712614.925, - '0.00440487701522666' - ], - [ - 1495712674.925, - '0.005479113333333174' - ], - [ - 1495712734.925, - '0.004726317619047547' - ], - [ - 1495712794.925, - '0.005582041102958029' - ], - [ - 1495712854.925, - '0.006381481216082099' - ], - [ - 1495712914.925, - '0.005474260014095208' - ], - [ - 1495712974.925, - '0.00567597142857188' - ], - [ - 1495713034.925, - '0.0064741233333332985' - ], - [ - 1495713094.925, - '0.005467475714285271' - ], - [ - 1495713154.925, - '0.004868648393824457' - ], - [ - 1495713214.925, - '0.005254923286444893' - ], - [ - 1495713274.925, - '0.005599217150312865' - ], - [ - 1495713334.925, - '0.005105413720618919' - ], - [ - 1495713394.925, - '0.007246073333333279' - ], - [ - 1495713454.925, - '0.005990312380952272' - ], - [ - 1495713514.925, - '0.005594601853351101' - ], - [ - 1495713574.925, - '0.004739258673727054' - ], - [ - 1495713634.925, - '0.003932121428571783' - ], - [ - 1495713694.925, - '0.005018188268459395' - ], - [ - 1495713754.925, - '0.004538238095237985' - ], - [ - 1495713814.925, - '0.00561816643265435' - ], - [ - 1495713874.925, - '0.0063132584495033586' - ], - [ - 1495713934.925, - '0.00442385238095213' - ], - [ - 1495713994.925, - '0.004181795887658453' - ], - [ - 1495714054.925, - '0.004437759047619037' - ], - [ - 1495714114.925, - '0.006421748157178241' - ], - [ - 1495714174.925, - '0.006525143809523842' - ], - [ - 1495714234.925, - '0.004715904935144247' - ], - [ - 1495714294.925, - '0.005966040152763461' - ], - [ - 1495714354.925, - '0.005614535466921674' - ], - [ - 1495714414.925, - '0.004934375119415906' - ], - [ - 1495714474.925, - '0.0054122933333327385' - ], - [ - 1495714534.925, - '0.004926540699612279' - ], - [ - 1495714594.925, - '0.006124649517134237' - ], - [ - 1495714654.925, - '0.004629427092013995' - ], - [ - 1495714714.925, - '0.005117951257607005' - ], - [ - 1495714774.925, - '0.004868774512685422' - ], - [ - 1495714834.925, - '0.005310093333333399' - ], - [ - 1495714894.925, - '0.0054907752286127345' - ], - [ - 1495714954.925, - '0.004597678117351089' - ], - [ - 1495715014.925, - '0.0059622552380952' - ], - [ - 1495715074.925, - '0.005352457072655368' - ], - [ - 1495715134.925, - '0.005491630952381143' - ], - [ - 1495715194.925, - '0.006391770078379791' - ], - [ - 1495715254.925, - '0.005933472857142518' - ], - [ - 1495715314.925, - '0.005301314285714163' - ], - [ - 1495715374.925, - '0.0058352959724814165' - ], - [ - 1495715434.925, - '0.006154755147867044' - ], - [ - 1495715494.925, - '0.009391935637482038' - ], - [ - 1495715554.925, - '0.007846462857142592' - ], - [ - 1495715614.925, - '0.00477608215316353' - ], - [ - 1495715674.925, - '0.006132865238094998' - ], - [ - 1495715734.925, - '0.006159762457649516' - ], - [ - 1495715794.925, - '0.005957307073265968' - ], - [ - 1495715854.925, - '0.006652319091792501' - ], - [ - 1495715914.925, - '0.005493557402895287' - ], - [ - 1495715974.925, - '0.0058652434829145166' - ], - [ - 1495716034.925, - '0.005627400430468021' - ], - [ - 1495716094.925, - '0.006240656190475609' - ], - [ - 1495716154.925, - '0.006305997676168624' - ], - [ - 1495716214.925, - '0.005388057732783248' - ], - [ - 1495716274.925, - '0.0052814916048421244' - ], - [ - 1495716334.925, - '0.00699498614272497' - ], - [ - 1495716394.925, - '0.00627768693035141' - ], - [ - 1495716454.925, - '0.0042411487048161145' - ], - [ - 1495716514.925, - '0.005348647473627653' - ], - [ - 1495716574.925, - '0.0047176657142853975' - ], - [ - 1495716634.925, - '0.004437898571428686' - ], - [ - 1495716694.925, - '0.004923527366927261' - ], - [ - 1495716754.925, - '0.005131935066048421' - ], - [ - 1495716814.925, - '0.005046949523809611' - ], - [ - 1495716874.925, - '0.00547184095238092' - ], - [ - 1495716934.925, - '0.005224140016380444' - ], - [ - 1495716994.925, - '0.005297991171665292' - ], - [ - 1495717054.925, - '0.005492965995623498' - ], - [ - 1495717114.925, - '0.005754660000000403' - ], - [ - 1495717174.925, - '0.005949557138639285' - ], - [ - 1495717234.925, - '0.006091816112534666' - ], - [ - 1495717294.925, - '0.005554210080192063' - ], - [ - 1495717354.925, - '0.006411504395279871' - ], - [ - 1495717414.925, - '0.006319643996609606' - ], - [ - 1495717474.925, - '0.005539174405717675' - ], - [ - 1495717534.925, - '0.0053157078842772255' - ], - [ - 1495717594.925, - '0.005247480952381066' - ], - [ - 1495717654.925, - '0.004820141620396252' - ], - [ - 1495717714.925, - '0.005906173868322844' - ], - [ - 1495717774.925, - '0.006173117219570961' - ], - [ - 1495717834.925, - '0.005963340952380661' - ], - [ - 1495717894.925, - '0.005698976627681527' - ], - [ - 1495717954.925, - '0.004751279096346378' - ], - [ - 1495718014.925, - '0.005733142379359711' - ], - [ - 1495718074.925, - '0.004831689010348035' - ], - [ - 1495718134.925, - '0.005188370476191092' - ], - [ - 1495718194.925, - '0.004793227554547938' - ], - [ - 1495718254.925, - '0.003997442857142731' - ], - [ - 1495718314.925, - '0.004386040132951264' - ] - ] - } - ] - } - ] - } - ] - } + metric: {}, + values: [ + [1495700554.925, '0.0010794445585559514'], + [1495700614.925, '0.003927214935433527'], + [1495700674.925, '0.0053045219047619975'], + [1495700734.925, '0.0048892095238097155'], + [1495700794.925, '0.005827140952381137'], + [1495700854.925, '0.00569846906219937'], + [1495700914.925, '0.004972616802849382'], + [1495700974.925, '0.005117509523809902'], + [1495701034.925, '0.00512389061919564'], + [1495701094.925, '0.005199100501890691'], + [1495701154.925, '0.005415746394885837'], + [1495701214.925, '0.005607682788146286'], + [1495701274.925, '0.005641300000000118'], + [1495701334.925, '0.0071166279368766495'], + [1495701394.925, '0.0063242138095234044'], + [1495701454.925, '0.005793314698235304'], + [1495701514.925, '0.00703934942237556'], + [1495701574.925, '0.006357007076123191'], + [1495701634.925, '0.003753167300126738'], + [1495701694.925, '0.005018469678430698'], + [1495701754.925, '0.0045217153371887'], + [1495701814.925, '0.006140104285714119'], + [1495701874.925, '0.004818684285714102'], + [1495701934.925, '0.005079509718955242'], + [1495701994.925, '0.005059981142498263'], + [1495702054.925, '0.005269098389538773'], + [1495702114.925, '0.005269954285714175'], + [1495702174.925, '0.014199241435795856'], + [1495702234.925, '0.01511936843111017'], + [1495702294.925, '0.0060933692920682875'], + [1495702354.925, '0.004945682380952493'], + [1495702414.925, '0.005641266666666565'], + [1495702474.925, '0.005223752857142996'], + [1495702534.925, '0.005743098505699831'], + [1495702594.925, '0.00538493380952391'], + [1495702654.925, '0.005507793883751339'], + [1495702714.925, '0.005666705714285466'], + [1495702774.925, '0.006231530000000112'], + [1495702834.925, '0.006570768635394899'], + [1495702894.925, '0.005551146666666895'], + [1495702954.925, '0.005602604737098058'], + [1495703014.925, '0.00613993580402159'], + [1495703074.925, '0.004770258764368832'], + [1495703134.925, '0.005512376671364914'], + [1495703194.925, '0.005254436666666674'], + [1495703254.925, '0.0050109839141320505'], + [1495703314.925, '0.0049478019256960016'], + [1495703374.925, '0.0037666860965123463'], + [1495703434.925, '0.004813526061656314'], + [1495703494.925, '0.005047748095238278'], + [1495703554.925, '0.00386494081008772'], + [1495703614.925, '0.004304037408111405'], + [1495703674.925, '0.004999466661587168'], + [1495703734.925, '0.004689140476190834'], + [1495703794.925, '0.004746126153582475'], + [1495703854.925, '0.004482706382572302'], + [1495703914.925, '0.004032808931864524'], + [1495703974.925, '0.005728319047618988'], + [1495704034.925, '0.004436139179627006'], + [1495704094.925, '0.004553455714285617'], + [1495704154.925, '0.003455244285714341'], + [1495704214.925, '0.004742244761904621'], + [1495704274.925, '0.005366978571428422'], + [1495704334.925, '0.004257954837665058'], + [1495704394.925, '0.005431603259831257'], + [1495704454.925, '0.0052009214498621986'], + [1495704514.925, '0.004317201904761618'], + [1495704574.925, '0.004307384285714157'], + [1495704634.925, '0.004789801146644822'], + [1495704694.925, '0.0051429795906706485'], + [1495704754.925, '0.005322495714285479'], + [1495704814.925, '0.004512809333244233'], + [1495704874.925, '0.004953843582568726'], + [1495704934.925, '0.005812690120858119'], + [1495704994.925, '0.004997024285714838'], + [1495705054.925, '0.005246216154439592'], + [1495705114.925, '0.0063494966618726795'], + [1495705174.925, '0.005306004342898225'], + [1495705234.925, '0.005081412857142978'], + [1495705294.925, '0.00511409523809522'], + [1495705354.925, '0.0047861001481192'], + [1495705414.925, '0.005107688228042962'], + [1495705474.925, '0.005271929582294012'], + [1495705534.925, '0.004453254502681249'], + [1495705594.925, '0.005799134293959226'], + [1495705654.925, '0.005340865929502478'], + [1495705714.925, '0.004911654761904942'], + [1495705774.925, '0.005888234873953261'], + [1495705834.925, '0.005565283333332954'], + [1495705894.925, '0.005522869047618869'], + [1495705954.925, '0.005177549737621646'], + [1495706014.925, '0.0053145810232096465'], + [1495706074.925, '0.004751095238095275'], + [1495706134.925, '0.006242077142856976'], + [1495706194.925, '0.00621034406957871'], + [1495706254.925, '0.006887592738978596'], + [1495706314.925, '0.006328128779726213'], + [1495706374.925, '0.007488363809523927'], + [1495706434.925, '0.006193758571428157'], + [1495706494.925, '0.0068798371839706935'], + [1495706554.925, '0.005757034340423128'], + [1495706614.925, '0.004571388497294698'], + [1495706674.925, '0.00620283044923395'], + [1495706734.925, '0.005607562380952455'], + [1495706794.925, '0.005506969933620308'], + [1495706854.925, '0.005621118095238131'], + [1495706914.925, '0.004876606098698849'], + [1495706974.925, '0.0047871205988517206'], + [1495707034.925, '0.00526405939458784'], + [1495707094.925, '0.005716323800605852'], + [1495707154.925, '0.005301459523809575'], + [1495707214.925, '0.0051613042857144905'], + [1495707274.925, '0.005384792857142714'], + [1495707334.925, '0.005259719047619222'], + [1495707394.925, '0.00584101142857182'], + [1495707454.925, '0.0060066121920326326'], + [1495707514.925, '0.006359978571428453'], + [1495707574.925, '0.006315876322151109'], + [1495707634.925, '0.005590012517198831'], + [1495707694.925, '0.005517419877137072'], + [1495707754.925, '0.006089813430348506'], + [1495707814.925, '0.00466754476190479'], + [1495707874.925, '0.006059954380517721'], + [1495707934.925, '0.005085657142856972'], + [1495707994.925, '0.005897665238095296'], + [1495708054.925, '0.0062282023199555885'], + [1495708114.925, '0.00526214553236979'], + [1495708174.925, '0.0044803300000000644'], + [1495708234.925, '0.005421443333333592'], + [1495708294.925, '0.005694326244512144'], + [1495708354.925, '0.005527721904761457'], + [1495708414.925, '0.005988819523809819'], + [1495708474.925, '0.005484704285714448'], + [1495708534.925, '0.005041123649230085'], + [1495708594.925, '0.005717767639612059'], + [1495708654.925, '0.005412954417342863'], + [1495708714.925, '0.005833343333333254'], + [1495708774.925, '0.005448135238094969'], + [1495708834.925, '0.005117341428571432'], + [1495708894.925, '0.005888345825277833'], + [1495708954.925, '0.005398543809524135'], + [1495709014.925, '0.005325611428571416'], + [1495709074.925, '0.005848668571428527'], + [1495709134.925, '0.005135003105145044'], + [1495709194.925, '0.0054551400000003'], + [1495709254.925, '0.005319472937322171'], + [1495709314.925, '0.00585677857142792'], + [1495709374.925, '0.0062146261904759215'], + [1495709434.925, '0.0067105060904182265'], + [1495709494.925, '0.005829691904762108'], + [1495709554.925, '0.005719280952381261'], + [1495709614.925, '0.005682603793416407'], + [1495709674.925, '0.0055272846277326934'], + [1495709734.925, '0.0057123680952386735'], + [1495709794.925, '0.00520597958075818'], + [1495709854.925, '0.005584358957263837'], + [1495709914.925, '0.005601104275197466'], + [1495709974.925, '0.005991657142857066'], + [1495710034.925, '0.00553722238095218'], + [1495710094.925, '0.005127883122696293'], + [1495710154.925, '0.005498111927534584'], + [1495710214.925, '0.005609934069084202'], + [1495710274.925, '0.00459206285714307'], + [1495710334.925, '0.0047910828571428084'], + [1495710394.925, '0.0056014671288845685'], + [1495710454.925, '0.005686936791078528'], + [1495710514.925, '0.00444480476190448'], + [1495710574.925, '0.005780394696738921'], + [1495710634.925, '0.0053107227550210365'], + [1495710694.925, '0.005096031495761817'], + [1495710754.925, '0.005451377979091524'], + [1495710814.925, '0.005328136666667083'], + [1495710874.925, '0.006020612857143043'], + [1495710934.925, '0.0061063585714285365'], + [1495710994.925, '0.006018346015752312'], + [1495711054.925, '0.005069130952381193'], + [1495711114.925, '0.005458406190476052'], + [1495711174.925, '0.00577219190476179'], + [1495711234.925, '0.005760814645658314'], + [1495711294.925, '0.005371875716579101'], + [1495711354.925, '0.0064232666666665834'], + [1495711414.925, '0.009369806836906667'], + [1495711474.925, '0.008956864761904692'], + [1495711534.925, '0.005266849368559271'], + [1495711594.925, '0.005335111364934262'], + [1495711654.925, '0.006461778319586945'], + [1495711714.925, '0.004687939890762393'], + [1495711774.925, '0.004438831245760684'], + [1495711834.925, '0.005142786666666613'], + [1495711894.925, '0.007257734212054963'], + [1495711954.925, '0.005621991904761494'], + [1495712014.925, '0.007868689999999862'], + [1495712074.925, '0.00910970215275738'], + [1495712134.925, '0.006151004285714278'], + [1495712194.925, '0.005447120924961522'], + [1495712254.925, '0.005150705153929503'], + [1495712314.925, '0.006358108714969314'], + [1495712374.925, '0.0057725354795696475'], + [1495712434.925, '0.005232139047619015'], + [1495712494.925, '0.004932809617949037'], + [1495712554.925, '0.004511607508499662'], + [1495712614.925, '0.00440487701522666'], + [1495712674.925, '0.005479113333333174'], + [1495712734.925, '0.004726317619047547'], + [1495712794.925, '0.005582041102958029'], + [1495712854.925, '0.006381481216082099'], + [1495712914.925, '0.005474260014095208'], + [1495712974.925, '0.00567597142857188'], + [1495713034.925, '0.0064741233333332985'], + [1495713094.925, '0.005467475714285271'], + [1495713154.925, '0.004868648393824457'], + [1495713214.925, '0.005254923286444893'], + [1495713274.925, '0.005599217150312865'], + [1495713334.925, '0.005105413720618919'], + [1495713394.925, '0.007246073333333279'], + [1495713454.925, '0.005990312380952272'], + [1495713514.925, '0.005594601853351101'], + [1495713574.925, '0.004739258673727054'], + [1495713634.925, '0.003932121428571783'], + [1495713694.925, '0.005018188268459395'], + [1495713754.925, '0.004538238095237985'], + [1495713814.925, '0.00561816643265435'], + [1495713874.925, '0.0063132584495033586'], + [1495713934.925, '0.00442385238095213'], + [1495713994.925, '0.004181795887658453'], + [1495714054.925, '0.004437759047619037'], + [1495714114.925, '0.006421748157178241'], + [1495714174.925, '0.006525143809523842'], + [1495714234.925, '0.004715904935144247'], + [1495714294.925, '0.005966040152763461'], + [1495714354.925, '0.005614535466921674'], + [1495714414.925, '0.004934375119415906'], + [1495714474.925, '0.0054122933333327385'], + [1495714534.925, '0.004926540699612279'], + [1495714594.925, '0.006124649517134237'], + [1495714654.925, '0.004629427092013995'], + [1495714714.925, '0.005117951257607005'], + [1495714774.925, '0.004868774512685422'], + [1495714834.925, '0.005310093333333399'], + [1495714894.925, '0.0054907752286127345'], + [1495714954.925, '0.004597678117351089'], + [1495715014.925, '0.0059622552380952'], + [1495715074.925, '0.005352457072655368'], + [1495715134.925, '0.005491630952381143'], + [1495715194.925, '0.006391770078379791'], + [1495715254.925, '0.005933472857142518'], + [1495715314.925, '0.005301314285714163'], + [1495715374.925, '0.0058352959724814165'], + [1495715434.925, '0.006154755147867044'], + [1495715494.925, '0.009391935637482038'], + [1495715554.925, '0.007846462857142592'], + [1495715614.925, '0.00477608215316353'], + [1495715674.925, '0.006132865238094998'], + [1495715734.925, '0.006159762457649516'], + [1495715794.925, '0.005957307073265968'], + [1495715854.925, '0.006652319091792501'], + [1495715914.925, '0.005493557402895287'], + [1495715974.925, '0.0058652434829145166'], + [1495716034.925, '0.005627400430468021'], + [1495716094.925, '0.006240656190475609'], + [1495716154.925, '0.006305997676168624'], + [1495716214.925, '0.005388057732783248'], + [1495716274.925, '0.0052814916048421244'], + [1495716334.925, '0.00699498614272497'], + [1495716394.925, '0.00627768693035141'], + [1495716454.925, '0.0042411487048161145'], + [1495716514.925, '0.005348647473627653'], + [1495716574.925, '0.0047176657142853975'], + [1495716634.925, '0.004437898571428686'], + [1495716694.925, '0.004923527366927261'], + [1495716754.925, '0.005131935066048421'], + [1495716814.925, '0.005046949523809611'], + [1495716874.925, '0.00547184095238092'], + [1495716934.925, '0.005224140016380444'], + [1495716994.925, '0.005297991171665292'], + [1495717054.925, '0.005492965995623498'], + [1495717114.925, '0.005754660000000403'], + [1495717174.925, '0.005949557138639285'], + [1495717234.925, '0.006091816112534666'], + [1495717294.925, '0.005554210080192063'], + [1495717354.925, '0.006411504395279871'], + [1495717414.925, '0.006319643996609606'], + [1495717474.925, '0.005539174405717675'], + [1495717534.925, '0.0053157078842772255'], + [1495717594.925, '0.005247480952381066'], + [1495717654.925, '0.004820141620396252'], + [1495717714.925, '0.005906173868322844'], + [1495717774.925, '0.006173117219570961'], + [1495717834.925, '0.005963340952380661'], + [1495717894.925, '0.005698976627681527'], + [1495717954.925, '0.004751279096346378'], + [1495718014.925, '0.005733142379359711'], + [1495718074.925, '0.004831689010348035'], + [1495718134.925, '0.005188370476191092'], + [1495718194.925, '0.004793227554547938'], + [1495718254.925, '0.003997442857142731'], + [1495718314.925, '0.004386040132951264'], + ], + }, + ], + }, + ], + }, + ], + }, ], - 'last_update': '2017-05-25T13:18:34.949Z' + last_update: '2017-05-25T13:18:34.949Z', }; export default metricsGroupsAPIResponse; @@ -2432,41 +651,44 @@ export const deploymentData = [ id: 111, iid: 3, sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', + commitUrl: + 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', ref: { - name: 'master' + name: 'master', }, created_at: '2017-05-31T21:23:37.881Z', tag: false, tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': true + 'last?': true, }, { id: 110, iid: 2, sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', - commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', + commitUrl: + 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187', ref: { - name: 'master' + name: 'master', }, created_at: '2017-05-30T20:08:04.629Z', tag: false, - tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': false + tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', + 'last?': false, }, { id: 109, iid: 1, sha: '6511e58faafaa7ad2228990ec57f19d66f7db7c2', - commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/6511e58faafaa7ad2228990ec57f19d66f7db7c2', + commitUrl: + 'http://test.host/frontend-fixtures/environments-project/commit/6511e58faafaa7ad2228990ec57f19d66f7db7c2', ref: { - name: 'update2-readme' + name: 'update2-readme', }, created_at: '2017-05-30T17:42:38.409Z', tag: false, tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false', - 'last?': false - } + 'last?': false, + }, ]; export const statePaths = { @@ -2476,5844 +698,5844 @@ export const statePaths = { }; export const singleRowMetricsMultipleSeries = [ - { - 'title': 'Multiple Time Series', - 'weight': 1, - 'y_label': 'Request Rates', - 'queries': [ - { - 'query_range': 'sum(rate(nginx_responses_total{environment="production"}[2m])) by (status_code)', - 'label': 'Requests', - 'unit': 'Req/sec', - 'result': [ - { - 'metric': { - 'status_code': '1xx' - }, - 'values': [ - { - 'time': '2017-08-27T11:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T11:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T12:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T13:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T14:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T15:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T16:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T17:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:01:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:02:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:03:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:04:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:05:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:06:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:07:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:08:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:09:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:10:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:11:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:12:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:13:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:14:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:15:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:16:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:17:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:18:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:19:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:20:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:21:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:22:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:23:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:24:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:25:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:26:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:27:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:28:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:29:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:30:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:31:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:32:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:33:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:34:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:35:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:36:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:37:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:38:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:39:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:40:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:41:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:42:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:43:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:44:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:45:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:46:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:47:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:48:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:49:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:50:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:51:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:52:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:53:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:54:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:55:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:56:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:57:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:58:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T18:59:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T19:00:51.462Z', - 'value': '0' - }, - { - 'time': '2017-08-27T19:01:51.462Z', - 'value': '0' - } - ] - }, - { - 'metric': { - 'status_code': '2xx' - }, - 'values': [ - { - 'time': '2017-08-27T11:01:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:02:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T11:03:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:04:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:05:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:06:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:07:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:08:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:09:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:10:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:11:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:12:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:13:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:14:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:15:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:16:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:17:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:18:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:19:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:20:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:21:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:22:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:23:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:24:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:25:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:26:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:27:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:28:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:29:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:30:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:31:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:32:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:33:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:34:51.462Z', - 'value': '1.333320635041571' - }, - { - 'time': '2017-08-27T11:35:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:36:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:37:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:38:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:39:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:40:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:41:51.462Z', - 'value': '1.3333587306424883' - }, - { - 'time': '2017-08-27T11:42:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:43:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:44:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:45:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:46:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:47:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:48:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:49:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T11:50:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:51:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:52:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:53:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:54:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:55:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:56:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:57:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T11:58:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T11:59:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:00:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:01:51.462Z', - 'value': '1.3333460318669703' - }, - { - 'time': '2017-08-27T12:02:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:03:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:04:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:05:51.462Z', - 'value': '1.31427319739812' - }, - { - 'time': '2017-08-27T12:06:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:07:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:08:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:09:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:10:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:11:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:12:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:13:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:14:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:15:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:16:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:17:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:18:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:19:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:20:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:21:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:22:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:23:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:24:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:25:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:26:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:27:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:28:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:29:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:30:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:31:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:32:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:33:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:34:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:35:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:36:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:37:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:38:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:39:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:40:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:41:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:42:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:43:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:44:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:45:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:46:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:47:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:48:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:49:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:50:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:51:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:52:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:53:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:54:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T12:55:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:56:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:57:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T12:58:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T12:59:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:00:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:01:51.462Z', - 'value': '1.295225759754669' - }, - { - 'time': '2017-08-27T13:02:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:03:51.462Z', - 'value': '1.2952627669098458' - }, - { - 'time': '2017-08-27T13:04:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:05:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:06:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:07:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:08:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:09:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:10:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:11:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:12:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:13:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:14:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:15:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T13:16:51.462Z', - 'value': '1.3333587306424883' - }, - { - 'time': '2017-08-27T13:17:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:18:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:19:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:20:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:21:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:22:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:23:51.462Z', - 'value': '1.276190476190476' - }, - { - 'time': '2017-08-27T13:24:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T13:25:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:26:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:27:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:28:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:29:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:30:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:31:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:32:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:33:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:34:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:35:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:36:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:37:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:38:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:39:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:40:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:41:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:42:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:43:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:44:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:45:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:46:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T13:47:51.462Z', - 'value': '1.276190476190476' - }, - { - 'time': '2017-08-27T13:48:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:49:51.462Z', - 'value': '1.295225759754669' - }, - { - 'time': '2017-08-27T13:50:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:51:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:52:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:53:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:54:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:55:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:56:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T13:57:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T13:58:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T13:59:51.462Z', - 'value': '1.295225759754669' - }, - { - 'time': '2017-08-27T14:00:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:01:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:02:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:03:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:04:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:05:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:06:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:07:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:08:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:09:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:10:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:11:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:12:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:13:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:14:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:15:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:16:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:17:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:18:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:19:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:20:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:21:51.462Z', - 'value': '1.3333079369916765' - }, - { - 'time': '2017-08-27T14:22:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:23:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:24:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:25:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:26:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:27:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:28:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:29:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:30:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:31:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:32:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:33:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T14:34:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:35:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:36:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:37:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:38:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:39:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:40:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:41:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:42:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:43:51.462Z', - 'value': '1.276190476190476' - }, - { - 'time': '2017-08-27T14:44:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T14:45:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:46:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:47:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:48:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:49:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:50:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:51:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:52:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:53:51.462Z', - 'value': '1.333320635041571' - }, - { - 'time': '2017-08-27T14:54:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:55:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T14:56:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:57:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T14:58:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T14:59:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:00:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:01:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:02:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:03:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:04:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T15:05:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:06:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:07:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:08:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:09:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:10:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:11:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:12:51.462Z', - 'value': '1.31427319739812' - }, - { - 'time': '2017-08-27T15:13:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:14:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:15:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:16:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:17:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:18:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:19:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:20:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:21:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:22:51.462Z', - 'value': '1.3333460318669703' - }, - { - 'time': '2017-08-27T15:23:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:24:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:25:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:26:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:27:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:28:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:29:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:30:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:31:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:32:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:33:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:34:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:35:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:36:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:37:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:38:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:39:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:40:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:41:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:42:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:43:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:44:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:45:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:46:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:47:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:48:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:49:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T15:50:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:51:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:52:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:53:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:54:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:55:51.462Z', - 'value': '1.3333587306424883' - }, - { - 'time': '2017-08-27T15:56:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T15:57:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:58:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T15:59:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:00:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:01:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:02:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:03:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:04:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:05:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:06:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:07:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:08:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:09:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:10:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:11:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:12:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:13:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:14:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:15:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:16:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:17:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:18:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:19:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:20:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:21:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:22:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:23:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:24:51.462Z', - 'value': '1.295225759754669' - }, - { - 'time': '2017-08-27T16:25:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:26:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:27:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:28:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:29:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:30:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:31:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:32:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:33:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:34:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:35:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:36:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:37:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:38:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:39:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:40:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:41:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:42:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:43:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:44:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:45:51.462Z', - 'value': '1.3142982314117277' - }, - { - 'time': '2017-08-27T16:46:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:47:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:48:51.462Z', - 'value': '1.333320635041571' - }, - { - 'time': '2017-08-27T16:49:51.462Z', - 'value': '1.31427319739812' - }, - { - 'time': '2017-08-27T16:50:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:51:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:52:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:53:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:54:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:55:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T16:56:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:57:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T16:58:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T16:59:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:00:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:01:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:02:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:03:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:04:51.462Z', - 'value': '1.2952504309564854' - }, - { - 'time': '2017-08-27T17:05:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:06:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:07:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:08:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:09:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:10:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:11:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:12:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:13:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:14:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:15:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:16:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:17:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:18:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:19:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:20:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:21:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:22:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:23:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:24:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:25:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:26:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:27:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:28:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:29:51.462Z', - 'value': '1.295225759754669' - }, - { - 'time': '2017-08-27T17:30:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:31:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:32:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:33:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:34:51.462Z', - 'value': '1.295225759754669' - }, - { - 'time': '2017-08-27T17:35:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:36:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:37:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:38:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:39:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:40:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:41:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:42:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:43:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:44:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:45:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:46:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:47:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:48:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:49:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:50:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:51:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:52:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:53:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:54:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:55:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T17:56:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:57:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T17:58:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T17:59:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T18:00:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:01:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:02:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:03:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:04:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:05:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:06:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:07:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:08:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:09:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:10:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:11:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:12:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T18:13:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:14:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:15:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:16:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:17:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:18:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:19:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:20:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:21:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:22:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:23:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:24:51.462Z', - 'value': '1.2571428571428571' - }, - { - 'time': '2017-08-27T18:25:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:26:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:27:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:28:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:29:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:30:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:31:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:32:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:33:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:34:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:35:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:36:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:37:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T18:38:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:39:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:40:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:41:51.462Z', - 'value': '1.580952380952381' - }, - { - 'time': '2017-08-27T18:42:51.462Z', - 'value': '1.7333333333333334' - }, - { - 'time': '2017-08-27T18:43:51.462Z', - 'value': '2.057142857142857' - }, - { - 'time': '2017-08-27T18:44:51.462Z', - 'value': '2.1904761904761902' - }, - { - 'time': '2017-08-27T18:45:51.462Z', - 'value': '1.8285714285714287' - }, - { - 'time': '2017-08-27T18:46:51.462Z', - 'value': '2.1142857142857143' - }, - { - 'time': '2017-08-27T18:47:51.462Z', - 'value': '1.619047619047619' - }, - { - 'time': '2017-08-27T18:48:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:49:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:50:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T18:51:51.462Z', - 'value': '1.2952504309564854' - }, - { - 'time': '2017-08-27T18:52:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:53:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:54:51.462Z', - 'value': '1.3333333333333333' - }, - { - 'time': '2017-08-27T18:55:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:56:51.462Z', - 'value': '1.314285714285714' - }, - { - 'time': '2017-08-27T18:57:51.462Z', - 'value': '1.295238095238095' - }, - { - 'time': '2017-08-27T18:58:51.462Z', - 'value': '1.7142857142857142' - }, - { - 'time': '2017-08-27T18:59:51.462Z', - 'value': '1.7333333333333334' - }, - { - 'time': '2017-08-27T19:00:51.462Z', - 'value': '1.3904761904761904' - }, - { - 'time': '2017-08-27T19:01:51.462Z', - 'value': '1.5047619047619047' - } - ] - }, - ], - 'when': [ - { - 'value': 'hundred(s)', - 'color': 'green', - }, - ], - } - ] - }, - { - 'title': 'Throughput', - 'weight': 1, - 'y_label': 'Requests / Sec', - 'queries': [ - { - 'query_range': 'sum(rate(nginx_requests_total{server_zone!=\'*\', server_zone!=\'_\', container_name!=\'POD\',environment=\'production\'}[2m]))', - 'label': 'Total', - 'unit': 'req / sec', - 'result': [ - { - 'metric': { - - }, - 'values': [ - { - 'time': '2017-08-27T11:01:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:02:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T11:03:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:04:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:05:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:06:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:07:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:08:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:09:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:10:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:11:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:12:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:13:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:14:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:15:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:16:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:17:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:18:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:19:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:20:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:21:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:22:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:23:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:24:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:25:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:26:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:27:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:28:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:29:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:30:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:31:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:32:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:33:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:34:51.462Z', - 'value': '0.4952333787297264' - }, - { - 'time': '2017-08-27T11:35:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:36:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:37:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:38:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:39:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:40:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:41:51.462Z', - 'value': '0.49524752852435283' - }, - { - 'time': '2017-08-27T11:42:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:43:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:44:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:45:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:46:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:47:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:48:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:49:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T11:50:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:51:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:52:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:53:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:54:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:55:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:56:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:57:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T11:58:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T11:59:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:00:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:01:51.462Z', - 'value': '0.49524281183630325' - }, - { - 'time': '2017-08-27T12:02:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:03:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:04:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:05:51.462Z', - 'value': '0.4857096599080009' - }, - { - 'time': '2017-08-27T12:06:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:07:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:08:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:09:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:10:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:11:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:12:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:13:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:14:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:15:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:16:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:17:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:18:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:19:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:20:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:21:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:22:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:23:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:24:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:25:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:26:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:27:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:28:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:29:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:30:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:31:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:32:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:33:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:34:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:35:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:36:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:37:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:38:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:39:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:40:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:41:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:42:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:43:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:44:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:45:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:46:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:47:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:48:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:49:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:50:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:51:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:52:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:53:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:54:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T12:55:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:56:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:57:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T12:58:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T12:59:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:00:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:01:51.462Z', - 'value': '0.4761859410862754' - }, - { - 'time': '2017-08-27T13:02:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:03:51.462Z', - 'value': '0.4761995466580315' - }, - { - 'time': '2017-08-27T13:04:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:05:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:06:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:07:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:08:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:09:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:10:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:11:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:12:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:13:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:14:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:15:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T13:16:51.462Z', - 'value': '0.49524752852435283' - }, - { - 'time': '2017-08-27T13:17:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:18:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:19:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:20:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:21:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:22:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:23:51.462Z', - 'value': '0.4666666666666667' - }, - { - 'time': '2017-08-27T13:24:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T13:25:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:26:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:27:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:28:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:29:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:30:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:31:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:32:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:33:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:34:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:35:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:36:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:37:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:38:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:39:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:40:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:41:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:42:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:43:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:44:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:45:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:46:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T13:47:51.462Z', - 'value': '0.4666666666666667' - }, - { - 'time': '2017-08-27T13:48:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:49:51.462Z', - 'value': '0.4761859410862754' - }, - { - 'time': '2017-08-27T13:50:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:51:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:52:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:53:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:54:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:55:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:56:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T13:57:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T13:58:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T13:59:51.462Z', - 'value': '0.4761859410862754' - }, - { - 'time': '2017-08-27T14:00:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:01:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:02:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:03:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:04:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:05:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:06:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:07:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:08:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:09:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:10:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:11:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:12:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:13:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:14:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:15:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:16:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:17:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:18:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:19:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:20:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:21:51.462Z', - 'value': '0.4952286623111941' - }, - { - 'time': '2017-08-27T14:22:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:23:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:24:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:25:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:26:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:27:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:28:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:29:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:30:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:31:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:32:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:33:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T14:34:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:35:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:36:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:37:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:38:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:39:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:40:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:41:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:42:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:43:51.462Z', - 'value': '0.4666666666666667' - }, - { - 'time': '2017-08-27T14:44:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T14:45:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:46:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:47:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:48:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:49:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:50:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:51:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:52:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:53:51.462Z', - 'value': '0.4952333787297264' - }, - { - 'time': '2017-08-27T14:54:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:55:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T14:56:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:57:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T14:58:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T14:59:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:00:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:01:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:02:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:03:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:04:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T15:05:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:06:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:07:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:08:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:09:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:10:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:11:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:12:51.462Z', - 'value': '0.4857096599080009' - }, - { - 'time': '2017-08-27T15:13:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:14:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:15:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:16:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:17:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:18:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:19:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:20:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:21:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:22:51.462Z', - 'value': '0.49524281183630325' - }, - { - 'time': '2017-08-27T15:23:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:24:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:25:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:26:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:27:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:28:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:29:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:30:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:31:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:32:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:33:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:34:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:35:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:36:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:37:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:38:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:39:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:40:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:41:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:42:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:43:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:44:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:45:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:46:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:47:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:48:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:49:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T15:50:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:51:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:52:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:53:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:54:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:55:51.462Z', - 'value': '0.49524752852435283' - }, - { - 'time': '2017-08-27T15:56:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T15:57:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:58:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T15:59:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:00:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:01:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:02:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:03:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:04:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:05:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:06:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:07:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:08:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:09:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:10:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:11:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:12:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:13:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:14:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:15:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:16:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:17:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:18:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:19:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:20:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:21:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:22:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:23:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:24:51.462Z', - 'value': '0.4761859410862754' - }, - { - 'time': '2017-08-27T16:25:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:26:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:27:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:28:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:29:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:30:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:31:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:32:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:33:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:34:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:35:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:36:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:37:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:38:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:39:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:40:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:41:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:42:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:43:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:44:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:45:51.462Z', - 'value': '0.485718911608682' - }, - { - 'time': '2017-08-27T16:46:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:47:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:48:51.462Z', - 'value': '0.4952333787297264' - }, - { - 'time': '2017-08-27T16:49:51.462Z', - 'value': '0.4857096599080009' - }, - { - 'time': '2017-08-27T16:50:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:51:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:52:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:53:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:54:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:55:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T16:56:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:57:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T16:58:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T16:59:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:00:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:01:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:02:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:03:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:04:51.462Z', - 'value': '0.47619501138106085' - }, - { - 'time': '2017-08-27T17:05:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:06:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:07:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:08:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:09:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:10:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:11:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:12:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:13:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:14:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:15:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:16:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:17:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:18:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:19:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:20:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:21:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:22:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:23:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:24:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:25:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:26:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:27:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:28:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:29:51.462Z', - 'value': '0.4761859410862754' - }, - { - 'time': '2017-08-27T17:30:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:31:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:32:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:33:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:34:51.462Z', - 'value': '0.4761859410862754' - }, - { - 'time': '2017-08-27T17:35:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:36:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:37:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:38:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:39:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:40:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:41:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:42:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:43:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:44:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:45:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:46:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:47:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:48:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:49:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:50:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:51:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:52:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:53:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:54:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:55:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T17:56:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:57:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T17:58:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T17:59:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T18:00:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:01:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:02:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:03:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:04:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:05:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:06:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:07:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:08:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:09:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:10:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:11:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:12:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T18:13:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:14:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:15:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:16:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:17:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:18:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:19:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:20:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:21:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:22:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:23:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:24:51.462Z', - 'value': '0.45714285714285713' - }, - { - 'time': '2017-08-27T18:25:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:26:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:27:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:28:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:29:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:30:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:31:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:32:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:33:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:34:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:35:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:36:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:37:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T18:38:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:39:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:40:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:41:51.462Z', - 'value': '0.6190476190476191' - }, - { - 'time': '2017-08-27T18:42:51.462Z', - 'value': '0.6952380952380952' - }, - { - 'time': '2017-08-27T18:43:51.462Z', - 'value': '0.857142857142857' - }, - { - 'time': '2017-08-27T18:44:51.462Z', - 'value': '0.9238095238095239' - }, - { - 'time': '2017-08-27T18:45:51.462Z', - 'value': '0.7428571428571429' - }, - { - 'time': '2017-08-27T18:46:51.462Z', - 'value': '0.8857142857142857' - }, - { - 'time': '2017-08-27T18:47:51.462Z', - 'value': '0.638095238095238' - }, - { - 'time': '2017-08-27T18:48:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:49:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:50:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T18:51:51.462Z', - 'value': '0.47619501138106085' - }, - { - 'time': '2017-08-27T18:52:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:53:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:54:51.462Z', - 'value': '0.4952380952380952' - }, - { - 'time': '2017-08-27T18:55:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:56:51.462Z', - 'value': '0.4857142857142857' - }, - { - 'time': '2017-08-27T18:57:51.462Z', - 'value': '0.47619047619047616' - }, - { - 'time': '2017-08-27T18:58:51.462Z', - 'value': '0.6857142857142856' - }, - { - 'time': '2017-08-27T18:59:51.462Z', - 'value': '0.6952380952380952' - }, - { - 'time': '2017-08-27T19:00:51.462Z', - 'value': '0.5238095238095237' - }, - { - 'time': '2017-08-27T19:01:51.462Z', - 'value': '0.5904761904761905' - } - ] - } - ] - } - ] - } + { + title: 'Multiple Time Series', + weight: 1, + y_label: 'Request Rates', + queries: [ + { + query_range: + 'sum(rate(nginx_responses_total{environment="production"}[2m])) by (status_code)', + label: 'Requests', + unit: 'Req/sec', + result: [ + { + metric: { + status_code: '1xx', + }, + values: [ + { + time: '2017-08-27T11:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T11:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T12:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T13:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T14:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T15:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T16:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T17:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:01:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:02:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:03:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:04:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:05:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:06:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:07:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:08:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:09:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:10:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:11:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:12:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:13:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:14:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:15:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:16:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:17:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:18:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:19:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:20:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:21:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:22:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:23:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:24:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:25:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:26:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:27:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:28:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:29:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:30:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:31:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:32:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:33:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:34:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:35:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:36:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:37:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:38:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:39:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:40:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:41:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:42:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:43:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:44:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:45:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:46:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:47:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:48:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:49:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:50:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:51:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:52:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:53:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:54:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:55:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:56:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:57:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:58:51.462Z', + value: '0', + }, + { + time: '2017-08-27T18:59:51.462Z', + value: '0', + }, + { + time: '2017-08-27T19:00:51.462Z', + value: '0', + }, + { + time: '2017-08-27T19:01:51.462Z', + value: '0', + }, + ], + }, + { + metric: { + status_code: '2xx', + }, + values: [ + { + time: '2017-08-27T11:01:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:02:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T11:03:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:04:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:05:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:06:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:07:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:08:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:09:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:10:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:11:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:12:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:13:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:14:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:15:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:16:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:17:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:18:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:19:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:20:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:21:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:22:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:23:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:24:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:25:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:26:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:27:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:28:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:29:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:30:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:31:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:32:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:33:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:34:51.462Z', + value: '1.333320635041571', + }, + { + time: '2017-08-27T11:35:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:36:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:37:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:38:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:39:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:40:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:41:51.462Z', + value: '1.3333587306424883', + }, + { + time: '2017-08-27T11:42:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:43:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:44:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:45:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:46:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:47:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:48:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:49:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T11:50:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:51:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:52:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:53:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:54:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:55:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:56:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:57:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T11:58:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T11:59:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:00:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:01:51.462Z', + value: '1.3333460318669703', + }, + { + time: '2017-08-27T12:02:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:03:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:04:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:05:51.462Z', + value: '1.31427319739812', + }, + { + time: '2017-08-27T12:06:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:07:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:08:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:09:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:10:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:11:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:12:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:13:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:14:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:15:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:16:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:17:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:18:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:19:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:20:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:21:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:22:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:23:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:24:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:25:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:26:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:27:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:28:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:29:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:30:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:31:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:32:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:33:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:34:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:35:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:36:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:37:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:38:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:39:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:40:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:41:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:42:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:43:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:44:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:45:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:46:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:47:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:48:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:49:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:50:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:51:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:52:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:53:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:54:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T12:55:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:56:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:57:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T12:58:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T12:59:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:00:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:01:51.462Z', + value: '1.295225759754669', + }, + { + time: '2017-08-27T13:02:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:03:51.462Z', + value: '1.2952627669098458', + }, + { + time: '2017-08-27T13:04:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:05:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:06:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:07:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:08:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:09:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:10:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:11:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:12:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:13:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:14:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:15:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T13:16:51.462Z', + value: '1.3333587306424883', + }, + { + time: '2017-08-27T13:17:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:18:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:19:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:20:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:21:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:22:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:23:51.462Z', + value: '1.276190476190476', + }, + { + time: '2017-08-27T13:24:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T13:25:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:26:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:27:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:28:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:29:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:30:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:31:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:32:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:33:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:34:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:35:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:36:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:37:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:38:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:39:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:40:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:41:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:42:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:43:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:44:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:45:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:46:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T13:47:51.462Z', + value: '1.276190476190476', + }, + { + time: '2017-08-27T13:48:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:49:51.462Z', + value: '1.295225759754669', + }, + { + time: '2017-08-27T13:50:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:51:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:52:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:53:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:54:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:55:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:56:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T13:57:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T13:58:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T13:59:51.462Z', + value: '1.295225759754669', + }, + { + time: '2017-08-27T14:00:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:01:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:02:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:03:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:04:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:05:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:06:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:07:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:08:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:09:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:10:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:11:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:12:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:13:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:14:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:15:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:16:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:17:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:18:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:19:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:20:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:21:51.462Z', + value: '1.3333079369916765', + }, + { + time: '2017-08-27T14:22:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:23:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:24:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:25:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:26:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:27:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:28:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:29:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:30:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:31:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:32:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:33:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T14:34:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:35:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:36:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:37:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:38:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:39:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:40:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:41:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:42:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:43:51.462Z', + value: '1.276190476190476', + }, + { + time: '2017-08-27T14:44:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T14:45:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:46:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:47:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:48:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:49:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:50:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:51:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:52:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:53:51.462Z', + value: '1.333320635041571', + }, + { + time: '2017-08-27T14:54:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:55:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T14:56:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:57:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T14:58:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T14:59:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:00:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:01:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:02:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:03:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:04:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T15:05:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:06:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:07:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:08:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:09:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:10:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:11:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:12:51.462Z', + value: '1.31427319739812', + }, + { + time: '2017-08-27T15:13:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:14:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:15:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:16:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:17:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:18:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:19:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:20:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:21:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:22:51.462Z', + value: '1.3333460318669703', + }, + { + time: '2017-08-27T15:23:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:24:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:25:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:26:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:27:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:28:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:29:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:30:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:31:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:32:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:33:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:34:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:35:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:36:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:37:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:38:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:39:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:40:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:41:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:42:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:43:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:44:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:45:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:46:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:47:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:48:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:49:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T15:50:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:51:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:52:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:53:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:54:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:55:51.462Z', + value: '1.3333587306424883', + }, + { + time: '2017-08-27T15:56:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T15:57:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:58:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T15:59:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:00:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:01:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:02:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:03:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:04:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:05:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:06:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:07:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:08:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:09:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:10:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:11:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:12:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:13:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:14:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:15:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:16:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:17:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:18:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:19:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:20:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:21:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:22:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:23:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:24:51.462Z', + value: '1.295225759754669', + }, + { + time: '2017-08-27T16:25:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:26:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:27:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:28:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:29:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:30:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:31:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:32:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:33:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:34:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:35:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:36:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:37:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:38:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:39:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:40:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:41:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:42:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:43:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:44:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:45:51.462Z', + value: '1.3142982314117277', + }, + { + time: '2017-08-27T16:46:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:47:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:48:51.462Z', + value: '1.333320635041571', + }, + { + time: '2017-08-27T16:49:51.462Z', + value: '1.31427319739812', + }, + { + time: '2017-08-27T16:50:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:51:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:52:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:53:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:54:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:55:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T16:56:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:57:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T16:58:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T16:59:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:00:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:01:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:02:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:03:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:04:51.462Z', + value: '1.2952504309564854', + }, + { + time: '2017-08-27T17:05:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:06:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:07:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:08:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:09:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:10:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:11:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:12:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:13:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:14:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:15:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:16:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:17:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:18:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:19:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:20:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:21:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:22:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:23:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:24:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:25:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:26:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:27:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:28:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:29:51.462Z', + value: '1.295225759754669', + }, + { + time: '2017-08-27T17:30:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:31:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:32:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:33:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:34:51.462Z', + value: '1.295225759754669', + }, + { + time: '2017-08-27T17:35:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:36:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:37:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:38:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:39:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:40:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:41:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:42:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:43:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:44:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:45:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:46:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:47:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:48:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:49:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:50:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:51:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:52:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:53:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:54:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:55:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T17:56:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:57:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T17:58:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T17:59:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T18:00:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:01:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:02:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:03:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:04:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:05:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:06:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:07:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:08:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:09:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:10:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:11:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:12:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T18:13:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:14:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:15:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:16:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:17:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:18:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:19:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:20:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:21:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:22:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:23:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:24:51.462Z', + value: '1.2571428571428571', + }, + { + time: '2017-08-27T18:25:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:26:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:27:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:28:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:29:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:30:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:31:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:32:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:33:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:34:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:35:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:36:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:37:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T18:38:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:39:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:40:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:41:51.462Z', + value: '1.580952380952381', + }, + { + time: '2017-08-27T18:42:51.462Z', + value: '1.7333333333333334', + }, + { + time: '2017-08-27T18:43:51.462Z', + value: '2.057142857142857', + }, + { + time: '2017-08-27T18:44:51.462Z', + value: '2.1904761904761902', + }, + { + time: '2017-08-27T18:45:51.462Z', + value: '1.8285714285714287', + }, + { + time: '2017-08-27T18:46:51.462Z', + value: '2.1142857142857143', + }, + { + time: '2017-08-27T18:47:51.462Z', + value: '1.619047619047619', + }, + { + time: '2017-08-27T18:48:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:49:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:50:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T18:51:51.462Z', + value: '1.2952504309564854', + }, + { + time: '2017-08-27T18:52:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:53:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:54:51.462Z', + value: '1.3333333333333333', + }, + { + time: '2017-08-27T18:55:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:56:51.462Z', + value: '1.314285714285714', + }, + { + time: '2017-08-27T18:57:51.462Z', + value: '1.295238095238095', + }, + { + time: '2017-08-27T18:58:51.462Z', + value: '1.7142857142857142', + }, + { + time: '2017-08-27T18:59:51.462Z', + value: '1.7333333333333334', + }, + { + time: '2017-08-27T19:00:51.462Z', + value: '1.3904761904761904', + }, + { + time: '2017-08-27T19:01:51.462Z', + value: '1.5047619047619047', + }, + ], + }, + ], + when: [ + { + value: 'hundred(s)', + color: 'green', + }, + ], + }, + ], + }, + { + title: 'Throughput', + weight: 1, + y_label: 'Requests / Sec', + queries: [ + { + query_range: + "sum(rate(nginx_requests_total{server_zone!='*', server_zone!='_', container_name!='POD',environment='production'}[2m]))", + label: 'Total', + unit: 'req / sec', + result: [ + { + metric: {}, + values: [ + { + time: '2017-08-27T11:01:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:02:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T11:03:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:04:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:05:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:06:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:07:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:08:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:09:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:10:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:11:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:12:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:13:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:14:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:15:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:16:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:17:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:18:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:19:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:20:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:21:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:22:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:23:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:24:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:25:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:26:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:27:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:28:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:29:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:30:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:31:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:32:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:33:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:34:51.462Z', + value: '0.4952333787297264', + }, + { + time: '2017-08-27T11:35:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:36:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:37:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:38:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:39:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:40:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:41:51.462Z', + value: '0.49524752852435283', + }, + { + time: '2017-08-27T11:42:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:43:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:44:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:45:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:46:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:47:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:48:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:49:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T11:50:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:51:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:52:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:53:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:54:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:55:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:56:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:57:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T11:58:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T11:59:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:00:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:01:51.462Z', + value: '0.49524281183630325', + }, + { + time: '2017-08-27T12:02:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:03:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:04:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:05:51.462Z', + value: '0.4857096599080009', + }, + { + time: '2017-08-27T12:06:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:07:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:08:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:09:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:10:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:11:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:12:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:13:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:14:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:15:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:16:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:17:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:18:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:19:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:20:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:21:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:22:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:23:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:24:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:25:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:26:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:27:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:28:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:29:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:30:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:31:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:32:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:33:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:34:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:35:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:36:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:37:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:38:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:39:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:40:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:41:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:42:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:43:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:44:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:45:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:46:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:47:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:48:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:49:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:50:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:51:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:52:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:53:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:54:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T12:55:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:56:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:57:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T12:58:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T12:59:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:00:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:01:51.462Z', + value: '0.4761859410862754', + }, + { + time: '2017-08-27T13:02:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:03:51.462Z', + value: '0.4761995466580315', + }, + { + time: '2017-08-27T13:04:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:05:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:06:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:07:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:08:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:09:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:10:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:11:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:12:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:13:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:14:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:15:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T13:16:51.462Z', + value: '0.49524752852435283', + }, + { + time: '2017-08-27T13:17:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:18:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:19:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:20:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:21:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:22:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:23:51.462Z', + value: '0.4666666666666667', + }, + { + time: '2017-08-27T13:24:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T13:25:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:26:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:27:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:28:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:29:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:30:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:31:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:32:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:33:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:34:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:35:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:36:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:37:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:38:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:39:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:40:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:41:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:42:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:43:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:44:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:45:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:46:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T13:47:51.462Z', + value: '0.4666666666666667', + }, + { + time: '2017-08-27T13:48:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:49:51.462Z', + value: '0.4761859410862754', + }, + { + time: '2017-08-27T13:50:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:51:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:52:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:53:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:54:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:55:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:56:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T13:57:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T13:58:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T13:59:51.462Z', + value: '0.4761859410862754', + }, + { + time: '2017-08-27T14:00:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:01:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:02:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:03:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:04:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:05:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:06:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:07:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:08:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:09:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:10:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:11:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:12:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:13:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:14:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:15:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:16:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:17:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:18:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:19:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:20:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:21:51.462Z', + value: '0.4952286623111941', + }, + { + time: '2017-08-27T14:22:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:23:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:24:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:25:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:26:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:27:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:28:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:29:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:30:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:31:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:32:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:33:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T14:34:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:35:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:36:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:37:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:38:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:39:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:40:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:41:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:42:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:43:51.462Z', + value: '0.4666666666666667', + }, + { + time: '2017-08-27T14:44:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T14:45:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:46:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:47:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:48:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:49:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:50:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:51:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:52:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:53:51.462Z', + value: '0.4952333787297264', + }, + { + time: '2017-08-27T14:54:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:55:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T14:56:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:57:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T14:58:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T14:59:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:00:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:01:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:02:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:03:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:04:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T15:05:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:06:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:07:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:08:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:09:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:10:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:11:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:12:51.462Z', + value: '0.4857096599080009', + }, + { + time: '2017-08-27T15:13:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:14:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:15:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:16:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:17:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:18:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:19:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:20:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:21:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:22:51.462Z', + value: '0.49524281183630325', + }, + { + time: '2017-08-27T15:23:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:24:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:25:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:26:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:27:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:28:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:29:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:30:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:31:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:32:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:33:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:34:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:35:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:36:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:37:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:38:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:39:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:40:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:41:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:42:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:43:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:44:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:45:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:46:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:47:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:48:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:49:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T15:50:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:51:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:52:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:53:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:54:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:55:51.462Z', + value: '0.49524752852435283', + }, + { + time: '2017-08-27T15:56:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T15:57:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:58:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T15:59:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:00:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:01:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:02:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:03:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:04:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:05:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:06:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:07:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:08:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:09:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:10:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:11:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:12:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:13:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:14:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:15:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:16:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:17:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:18:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:19:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:20:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:21:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:22:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:23:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:24:51.462Z', + value: '0.4761859410862754', + }, + { + time: '2017-08-27T16:25:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:26:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:27:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:28:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:29:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:30:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:31:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:32:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:33:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:34:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:35:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:36:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:37:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:38:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:39:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:40:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:41:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:42:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:43:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:44:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:45:51.462Z', + value: '0.485718911608682', + }, + { + time: '2017-08-27T16:46:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:47:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:48:51.462Z', + value: '0.4952333787297264', + }, + { + time: '2017-08-27T16:49:51.462Z', + value: '0.4857096599080009', + }, + { + time: '2017-08-27T16:50:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:51:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:52:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:53:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:54:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:55:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T16:56:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:57:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T16:58:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T16:59:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:00:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:01:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:02:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:03:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:04:51.462Z', + value: '0.47619501138106085', + }, + { + time: '2017-08-27T17:05:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:06:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:07:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:08:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:09:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:10:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:11:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:12:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:13:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:14:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:15:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:16:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:17:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:18:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:19:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:20:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:21:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:22:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:23:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:24:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:25:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:26:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:27:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:28:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:29:51.462Z', + value: '0.4761859410862754', + }, + { + time: '2017-08-27T17:30:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:31:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:32:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:33:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:34:51.462Z', + value: '0.4761859410862754', + }, + { + time: '2017-08-27T17:35:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:36:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:37:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:38:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:39:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:40:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:41:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:42:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:43:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:44:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:45:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:46:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:47:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:48:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:49:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:50:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:51:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:52:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:53:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:54:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:55:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T17:56:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:57:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T17:58:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T17:59:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T18:00:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:01:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:02:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:03:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:04:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:05:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:06:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:07:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:08:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:09:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:10:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:11:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:12:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T18:13:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:14:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:15:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:16:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:17:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:18:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:19:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:20:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:21:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:22:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:23:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:24:51.462Z', + value: '0.45714285714285713', + }, + { + time: '2017-08-27T18:25:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:26:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:27:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:28:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:29:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:30:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:31:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:32:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:33:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:34:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:35:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:36:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:37:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T18:38:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:39:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:40:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:41:51.462Z', + value: '0.6190476190476191', + }, + { + time: '2017-08-27T18:42:51.462Z', + value: '0.6952380952380952', + }, + { + time: '2017-08-27T18:43:51.462Z', + value: '0.857142857142857', + }, + { + time: '2017-08-27T18:44:51.462Z', + value: '0.9238095238095239', + }, + { + time: '2017-08-27T18:45:51.462Z', + value: '0.7428571428571429', + }, + { + time: '2017-08-27T18:46:51.462Z', + value: '0.8857142857142857', + }, + { + time: '2017-08-27T18:47:51.462Z', + value: '0.638095238095238', + }, + { + time: '2017-08-27T18:48:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:49:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:50:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T18:51:51.462Z', + value: '0.47619501138106085', + }, + { + time: '2017-08-27T18:52:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:53:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:54:51.462Z', + value: '0.4952380952380952', + }, + { + time: '2017-08-27T18:55:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:56:51.462Z', + value: '0.4857142857142857', + }, + { + time: '2017-08-27T18:57:51.462Z', + value: '0.47619047619047616', + }, + { + time: '2017-08-27T18:58:51.462Z', + value: '0.6857142857142856', + }, + { + time: '2017-08-27T18:59:51.462Z', + value: '0.6952380952380952', + }, + { + time: '2017-08-27T19:00:51.462Z', + value: '0.5238095238095237', + }, + { + time: '2017-08-27T19:01:51.462Z', + value: '0.5904761904761905', + }, + ], + }, + ], + }, + ], + }, ]; export function convertDatesMultipleSeries(multipleSeries) { const convertedMultiple = multipleSeries; multipleSeries.forEach((column, index) => { let convertedResult = []; - convertedResult = column.queries[0].result.map((resultObj) => { + convertedResult = column.queries[0].result.map(resultObj => { const convertedMetrics = {}; convertedMetrics.values = resultObj.values.map(val => ({ - time: new Date(val.time), - value: val.value, + time: new Date(val.time), + value: val.value, })); convertedMetrics.metric = resultObj.metric; return convertedMetrics; diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js index 2d88cee61f1..24388fba219 100644 --- a/spec/javascripts/notes/mock_data.js +++ b/spec/javascripts/notes/mock_data.js @@ -52,6 +52,7 @@ export const noteableDataMock = { updated_at: '2017-08-04T09:53:01.226Z', updated_by_id: 1, web_url: '/gitlab-org/gitlab-ce/issues/26', + noteableType: 'issue', }; export const lastFetchedAt = '1501862675'; diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js index e8fcd4b1a36..581209f215d 100644 --- a/spec/javascripts/pipelines/graph/action_component_spec.js +++ b/spec/javascripts/pipelines/graph/action_component_spec.js @@ -1,25 +1,30 @@ import Vue from 'vue'; import actionComponent from '~/pipelines/components/graph/action_component.vue'; +import eventHub from '~/pipelines/event_hub'; +import mountComponent from '../../helpers/vue_mount_component_helper'; describe('pipeline graph action component', () => { let component; beforeEach((done) => { const ActionComponent = Vue.extend(actionComponent); - component = new ActionComponent({ - propsData: { - tooltipText: 'bar', - link: 'foo', - actionMethod: 'post', - actionIcon: 'cancel', - }, - }).$mount(); + component = mountComponent(ActionComponent, { + tooltipText: 'bar', + link: 'foo', + actionIcon: 'cancel', + }); Vue.nextTick(done); }); - it('should render a link', () => { - expect(component.$el.getAttribute('href')).toEqual('foo'); + afterEach(() => { + component.$destroy(); + }); + + it('should emit an event with the provided link', () => { + eventHub.$on('graphAction', (link) => { + expect(link).toEqual('foo'); + }); }); it('should render the provided title as a bootstrap tooltip', () => { diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js index ce181a1e515..c9677ae209a 100644 --- a/spec/javascripts/pipelines/graph/job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_component_spec.js @@ -13,6 +13,7 @@ describe('pipeline graph job component', () => { icon: 'icon_status_success', text: 'passed', label: 'passed', + tooltip: 'passed', group: 'success', details_path: '/root/ci-mock/builds/4256', has_details: true, @@ -137,6 +138,7 @@ describe('pipeline graph job component', () => { status: { icon: 'icon_status_success', label: 'success', + tooltip: 'success', }, }, }); diff --git a/spec/javascripts/profile/account/components/update_username_spec.js b/spec/javascripts/profile/account/components/update_username_spec.js new file mode 100644 index 00000000000..bac306edf5a --- /dev/null +++ b/spec/javascripts/profile/account/components/update_username_spec.js @@ -0,0 +1,172 @@ +import Vue from 'vue'; +import axios from '~/lib/utils/axios_utils'; +import MockAdapter from 'axios-mock-adapter'; + +import updateUsername from '~/profile/account/components/update_username.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +describe('UpdateUsername component', () => { + const rootUrl = gl.TEST_HOST; + const actionUrl = `${gl.TEST_HOST}/update/username`; + const username = 'hasnoname'; + const newUsername = 'new_username'; + let Component; + let vm; + let axiosMock; + + beforeEach(() => { + axiosMock = new MockAdapter(axios); + Component = Vue.extend(updateUsername); + vm = mountComponent(Component, { + actionUrl, + rootUrl, + initialUsername: username, + }); + }); + + afterEach(() => { + vm.$destroy(); + axiosMock.restore(); + }); + + const findElements = () => { + const modalSelector = `#${vm.$options.modalId}`; + + return { + input: vm.$el.querySelector(`#${vm.$options.inputId}`), + openModalBtn: vm.$el.querySelector(`[data-target="${modalSelector}"]`), + modal: vm.$el.querySelector(modalSelector), + modalBody: vm.$el.querySelector(`${modalSelector} .modal-body`), + modalHeader: vm.$el.querySelector(`${modalSelector} .modal-title`), + confirmModalBtn: vm.$el.querySelector(`${modalSelector} .btn-warning`), + }; + }; + + it('has a disabled button if the username was not changed', done => { + const { input, openModalBtn } = findElements(); + input.dispatchEvent(new Event('input')); + + Vue.nextTick() + .then(() => { + expect(vm.username).toBe(username); + expect(vm.newUsername).toBe(username); + expect(openModalBtn).toBeDisabled(); + }) + .then(done) + .catch(done.fail); + }); + + it('has an enabled button which if the username was changed', done => { + const { input, openModalBtn } = findElements(); + input.value = newUsername; + input.dispatchEvent(new Event('input')); + + Vue.nextTick() + .then(() => { + expect(vm.username).toBe(username); + expect(vm.newUsername).toBe(newUsername); + expect(openModalBtn).not.toBeDisabled(); + }) + .then(done) + .catch(done.fail); + }); + + it('confirmation modal contains proper header and body', done => { + const { modalBody, modalHeader } = findElements(); + + vm.newUsername = newUsername; + + Vue.nextTick() + .then(() => { + expect(modalHeader.textContent).toContain('Change username?'); + expect(modalBody.textContent).toContain( + `You are going to change the username ${username} to ${newUsername}`, + ); + }) + .then(done) + .catch(done.fail); + }); + + it('confirmation modal should escape usernames properly', done => { + const { modalBody } = findElements(); + + vm.username = vm.newUsername = '<i>Italic</i>'; + + Vue.nextTick() + .then(() => { + expect(modalBody.innerHTML).toContain('<i>Italic</i>'); + expect(modalBody.innerHTML).not.toContain(vm.username); + }) + .then(done) + .catch(done.fail); + }); + + it('executes API call on confirmation button click', done => { + const { confirmModalBtn } = findElements(); + + axiosMock.onPut(actionUrl).replyOnce(() => [200, { message: 'Username changed' }]); + spyOn(axios, 'put').and.callThrough(); + + vm.newUsername = newUsername; + + Vue.nextTick() + .then(() => { + confirmModalBtn.click(); + expect(axios.put).toHaveBeenCalledWith(actionUrl, { user: { username: newUsername } }); + }) + .then(done) + .catch(done.fail); + }); + + it('sets the username after a successful update', done => { + const { input, openModalBtn } = findElements(); + + axiosMock.onPut(actionUrl).replyOnce(() => { + expect(input).toBeDisabled(); + expect(openModalBtn).toBeDisabled(); + + return [200, { message: 'Username changed' }]; + }); + + vm.newUsername = newUsername; + + vm + .onConfirm() + .then(() => { + expect(vm.username).toBe(newUsername); + expect(vm.newUsername).toBe(newUsername); + expect(input).not.toBeDisabled(); + expect(input.value).toBe(newUsername); + expect(openModalBtn).toBeDisabled(); + }) + .then(done) + .catch(done.fail); + }); + + it('does not set the username after a erroneous update', done => { + const { input, openModalBtn } = findElements(); + + axiosMock.onPut(actionUrl).replyOnce(() => { + expect(input).toBeDisabled(); + expect(openModalBtn).toBeDisabled(); + + return [400, { message: 'Invalid username' }]; + }); + + const invalidUsername = 'anything.git'; + vm.newUsername = invalidUsername; + + vm + .onConfirm() + .then(() => done.fail('Expected onConfirm to throw!')) + .catch(() => { + expect(vm.username).toBe(username); + expect(vm.newUsername).toBe(invalidUsername); + expect(input).not.toBeDisabled(); + expect(input.value).toBe(invalidUsername); + expect(openModalBtn).not.toBeDisabled(); + }) + .then(done) + .catch(done.fail); + }); +}); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index 40115792652..1a27955983d 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -6,8 +6,21 @@ import SearchAutocomplete from '~/search_autocomplete'; import '~/lib/utils/common_utils'; import * as urlUtils from '~/lib/utils/url_utility'; -(function() { - var assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget; +describe('Search autocomplete dropdown', () => { + var assertLinks, + dashboardIssuesPath, + dashboardMRsPath, + groupIssuesPath, + groupMRsPath, + groupName, + mockDashboardOptions, + mockGroupOptions, + mockProjectOptions, + projectIssuesPath, + projectMRsPath, + projectName, + userId, + widget; var userName = 'root'; widget = null; @@ -66,133 +79,126 @@ import * as urlUtils from '~/lib/utils/url_utility'; // Mock `gl` object in window for dashboard specific page. App code will need it. mockDashboardOptions = function() { window.gl || (window.gl = {}); - return window.gl.dashboardOptions = { + return (window.gl.dashboardOptions = { issuesPath: dashboardIssuesPath, - mrPath: dashboardMRsPath - }; + mrPath: dashboardMRsPath, + }); }; // Mock `gl` object in window for project specific page. App code will need it. mockProjectOptions = function() { window.gl || (window.gl = {}); - return window.gl.projectOptions = { + return (window.gl.projectOptions = { 'gitlab-ce': { issuesPath: projectIssuesPath, mrPath: projectMRsPath, - projectName: projectName - } - }; + projectName: projectName, + }, + }); }; mockGroupOptions = function() { window.gl || (window.gl = {}); - return window.gl.groupOptions = { + return (window.gl.groupOptions = { 'gitlab-org': { issuesPath: groupIssuesPath, mrPath: groupMRsPath, - projectName: groupName - } - }; + projectName: groupName, + }, + }); }; assertLinks = function(list, issuesPath, mrsPath) { - var a1, a2, a3, a4, issuesAssignedToMeLink, issuesIHaveCreatedLink, mrsAssignedToMeLink, mrsIHaveCreatedLink; if (issuesPath) { - issuesAssignedToMeLink = issuesPath + "/?assignee_username=" + userName; - issuesIHaveCreatedLink = issuesPath + "/?author_username=" + userName; - a1 = "a[href='" + issuesAssignedToMeLink + "']"; - a2 = "a[href='" + issuesIHaveCreatedLink + "']"; - expect(list.find(a1).length).toBe(1); - expect(list.find(a1).text()).toBe('Issues assigned to me'); - expect(list.find(a2).length).toBe(1); - expect(list.find(a2).text()).toBe("Issues I've created"); + const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_id=${userId}"]`; + const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_id=${userId}"]`; + expect(list.find(issuesAssignedToMeLink).length).toBe(1); + expect(list.find(issuesAssignedToMeLink).text()).toBe('Issues assigned to me'); + expect(list.find(issuesIHaveCreatedLink).length).toBe(1); + expect(list.find(issuesIHaveCreatedLink).text()).toBe("Issues I've created"); } - mrsAssignedToMeLink = mrsPath + "/?assignee_username=" + userName; - mrsIHaveCreatedLink = mrsPath + "/?author_username=" + userName; - a3 = "a[href='" + mrsAssignedToMeLink + "']"; - a4 = "a[href='" + mrsIHaveCreatedLink + "']"; - expect(list.find(a3).length).toBe(1); - expect(list.find(a3).text()).toBe('Merge requests assigned to me'); - expect(list.find(a4).length).toBe(1); - return expect(list.find(a4).text()).toBe("Merge requests I've created"); + const mrsAssignedToMeLink = `a[href="${mrsPath}/?assignee_id=${userId}"]`; + const mrsIHaveCreatedLink = `a[href="${mrsPath}/?author_id=${userId}"]`; + expect(list.find(mrsAssignedToMeLink).length).toBe(1); + expect(list.find(mrsAssignedToMeLink).text()).toBe('Merge requests assigned to me'); + expect(list.find(mrsIHaveCreatedLink).length).toBe(1); + expect(list.find(mrsIHaveCreatedLink).text()).toBe("Merge requests I've created"); }; - describe('Search autocomplete dropdown', function() { - preloadFixtures('static/search_autocomplete.html.raw'); - beforeEach(function() { - loadFixtures('static/search_autocomplete.html.raw'); + preloadFixtures('static/search_autocomplete.html.raw'); + beforeEach(function() { + loadFixtures('static/search_autocomplete.html.raw'); - // Prevent turbolinks from triggering within gl_dropdown - spyOn(urlUtils, 'visitUrl').and.returnValue(true); + // Prevent turbolinks from triggering within gl_dropdown + spyOn(urlUtils, 'visitUrl').and.returnValue(true); - window.gon = {}; - window.gon.current_user_id = userId; - window.gon.current_username = userName; + window.gon = {}; + window.gon.current_user_id = userId; + window.gon.current_username = userName; - return widget = new SearchAutocomplete(); - }); + return (widget = new SearchAutocomplete()); + }); - afterEach(function() { - // Undo what we did to the shared <body> - removeBodyAttributes(); - window.gon = {}; - }); - it('should show Dashboard specific dropdown menu', function() { - var list; - addBodyAttributes(); - mockDashboardOptions(); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - return assertLinks(list, dashboardIssuesPath, dashboardMRsPath); - }); - it('should show Group specific dropdown menu', function() { - var list; - addBodyAttributes('group'); - mockGroupOptions(); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - return assertLinks(list, groupIssuesPath, groupMRsPath); - }); - it('should show Project specific dropdown menu', function() { - var list; - addBodyAttributes('project'); - mockProjectOptions(); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - return assertLinks(list, projectIssuesPath, projectMRsPath); - }); - it('should show only Project mergeRequest dropdown menu items when project issues are disabled', function() { - addBodyAttributes('project'); - disableProjectIssues(); - mockProjectOptions(); - widget.searchInput.triggerHandler('focus'); - const list = widget.wrap.find('.dropdown-menu').find('ul'); - assertLinks(list, null, projectMRsPath); - }); - it('should not show category related menu if there is text in the input', function() { - var link, list; - addBodyAttributes('project'); - mockProjectOptions(); - widget.searchInput.val('help'); - widget.searchInput.triggerHandler('focus'); - list = widget.wrap.find('.dropdown-menu').find('ul'); - link = "a[href='" + projectIssuesPath + "/?assignee_id=" + userId + "']"; - return expect(list.find(link).length).toBe(0); - }); - return it('should not submit the search form when selecting an autocomplete row with the keyboard', function() { - var ENTER = 13; - var DOWN = 40; - addBodyAttributes(); - mockDashboardOptions(true); - var submitSpy = spyOnEvent('form', 'submit'); - widget.searchInput.triggerHandler('focus'); - widget.wrap.trigger($.Event('keydown', { which: DOWN })); - var enterKeyEvent = $.Event('keydown', { which: ENTER }); - widget.searchInput.trigger(enterKeyEvent); - // This does not currently catch failing behavior. For security reasons, - // browsers will not trigger default behavior (form submit, in this - // example) on JavaScript-created keypresses. - expect(submitSpy).not.toHaveBeenTriggered(); - }); + afterEach(function() { + // Undo what we did to the shared <body> + removeBodyAttributes(); + window.gon = {}; + }); + it('should show Dashboard specific dropdown menu', function() { + var list; + addBodyAttributes(); + mockDashboardOptions(); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, dashboardIssuesPath, dashboardMRsPath); + }); + it('should show Group specific dropdown menu', function() { + var list; + addBodyAttributes('group'); + mockGroupOptions(); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, groupIssuesPath, groupMRsPath); + }); + it('should show Project specific dropdown menu', function() { + var list; + addBodyAttributes('project'); + mockProjectOptions(); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, projectIssuesPath, projectMRsPath); + }); + it('should show only Project mergeRequest dropdown menu items when project issues are disabled', function() { + addBodyAttributes('project'); + disableProjectIssues(); + mockProjectOptions(); + widget.searchInput.triggerHandler('focus'); + const list = widget.wrap.find('.dropdown-menu').find('ul'); + assertLinks(list, null, projectMRsPath); + }); + it('should not show category related menu if there is text in the input', function() { + var link, list; + addBodyAttributes('project'); + mockProjectOptions(); + widget.searchInput.val('help'); + widget.searchInput.triggerHandler('focus'); + list = widget.wrap.find('.dropdown-menu').find('ul'); + link = "a[href='" + projectIssuesPath + '/?assignee_id=' + userId + "']"; + return expect(list.find(link).length).toBe(0); + }); + it('should not submit the search form when selecting an autocomplete row with the keyboard', function() { + var ENTER = 13; + var DOWN = 40; + addBodyAttributes(); + mockDashboardOptions(true); + var submitSpy = spyOnEvent('form', 'submit'); + widget.searchInput.triggerHandler('focus'); + widget.wrap.trigger($.Event('keydown', { which: DOWN })); + var enterKeyEvent = $.Event('keydown', { which: ENTER }); + widget.searchInput.trigger(enterKeyEvent); + // This does not currently catch failing behavior. For security reasons, + // browsers will not trigger default behavior (form submit, in this + // example) on JavaScript-created keypresses. + expect(submitSpy).not.toHaveBeenTriggered(); }); -}).call(window); +}); diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 1bcfdfe72b6..d158786e484 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -7,6 +7,9 @@ import Vue from 'vue'; import VueResource from 'vue-resource'; import { getDefaultAdapter } from '~/lib/utils/axios_utils'; +import { FIXTURES_PATH, TEST_HOST } from './test_constants'; + +import customMatchers from './matchers'; const isHeadlessChrome = /\bHeadlessChrome\//.test(navigator.userAgent); Vue.config.devtools = !isHeadlessChrome; @@ -27,15 +30,17 @@ Vue.config.errorHandler = function (err) { Vue.use(VueResource); // enable test fixtures -jasmine.getFixtures().fixturesPath = '/base/spec/javascripts/fixtures'; -jasmine.getJSONFixtures().fixturesPath = '/base/spec/javascripts/fixtures'; +jasmine.getFixtures().fixturesPath = FIXTURES_PATH; +jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH; + +beforeAll(() => jasmine.addMatchers(customMatchers)); // globalize common libraries window.$ = window.jQuery = $; // stub expected globals window.gl = window.gl || {}; -window.gl.TEST_HOST = 'http://test.host'; +window.gl.TEST_HOST = TEST_HOST; window.gon = window.gon || {}; window.gon.test_env = true; diff --git a/spec/javascripts/test_constants.js b/spec/javascripts/test_constants.js new file mode 100644 index 00000000000..df59195e9f6 --- /dev/null +++ b/spec/javascripts/test_constants.js @@ -0,0 +1,4 @@ +export const FIXTURES_PATH = '/base/spec/javascripts/fixtures'; +export const TEST_HOST = 'http://test.host'; + +export const DUMMY_IMAGE_URL = `${FIXTURES_PATH}/one_white_pixel.png`; diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js index c7c454a0b45..383f0cd29ea 100644 --- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js @@ -38,4 +38,33 @@ describe('ContentViewer', () => { done(); }); }); + + it('renders image preview', done => { + createComponent({ + path: 'test.jpg', + fileSize: 1024, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe('test.jpg'); + + done(); + }); + }); + + it('renders fallback download control', done => { + createComponent({ + path: 'test.abc', + fileSize: 1024, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain( + 'test.abc (1.00 KiB)', + ); + expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toContain('Download'); + + done(); + }); + }); }); diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb index 35f8792ff35..b18af806118 100644 --- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb @@ -207,4 +207,35 @@ describe Banzai::Filter::CommitReferenceFilter do expect(reference_filter(act).to_html).to match(%r{<a.+>#{Regexp.escape(invalidate_reference(reference))}</a>}) end end + + context 'URL reference for a commit patch' do + let(:namespace) { create(:namespace) } + let(:project2) { create(:project, :public, :repository, namespace: namespace) } + let(:commit) { project2.commit } + let(:link) { urls.project_commit_url(project2, commit.id) } + let(:extension) { '.patch' } + let(:reference) { link + extension } + + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") + + expect(doc.css('a').first.attr('href')) + .to eq reference + end + + it 'has valid text' do + doc = reference_filter("See #{reference}") + + expect(doc.text).to eq("See #{commit.reference_link_text(project)} (patch)") + end + + it 'does not link to patch when extension match is after the path' do + invalidate_commit_reference = reference_filter("#{link}/builds.patch") + + doc = reference_filter("See (#{invalidate_commit_reference})") + + expect(doc.css('a').first.attr('href')).to eq "#{link}/builds" + expect(doc.text).to eq("See (#{commit.reference_link_text(project)} (builds).patch)") + end + end end diff --git a/spec/lib/forever_spec.rb b/spec/lib/forever_spec.rb new file mode 100644 index 00000000000..cf40c467c72 --- /dev/null +++ b/spec/lib/forever_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Forever do + describe '.date' do + subject { described_class.date } + + context 'when using PostgreSQL' do + it 'should return Postgresql future date' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + expect(subject).to eq(described_class::POSTGRESQL_DATE) + end + end + + context 'when using MySQL' do + it 'should return MySQL future date' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + expect(subject).to eq(described_class::MYSQL_DATE) + end + end + end +end diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 18cef8ec996..9ccd0b206cc 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::Auth do describe 'constants' do it 'API_SCOPES contains all scopes for API access' do - expect(subject::API_SCOPES).to eq %i[api read_user sudo] + expect(subject::API_SCOPES).to eq %i[api read_user sudo read_repository] end it 'OPENID_SCOPES contains all scopes for OpenID Connect' do @@ -19,7 +19,7 @@ describe Gitlab::Auth do it 'optional_scopes contains all non-default scopes' do stub_container_registry_config(enabled: true) - expect(subject.optional_scopes).to eq %i[read_user sudo read_registry openid] + expect(subject.optional_scopes).to eq %i[read_user sudo read_repository read_registry openid] end context 'registry_scopes' do @@ -231,7 +231,7 @@ describe Gitlab::Auth do .to eq(Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)) end - it 'falls through oauth authentication when the username is oauth2' do + it 'fails through oauth authentication when the username is oauth2' do user = create( :user, username: 'oauth2', @@ -255,6 +255,122 @@ describe Gitlab::Auth do expect { gl_auth.find_for_git_client('foo', 'bar', project: nil, ip: 'ip') }.to raise_error(Gitlab::Auth::MissingPersonalAccessTokenError) end + + context 'while using deploy tokens' do + let(:project) { create(:project) } + let(:auth_failure) { Gitlab::Auth::Result.new(nil, nil) } + + context 'when the deploy token has read_repository as scope' do + let(:deploy_token) { create(:deploy_token, read_registry: false, projects: [project]) } + let(:login) { deploy_token.username } + + it 'succeeds when login and token are valid' do + auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:download_code]) + + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: login) + expect(gl_auth.find_for_git_client(login, deploy_token.token, project: project, ip: 'ip')) + .to eq(auth_success) + end + + it 'fails when login is not valid' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'random_login') + expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails when token is not valid' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails if token is nil' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, nil, project: project, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails if token is not related to project' do + another_deploy_token = create(:deploy_token) + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, another_deploy_token.token, project: project, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails if token has been revoked' do + deploy_token.revoke! + + expect(deploy_token.revoked?).to be_truthy + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deploy-token') + expect(gl_auth.find_for_git_client('deploy-token', deploy_token.token, project: project, ip: 'ip')) + .to eq(auth_failure) + end + end + + context 'when the deploy token has read_registry as a scope' do + let(:deploy_token) { create(:deploy_token, read_repository: false, projects: [project]) } + let(:login) { deploy_token.username } + + context 'when registry enabled' do + before do + stub_container_registry_config(enabled: true) + end + + it 'succeeds when login and token are valid' do + auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:read_container_image]) + + expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: login) + expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip')) + .to eq(auth_success) + end + + it 'fails when login is not valid' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'random_login') + expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails when token is not valid' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, '123123', project: project, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails if token is nil' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, nil, project: nil, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails if token is not related to project' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, 'abcdef', project: nil, ip: 'ip')) + .to eq(auth_failure) + end + + it 'fails if token has been revoked' do + deploy_token.revoke! + + expect(deploy_token.revoked?).to be_truthy + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'deploy-token') + expect(gl_auth.find_for_git_client('deploy-token', deploy_token.token, project: nil, ip: 'ip')) + .to eq(auth_failure) + end + end + + context 'when registry disabled' do + before do + stub_container_registry_config(enabled: false) + end + + it 'fails when login and token are valid' do + expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: login) + expect(gl_auth.find_for_git_client(login, deploy_token.token, project: nil, ip: 'ip')) + .to eq(auth_failure) + end + end + end + end end describe 'find_with_user_password' do diff --git a/spec/lib/gitlab/ci/status/build/action_spec.rb b/spec/lib/gitlab/ci/status/build/action_spec.rb index d612d29e3e0..bdec582b57b 100644 --- a/spec/lib/gitlab/ci/status/build/action_spec.rb +++ b/spec/lib/gitlab/ci/status/build/action_spec.rb @@ -53,4 +53,14 @@ describe Gitlab::Ci::Status::Build::Action do end end end + + describe '#badge_tooltip' do + let(:user) { create(:user) } + let(:build) { create(:ci_build, :non_playable) } + let(:status) { Gitlab::Ci::Status::Core.new(build, user) } + + it 'returns the status' do + expect(subject.badge_tooltip).to eq('created') + end + end end diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb index 9cdebaa5cf2..78d6fa65b5a 100644 --- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb @@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Cancelable do end end + describe '#status_tooltip' do + it 'does not override status status_tooltip' do + expect(status).to receive(:status_tooltip) + + subject.status_tooltip + end + end + + describe '#badge_tooltip' do + let(:user) { create(:user) } + let(:build) { create(:ci_build) } + let(:status) { Gitlab::Ci::Status::Core.new(build, user) } + + it 'returns the status' do + expect(subject.badge_tooltip).to eq('pending') + end + end + describe 'action details' do let(:user) { create(:user) } let(:build) { create(:ci_build) } @@ -72,6 +90,10 @@ describe Gitlab::Ci::Status::Build::Cancelable do describe '#action_title' do it { expect(subject.action_title).to eq 'Cancel' } end + + describe '#action_button_title' do + it { expect(subject.action_button_title).to eq 'Cancel this job' } + end end describe '.matches?' do diff --git a/spec/lib/gitlab/ci/status/build/canceled_spec.rb b/spec/lib/gitlab/ci/status/build/canceled_spec.rb new file mode 100644 index 00000000000..c6b5cc68770 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/canceled_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Canceled do + let(:user) { create(:user) } + + subject do + described_class.new(double('subject')) + end + + describe '#illustration' do + it { expect(subject.illustration).to include(:image, :size, :title) } + end + + describe '.matches?' do + subject {described_class.matches?(build, user) } + + context 'when build is canceled' do + let(:build) { create(:ci_build, :canceled) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when build is not canceled' do + let(:build) { create(:ci_build) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/created_spec.rb b/spec/lib/gitlab/ci/status/build/created_spec.rb new file mode 100644 index 00000000000..8bdfe6ef7a2 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/created_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Created do + let(:user) { create(:user) } + + subject do + described_class.new(double('subject')) + end + + describe '#illustration' do + it { expect(subject.illustration).to include(:image, :size, :title, :content) } + end + + describe '.matches?' do + subject {described_class.matches?(build, user) } + + context 'when build is created' do + let(:build) { create(:ci_build, :created) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when build is not created' do + let(:build) { create(:ci_build) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/erased_spec.rb b/spec/lib/gitlab/ci/status/build/erased_spec.rb new file mode 100644 index 00000000000..0acd271e375 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/erased_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Erased do + let(:user) { create(:user) } + + subject do + described_class.new(double('subject')) + end + + describe '#illustration' do + it { expect(subject.illustration).to include(:image, :size, :title) } + end + + describe '.matches?' do + subject { described_class.matches?(build, user) } + + context 'when build is erased' do + let(:build) { create(:ci_build, :success, :erased) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when build is not erased' do + let(:build) { create(:ci_build, :success, :trace_artifact) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb index d196bc6a4c2..6d5b73bb01b 100644 --- a/spec/lib/gitlab/ci/status/build/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb @@ -13,7 +13,7 @@ describe Gitlab::Ci::Status::Build::Factory do end context 'when build is successful' do - let(:build) { create(:ci_build, :success) } + let(:build) { create(:ci_build, :success, :trace_artifact) } it 'matches correct core status' do expect(factory.core_status).to be_a Gitlab::Ci::Status::Success @@ -38,6 +38,33 @@ describe Gitlab::Ci::Status::Build::Factory do end end + context 'when build is erased' do + let(:build) { create(:ci_build, :success, :erased) } + + it 'matches correct core status' do + expect(factory.core_status).to be_a Gitlab::Ci::Status::Success + end + + it 'matches correct extended statuses' do + expect(factory.extended_statuses) + .to eq [Gitlab::Ci::Status::Build::Erased, + Gitlab::Ci::Status::Build::Retryable] + end + + it 'fabricates a retryable build status' do + expect(status).to be_a Gitlab::Ci::Status::Build::Retryable + end + + it 'fabricates status with correct details' do + expect(status.text).to eq 'passed' + expect(status.icon).to eq 'status_success' + expect(status.favicon).to eq 'favicon_status_success' + expect(status.label).to eq 'passed' + expect(status).to have_details + expect(status).to have_action + end + end + context 'when build is failed' do context 'when build is not allowed to fail' do let(:build) { create(:ci_build, :failed) } @@ -48,11 +75,11 @@ describe Gitlab::Ci::Status::Build::Factory do it 'matches correct extended statuses' do expect(factory.extended_statuses) - .to eq [Gitlab::Ci::Status::Build::Retryable] + .to eq [Gitlab::Ci::Status::Build::Retryable, Gitlab::Ci::Status::Build::Failed] end - it 'fabricates a retryable build status' do - expect(status).to be_a Gitlab::Ci::Status::Build::Retryable + it 'fabricates a failed build status' do + expect(status).to be_a Gitlab::Ci::Status::Build::Failed end it 'fabricates status with correct details' do @@ -60,6 +87,7 @@ describe Gitlab::Ci::Status::Build::Factory do expect(status.icon).to eq 'status_failed' expect(status.favicon).to eq 'favicon_status_failed' expect(status.label).to eq 'failed' + expect(status.status_tooltip).to eq 'failed <br> (unknown failure)' expect(status).to have_details expect(status).to have_action end @@ -75,6 +103,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'matches correct extended statuses' do expect(factory.extended_statuses) .to eq [Gitlab::Ci::Status::Build::Retryable, + Gitlab::Ci::Status::Build::Failed, Gitlab::Ci::Status::Build::FailedAllowed] end @@ -104,7 +133,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'matches correct extended statuses' do expect(factory.extended_statuses) - .to eq [Gitlab::Ci::Status::Build::Retryable] + .to eq [Gitlab::Ci::Status::Build::Canceled, Gitlab::Ci::Status::Build::Retryable] end it 'fabricates a retryable build status' do @@ -115,6 +144,7 @@ describe Gitlab::Ci::Status::Build::Factory do expect(status.text).to eq 'canceled' expect(status.icon).to eq 'status_canceled' expect(status.favicon).to eq 'favicon_status_canceled' + expect(status.illustration).to include(:image, :size, :title) expect(status.label).to eq 'canceled' expect(status).to have_details expect(status).to have_action @@ -156,7 +186,7 @@ describe Gitlab::Ci::Status::Build::Factory do it 'matches correct extended statuses' do expect(factory.extended_statuses) - .to eq [Gitlab::Ci::Status::Build::Cancelable] + .to eq [Gitlab::Ci::Status::Build::Pending, Gitlab::Ci::Status::Build::Cancelable] end it 'fabricates a cancelable build status' do @@ -167,6 +197,7 @@ describe Gitlab::Ci::Status::Build::Factory do expect(status.text).to eq 'pending' expect(status.icon).to eq 'status_pending' expect(status.favicon).to eq 'favicon_status_pending' + expect(status.illustration).to include(:image, :size, :title, :content) expect(status.label).to eq 'pending' expect(status).to have_details expect(status).to have_action @@ -180,18 +211,19 @@ describe Gitlab::Ci::Status::Build::Factory do expect(factory.core_status).to be_a Gitlab::Ci::Status::Skipped end - it 'does not match extended statuses' do - expect(factory.extended_statuses).to be_empty + it 'matches correct extended statuses' do + expect(factory.extended_statuses).to eq [Gitlab::Ci::Status::Build::Skipped] end - it 'fabricates a core skipped status' do - expect(status).to be_a Gitlab::Ci::Status::Skipped + it 'fabricates a skipped build status' do + expect(status).to be_a Gitlab::Ci::Status::Build::Skipped end it 'fabricates status with correct details' do expect(status.text).to eq 'skipped' expect(status.icon).to eq 'status_skipped' expect(status.favicon).to eq 'favicon_status_skipped' + expect(status.illustration).to include(:image, :size, :title) expect(status.label).to eq 'skipped' expect(status).to have_details expect(status).not_to have_action @@ -208,7 +240,8 @@ describe Gitlab::Ci::Status::Build::Factory do it 'matches correct extended statuses' do expect(factory.extended_statuses) - .to eq [Gitlab::Ci::Status::Build::Play, + .to eq [Gitlab::Ci::Status::Build::Manual, + Gitlab::Ci::Status::Build::Play, Gitlab::Ci::Status::Build::Action] end @@ -221,6 +254,7 @@ describe Gitlab::Ci::Status::Build::Factory do expect(status.group).to eq 'manual' expect(status.icon).to eq 'status_manual' expect(status.favicon).to eq 'favicon_status_manual' + expect(status.illustration).to include(:image, :size, :title, :content) expect(status.label).to include 'manual play action' expect(status).to have_details expect(status.action_path).to include 'play' @@ -255,7 +289,8 @@ describe Gitlab::Ci::Status::Build::Factory do it 'matches correct extended statuses' do expect(factory.extended_statuses) - .to eq [Gitlab::Ci::Status::Build::Stop, + .to eq [Gitlab::Ci::Status::Build::Manual, + Gitlab::Ci::Status::Build::Stop, Gitlab::Ci::Status::Build::Action] end diff --git a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb index 99a5a7e4aca..bfaa508785e 100644 --- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb +++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe Gitlab::Ci::Status::Build::FailedAllowed do let(:status) { double('core status') } let(:user) { double('user') } + let(:build) { create(:ci_build, :failed, :allowed_to_fail) } subject do described_class.new(status) @@ -68,6 +69,28 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do end end + describe '#badge_tooltip' do + let(:user) { create(:user) } + let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) } + let(:build_status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) } + let(:status) { described_class.new(build_status) } + + it 'does override badge_tooltip' do + expect(status.badge_tooltip).to eq('failed <br> (unknown failure)') + end + end + + describe '#status_tooltip' do + let(:user) { create(:user) } + let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) } + let(:build_status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) } + let(:status) { described_class.new(build_status) } + + it 'does override status_tooltip' do + expect(status.status_tooltip).to eq 'failed <br> (unknown failure) (allowed to fail)' + end + end + describe '.matches?' do subject { described_class.matches?(build, user) } diff --git a/spec/lib/gitlab/ci/status/build/failed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_spec.rb new file mode 100644 index 00000000000..cadb424ea2c --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/failed_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Failed do + let(:build) { create(:ci_build, :script_failure) } + let(:status) { double('core status') } + let(:user) { double('user') } + + subject { described_class.new(status) } + + describe '#text' do + it 'does not override status text' do + expect(status).to receive(:text) + + subject.text + end + end + + describe '#icon' do + it 'does not override status icon' do + expect(status).to receive(:icon) + + subject.icon + end + end + + describe '#group' do + it 'does not override status group' do + expect(status).to receive(:group) + + subject.group + end + end + + describe '#favicon' do + it 'does not override status label' do + expect(status).to receive(:favicon) + + subject.favicon + end + end + + describe '#label' do + it 'does not override label' do + expect(status).to receive(:label) + + subject.label + end + end + + describe '#badge_tooltip' do + let(:user) { create(:user) } + let(:status) { Gitlab::Ci::Status::Failed.new(build, user) } + + it 'does override badge_tooltip' do + expect(subject.badge_tooltip).to eq 'failed <br> (script failure)' + end + end + + describe '#status_tooltip' do + let(:user) { create(:user) } + let(:status) { Gitlab::Ci::Status::Failed.new(build, user) } + + it 'does override status_tooltip' do + expect(subject.status_tooltip).to eq 'failed <br> (script failure)' + end + end + + describe '.matches?' do + context 'with a failed build' do + it 'returns true' do + expect(described_class.matches?(build, user)).to be_truthy + end + end + + context 'with any other type of build' do + let(:build) { create(:ci_build, :success) } + + it 'returns false' do + expect(described_class.matches?(build, user)).to be_falsy + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/manual_spec.rb b/spec/lib/gitlab/ci/status/build/manual_spec.rb new file mode 100644 index 00000000000..6386296f992 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/manual_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Manual do + let(:user) { create(:user) } + + subject do + build = create(:ci_build, :manual) + described_class.new(Gitlab::Ci::Status::Core.new(build, user)) + end + + describe '#illustration' do + it { expect(subject.illustration).to include(:image, :size, :title, :content) } + end + + describe '.matches?' do + subject {described_class.matches?(build, user) } + + context 'when build is manual' do + let(:build) { create(:ci_build, :manual) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when build is not manual' do + let(:build) { create(:ci_build) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/pending_spec.rb b/spec/lib/gitlab/ci/status/build/pending_spec.rb new file mode 100644 index 00000000000..4cf70828e53 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/pending_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Pending do + let(:user) { create(:user) } + + subject do + described_class.new(double('subject')) + end + + describe '#illustration' do + it { expect(subject.illustration).to include(:image, :size, :title, :content) } + end + + describe '.matches?' do + subject {described_class.matches?(build, user) } + + context 'when build is pending' do + let(:build) { create(:ci_build, :pending) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when build is not pending' do + let(:build) { create(:ci_build, :success) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb index 81d5f553fd1..f128c1d4ca4 100644 --- a/spec/lib/gitlab/ci/status/build/play_spec.rb +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -14,6 +14,22 @@ describe Gitlab::Ci::Status::Build::Play do end end + describe '#status_tooltip' do + it 'does not override status status_tooltip' do + expect(status).to receive(:status_tooltip) + + subject.status_tooltip + end + end + + describe '#badge_tooltip' do + it 'does not override status badge_tooltip' do + expect(status).to receive(:badge_tooltip) + + subject.badge_tooltip + end + end + describe '#has_action?' do context 'when user is allowed to update build' do context 'when user is allowed to trigger protected action' do @@ -53,6 +69,10 @@ describe Gitlab::Ci::Status::Build::Play do it { expect(subject.action_title).to eq 'Play' } end + describe '#action_button_title' do + it { expect(subject.action_button_title).to eq 'Trigger this manual action' } + end + describe '.matches?' do subject { described_class.matches?(build, user) } diff --git a/spec/lib/gitlab/ci/status/build/retried_spec.rb b/spec/lib/gitlab/ci/status/build/retried_spec.rb new file mode 100644 index 00000000000..ee9acaf1c21 --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/retried_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Retried do + let(:build) { create(:ci_build, :retried) } + let(:status) { double('core status') } + let(:user) { double('user') } + + subject { described_class.new(status) } + + describe '#text' do + it 'does not override status text' do + expect(status).to receive(:text) + + subject.text + end + end + + describe '#icon' do + it 'does not override status icon' do + expect(status).to receive(:icon) + + subject.icon + end + end + + describe '#group' do + it 'does not override status group' do + expect(status).to receive(:group) + + subject.group + end + end + + describe '#favicon' do + it 'does not override status label' do + expect(status).to receive(:favicon) + + subject.favicon + end + end + + describe '#label' do + it 'does not override status label' do + expect(status).to receive(:label) + + subject.label + end + end + + describe '#badge_tooltip' do + let(:user) { create(:user) } + let(:build) { create(:ci_build, :retried) } + let(:status) { Gitlab::Ci::Status::Success.new(build, user) } + + it 'returns status' do + expect(status.badge_tooltip).to eq('pending') + end + end + + describe '#status_tooltip' do + let(:user) { create(:user) } + + context 'with a failed build' do + let(:build) { create(:ci_build, :failed, :retried) } + let(:failed_status) { Gitlab::Ci::Status::Failed.new(build, user) } + let(:status) { Gitlab::Ci::Status::Build::Failed.new(failed_status) } + + it 'does override status_tooltip' do + expect(subject.status_tooltip).to eq 'failed <br> (unknown failure) (retried)' + end + end + + context 'with another build' do + let(:build) { create(:ci_build, :retried) } + let(:status) { Gitlab::Ci::Status::Success.new(build, user) } + + it 'does override status_tooltip' do + expect(subject.status_tooltip).to eq 'passed (retried)' + end + end + end + + describe '.matches?' do + subject { described_class.matches?(build, user) } + + context 'with a retried build' do + it { is_expected.to be_truthy } + end + + context 'with a build that has not been retried' do + let(:build) { create(:ci_build, :success) } + + it { is_expected.to be_falsy } + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb index 14d42e0d70f..84d98588f2d 100644 --- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb +++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb @@ -40,6 +40,24 @@ describe Gitlab::Ci::Status::Build::Retryable do end end + describe '#status_tooltip' do + it 'does not override status status_tooltip' do + expect(status).to receive(:status_tooltip) + + subject.status_tooltip + end + end + + describe '#badge_tooltip' do + let(:user) { create(:user) } + let(:build) { create(:ci_build) } + let(:status) { Gitlab::Ci::Status::Core.new(build, user) } + + it 'does return status' do + expect(status.badge_tooltip).to eq('pending') + end + end + describe 'action details' do let(:user) { create(:user) } let(:build) { create(:ci_build) } @@ -72,6 +90,10 @@ describe Gitlab::Ci::Status::Build::Retryable do describe '#action_title' do it { expect(subject.action_title).to eq 'Retry' } end + + describe '#action_button_title' do + it { expect(subject.action_button_title).to eq 'Retry this job' } + end end describe '.matches?' do diff --git a/spec/lib/gitlab/ci/status/build/skipped_spec.rb b/spec/lib/gitlab/ci/status/build/skipped_spec.rb new file mode 100644 index 00000000000..46f6933025a --- /dev/null +++ b/spec/lib/gitlab/ci/status/build/skipped_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Build::Skipped do + let(:user) { create(:user) } + + subject do + described_class.new(double('subject')) + end + + describe '#illustration' do + it { expect(subject.illustration).to include(:image, :size, :title) } + end + + describe '.matches?' do + subject {described_class.matches?(build, user) } + + context 'when build is skipped' do + let(:build) { create(:ci_build, :skipped) } + + it 'is a correct match' do + expect(subject).to be true + end + end + + context 'when build is not skipped' do + let(:build) { create(:ci_build) } + + it 'does not match' do + expect(subject).to be false + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb index 18e250772f0..5b7534c96c1 100644 --- a/spec/lib/gitlab/ci/status/build/stop_spec.rb +++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb @@ -44,6 +44,10 @@ describe Gitlab::Ci::Status::Build::Stop do describe '#action_title' do it { expect(subject.action_title).to eq 'Stop' } end + + describe '#action_button_title' do + it { expect(subject.action_button_title).to eq 'Stop this environment' } + end end describe '.matches?' do @@ -77,4 +81,24 @@ describe Gitlab::Ci::Status::Build::Stop do end end end + + describe '#status_tooltip' do + it 'does not override status status_tooltip' do + expect(status).to receive(:status_tooltip) + + subject.status_tooltip + end + end + + describe '#badge_tooltip' do + let(:user) { create(:user) } + let(:build) { create(:ci_build, :playable) } + let(:status) { Gitlab::Ci::Status::Core.new(build, user) } + + it 'does not override status badge_tooltip' do + expect(status).to receive(:badge_tooltip) + + subject.badge_tooltip + end + end end diff --git a/spec/lib/gitlab/ci/status/success_warning_spec.rb b/spec/lib/gitlab/ci/status/success_warning_spec.rb index 4582354e739..6d05545d1d8 100644 --- a/spec/lib/gitlab/ci/status/success_warning_spec.rb +++ b/spec/lib/gitlab/ci/status/success_warning_spec.rb @@ -1,8 +1,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::SuccessWarning do + let(:status) { double('status') } + subject do - described_class.new(double('status')) + described_class.new(status) end describe '#test' do diff --git a/spec/lib/gitlab/database/sha_attribute_spec.rb b/spec/lib/gitlab/database/sha_attribute_spec.rb index 62c1d37ea1c..778bfa2cc47 100644 --- a/spec/lib/gitlab/database/sha_attribute_spec.rb +++ b/spec/lib/gitlab/database/sha_attribute_spec.rb @@ -19,15 +19,15 @@ describe Gitlab::Database::ShaAttribute do let(:attribute) { described_class.new } - describe '#type_cast_from_database' do + describe '#deserialize' do it 'converts the binary SHA to a String' do - expect(attribute.type_cast_from_database(binary_from_db)).to eq(sha) + expect(attribute.deserialize(binary_from_db)).to eq(sha) end end - describe '#type_cast_for_database' do + describe '#serialize' do it 'converts a SHA String to binary data' do - expect(attribute.type_cast_for_database(sha).to_s).to eq(binary_sha) + expect(attribute.serialize(sha).to_s).to eq(binary_sha) end end end diff --git a/spec/lib/gitlab/email/handler_spec.rb b/spec/lib/gitlab/email/handler_spec.rb index 650b01c4df4..386d73e6115 100644 --- a/spec/lib/gitlab/email/handler_spec.rb +++ b/spec/lib/gitlab/email/handler_spec.rb @@ -14,4 +14,28 @@ describe Gitlab::Email::Handler do expect(described_class.for('email', '')).to be_nil end end + + describe 'regexps are set properly' do + let(:addresses) do + %W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX} sent_notification_key path/to/project+merge-request+user_email_token path/to/project+user_email_token) + end + + it 'picks each handler at least once' do + matched_handlers = addresses.map do |address| + described_class.for('email', address).class + end + + expect(matched_handlers.uniq).to match_array(Gitlab::Email::Handler::HANDLERS) + end + + it 'can pick exactly one handler for each address' do + addresses.each do |address| + matched_handlers = Gitlab::Email::Handler::HANDLERS.select do |handler| + handler.new('email', address).can_handle? + end + + expect(matched_handlers.count).to eq(1), "#{address} matches #{matched_handlers.count} handlers: #{matched_handlers}" + end + end + end end diff --git a/spec/lib/gitlab/git/checksum_spec.rb b/spec/lib/gitlab/git/checksum_spec.rb deleted file mode 100644 index 8ff310905bf..00000000000 --- a/spec/lib/gitlab/git/checksum_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Git::Checksum, seed_helper: true do - let(:storage) { 'default' } - - it 'raises Gitlab::Git::Repository::NoRepository when there is no repo' do - checksum = described_class.new(storage, 'nonexistent-repo') - - expect { checksum.calculate }.to raise_error Gitlab::Git::Repository::NoRepository - end - - it 'pretends that checksum is 000000... when the repo is empty' do - FileUtils.rm_rf(File.join(SEED_STORAGE_PATH, 'empty-repo.git')) - - system(git_env, *%W(#{Gitlab.config.git.bin_path} init --bare empty-repo.git), - chdir: SEED_STORAGE_PATH, - out: '/dev/null', - err: '/dev/null') - - checksum = described_class.new(storage, 'empty-repo') - - expect(checksum.calculate).to eq '0000000000000000000000000000000000000000' - end - - it 'raises Gitlab::Git::Repository::Failure when shelling out to git return non-zero status' do - checksum = described_class.new(storage, 'gitlab-git-test') - - allow(checksum).to receive(:popen).and_return(['output', nil]) - - expect { checksum.calculate }.to raise_error Gitlab::Git::Checksum::Failure - end - - it 'calculates the checksum when there is a repo' do - checksum = described_class.new(storage, 'gitlab-git-test') - - expect(checksum.calculate).to eq '54f21be4c32c02f6788d72207fa03ad3bce725e4' - end -end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 5cbe2808d0b..d3ab61746f4 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -247,38 +247,44 @@ describe Gitlab::Git::Repository, seed_helper: true do end it 'returns parameterised string for a ref containing slashes' do - prefix = repository.archive_prefix('test/branch', 'SHA') + prefix = repository.archive_prefix('test/branch', 'SHA', append_sha: nil) expect(prefix).to eq("#{project_name}-test-branch-SHA") end it 'returns correct string for a ref containing dots' do - prefix = repository.archive_prefix('test.branch', 'SHA') + prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: nil) expect(prefix).to eq("#{project_name}-test.branch-SHA") end + + it 'returns string with sha when append_sha is false' do + prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: false) + + expect(prefix).to eq("#{project_name}-test.branch") + end end describe '#archive' do - let(:metadata) { repository.archive_metadata('master', '/tmp') } + let(:metadata) { repository.archive_metadata('master', '/tmp', append_sha: true) } it_should_behave_like 'archive check', '.tar.gz' end describe '#archive_zip' do - let(:metadata) { repository.archive_metadata('master', '/tmp', 'zip') } + let(:metadata) { repository.archive_metadata('master', '/tmp', 'zip', append_sha: true) } it_should_behave_like 'archive check', '.zip' end describe '#archive_bz2' do - let(:metadata) { repository.archive_metadata('master', '/tmp', 'tbz2') } + let(:metadata) { repository.archive_metadata('master', '/tmp', 'tbz2', append_sha: true) } it_should_behave_like 'archive check', '.tar.bz2' end describe '#archive_fallback' do - let(:metadata) { repository.archive_metadata('master', '/tmp', 'madeup') } + let(:metadata) { repository.archive_metadata('master', '/tmp', 'madeup', append_sha: true) } it_should_behave_like 'archive check', '.tar.gz' end @@ -2178,6 +2184,55 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#checksum' do + shared_examples 'calculating checksum' do + it 'calculates the checksum for non-empty repo' do + expect(repository.checksum).to eq '54f21be4c32c02f6788d72207fa03ad3bce725e4' + end + + it 'returns 0000000000000000000000000000000000000000 for an empty repo' do + FileUtils.rm_rf(File.join(storage_path, 'empty-repo.git')) + + system(git_env, *%W(#{Gitlab.config.git.bin_path} init --bare empty-repo.git), + chdir: storage_path, + out: '/dev/null', + err: '/dev/null') + + empty_repo = described_class.new('default', 'empty-repo.git', '') + + expect(empty_repo.checksum).to eq '0000000000000000000000000000000000000000' + end + + it 'raises a no repository exception when there is no repo' do + broken_repo = described_class.new('default', 'a/path.git', '') + + expect { broken_repo.checksum }.to raise_error(Gitlab::Git::Repository::NoRepository) + end + end + + context 'when calculate_checksum Gitaly feature is enabled' do + it_behaves_like 'calculating checksum' + end + + context 'when calculate_checksum Gitaly feature is disabled', :disable_gitaly do + it_behaves_like 'calculating checksum' + + describe 'when storage is broken', :broken_storage do + it 'raises a storage exception when storage is not available' do + broken_repo = described_class.new('broken', 'a/path.git', '') + + expect { broken_repo.rugged }.to raise_error(Gitlab::Git::Storage::Inaccessible) + end + end + + it "raises a Gitlab::Git::Repository::Failure error if the `popen` call to git returns a non-zero exit code" do + allow(repository).to receive(:popen).and_return(['output', nil]) + + expect { repository.checksum }.to raise_error Gitlab::Git::Repository::ChecksumError + end + end + end + context 'gitlab_projects commands' do let(:gitlab_projects) { repository.gitlab_projects } let(:timeout) { Gitlab.config.gitlab_shell.git_timeout } @@ -2251,6 +2306,39 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + describe '#clean_stale_repository_files' do + let(:worktree_path) { File.join(repository.path, 'worktrees', 'delete-me') } + + it 'cleans up the files' do + repository.with_worktree(worktree_path, 'master', env: ENV) do + FileUtils.touch(worktree_path, mtime: Time.now - 8.hours) + # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object, + # but the HEAD must be 40 characters long or git will ignore it. + File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA) + + # git 2.16 fails with "fatal: bad object HEAD" + expect { repository.rev_list(including: :all) }.to raise_error(Gitlab::Git::Repository::GitError) + + repository.clean_stale_repository_files + + expect { repository.rev_list(including: :all) }.not_to raise_error + expect(File.exist?(worktree_path)).to be_falsey + end + end + + it 'increments a counter upon an error' do + expect(repository.gitaly_repository_client).to receive(:cleanup).and_raise(Gitlab::Git::CommandError) + + counter = double(:counter) + + expect(counter).to receive(:increment) + expect(Gitlab::Metrics).to receive(:counter).with(:failed_repository_cleanup_total, + 'Number of failed repository cleanup events').and_return(counter) + + repository.clean_stale_repository_files + end + end + describe '#delete_remote_branches' do subject do repository.delete_remote_branches('downstream-remote', ['master']) diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index f8f09d29c73..6c625596605 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -10,12 +10,13 @@ describe Gitlab::GitAccess do let(:protocol) { 'ssh' } let(:authentication_abilities) { %i[read_project download_code push_code] } let(:redirected_path) { nil } + let(:auth_result_type) { nil } let(:access) do described_class.new(actor, project, protocol, authentication_abilities: authentication_abilities, namespace_path: namespace_path, project_path: project_path, - redirected_path: redirected_path) + redirected_path: redirected_path, auth_result_type: auth_result_type) end let(:changes) { '_any' } @@ -45,6 +46,7 @@ describe Gitlab::GitAccess do before do disable_protocol('http') + project.add_master(user) end it 'blocks http push and pull' do @@ -53,6 +55,26 @@ describe Gitlab::GitAccess do expect { pull_access_check }.to raise_unauthorized('Git access over HTTP is not allowed') end end + + context 'when request is made from CI' do + let(:auth_result_type) { :build } + + it "doesn't block http pull" do + aggregate_failures do + expect { pull_access_check }.not_to raise_unauthorized('Git access over HTTP is not allowed') + end + end + + context 'when legacy CI credentials are used' do + let(:auth_result_type) { :ci } + + it "doesn't block http pull" do + aggregate_failures do + expect { pull_access_check }.not_to raise_unauthorized('Git access over HTTP is not allowed') + end + end + end + end end end @@ -123,6 +145,33 @@ describe Gitlab::GitAccess do expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:auth_upload]) end end + + context 'when actor is DeployToken' do + let(:actor) { create(:deploy_token, projects: [project]) } + + context 'when DeployToken is active and belongs to project' do + it 'allows pull access' do + expect { pull_access_check }.not_to raise_error + end + + it 'blocks the push' do + expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) + end + end + + context 'when DeployToken does not belong to project' do + let(:another_project) { create(:project) } + let(:actor) { create(:deploy_token, projects: [another_project]) } + + it 'blocks pull access' do + expect { pull_access_check }.to raise_not_found + end + + it 'blocks the push' do + expect { push_access_check }.to raise_not_found + end + end + end end context 'when actor is nil' do @@ -572,6 +621,41 @@ describe Gitlab::GitAccess do end end + describe 'deploy token permissions' do + let(:deploy_token) { create(:deploy_token) } + let(:actor) { deploy_token } + + context 'pull code' do + context 'when project is authorized' do + before do + deploy_token.projects << project + end + + it { expect { pull_access_check }.not_to raise_error } + end + + context 'when unauthorized' do + context 'from public project' do + let(:project) { create(:project, :public, :repository) } + + it { expect { pull_access_check }.not_to raise_error } + end + + context 'from internal project' do + let(:project) { create(:project, :internal, :repository) } + + it { expect { pull_access_check }.to raise_not_found } + end + + context 'from private project' do + let(:project) { create(:project, :private, :repository) } + + it { expect { pull_access_check }.to raise_not_found } + end + end + end + end + describe 'build authentication_abilities permissions' do let(:authentication_abilities) { build_authentication_abilities } @@ -833,6 +917,20 @@ describe Gitlab::GitAccess do admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false })) end end + + context 'when pushing to a project' do + let(:project) { create(:project, :public, :repository) } + let(:changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow" } + + before do + project.add_developer(user) + end + + it 'cleans up the files' do + expect(project.repository).to receive(:clean_stale_repository_files).and_call_original + expect { push_access_check }.not_to raise_error + end + end end describe 'build authentication abilities' do diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb index 1c41dbcb9ef..21592688bf0 100644 --- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -17,6 +17,16 @@ describe Gitlab::GitalyClient::RepositoryService do end end + describe '#cleanup' do + it 'sends a cleanup message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:cleanup) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + + client.cleanup + end + end + describe '#garbage_collect' do it 'sends a garbage_collect message' do expect_any_instance_of(Gitaly::RepositoryService::Stub) @@ -124,4 +134,15 @@ describe Gitlab::GitalyClient::RepositoryService do client.squash_in_progress?(squash_id) end end + + describe '#calculate_checksum' do + it 'sends a calculate_checksum message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:calculate_checksum) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(checksum: 0)) + + client.calculate_checksum + end + end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index b675d5dc031..897a5984782 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -145,6 +145,9 @@ pipeline_schedule: - pipelines pipeline_schedule_variables: - pipeline_schedule +deploy_tokens: +- project_deploy_tokens +- projects deploy_keys: - user - deploy_keys_projects @@ -281,6 +284,8 @@ project: - project_badges - source_of_merge_requests - internal_ids +- project_deploy_tokens +- deploy_tokens award_emoji: - awardable - user diff --git a/spec/lib/gitlab/import_export/importer_spec.rb b/spec/lib/gitlab/import_export/importer_spec.rb index d75416f2a62..991e354f499 100644 --- a/spec/lib/gitlab/import_export/importer_spec.rb +++ b/spec/lib/gitlab/import_export/importer_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' describe Gitlab::ImportExport::Importer do + let(:user) { create(:user) } let(:test_path) { "#{Dir.tmpdir}/importer_spec" } let(:shared) { project.import_export_shared } let(:project) { create(:project, import_source: File.join(test_path, 'exported-project.gz')) } @@ -11,6 +12,7 @@ describe Gitlab::ImportExport::Importer do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(test_path) FileUtils.mkdir_p(shared.export_path) FileUtils.cp(Rails.root.join('spec', 'fixtures', 'exported-project.gz'), test_path) + allow(subject).to receive(:remove_import_file) end after do @@ -42,7 +44,8 @@ describe Gitlab::ImportExport::Importer do Gitlab::ImportExport::RepoRestorer, Gitlab::ImportExport::WikiRestorer, Gitlab::ImportExport::UploadsRestorer, - Gitlab::ImportExport::LfsRestorer + Gitlab::ImportExport::LfsRestorer, + Gitlab::ImportExport::StatisticsRestorer ].each do |restorer| it "calls the #{restorer}" do fake_restorer = double(restorer.to_s) @@ -60,5 +63,42 @@ describe Gitlab::ImportExport::Importer do importer.execute end end + + context 'when project successfully restored' do + let!(:existing_project) { create(:project, namespace: user.namespace) } + let(:project) { create(:project, namespace: user.namespace, name: 'whatever', path: 'whatever') } + + before do + restorers = double + + allow(subject).to receive(:import_file).and_return(true) + allow(subject).to receive(:check_version!).and_return(true) + allow(subject).to receive(:restorers).and_return(restorers) + allow(restorers).to receive(:all?).and_return(true) + allow(project).to receive(:import_data).and_return(double(data: { 'original_path' => existing_project.path })) + end + + context 'when import_data' do + context 'has original_path' do + it 'overwrites existing project' do + expect_any_instance_of(::Projects::OverwriteProjectService).to receive(:execute).with(existing_project) + + subject.execute + end + end + + context 'has not original_path' do + before do + allow(project).to receive(:import_data).and_return(double(data: {})) + end + + it 'does not call the overwrite service' do + expect_any_instance_of(::Projects::OverwriteProjectService).not_to receive(:execute).with(existing_project) + + subject.execute + end + end + end + end end end diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 8c1bf6f7be8..6d63749296e 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -6180,12 +6180,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": null - }, - "artifacts_metadata": { - "url": null - }, "erased_by_id": null, "erased_at": null, "type": "Ci::Build", @@ -6218,12 +6212,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts_metadata.gz" - }, "erased_by_id": null, "erased_at": null } @@ -6292,12 +6280,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts_metadata.gz" - }, "erased_by_id": null, "erased_at": null }, @@ -6327,12 +6309,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": null - }, - "artifacts_metadata": { - "url": null - }, "erased_by_id": null, "erased_at": null } @@ -6392,12 +6368,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts_metadata.gz" - }, "erased_by_id": null, "erased_at": null }, @@ -6427,12 +6397,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts_metadata.gz" - }, "erased_by_id": null, "erased_at": null } @@ -6492,12 +6456,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts_metadata.gz" - }, "erased_by_id": null, "erased_at": null }, @@ -6527,12 +6485,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts_metadata.gz" - }, "erased_by_id": null, "erased_at": null } @@ -6592,12 +6544,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": null - }, - "artifacts_metadata": { - "url": null - }, "erased_by_id": null, "erased_at": null }, @@ -6627,12 +6573,6 @@ "user_id": null, "target_url": null, "description": null, - "artifacts_file": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts.zip" - }, - "artifacts_metadata": { - "url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts_metadata.gz" - }, "erased_by_id": null, "erased_at": null } diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 2b3ffb2d7c0..d64ea72e346 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::Workhorse do let(:ref) { 'master' } let(:format) { 'zip' } let(:storage_path) { Gitlab.config.gitlab.repository_downloads_path } - let(:base_params) { repository.archive_metadata(ref, storage_path, format) } + let(:base_params) { repository.archive_metadata(ref, storage_path, format, append_sha: nil) } let(:gitaly_params) do base_params.merge( 'GitalyServer' => { @@ -29,7 +29,7 @@ describe Gitlab::Workhorse do let(:cache_disabled) { false } subject do - described_class.send_git_archive(repository, ref: ref, format: format) + described_class.send_git_archive(repository, ref: ref, format: format, append_sha: nil) end before do diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb new file mode 100644 index 00000000000..cc99e7e8911 --- /dev/null +++ b/spec/lib/uploaded_file_spec.rb @@ -0,0 +1,116 @@ +require 'spec_helper' + +describe UploadedFile do + describe ".from_params" do + let(:temp_dir) { Dir.tmpdir } + let(:temp_file) { Tempfile.new("test", temp_dir) } + let(:upload_path) { nil } + + subject do + described_class.from_params(params, :file, upload_path) + end + + before do + FileUtils.touch(temp_file) + end + + after do + FileUtils.rm_f(temp_file) + FileUtils.rm_r(upload_path) if upload_path + end + + context 'when valid file is specified' do + context 'only local path is specified' do + let(:params) do + { 'file.path' => temp_file.path } + end + + it "succeeds" do + is_expected.not_to be_nil + end + + it "generates filename from path" do + expect(subject.original_filename).to eq(::File.basename(temp_file.path)) + end + end + + context 'all parameters are specified' do + let(:params) do + { 'file.path' => temp_file.path, + 'file.name' => 'my_file.txt', + 'file.type' => 'my/type', + 'file.sha256' => 'sha256', + 'file.remote_id' => 'remote_id' } + end + + it "succeeds" do + is_expected.not_to be_nil + end + + it "generates filename from path" do + expect(subject.original_filename).to eq('my_file.txt') + expect(subject.content_type).to eq('my/type') + expect(subject.sha256).to eq('sha256') + expect(subject.remote_id).to eq('remote_id') + end + end + end + + context 'when no params are specified' do + let(:params) do + {} + end + + it "does not return an object" do + is_expected.to be_nil + end + end + + context 'when only remote id is specified' do + let(:params) do + { 'file.remote_id' => 'remote_id' } + end + + it "raises an error" do + expect { subject }.to raise_error(UploadedFile::InvalidPathError, /file is invalid/) + end + end + + context 'when verifying allowed paths' do + let(:params) do + { 'file.path' => temp_file.path } + end + + context 'when file is stored in system temporary folder' do + let(:temp_dir) { Dir.tmpdir } + + it "succeeds" do + is_expected.not_to be_nil + end + end + + context 'when file is stored in user provided upload path' do + let(:upload_path) { Dir.mktmpdir } + let(:temp_dir) { upload_path } + + it "succeeds" do + is_expected.not_to be_nil + end + end + + context 'when file is stored outside of user provided upload path' do + let!(:generated_dir) { Dir.mktmpdir } + let!(:temp_dir) { Dir.mktmpdir } + + before do + # We overwrite default temporary path + allow(Dir).to receive(:tmpdir).and_return(generated_dir) + end + + it "raises an error" do + expect { subject }.to raise_error(UploadedFile::InvalidPathError, /insecure path used/) + end + end + end + end +end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 971a88e9ee9..43e419cd7de 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -390,11 +390,11 @@ describe Notify do end end - describe 'that have new commits' do + shared_examples 'a push to an existing merge request' do let(:push_user) { create(:user) } subject do - described_class.push_to_merge_request_email(recipient.id, merge_request.id, push_user.id, new_commits: merge_request.commits) + described_class.push_to_merge_request_email(recipient.id, merge_request.id, push_user.id, new_commits: merge_request.commits, existing_commits: existing_commits) end it_behaves_like 'a multiple recipients email' @@ -419,6 +419,18 @@ describe Notify do end end end + + describe 'that have new commits' do + let(:existing_commits) { [] } + + it_behaves_like 'a push to an existing merge request' + end + + describe 'that have new commits on top of an existing one' do + let(:existing_commits) { [merge_request.commits.first] } + + it_behaves_like 'a push to an existing merge request' + end end context 'for issue notes' do diff --git a/spec/migrations/add_foreign_keys_to_todos_spec.rb b/spec/migrations/add_foreign_keys_to_todos_spec.rb index 4a22bd6f342..bf2fa5c0f56 100644 --- a/spec/migrations/add_foreign_keys_to_todos_spec.rb +++ b/spec/migrations/add_foreign_keys_to_todos_spec.rb @@ -4,8 +4,8 @@ require Rails.root.join('db', 'migrate', '20180201110056_add_foreign_keys_to_tod describe AddForeignKeysToTodos, :migration do let(:todos) { table(:todos) } - let(:project) { create(:project) } - let(:user) { create(:user) } + let(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs context 'add foreign key on user_id' do let!(:todo_with_user) { create_todo(user_id: user.id) } @@ -34,7 +34,7 @@ describe AddForeignKeysToTodos, :migration do end context 'add foreign key on note_id' do - let(:note) { create(:note) } + let(:note) { create(:note) } # rubocop:disable RSpec/FactoriesInMigrationSpecs let!(:todo_with_note) { create_todo(note_id: note.id) } let!(:todo_with_invalid_note) { create_todo(note_id: 4711) } let!(:todo_without_note) { create_todo(note_id: nil) } diff --git a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb index 63defcb39bf..d8dd7a2fb83 100644 --- a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb +++ b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb @@ -6,18 +6,18 @@ describe AddHeadPipelineForEachMergeRequest, :delete do let(:migration) { described_class.new } - let!(:project) { create(:project) } + let!(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs let!(:other_project) { fork_project(project) } - let!(:pipeline_1) { create(:ci_pipeline, project: project, ref: "branch_1") } - let!(:pipeline_2) { create(:ci_pipeline, project: other_project, ref: "branch_1") } - let!(:pipeline_3) { create(:ci_pipeline, project: other_project, ref: "branch_1") } - let!(:pipeline_4) { create(:ci_pipeline, project: project, ref: "branch_2") } + let!(:pipeline_1) { create(:ci_pipeline, project: project, ref: "branch_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:pipeline_2) { create(:ci_pipeline, project: other_project, ref: "branch_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:pipeline_3) { create(:ci_pipeline, project: other_project, ref: "branch_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:pipeline_4) { create(:ci_pipeline, project: project, ref: "branch_2") } # rubocop:disable RSpec/FactoriesInMigrationSpecs - let!(:mr_1) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_1", target_branch: "target_1") } - let!(:mr_2) { create(:merge_request, source_project: other_project, target_project: project, source_branch: "branch_1", target_branch: "target_2") } - let!(:mr_3) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_2", target_branch: "master") } - let!(:mr_4) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_3", target_branch: "master") } + let!(:mr_1) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_1", target_branch: "target_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:mr_2) { create(:merge_request, source_project: other_project, target_project: project, source_branch: "branch_1", target_branch: "target_2") } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:mr_3) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_2", target_branch: "master") } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:mr_4) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_3", target_branch: "master") } # rubocop:disable RSpec/FactoriesInMigrationSpecs context "#up" do context "when source_project and source_branch of pipeline are the same of merge request" do diff --git a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb index f3a46025376..19f06810e54 100644 --- a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb +++ b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb @@ -6,7 +6,7 @@ require Rails.root.join('db', 'post_migrate', '20170803090603_calculate_conv_dev describe CalculateConvDevIndexPercentages, :delete do let(:migration) { described_class.new } let!(:conv_dev_index) do - create(:conversational_development_index_metric, + create(:conversational_development_index_metric, # rubocop:disable RSpec/FactoriesInMigrationSpecs leader_notes: 0, instance_milestones: 0, percentage_issues: 0, diff --git a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb index 033d0e7584d..b5980cb9ddb 100644 --- a/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb +++ b/spec/migrations/cleanup_namespaceless_pending_delete_projects_spec.rb @@ -10,9 +10,9 @@ describe CleanupNamespacelessPendingDeleteProjects, :migration, schema: 20180222 describe '#up' do it 'only cleans up pending delete projects' do - create(:project) - create(:project, pending_delete: true) - project = build(:project, pending_delete: true, namespace_id: nil) + create(:project) # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:project, pending_delete: true) # rubocop:disable RSpec/FactoriesInMigrationSpecs + project = build(:project, pending_delete: true, namespace_id: nil) # rubocop:disable RSpec/FactoriesInMigrationSpecs project.save(validate: false) expect(NamespacelessProjectDestroyWorker).to receive(:bulk_perform_async).with([[project.id]]) @@ -21,8 +21,8 @@ describe CleanupNamespacelessPendingDeleteProjects, :migration, schema: 20180222 end it 'does nothing when no pending delete projects without namespace found' do - create(:project) - create(:project, pending_delete: true) + create(:project) # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:project, pending_delete: true) # rubocop:disable RSpec/FactoriesInMigrationSpecs expect(NamespacelessProjectDestroyWorker).not_to receive(:bulk_perform_async) diff --git a/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb b/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb index 7879105a334..8f40ac3e38b 100644 --- a/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb +++ b/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb @@ -9,11 +9,11 @@ describe CleanupNonexistingNamespacePendingDeleteProjects do end describe '#up' do - set(:some_project) { create(:project) } + set(:some_project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs it 'only cleans up when namespace does not exist' do - create(:project, pending_delete: true) - project = build(:project, pending_delete: true, namespace: nil, namespace_id: Namespace.maximum(:id).to_i.succ) + create(:project, pending_delete: true) # rubocop:disable RSpec/FactoriesInMigrationSpecs + project = build(:project, pending_delete: true, namespace: nil, namespace_id: Namespace.maximum(:id).to_i.succ) # rubocop:disable RSpec/FactoriesInMigrationSpecs project.save(validate: false) expect(NamespacelessProjectDestroyWorker).to receive(:bulk_perform_async).with([[project.id]]) @@ -22,7 +22,7 @@ describe CleanupNonexistingNamespacePendingDeleteProjects do end it 'does nothing when no pending delete projects without namespace found' do - create(:project, pending_delete: true, namespace: create(:namespace)) + create(:project, pending_delete: true, namespace: create(:namespace)) # rubocop:disable RSpec/FactoriesInMigrationSpecs expect(NamespacelessProjectDestroyWorker).not_to receive(:bulk_perform_async) diff --git a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb index d2eef81f396..dd2b08099f2 100644 --- a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb +++ b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb @@ -5,9 +5,9 @@ require Rails.root.join('db', 'migrate', '20171106151218_issues_moved_to_id_fore # only_mirror_protected_branches column in the projects table to create a # project via FactoryBot. describe IssuesMovedToIdForeignKey, :migration, schema: 20171114150259 do - let!(:issue_first) { create(:issue, moved_to_id: issue_second.id) } - let!(:issue_second) { create(:issue, moved_to_id: issue_third.id) } - let!(:issue_third) { create(:issue) } + let!(:issue_first) { create(:issue, moved_to_id: issue_second.id) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:issue_second) { create(:issue, moved_to_id: issue_third.id) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:issue_third) { create(:issue) } # rubocop:disable RSpec/FactoriesInMigrationSpecs subject { described_class.new } diff --git a/spec/migrations/migrate_old_artifacts_spec.rb b/spec/migrations/migrate_old_artifacts_spec.rb index 638b2853374..4187ab149a5 100644 --- a/spec/migrations/migrate_old_artifacts_spec.rb +++ b/spec/migrations/migrate_old_artifacts_spec.rb @@ -16,18 +16,18 @@ describe MigrateOldArtifacts do end context 'with migratable data' do - set(:project1) { create(:project, ci_id: 2) } - set(:project2) { create(:project, ci_id: 3) } - set(:project3) { create(:project) } - - set(:pipeline1) { create(:ci_empty_pipeline, project: project1) } - set(:pipeline2) { create(:ci_empty_pipeline, project: project2) } - set(:pipeline3) { create(:ci_empty_pipeline, project: project3) } - - let!(:build_with_legacy_artifacts) { create(:ci_build, pipeline: pipeline1) } - let!(:build_without_artifacts) { create(:ci_build, pipeline: pipeline1) } - let!(:build2) { create(:ci_build, pipeline: pipeline2) } - let!(:build3) { create(:ci_build, pipeline: pipeline3) } + set(:project1) { create(:project, ci_id: 2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + set(:project2) { create(:project, ci_id: 3) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + set(:project3) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + + set(:pipeline1) { create(:ci_empty_pipeline, project: project1) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + set(:pipeline2) { create(:ci_empty_pipeline, project: project2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + set(:pipeline3) { create(:ci_empty_pipeline, project: project3) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + + let!(:build_with_legacy_artifacts) { create(:ci_build, pipeline: pipeline1) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:build_without_artifacts) { create(:ci_build, pipeline: pipeline1) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:build2) { create(:ci_build, pipeline: pipeline2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:build3) { create(:ci_build, pipeline: pipeline3) } # rubocop:disable RSpec/FactoriesInMigrationSpecs before do setup_builds(build2, build3) diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb index 657113812bd..4ee1d255fbd 100644 --- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb +++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb @@ -4,8 +4,8 @@ require 'spec_helper' require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_worker_jobs.rb') describe MigrateProcessCommitWorkerJobs do - let(:project) { create(:project, :legacy_storage, :repository) } - let(:user) { create(:user) } + let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs let(:commit) { project.commit.raw.rugged_commit } describe 'Project' do diff --git a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb index a17c9c72bde..99173708190 100644 --- a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb +++ b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb @@ -5,8 +5,8 @@ require Rails.root.join('db', 'post_migrate', '20170324160416_migrate_user_activ describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :delete do let(:migration) { described_class.new } - let!(:user_active_1) { create(:user) } - let!(:user_active_2) { create(:user) } + let!(:user_active_1) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:user_active_2) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs def record_activity(user, time) Gitlab::Redis::SharedState.with do |redis| diff --git a/spec/migrations/migrate_user_project_view_spec.rb b/spec/migrations/migrate_user_project_view_spec.rb index 31d16e17d7b..80468b9d01e 100644 --- a/spec/migrations/migrate_user_project_view_spec.rb +++ b/spec/migrations/migrate_user_project_view_spec.rb @@ -5,7 +5,7 @@ require Rails.root.join('db', 'post_migrate', '20170406142253_migrate_user_proje describe MigrateUserProjectView, :delete do let(:migration) { described_class.new } - let!(:user) { create(:user, project_view: 'readme') } + let!(:user) { create(:user, project_view: 'readme') } # rubocop:disable RSpec/FactoriesInMigrationSpecs describe '#up' do it 'updates project view setting with new value' do diff --git a/spec/migrations/move_personal_snippets_files_spec.rb b/spec/migrations/move_personal_snippets_files_spec.rb index 1a319eccc0d..1f39ad98fb8 100644 --- a/spec/migrations/move_personal_snippets_files_spec.rb +++ b/spec/migrations/move_personal_snippets_files_spec.rb @@ -16,14 +16,14 @@ describe MovePersonalSnippetsFiles do describe "#up" do let(:snippet) do - snippet = create(:personal_snippet) + snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs create_upload('picture.jpg', snippet) snippet.update(description: markdown_linking_file('picture.jpg', snippet)) snippet end let(:snippet_with_missing_file) do - snippet = create(:snippet) + snippet = create(:snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs create_upload('picture.jpg', snippet, create_file: false) snippet.update(description: markdown_linking_file('picture.jpg', snippet)) snippet @@ -62,7 +62,7 @@ describe MovePersonalSnippetsFiles do secret = "secret#{snippet.id}" file_location = "/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/picture.jpg" markdown = markdown_linking_file('picture.jpg', snippet) - note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}") + note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}") # rubocop:disable RSpec/FactoriesInMigrationSpecs migration.up @@ -73,14 +73,14 @@ describe MovePersonalSnippetsFiles do describe "#down" do let(:snippet) do - snippet = create(:personal_snippet) + snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs create_upload('picture.jpg', snippet, in_new_path: true) snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true)) snippet end let(:snippet_with_missing_file) do - snippet = create(:personal_snippet) + snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs create_upload('picture.jpg', snippet, create_file: false, in_new_path: true) snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true)) snippet @@ -119,7 +119,7 @@ describe MovePersonalSnippetsFiles do markdown = markdown_linking_file('picture.jpg', snippet, in_new_path: true) secret = "secret#{snippet.id}" file_location = "/uploads/personal_snippet/#{snippet.id}/#{secret}/picture.jpg" - note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}") + note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}") # rubocop:disable RSpec/FactoriesInMigrationSpecs migration.down @@ -135,7 +135,7 @@ describe MovePersonalSnippetsFiles do secret = '123456789' filename = 'hello.jpg' - snippet = create(:personal_snippet) + snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs path_before = "/uploads/personal_snippet/#{snippet.id}/#{secret}/#{filename}" path_after = "/uploads/system/personal_snippet/#{snippet.id}/#{secret}/#{filename}" @@ -161,7 +161,7 @@ describe MovePersonalSnippetsFiles do FileUtils.touch(absolute_path) end - create(:upload, model: snippet, path: "#{secret}/#{filename}", uploader: PersonalFileUploader) + create(:upload, model: snippet, path: "#{secret}/#{filename}", uploader: PersonalFileUploader) # rubocop:disable RSpec/FactoriesInMigrationSpecs end def markdown_linking_file(filename, snippet, in_new_path: false) diff --git a/spec/migrations/remove_dot_git_from_usernames_spec.rb b/spec/migrations/remove_dot_git_from_usernames_spec.rb index 3a88a66a476..f11880a83e9 100644 --- a/spec/migrations/remove_dot_git_from_usernames_spec.rb +++ b/spec/migrations/remove_dot_git_from_usernames_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require Rails.root.join('db', 'migrate', '20161226122833_remove_dot_git_from_usernames.rb') describe RemoveDotGitFromUsernames do - let(:user) { create(:user) } + let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs let(:migration) { described_class.new } describe '#up' do @@ -23,7 +23,7 @@ describe RemoveDotGitFromUsernames do context 'when new path exists already' do describe '#up' do - let(:user2) { create(:user) } + let(:user2) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs before do update_namespace(user, 'test.git') diff --git a/spec/migrations/remove_duplicate_mr_events_spec.rb b/spec/migrations/remove_duplicate_mr_events_spec.rb index e51872239ad..2509ac6afd6 100644 --- a/spec/migrations/remove_duplicate_mr_events_spec.rb +++ b/spec/migrations/remove_duplicate_mr_events_spec.rb @@ -5,17 +5,17 @@ describe RemoveDuplicateMrEvents, :delete do let(:migration) { described_class.new } describe '#up' do - let(:user) { create(:user) } - let(:merge_requests) { create_list(:merge_request, 2) } - let(:issue) { create(:issue) } + let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let(:merge_requests) { create_list(:merge_request, 2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let(:issue) { create(:issue) } # rubocop:disable RSpec/FactoriesInMigrationSpecs let!(:events) do [ - create(:event, :created, author: user, target: merge_requests.first), - create(:event, :created, author: user, target: merge_requests.first), - create(:event, :updated, author: user, target: merge_requests.first), - create(:event, :created, author: user, target: merge_requests.second), - create(:event, :created, author: user, target: issue), - create(:event, :created, author: user, target: issue) + create(:event, :created, author: user, target: merge_requests.first), # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:event, :created, author: user, target: merge_requests.first), # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:event, :updated, author: user, target: merge_requests.first), # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:event, :created, author: user, target: merge_requests.second), # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:event, :created, author: user, target: issue), # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:event, :created, author: user, target: issue) # rubocop:disable RSpec/FactoriesInMigrationSpecs ] end diff --git a/spec/migrations/remove_project_labels_group_id_spec.rb b/spec/migrations/remove_project_labels_group_id_spec.rb index d80d61af20b..01b09e71d83 100644 --- a/spec/migrations/remove_project_labels_group_id_spec.rb +++ b/spec/migrations/remove_project_labels_group_id_spec.rb @@ -5,9 +5,9 @@ require Rails.root.join('db', 'post_migrate', '20180202111106_remove_project_lab describe RemoveProjectLabelsGroupId, :delete do let(:migration) { described_class.new } - let(:group) { create(:group) } - let!(:project_label) { create(:label, group_id: group.id) } - let!(:group_label) { create(:group_label) } + let(:group) { create(:group) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:project_label) { create(:label, group_id: group.id) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:group_label) { create(:group_label) } # rubocop:disable RSpec/FactoriesInMigrationSpecs describe '#up' do it 'updates the project labels group ID' do diff --git a/spec/migrations/remove_soft_removed_objects_spec.rb b/spec/migrations/remove_soft_removed_objects_spec.rb index ec089f9106d..fb70c284f5e 100644 --- a/spec/migrations/remove_soft_removed_objects_spec.rb +++ b/spec/migrations/remove_soft_removed_objects_spec.rb @@ -8,7 +8,7 @@ describe RemoveSoftRemovedObjects, :migration do create_with_deleted_at(:issue) end - regular_issue = create(:issue) + regular_issue = create(:issue) # rubocop:disable RSpec/FactoriesInMigrationSpecs run_migration @@ -28,7 +28,7 @@ describe RemoveSoftRemovedObjects, :migration do it 'removes routes of soft removed personal namespaces' do namespace = create_with_deleted_at(:namespace) - group = create(:group) + group = create(:group) # rubocop:disable RSpec/FactoriesInMigrationSpecs expect(Route.where(source: namespace).exists?).to eq(true) expect(Route.where(source: group).exists?).to eq(true) @@ -41,7 +41,7 @@ describe RemoveSoftRemovedObjects, :migration do it 'schedules the removal of soft removed groups' do group = create_with_deleted_at(:group) - admin = create(:user, admin: true) + admin = create(:user, admin: true) # rubocop:disable RSpec/FactoriesInMigrationSpecs expect_any_instance_of(GroupDestroyWorker) .to receive(:perform) @@ -67,7 +67,7 @@ describe RemoveSoftRemovedObjects, :migration do end def create_with_deleted_at(*args) - row = create(*args) + row = create(*args) # rubocop:disable RSpec/FactoriesInMigrationSpecs # We set "deleted_at" this way so we don't run into any column cache issues. row.class.where(id: row.id).update_all(deleted_at: 1.year.ago) diff --git a/spec/migrations/rename_more_reserved_project_names_spec.rb b/spec/migrations/rename_more_reserved_project_names_spec.rb index 75310075cc5..034e8a6a4e5 100644 --- a/spec/migrations/rename_more_reserved_project_names_spec.rb +++ b/spec/migrations/rename_more_reserved_project_names_spec.rb @@ -8,7 +8,7 @@ require Rails.root.join('db', 'post_migrate', '20170313133418_rename_more_reserv # around this we use the DELETE cleaning strategy. describe RenameMoreReservedProjectNames, :delete do let(:migration) { described_class.new } - let!(:project) { create(:project) } + let!(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs before do project.path = 'artifacts' diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb index 34336d705b1..592ac2b5fb9 100644 --- a/spec/migrations/rename_reserved_project_names_spec.rb +++ b/spec/migrations/rename_reserved_project_names_spec.rb @@ -12,7 +12,7 @@ require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_pr # Ideally, the test should not use factories and rely on the `table` helper instead. describe RenameReservedProjectNames, :migration, schema: :latest do let(:migration) { described_class.new } - let!(:project) { create(:project) } + let!(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs before do project.path = 'projects' diff --git a/spec/migrations/rename_users_with_renamed_namespace_spec.rb b/spec/migrations/rename_users_with_renamed_namespace_spec.rb index e2994103ed7..b8a4dc2b2c0 100644 --- a/spec/migrations/rename_users_with_renamed_namespace_spec.rb +++ b/spec/migrations/rename_users_with_renamed_namespace_spec.rb @@ -3,12 +3,12 @@ require Rails.root.join('db', 'post_migrate', '20170518200835_rename_users_with_ describe RenameUsersWithRenamedNamespace, :delete do it 'renames a user that had their namespace renamed to the namespace path' do - other_user = create(:user, username: 'kodingu') - other_user1 = create(:user, username: 'api0') + other_user = create(:user, username: 'kodingu') # rubocop:disable RSpec/FactoriesInMigrationSpecs + other_user1 = create(:user, username: 'api0') # rubocop:disable RSpec/FactoriesInMigrationSpecs - user = create(:user, username: "Users0") + user = create(:user, username: "Users0") # rubocop:disable RSpec/FactoriesInMigrationSpecs user.update_column(:username, 'Users') - user1 = create(:user, username: "import0") + user1 = create(:user, username: "import0") # rubocop:disable RSpec/FactoriesInMigrationSpecs user1.update_column(:username, 'import') described_class.new.up diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb index 65ec07da31c..ed306fb3d62 100644 --- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb +++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb @@ -3,8 +3,8 @@ require Rails.root.join('db', 'post_migrate', '20171005130944_schedule_create_gp describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do before do - create(:gpg_key, id: 1, key: GpgHelpers::User1.public_key) - create(:gpg_key, id: 2, key: GpgHelpers::User3.public_key) + create(:gpg_key, id: 1, key: GpgHelpers::User1.public_key) # rubocop:disable RSpec/FactoriesInMigrationSpecs + create(:gpg_key, id: 2, key: GpgHelpers::User3.public_key) # rubocop:disable RSpec/FactoriesInMigrationSpecs # Delete all subkeys so they can be recreated GpgKeySubkey.destroy_all end diff --git a/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb index 7494624066a..578440cba20 100644 --- a/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb +++ b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb @@ -8,7 +8,7 @@ describe SchedulePopulateMergeRequestMetricsWithEventsData, :migration, :sidekiq .to receive(:commits_count=).and_return(nil) end - let!(:mrs) { create_list(:merge_request, 3) } + let!(:mrs) { create_list(:merge_request, 3) } # rubocop:disable RSpec/FactoriesInMigrationSpecs it 'correctly schedules background migrations' do stub_const("#{described_class.name}::BATCH_SIZE", 2) diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb index 528dc54781d..560409f08de 100644 --- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb +++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb @@ -2,10 +2,10 @@ require 'spec_helper' require Rails.root.join('db', 'migrate', '20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb') describe TurnNestedGroupsIntoRegularGroupsForMysql do - let!(:parent_group) { create(:group) } - let!(:child_group) { create(:group, parent: parent_group) } - let!(:project) { create(:project, :legacy_storage, :empty_repo, namespace: child_group) } - let!(:member) { create(:user) } + let!(:parent_group) { create(:group) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:child_group) { create(:group, parent: parent_group) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:project) { create(:project, :legacy_storage, :empty_repo, namespace: child_group) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:member) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs let(:migration) { described_class.new } before do diff --git a/spec/migrations/update_retried_for_ci_build_spec.rb b/spec/migrations/update_retried_for_ci_build_spec.rb index ccb77766b84..637dcbb8e01 100644 --- a/spec/migrations/update_retried_for_ci_build_spec.rb +++ b/spec/migrations/update_retried_for_ci_build_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20170503004427_update_retried_for_ci_build.rb') describe UpdateRetriedForCiBuild, :delete do - let(:pipeline) { create(:ci_pipeline) } - let!(:build_old) { create(:ci_build, pipeline: pipeline, name: 'test') } - let!(:build_new) { create(:ci_build, pipeline: pipeline, name: 'test') } + let(:pipeline) { create(:ci_pipeline) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:build_old) { create(:ci_build, pipeline: pipeline, name: 'test') } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let!(:build_new) { create(:ci_build, pipeline: pipeline, name: 'test') } # rubocop:disable RSpec/FactoriesInMigrationSpecs before do described_class.new.up diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb new file mode 100644 index 00000000000..5a15c23def4 --- /dev/null +++ b/spec/models/deploy_token_spec.rb @@ -0,0 +1,134 @@ +require 'spec_helper' + +describe DeployToken do + subject(:deploy_token) { create(:deploy_token) } + + it { is_expected.to have_many :project_deploy_tokens } + it { is_expected.to have_many(:projects).through(:project_deploy_tokens) } + + describe '#ensure_token' do + it 'should ensure a token' do + deploy_token.token = nil + deploy_token.save + + expect(deploy_token.token).not_to be_empty + end + end + + describe '#ensure_at_least_one_scope' do + context 'with at least one scope' do + it 'should be valid' do + is_expected.to be_valid + end + end + + context 'with no scopes' do + it 'should be invalid' do + deploy_token = build(:deploy_token, read_repository: false, read_registry: false) + + expect(deploy_token).not_to be_valid + expect(deploy_token.errors[:base].first).to eq("Scopes can't be blank") + end + end + end + + describe '#scopes' do + context 'with all the scopes' do + it 'should return scopes assigned to DeployToken' do + expect(deploy_token.scopes).to eq([:read_repository, :read_registry]) + end + end + + context 'with only one scope' do + it 'should return scopes assigned to DeployToken' do + deploy_token = create(:deploy_token, read_registry: false) + expect(deploy_token.scopes).to eq([:read_repository]) + end + end + end + + describe '#revoke!' do + it 'should update revoke attribute' do + deploy_token.revoke! + expect(deploy_token.revoked?).to be_truthy + end + end + + describe "#active?" do + context "when it has been revoked" do + it 'should return false' do + deploy_token.revoke! + expect(deploy_token.active?).to be_falsy + end + end + + context "when it hasn't been revoked" do + it 'should return true' do + expect(deploy_token.active?).to be_truthy + end + end + end + + describe '#username' do + it 'returns a harcoded username' do + expect(deploy_token.username).to eq("gitlab+deploy-token-#{deploy_token.id}") + end + end + + describe '#has_access_to?' do + let(:project) { create(:project) } + + subject(:deploy_token) { create(:deploy_token, projects: [project]) } + + context 'when the deploy token has access to the project' do + it 'should return true' do + expect(deploy_token.has_access_to?(project)).to be_truthy + end + end + + context 'when the deploy token does not have access to the project' do + it 'should return false' do + another_project = create(:project) + expect(deploy_token.has_access_to?(another_project)).to be_falsy + end + end + end + + describe '#expires_at' do + context 'when using Forever.date' do + let(:deploy_token) { create(:deploy_token, expires_at: nil) } + + it 'should return nil' do + expect(deploy_token.expires_at).to be_nil + end + end + + context 'when using a personalized date' do + let(:expires_at) { Date.today + 5.months } + let(:deploy_token) { create(:deploy_token, expires_at: expires_at) } + + it 'should return the personalized date' do + expect(deploy_token.expires_at).to eq(expires_at) + end + end + end + + describe '#expires_at=' do + context 'when passing nil' do + let(:deploy_token) { create(:deploy_token, expires_at: nil) } + + it 'should assign Forever.date' do + expect(deploy_token.read_attribute(:expires_at)).to eq(Forever.date) + end + end + + context 'when passign a value' do + let(:expires_at) { Date.today + 5.months } + let(:deploy_token) { create(:deploy_token, expires_at: expires_at) } + + it 'should respect the value' do + expect(deploy_token.read_attribute(:expires_at)).to eq(expires_at) + end + end + end +end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 412eca4a56b..56161bfcc28 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -368,6 +368,32 @@ describe Environment do end end + describe '#deployment_platform' do + context 'when there is a deployment platform for environment' do + let!(:cluster) do + create(:cluster, :provided_by_gcp, + environment_scope: '*', projects: [project]) + end + + it 'finds a deployment platform' do + expect(environment.deployment_platform).to eq cluster.platform + end + end + + context 'when there is no deployment platform for environment' do + it 'returns nil' do + expect(environment.deployment_platform).to be_nil + end + end + + it 'checks deployment platforms associated with a project' do + expect(project).to receive(:deployment_platform) + .with(environment: environment.name) + + environment.deployment_platform + end + end + describe '#terminals' do subject { environment.terminals } diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb index 7709cf43200..8c01a7ac18f 100644 --- a/spec/models/merge_request_diff_commit_spec.rb +++ b/spec/models/merge_request_diff_commit_spec.rb @@ -36,7 +36,7 @@ describe MergeRequestDiffCommit do "committer_email": "dmitriy.zaporozhets@gmail.com", "merge_request_diff_id": merge_request_diff_id, "relative_order": 0, - "sha": sha_attribute.type_cast_for_database('5937ac0a7beb003549fc5fd26fc247adbce4a52e') + "sha": sha_attribute.serialize("5937ac0a7beb003549fc5fd26fc247adbce4a52e") }, { "message": "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \u003cdmitriy.zaporozhets@gmail.com\u003e\n", @@ -48,7 +48,7 @@ describe MergeRequestDiffCommit do "committer_email": "dmitriy.zaporozhets@gmail.com", "merge_request_diff_id": merge_request_diff_id, "relative_order": 1, - "sha": sha_attribute.type_cast_for_database('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') + "sha": sha_attribute.serialize("570e7b2abdd848b95f2f578043fc23bd6f6fd24d") } ] end @@ -79,7 +79,7 @@ describe MergeRequestDiffCommit do "committer_email": "alejorro70@gmail.com", "merge_request_diff_id": merge_request_diff_id, "relative_order": 0, - "sha": sha_attribute.type_cast_for_database('ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69') + "sha": sha_attribute.serialize("ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69") }] end diff --git a/spec/models/project_deploy_token_spec.rb b/spec/models/project_deploy_token_spec.rb new file mode 100644 index 00000000000..9e2e40c2e8f --- /dev/null +++ b/spec/models/project_deploy_token_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe ProjectDeployToken, type: :model do + let(:project) { create(:project) } + let(:deploy_token) { create(:deploy_token) } + subject(:project_deploy_token) { create(:project_deploy_token, project: project, deploy_token: deploy_token) } + + it { is_expected.to belong_to :project } + it { is_expected.to belong_to :deploy_token } + + it { is_expected.to validate_presence_of :deploy_token } + it { is_expected.to validate_presence_of :project } + it { is_expected.to validate_uniqueness_of(:deploy_token_id).scoped_to(:project_id) } +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 7007f78e702..2675c2f52c1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -84,6 +84,8 @@ describe Project do it { is_expected.to have_many(:custom_attributes).class_name('ProjectCustomAttribute') } it { is_expected.to have_many(:project_badges).class_name('ProjectBadge') } it { is_expected.to have_many(:lfs_file_locks) } + it { is_expected.to have_many(:project_deploy_tokens) } + it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) } context 'after initialized' do it "has a project_feature" do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a600987d0bf..35db7616efb 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1850,6 +1850,21 @@ describe User do it_behaves_like :member end + + context 'with subgroup with different owner for project runner', :nested_groups do + let(:group) { create(:group) } + let(:another_user) { create(:user) } + let(:subgroup) { create(:group, parent: group) } + let(:project) { create(:project, group: subgroup) } + + def add_user(access) + group.add_user(user, access) + group.add_user(another_user, :owner) + subgroup.add_user(another_user, :owner) + end + + it_behaves_like :member + end end describe '#projects_with_reporter_access_limited_to' do @@ -2234,6 +2249,20 @@ describe User do end end + context '#invalidate_personal_projects_count' do + let(:user) { build_stubbed(:user) } + + it 'invalidates cache for personal projects counter' do + cache_mock = double + + expect(cache_mock).to receive(:delete).with(['users', user.id, 'personal_projects_count']) + + allow(Rails).to receive(:cache).and_return(cache_mock) + + user.invalidate_personal_projects_count + end + end + describe '#allow_password_authentication_for_web?' do context 'regular user' do let(:user) { build(:user) } @@ -2283,11 +2312,9 @@ describe User do user = build(:user) projects = double(:projects, count: 1) - expect(user).to receive(:personal_projects).once.and_return(projects) + expect(user).to receive(:personal_projects).and_return(projects) - 2.times do - expect(user.personal_projects_count).to eq(1) - end + expect(user.personal_projects_count).to eq(1) end end diff --git a/spec/policies/deploy_token_policy_spec.rb b/spec/policies/deploy_token_policy_spec.rb new file mode 100644 index 00000000000..eea287d895e --- /dev/null +++ b/spec/policies/deploy_token_policy_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe DeployTokenPolicy do + let(:current_user) { create(:user) } + let(:project) { create(:project) } + let(:deploy_token) { create(:deploy_token, projects: [project]) } + + subject { described_class.new(current_user, deploy_token) } + + describe 'creating a deploy key' do + context 'when user is master' do + before do + project.add_master(current_user) + end + + it { is_expected.to be_allowed(:create_deploy_token) } + end + + context 'when user is not master' do + before do + project.add_developer(current_user) + end + + it { is_expected.to be_disallowed(:create_deploy_token) } + end + end + + describe 'updating a deploy key' do + context 'when user is master' do + before do + project.add_master(current_user) + end + + it { is_expected.to be_allowed(:update_deploy_token) } + end + + context 'when user is not master' do + before do + project.add_developer(current_user) + end + + it { is_expected.to be_disallowed(:update_deploy_token) } + end + end +end diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb index 1a8001be6ab..cc16d0f156b 100644 --- a/spec/presenters/ci/build_presenter_spec.rb +++ b/spec/presenters/ci/build_presenter_spec.rb @@ -72,13 +72,44 @@ describe Ci::BuildPresenter do end end - context 'when build is not auto-canceled' do - before do - expect(build).to receive(:auto_canceled?).and_return(false) + context 'when build failed' do + let(:build) { create(:ci_build, :failed, pipeline: pipeline) } + + it 'returns the reason of failure' do + status_title = presenter.status_title + + expect(status_title).to eq('Failed <br> (unknown failure)') end + end + + context 'when build has failed && retried' do + let(:build) { create(:ci_build, :failed, :retried, pipeline: pipeline) } - it 'does not have a status title' do - expect(presenter.status_title).to be_nil + it 'does not include retried title' do + status_title = presenter.status_title + + expect(status_title).not_to include('(retried)') + expect(status_title).to eq('Failed <br> (unknown failure)') + end + end + + context 'when build has failed and is allowed to' do + let(:build) { create(:ci_build, :failed, :allowed_to_fail, pipeline: pipeline) } + + it 'returns the reason of failure' do + status_title = presenter.status_title + + expect(status_title).to eq('Failed <br> (unknown failure)') + end + end + + context 'For any other build' do + let(:build) { create(:ci_build, :success, pipeline: pipeline) } + + it 'returns the status' do + tooltip_description = presenter.status_title + + expect(tooltip_description).to eq('Success') end end end @@ -134,4 +165,56 @@ describe Ci::BuildPresenter do end end end + + describe '#tooltip_message' do + context 'When build has failed' do + let(:build) { create(:ci_build, :script_failure, pipeline: pipeline) } + + it 'returns the reason of failure' do + tooltip = subject.tooltip_message + + expect(tooltip).to eq("#{build.name} - failed <br> (script failure)") + end + end + + context 'When build has failed and retried' do + let(:build) { create(:ci_build, :script_failure, :retried, pipeline: pipeline) } + + it 'should include the reason of failure and the retried title' do + tooltip = subject.tooltip_message + + expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (retried)") + end + end + + context 'When build has failed and is allowed to' do + let(:build) { create(:ci_build, :script_failure, :allowed_to_fail, pipeline: pipeline) } + + it 'should include the reason of failure' do + tooltip = subject.tooltip_message + + expect(tooltip).to eq("#{build.name} - failed <br> (script failure) (allowed to fail)") + end + end + + context 'For any other build (no retried)' do + let(:build) { create(:ci_build, :success, pipeline: pipeline) } + + it 'should include build name and status' do + tooltip = subject.tooltip_message + + expect(tooltip).to eq("#{build.name} - passed") + end + end + + context 'For any other build (retried)' do + let(:build) { create(:ci_build, :success, :retried, pipeline: pipeline) } + + it 'should include build name and status' do + tooltip = subject.tooltip_message + + expect(tooltip).to eq("#{build.name} - passed (retried)") + end + end + end end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 6614e8cea43..90f9c4ad214 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -384,6 +384,30 @@ describe API::Issues do end let(:base_url) { "/groups/#{group.id}/issues" } + context 'when group has subgroups', :nested_groups do + let(:subgroup_1) { create(:group, parent: group) } + let(:subgroup_2) { create(:group, parent: subgroup_1) } + + let(:subgroup_1_project) { create(:project, namespace: subgroup_1) } + let(:subgroup_2_project) { create(:project, namespace: subgroup_2) } + + let!(:issue_1) { create(:issue, project: subgroup_1_project) } + let!(:issue_2) { create(:issue, project: subgroup_2_project) } + + before do + group.add_developer(user) + end + + it 'also returns subgroups projects issues' do + get api(base_url, user) + + issue_ids = json_response.map { |issue| issue['id'] } + + expect_paginated_array_response(size: 5) + expect(issue_ids).to include(issue_1.id, issue_2.id) + end + end + it 'returns all group issues (including opened and closed)' do get api(base_url, admin) diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 5d13e6de741..f68057a92a1 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -114,6 +114,29 @@ describe API::ProjectImport do expect(import_project.description).to eq('Hello world') end + context 'when target path already exists in namespace' do + let(:existing_project) { create(:project, namespace: user.namespace) } + + it 'does not schedule an import' do + expect_any_instance_of(Project).not_to receive(:import_schedule) + + post api('/projects/import', user), path: existing_project.path, file: fixture_file_upload(file) + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']).to eq('Name has already been taken') + end + + context 'when param overwrite is true' do + it 'schedules an import' do + stub_import(user.namespace) + + post api('/projects/import', user), path: existing_project.path, file: fixture_file_upload(file), overwrite: true + + expect(response).to have_gitlab_http_status(201) + end + end + end + def stub_import(namespace) expect_any_instance_of(Project).to receive(:import_schedule) expect(::Projects::CreateService).to receive(:new).with(user, hash_including(namespace_id: namespace.id)).and_call_original diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 4f3420cc0ad..28d51ac86c6 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -950,12 +950,53 @@ describe API::Runner do describe 'POST /api/v4/jobs/:id/artifacts/authorize' do context 'when using token as parameter' do - it 'authorizes posting artifacts to running job' do - authorize_artifacts_with_token_in_params + context 'posting artifacts to running job' do + subject do + authorize_artifacts_with_token_in_params + end - expect(response).to have_gitlab_http_status(200) - expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) - expect(json_response['TempPath']).not_to be_nil + shared_examples 'authorizes local file' do + it 'succeeds' do + subject + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(json_response['TempPath']).to eq(JobArtifactUploader.workhorse_local_upload_path) + expect(json_response['RemoteObject']).to be_nil + end + end + + context 'when using local storage' do + it_behaves_like 'authorizes local file' + end + + context 'when using remote storage' do + context 'when direct upload is enabled' do + before do + stub_artifacts_object_storage(enabled: true, direct_upload: true) + end + + it 'succeeds' do + subject + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) + expect(json_response['TempPath']).to eq(JobArtifactUploader.workhorse_local_upload_path) + expect(json_response['RemoteObject']).to have_key('ID') + expect(json_response['RemoteObject']).to have_key('GetURL') + expect(json_response['RemoteObject']).to have_key('StoreURL') + expect(json_response['RemoteObject']).to have_key('DeleteURL') + end + end + + context 'when direct upload is disabled' do + before do + stub_artifacts_object_storage(enabled: true, direct_upload: false) + end + + it_behaves_like 'authorizes local file' + end + end end it 'fails to post too large artifact' do @@ -1051,20 +1092,45 @@ describe API::Runner do end end - context 'when uses regular file post' do - before do - upload_artifacts(file_upload, headers_with_token, false) + context 'when uses accelerated file post' do + context 'for file stored locally' do + before do + upload_artifacts(file_upload, headers_with_token) + end + + it_behaves_like 'successful artifacts upload' end - it_behaves_like 'successful artifacts upload' - end + context 'for file stored remotelly' do + let!(:fog_connection) do + stub_artifacts_object_storage(direct_upload: true) + end - context 'when uses accelerated file post' do - before do - upload_artifacts(file_upload, headers_with_token, true) - end + before do + fog_connection.directories.get('artifacts').files.create( + key: 'tmp/upload/12312300', + body: 'content' + ) - it_behaves_like 'successful artifacts upload' + upload_artifacts(file_upload, headers_with_token, + { 'file.remote_id' => remote_id }) + end + + context 'when valid remote_id is used' do + let(:remote_id) { '12312300' } + + it_behaves_like 'successful artifacts upload' + end + + context 'when invalid remote_id is used' do + let(:remote_id) { 'invalid id' } + + it 'responds with bad request' do + expect(response).to have_gitlab_http_status(500) + expect(json_response['message']).to eq("Missing file") + end + end + end end context 'when using runners token' do @@ -1208,15 +1274,19 @@ describe API::Runner do end context 'when artifacts are being stored outside of tmp path' do + let(:new_tmpdir) { Dir.mktmpdir } + before do + # init before overwriting tmp dir + file_upload + # by configuring this path we allow to pass file from @tmpdir only # but all temporary files are stored in system tmp directory - @tmpdir = Dir.mktmpdir - allow(JobArtifactUploader).to receive(:workhorse_upload_path).and_return(@tmpdir) + allow(Dir).to receive(:tmpdir).and_return(new_tmpdir) end after do - FileUtils.remove_entry @tmpdir + FileUtils.remove_entry(new_tmpdir) end it' "fails to post artifacts for outside of tmp path"' do @@ -1226,12 +1296,11 @@ describe API::Runner do end end - def upload_artifacts(file, headers = {}, accelerated = true) - params = if accelerated - { 'file.path' => file.path, 'file.name' => file.original_filename } - else - { 'file' => file } - end + def upload_artifacts(file, headers = {}, params = {}) + params = params.merge({ + 'file.path' => file.path, + 'file.name' => file.original_filename + }) post api("/jobs/#{job.id}/artifacts"), params, headers end diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb index 1e6bd993c08..f80abb06fca 100644 --- a/spec/requests/lfs_http_spec.rb +++ b/spec/requests/lfs_http_spec.rb @@ -1016,7 +1016,7 @@ describe 'Git LFS API and storage' do it_behaves_like 'a valid response' do it 'responds with status 200, location of lfs remote store and object details' do - expect(json_response['TempPath']).to be_nil + expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path) expect(json_response['RemoteObject']).to have_key('ID') expect(json_response['RemoteObject']).to have_key('GetURL') expect(json_response['RemoteObject']).to have_key('StoreURL') @@ -1073,7 +1073,9 @@ describe 'Git LFS API and storage' do ['123123', '../../123123'].each do |remote_id| context "with invalid remote_id: #{remote_id}" do subject do - put_finalize_with_args('file.remote_id' => remote_id) + put_finalize(with_tempfile: true, args: { + 'file.remote_id' => remote_id + }) end it 'responds with status 403' do @@ -1093,9 +1095,10 @@ describe 'Git LFS API and storage' do end subject do - put_finalize_with_args( + put_finalize(with_tempfile: true, args: { 'file.remote_id' => '12312300', - 'file.name' => 'name') + 'file.name' => 'name' + }) end it 'responds with status 200' do @@ -1331,7 +1334,7 @@ describe 'Git LFS API and storage' do put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, authorize_headers end - def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false) + def put_finalize(lfs_tmp = lfs_tmp_file, with_tempfile: false, args: {}) upload_path = LfsObjectUploader.workhorse_local_upload_path file_path = upload_path + '/' + lfs_tmp if lfs_tmp @@ -1340,12 +1343,12 @@ describe 'Git LFS API and storage' do FileUtils.touch(file_path) end - args = { + extra_args = { 'file.path' => file_path, 'file.name' => File.basename(file_path) - }.compact + } - put_finalize_with_args(args) + put_finalize_with_args(args.merge(extra_args).compact) end def put_finalize_with_args(args) diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index fb1281a6b42..e1b4e618092 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -164,20 +164,36 @@ describe 'project routing' do # archive_project_repository GET /:project_id/repository/archive(.:format) projects/repositories#archive # edit_project_repository GET /:project_id/repository/edit(.:format) projects/repositories#edit describe Projects::RepositoriesController, 'routing' do - it 'to #archive' do - expect(get('/gitlab/gitlabhq/repository/master/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', ref: 'master') - end - it 'to #archive format:zip' do - expect(get('/gitlab/gitlabhq/repository/master/archive.zip')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'zip', ref: 'master') + expect(get('/gitlab/gitlabhq/-/archive/master/archive.zip')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'zip', id: 'master/archive') end it 'to #archive format:tar.bz2' do - expect(get('/gitlab/gitlabhq/repository/master/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2', ref: 'master') + expect(get('/gitlab/gitlabhq/-/archive/master/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2', id: 'master/archive') end it 'to #archive with "/" in route' do - expect(get('/gitlab/gitlabhq/repository/improve/awesome/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', ref: 'improve/awesome') + expect(get('/gitlab/gitlabhq/-/archive/improve/awesome/gitlabhq-improve-awesome.tar.gz')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.gz', id: 'improve/awesome/gitlabhq-improve-awesome') + end + + it 'to #archive_alternative' do + expect(get('/gitlab/gitlabhq/repository/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', append_sha: true) + end + + it 'to #archive_deprecated' do + expect(get('/gitlab/gitlabhq/repository/master/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', append_sha: true) + end + + it 'to #archive_deprecated format:zip' do + expect(get('/gitlab/gitlabhq/repository/master/archive.zip')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'zip', id: 'master', append_sha: true) + end + + it 'to #archive_deprecated format:tar.bz2' do + expect(get('/gitlab/gitlabhq/repository/master/archive.tar.bz2')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', format: 'tar.bz2', id: 'master', append_sha: true) + end + + it 'to #archive_deprecated with "/" in route' do + expect(get('/gitlab/gitlabhq/repository/improve/awesome/archive')).to route_to('projects/repositories#archive', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'improve/awesome', append_sha: true) end end diff --git a/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb b/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb new file mode 100644 index 00000000000..2763f2bda21 --- /dev/null +++ b/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../../rubocop/cop/rspec/factories_in_migration_specs' + +describe RuboCop::Cop::RSpec::FactoriesInMigrationSpecs do + include CopHelper + + let(:source_file) { 'spec/migrations/foo_spec.rb' } + + subject(:cop) { described_class.new } + + shared_examples 'an offensive factory call' do |namespace| + %i[build build_list create create_list].each do |forbidden_method| + namespaced_forbidden_method = "#{namespace}#{forbidden_method}(:user)" + + it "registers an offense for #{namespaced_forbidden_method}" do + expect_offense(<<-RUBY) + describe 'foo' do + let(:user) { #{namespaced_forbidden_method} } + #{'^' * namespaced_forbidden_method.size} Don't use FactoryBot.#{forbidden_method} in migration specs, use `table` instead. + end + RUBY + end + end + end + + context 'in a migration spec file' do + before do + allow(cop).to receive(:in_migration_spec?).and_return(true) + end + + it_behaves_like 'an offensive factory call', '' + it_behaves_like 'an offensive factory call', 'FactoryBot.' + end + + context 'outside of a migration spec file' do + it "does not register an offense" do + expect_no_offenses(<<-RUBY) + describe 'foo' do + let(:user) { create(:user) } + end + RUBY + end + end +end diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb index 9673b11c2a2..98cd15e248b 100644 --- a/spec/serializers/build_serializer_spec.rb +++ b/spec/serializers/build_serializer_spec.rb @@ -28,15 +28,31 @@ describe BuildSerializer do end describe '#represent_status' do - context 'when represents only status' do - let(:resource) { create(:ci_build) } + context 'for a failed build' do + let(:resource) { create(:ci_build, :failed) } + let(:status) { resource.detailed_status(double('user')) } + + subject { serializer.represent_status(resource) } + + it 'serializes only status' do + expect(subject[:text]).to eq(status.text) + expect(subject[:label]).to eq('failed') + expect(subject[:tooltip]).to eq('failed <br> (unknown failure)') + expect(subject[:icon]).to eq(status.icon) + expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") + end + end + + context 'for any other type of build' do + let(:resource) { create(:ci_build, :success) } let(:status) { resource.detailed_status(double('user')) } subject { serializer.represent_status(resource) } it 'serializes only status' do expect(subject[:text]).to eq(status.text) - expect(subject[:label]).to eq(status.label) + expect(subject[:label]).to eq('passed') + expect(subject[:tooltip]).to eq('passed') expect(subject[:icon]).to eq(status.icon) expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico") end diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb index 026360e91a3..24a6f1a2a8a 100644 --- a/spec/serializers/job_entity_spec.rb +++ b/spec/serializers/job_entity_spec.rb @@ -38,7 +38,7 @@ describe JobEntity do it 'contains details' do expect(subject).to include :status - expect(subject[:status]).to include :icon, :favicon, :text, :label + expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip end context 'when job is retryable' do @@ -126,7 +126,29 @@ describe JobEntity do it 'contains details' do expect(subject).to include :status - expect(subject[:status]).to include :icon, :favicon, :text, :label + expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip + end + end + + context 'when job failed' do + let(:job) { create(:ci_build, :script_failure) } + + describe 'status' do + it 'should contain the failure reason inside label' do + expect(subject[:status]).to include :icon, :favicon, :text, :label, :tooltip + expect(subject[:status][:label]).to eq('failed') + expect(subject[:status][:tooltip]).to eq('failed <br> (script failure)') + end + end + end + + context 'when job passed' do + let(:job) { create(:ci_build, :success) } + + describe 'status' do + it 'should not contain the failure reason inside label' do + expect(subject[:status][:label]).to eq('passed') + end end end end diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb index 248552d1858..2473c561f4b 100644 --- a/spec/serializers/pipeline_entity_spec.rb +++ b/spec/serializers/pipeline_entity_spec.rb @@ -30,7 +30,7 @@ describe PipelineEntity do expect(subject).to include :details expect(subject[:details]) .to include :duration, :finished_at - expect(subject[:details][:status]).to include :icon, :favicon, :text, :label + expect(subject[:details][:status]).to include :icon, :favicon, :text, :label, :tooltip end it 'contains flags' do diff --git a/spec/serializers/stage_entity_spec.rb b/spec/serializers/stage_entity_spec.rb index 40e303f7b89..2034c7891ef 100644 --- a/spec/serializers/stage_entity_spec.rb +++ b/spec/serializers/stage_entity_spec.rb @@ -26,7 +26,7 @@ describe StageEntity do end it 'contains detailed status' do - expect(subject[:status]).to include :text, :label, :group, :icon + expect(subject[:status]).to include :text, :label, :group, :icon, :tooltip expect(subject[:status][:label]).to eq 'passed' end diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb index 70402bac2e2..559475e571c 100644 --- a/spec/serializers/status_entity_spec.rb +++ b/spec/serializers/status_entity_spec.rb @@ -16,7 +16,7 @@ describe StatusEntity do subject { entity.as_json } it 'contains status details' do - expect(subject).to include :text, :icon, :favicon, :label, :group + expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip expect(subject).to include :has_details, :details_path expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico') end diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index b4efa3e44b6..27a7bf0e605 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -48,10 +48,8 @@ describe Boards::Issues::ListService do context 'when parent is a group' do let(:user) { create(:user) } - let(:group) { create(:group) } let(:project) { create(:project, :empty_repo, namespace: group) } let(:project1) { create(:project, :empty_repo, namespace: group) } - let(:board) { create(:board, group: group) } let(:m1) { create(:milestone, group: group) } let(:m2) { create(:milestone, group: group) } @@ -92,13 +90,30 @@ describe Boards::Issues::ListService do let!(:closed_issue4) { create(:labeled_issue, :closed, project: project1, labels: [p1, p1_project1]) } let!(:closed_issue5) { create(:labeled_issue, :closed, project: project1, labels: [development]) } - let(:parent) { group } - before do group.add_developer(user) end - it_behaves_like 'issues list service' + context 'and group has no parent' do + let(:parent) { group } + let(:group) { create(:group) } + let(:board) { create(:board, group: group) } + + it_behaves_like 'issues list service' + end + + context 'and group is an ancestor', :nested_groups do + let(:parent) { create(:group) } + let(:group) { create(:group, parent: parent) } + let!(:backlog) { create(:backlog_list, board: board) } + let(:board) { create(:board, group: parent) } + + before do + parent.add_developer(user) + end + + it_behaves_like 'issues list service' + end end end end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 97a563c1ce1..aa7cc268dd7 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -370,10 +370,89 @@ module Ci it_behaves_like 'validation is not active' end end + end - def execute(runner) - described_class.new(runner).execute.build + describe '#register_success' do + let!(:current_time) { Time.new(2018, 4, 5, 14, 0, 0) } + let!(:attempt_counter) { double('Gitlab::Metrics::NullMetric') } + let!(:job_queue_duration_seconds) { double('Gitlab::Metrics::NullMetric') } + + before do + allow(Time).to receive(:now).and_return(current_time) + + # Stub defaults for any metrics other than the ones we're testing + allow(Gitlab::Metrics).to receive(:counter) + .with(any_args) + .and_return(Gitlab::Metrics::NullMetric.instance) + allow(Gitlab::Metrics).to receive(:histogram) + .with(any_args) + .and_return(Gitlab::Metrics::NullMetric.instance) + + # Stub tested metrics + allow(Gitlab::Metrics).to receive(:counter) + .with(:job_register_attempts_total, anything) + .and_return(attempt_counter) + allow(Gitlab::Metrics).to receive(:histogram) + .with(:job_queue_duration_seconds, anything, anything, anything) + .and_return(job_queue_duration_seconds) + + project.update(shared_runners_enabled: true) + pending_job.update(created_at: current_time - 3600, queued_at: current_time - 1800) end + + shared_examples 'metrics collector' do + it 'increments attempt counter' do + allow(job_queue_duration_seconds).to receive(:observe) + expect(attempt_counter).to receive(:increment) + + execute(runner) + end + + it 'counts job queuing time histogram with expected labels' do + allow(attempt_counter).to receive(:increment) + expect(job_queue_duration_seconds).to receive(:observe) + .with({ shared_runner: expected_shared_runner, + jobs_running_for_project: expected_jobs_running_for_project_first_job }, 1800) + + execute(runner) + end + + context 'when project already has running jobs' do + let!(:build2) { create( :ci_build, :running, pipeline: pipeline, runner: shared_runner) } + let!(:build3) { create( :ci_build, :running, pipeline: pipeline, runner: shared_runner) } + + it 'counts job queuing time histogram with expected labels' do + allow(attempt_counter).to receive(:increment) + expect(job_queue_duration_seconds).to receive(:observe) + .with({ shared_runner: expected_shared_runner, + jobs_running_for_project: expected_jobs_running_for_project_third_job }, 1800) + + execute(runner) + end + end + end + + context 'when shared runner is used' do + let(:runner) { shared_runner } + let(:expected_shared_runner) { true } + let(:expected_jobs_running_for_project_first_job) { 0 } + let(:expected_jobs_running_for_project_third_job) { 2 } + + it_behaves_like 'metrics collector' + end + + context 'when specific runner is used' do + let(:runner) { specific_runner } + let(:expected_shared_runner) { false } + let(:expected_jobs_running_for_project_first_job) { '+Inf' } + let(:expected_jobs_running_for_project_third_job) { '+Inf' } + + it_behaves_like 'metrics collector' + end + end + + def execute(runner) + described_class.new(runner).execute.build end end end diff --git a/spec/services/deploy_tokens/create_service_spec.rb b/spec/services/deploy_tokens/create_service_spec.rb new file mode 100644 index 00000000000..3a2bbf1ecd1 --- /dev/null +++ b/spec/services/deploy_tokens/create_service_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe DeployTokens::CreateService do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:deploy_token_params) { attributes_for(:deploy_token) } + + describe '#execute' do + subject { described_class.new(project, user, deploy_token_params).execute } + + context 'when the deploy token is valid' do + it 'should create a new DeployToken' do + expect { subject }.to change { DeployToken.count }.by(1) + end + + it 'should create a new ProjectDeployToken' do + expect { subject }.to change { ProjectDeployToken.count }.by(1) + end + + it 'returns a DeployToken' do + expect(subject).to be_an_instance_of DeployToken + end + end + + context 'when expires at date is not passed' do + let(:deploy_token_params) { attributes_for(:deploy_token, expires_at: '') } + + it 'should set Forever.date' do + expect(subject.read_attribute(:expires_at)).to eq(Forever.date) + end + end + + context 'when the deploy token is invalid' do + let(:deploy_token_params) { attributes_for(:deploy_token, read_repository: false, read_registry: false) } + + it 'should not create a new DeployToken' do + expect { subject }.not_to change { DeployToken.count } + end + + it 'should not create a new ProjectDeployToken' do + expect { subject }.not_to change { ProjectDeployToken.count } + end + end + end +end diff --git a/spec/services/issuable/destroy_service_spec.rb b/spec/services/issuable/destroy_service_spec.rb index 0a3647a814f..8ccbba7fa58 100644 --- a/spec/services/issuable/destroy_service_spec.rb +++ b/spec/services/issuable/destroy_service_spec.rb @@ -8,7 +8,7 @@ describe Issuable::DestroyService do describe '#execute' do context 'when issuable is an issue' do - let!(:issue) { create(:issue, project: project, author: user) } + let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) } it 'destroys the issue' do expect { service.execute(issue) }.to change { project.issues.count }.by(-1) @@ -26,10 +26,15 @@ describe Issuable::DestroyService do expect { service.execute(issue) } .to change { user.todos_pending_count }.from(1).to(0) end + + it 'invalidates the issues count cache for the assignees' do + expect_any_instance_of(User).to receive(:invalidate_cache_counts).once + service.execute(issue) + end end context 'when issuable is a merge request' do - let!(:merge_request) { create(:merge_request, target_project: project, source_project: project, author: user) } + let!(:merge_request) { create(:merge_request, target_project: project, source_project: project, author: user, assignee: user) } it 'destroys the merge request' do expect { service.execute(merge_request) }.to change { project.merge_requests.count }.by(-1) @@ -41,6 +46,11 @@ describe Issuable::DestroyService do service.execute(merge_request) end + it 'invalidates the merge request caches for the MR assignee' do + expect_any_instance_of(User).to receive(:invalidate_cache_counts).once + service.execute(merge_request) + end + it 'updates the todo caches for users with todos on the merge request' do create(:todo, target: merge_request, user: user, author: user, project: project) diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index f95474208f3..23b1134b5a3 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -97,11 +97,13 @@ describe Issues::UpdateService, :mailer do expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) end - context 'when moving issue between issues from different projects' do + context 'when moving issue between issues from different projects', :nested_groups do let(:group) { create(:group) } + let(:subgroup) { create(:group, parent: group) } + let(:project_1) { create(:project, namespace: group) } let(:project_2) { create(:project, namespace: group) } - let(:project_3) { create(:project, namespace: group) } + let(:project_3) { create(:project, namespace: subgroup) } let(:issue_1) { create(:issue, project: project_1) } let(:issue_2) { create(:issue, project: project_2) } diff --git a/spec/services/merge_requests/conflicts/list_service_spec.rb b/spec/services/merge_requests/conflicts/list_service_spec.rb index 6cadcd438c3..837b8a56d12 100644 --- a/spec/services/merge_requests/conflicts/list_service_spec.rb +++ b/spec/services/merge_requests/conflicts/list_service_spec.rb @@ -77,6 +77,14 @@ describe MergeRequests::Conflicts::ListService do expect(service.can_be_resolved_in_ui?).to be_falsey end + it 'returns a falsey value when the MR has a missing revision after a force push' do + merge_request = create_merge_request('conflict-resolvable') + service = conflicts_service(merge_request) + allow(merge_request).to receive_message_chain(:target_branch_head, :raw, :id).and_return(Gitlab::Git::BLANK_SHA) + + expect(service.can_be_resolved_in_ui?).to be_falsey + end + context 'with gitaly disabled', :skip_gitaly_mock do it 'returns a falsey value when the MR has a missing ref after a force push' do merge_request = create_merge_request('conflict-resolvable') @@ -85,6 +93,14 @@ describe MergeRequests::Conflicts::ListService do expect(service.can_be_resolved_in_ui?).to be_falsey end + + it 'returns a falsey value when the MR has a missing revision after a force push' do + merge_request = create_merge_request('conflict-resolvable') + service = conflicts_service(merge_request) + allow(merge_request).to receive_message_chain(:target_branch_head, :raw, :id).and_return(Gitlab::Git::BLANK_SHA) + + expect(service.can_be_resolved_in_ui?).to be_falsey + end end end end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 2cacb97a293..e35f0f6337a 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -28,6 +28,14 @@ describe Projects::CreateService, '#execute' do end end + describe 'after create actions' do + it 'invalidate personal_projects_count caches' do + expect(user).to receive(:invalidate_personal_projects_count) + + create_project(user, opts) + end + end + context "admin creates project with other user's namespace_id" do it 'sets the correct permissions' do admin = create(:admin) diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 0bec2054f50..a66e3c5e995 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -66,6 +66,12 @@ describe Projects::DestroyService do end it_behaves_like 'deleting the project' + + it 'invalidates personal_project_count cache' do + expect(user).to receive(:invalidate_personal_projects_count) + + destroy_project(project, user) + end end context 'Sidekiq fake' do @@ -242,6 +248,28 @@ describe Projects::DestroyService do end end + context '#attempt_restore_repositories' do + let(:path) { project.disk_path + '.git' } + + before do + expect(project.gitlab_shell.exists?(project.repository_storage_path, path)).to be_truthy + expect(project.gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey + + # Dont run sidekiq to check if renamed repository exists + Sidekiq::Testing.fake! { destroy_project(project, user, {}) } + + expect(project.gitlab_shell.exists?(project.repository_storage_path, path)).to be_falsey + expect(project.gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_truthy + end + + it 'restores the repositories' do + Sidekiq::Testing.fake! { described_class.new(project, user).attempt_repositories_rollback } + + expect(project.gitlab_shell.exists?(project.repository_storage_path, path)).to be_truthy + expect(project.gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey + end + end + def destroy_project(project, user, params = {}) if async Projects::DestroyService.new(project, user, params).async_execute diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb index 880b2aae66a..ee1a886f5d6 100644 --- a/spec/services/projects/gitlab_projects_import_service_spec.rb +++ b/spec/services/projects/gitlab_projects_import_service_spec.rb @@ -4,7 +4,8 @@ describe Projects::GitlabProjectsImportService do set(:namespace) { create(:namespace) } let(:path) { 'test-path' } let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } - let(:import_params) { { namespace_id: namespace.id, path: path, file: file } } + let(:overwrite) { false } + let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } } subject { described_class.new(namespace.owner, import_params) } describe '#execute' do @@ -37,5 +38,28 @@ describe Projects::GitlabProjectsImportService do expect(project.import_data.data['override_params']['description']).to eq('Hello') end end + + context 'when there is a project with the same path' do + let(:existing_project) { create(:project, namespace: namespace) } + let(:path) { existing_project.path} + + it 'does not create the project' do + project = subject.execute + + expect(project).to be_invalid + expect(project).not_to be_persisted + end + + context 'when overwrite param is set' do + let(:overwrite) { true } + + it 'creates a project in a temporary full_path' do + project = subject.execute + + expect(project).to be_valid + expect(project).to be_persisted + end + end + end end end diff --git a/spec/services/projects/move_access_service_spec.rb b/spec/services/projects/move_access_service_spec.rb new file mode 100644 index 00000000000..a820ebd91f4 --- /dev/null +++ b/spec/services/projects/move_access_service_spec.rb @@ -0,0 +1,114 @@ +require 'spec_helper' + +describe Projects::MoveAccessService do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project_with_access) { create(:project, namespace: user.namespace) } + let(:master_user) { create(:user) } + let(:reporter_user) { create(:user) } + let(:developer_user) { create(:user) } + let(:master_group) { create(:group) } + let(:reporter_group) { create(:group) } + let(:developer_group) { create(:group) } + + before do + project_with_access.add_master(master_user) + project_with_access.add_developer(developer_user) + project_with_access.add_reporter(reporter_user) + project_with_access.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER) + project_with_access.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER) + project_with_access.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER) + end + + subject { described_class.new(target_project, user) } + + describe '#execute' do + shared_examples 'move the accesses' do + it do + expect(project_with_access.project_members.count).to eq 4 + expect(project_with_access.project_group_links.count).to eq 3 + expect(project_with_access.authorized_users.count).to eq 4 + + subject.execute(project_with_access) + + expect(project_with_access.project_members.count).to eq 0 + expect(project_with_access.project_group_links.count).to eq 0 + expect(project_with_access.authorized_users.count).to eq 1 + expect(target_project.project_members.count).to eq 4 + expect(target_project.project_group_links.count).to eq 3 + expect(target_project.authorized_users.count).to eq 4 + end + + it 'rollbacks if an exception is raised' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_groups) }.to raise_error(StandardError) + + expect(project_with_access.project_members.count).to eq 4 + expect(project_with_access.project_group_links.count).to eq 3 + expect(project_with_access.authorized_users.count).to eq 4 + end + end + + context 'when both projects are in the same namespace' do + let(:target_project) { create(:project, namespace: user.namespace) } + + it 'does not refresh project owner authorized projects' do + allow(project_with_access).to receive(:namespace).and_return(user.namespace) + expect(project_with_access.namespace).not_to receive(:refresh_project_authorizations) + expect(target_project.namespace).not_to receive(:refresh_project_authorizations) + + subject.execute(project_with_access) + end + + it_behaves_like 'move the accesses' + end + + context 'when projects are in different namespaces' do + let(:target_project) { create(:project, namespace: group) } + + before do + group.add_owner(user) + end + + it 'refreshes both project owner authorized projects' do + allow(project_with_access).to receive(:namespace).and_return(user.namespace) + expect(user.namespace).to receive(:refresh_project_authorizations).once + expect(group).to receive(:refresh_project_authorizations).once + + subject.execute(project_with_access) + end + + it_behaves_like 'move the accesses' + end + + context 'when remove_remaining_elements is false' do + let(:target_project) { create(:project, namespace: user.namespace) } + let(:options) { { remove_remaining_elements: false } } + + it 'does not remove remaining memberships' do + target_project.add_master(master_user) + + subject.execute(project_with_access, options) + + expect(project_with_access.project_members.count).not_to eq 0 + end + + it 'does not remove remaining group links' do + target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER) + + subject.execute(project_with_access, options) + + expect(project_with_access.project_group_links.count).not_to eq 0 + end + + it 'does not remove remaining authorizations' do + target_project.add_developer(developer_user) + + subject.execute(project_with_access, options) + + expect(project_with_access.project_authorizations.count).not_to eq 0 + end + end + end +end diff --git a/spec/services/projects/move_deploy_keys_projects_service_spec.rb b/spec/services/projects/move_deploy_keys_projects_service_spec.rb new file mode 100644 index 00000000000..c548edf39a8 --- /dev/null +++ b/spec/services/projects/move_deploy_keys_projects_service_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe Projects::MoveDeployKeysProjectsService do + let!(:user) { create(:user) } + let!(:project_with_deploy_keys) { create(:project, namespace: user.namespace) } + let!(:target_project) { create(:project, namespace: user.namespace) } + + subject { described_class.new(target_project, user) } + + describe '#execute' do + before do + create_list(:deploy_keys_project, 2, project: project_with_deploy_keys) + end + + it 'moves the user\'s deploy keys from one project to another' do + expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 2 + expect(target_project.deploy_keys_projects.count).to eq 0 + + subject.execute(project_with_deploy_keys) + + expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 0 + expect(target_project.deploy_keys_projects.count).to eq 2 + end + + it 'does not link existent deploy_keys in the current project' do + target_project.deploy_keys << project_with_deploy_keys.deploy_keys.first + + expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 2 + expect(target_project.deploy_keys_projects.count).to eq 1 + + subject.execute(project_with_deploy_keys) + + expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 0 + expect(target_project.deploy_keys_projects.count).to eq 2 + end + + it 'rollbacks changes if transaction fails' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_deploy_keys) }.to raise_error(StandardError) + + expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 2 + expect(target_project.deploy_keys_projects.count).to eq 0 + end + + context 'when remove_remaining_elements is false' do + let(:options) { { remove_remaining_elements: false } } + + it 'does not remove remaining deploy keys projects' do + target_project.deploy_keys << project_with_deploy_keys.deploy_keys.first + + subject.execute(project_with_deploy_keys, options) + + expect(project_with_deploy_keys.deploy_keys_projects.count).not_to eq 0 + end + end + end +end diff --git a/spec/services/projects/move_forks_service_spec.rb b/spec/services/projects/move_forks_service_spec.rb new file mode 100644 index 00000000000..f4a5a7f9fc2 --- /dev/null +++ b/spec/services/projects/move_forks_service_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +describe Projects::MoveForksService do + include ProjectForksHelper + + let!(:user) { create(:user) } + let!(:project_with_forks) { create(:project, namespace: user.namespace) } + let!(:target_project) { create(:project, namespace: user.namespace) } + let!(:lvl1_forked_project_1) { fork_project(project_with_forks, user) } + let!(:lvl1_forked_project_2) { fork_project(project_with_forks, user) } + let!(:lvl2_forked_project_1_1) { fork_project(lvl1_forked_project_1, user) } + let!(:lvl2_forked_project_1_2) { fork_project(lvl1_forked_project_1, user) } + + subject { described_class.new(target_project, user) } + + describe '#execute' do + context 'when moving a root forked project' do + it 'moves the descendant forks' do + expect(project_with_forks.forks.count).to eq 2 + expect(target_project.forks.count).to eq 0 + + subject.execute(project_with_forks) + + expect(project_with_forks.forks.count).to eq 0 + expect(target_project.forks.count).to eq 2 + expect(lvl1_forked_project_1.forked_from_project).to eq target_project + expect(lvl1_forked_project_1.fork_network_member.forked_from_project).to eq target_project + expect(lvl1_forked_project_2.forked_from_project).to eq target_project + expect(lvl1_forked_project_2.fork_network_member.forked_from_project).to eq target_project + end + + it 'updates the fork network' do + expect(project_with_forks.fork_network.root_project).to eq project_with_forks + expect(project_with_forks.fork_network.fork_network_members.map(&:project)).to include project_with_forks + + subject.execute(project_with_forks) + + expect(target_project.reload.fork_network.root_project).to eq target_project + expect(target_project.fork_network.fork_network_members.map(&:project)).not_to include project_with_forks + end + end + + context 'when moving a intermediate forked project' do + it 'moves the descendant forks' do + expect(lvl1_forked_project_1.forks.count).to eq 2 + expect(target_project.forks.count).to eq 0 + + subject.execute(lvl1_forked_project_1) + + expect(lvl1_forked_project_1.forks.count).to eq 0 + expect(target_project.forks.count).to eq 2 + expect(lvl2_forked_project_1_1.forked_from_project).to eq target_project + expect(lvl2_forked_project_1_1.fork_network_member.forked_from_project).to eq target_project + expect(lvl2_forked_project_1_2.forked_from_project).to eq target_project + expect(lvl2_forked_project_1_2.fork_network_member.forked_from_project).to eq target_project + end + + it 'moves the ascendant fork' do + subject.execute(lvl1_forked_project_1) + + expect(target_project.forked_from_project).to eq project_with_forks + expect(target_project.fork_network_member.forked_from_project).to eq project_with_forks + end + + it 'does not update fork network' do + subject.execute(lvl1_forked_project_1) + + expect(target_project.reload.fork_network.root_project).to eq project_with_forks + end + end + + context 'when moving a leaf forked project' do + it 'moves the ascendant fork' do + subject.execute(lvl2_forked_project_1_1) + + expect(target_project.forked_from_project).to eq lvl1_forked_project_1 + expect(target_project.fork_network_member.forked_from_project).to eq lvl1_forked_project_1 + end + + it 'does not update fork network' do + subject.execute(lvl2_forked_project_1_1) + + expect(target_project.reload.fork_network.root_project).to eq project_with_forks + end + end + + it 'rollbacks changes if transaction fails' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_forks) }.to raise_error(StandardError) + + expect(project_with_forks.forks.count).to eq 2 + expect(target_project.forks.count).to eq 0 + end + end +end diff --git a/spec/services/projects/move_lfs_objects_projects_service_spec.rb b/spec/services/projects/move_lfs_objects_projects_service_spec.rb new file mode 100644 index 00000000000..517a24a982a --- /dev/null +++ b/spec/services/projects/move_lfs_objects_projects_service_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe Projects::MoveLfsObjectsProjectsService do + let!(:user) { create(:user) } + let!(:project_with_lfs_objects) { create(:project, namespace: user.namespace) } + let!(:target_project) { create(:project, namespace: user.namespace) } + + subject { described_class.new(target_project, user) } + + before do + create_list(:lfs_objects_project, 3, project: project_with_lfs_objects) + end + + describe '#execute' do + it 'links the lfs objects from existent in source project' do + expect(target_project.lfs_objects.count).to eq 0 + + subject.execute(project_with_lfs_objects) + + expect(project_with_lfs_objects.reload.lfs_objects.count).to eq 0 + expect(target_project.reload.lfs_objects.count).to eq 3 + end + + it 'does not link existent lfs_object in the current project' do + target_project.lfs_objects << project_with_lfs_objects.lfs_objects.first(2) + + expect(target_project.lfs_objects.count).to eq 2 + + subject.execute(project_with_lfs_objects) + + expect(target_project.lfs_objects.count).to eq 3 + end + + it 'rollbacks changes if transaction fails' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_lfs_objects) }.to raise_error(StandardError) + + expect(project_with_lfs_objects.lfs_objects.count).to eq 3 + expect(target_project.lfs_objects.count).to eq 0 + end + + context 'when remove_remaining_elements is false' do + let(:options) { { remove_remaining_elements: false } } + + it 'does not remove remaining lfs objects' do + target_project.lfs_objects << project_with_lfs_objects.lfs_objects.first(2) + + subject.execute(project_with_lfs_objects, options) + + expect(project_with_lfs_objects.lfs_objects.count).not_to eq 0 + end + end + end +end diff --git a/spec/services/projects/move_notification_settings_service_spec.rb b/spec/services/projects/move_notification_settings_service_spec.rb new file mode 100644 index 00000000000..24d69eef86a --- /dev/null +++ b/spec/services/projects/move_notification_settings_service_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe Projects::MoveNotificationSettingsService do + let(:user) { create(:user) } + let(:project_with_notifications) { create(:project, namespace: user.namespace) } + let(:target_project) { create(:project, namespace: user.namespace) } + + subject { described_class.new(target_project, user) } + + describe '#execute' do + context 'with notification settings' do + before do + create_list(:notification_setting, 2, source: project_with_notifications) + end + + it 'moves the user\'s notification settings from one project to another' do + expect(project_with_notifications.notification_settings.count).to eq 3 + expect(target_project.notification_settings.count).to eq 1 + + subject.execute(project_with_notifications) + + expect(project_with_notifications.notification_settings.count).to eq 0 + expect(target_project.notification_settings.count).to eq 3 + end + + it 'rollbacks changes if transaction fails' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_notifications) }.to raise_error(StandardError) + + expect(project_with_notifications.notification_settings.count).to eq 3 + expect(target_project.notification_settings.count).to eq 1 + end + end + + it 'does not move existent notification settings in the current project' do + expect(project_with_notifications.notification_settings.count).to eq 1 + expect(target_project.notification_settings.count).to eq 1 + expect(user.notification_settings.count).to eq 2 + + subject.execute(project_with_notifications) + + expect(user.notification_settings.count).to eq 1 + end + + context 'when remove_remaining_elements is false' do + let(:options) { { remove_remaining_elements: false } } + + it 'does not remove remaining notification settings' do + subject.execute(project_with_notifications, options) + + expect(project_with_notifications.notification_settings.count).not_to eq 0 + end + end + end +end diff --git a/spec/services/projects/move_project_authorizations_service_spec.rb b/spec/services/projects/move_project_authorizations_service_spec.rb new file mode 100644 index 00000000000..f7262b9b887 --- /dev/null +++ b/spec/services/projects/move_project_authorizations_service_spec.rb @@ -0,0 +1,56 @@ +require 'spec_helper' + +describe Projects::MoveProjectAuthorizationsService do + let!(:user) { create(:user) } + let(:project_with_users) { create(:project, namespace: user.namespace) } + let(:target_project) { create(:project, namespace: user.namespace) } + let(:master_user) { create(:user) } + let(:reporter_user) { create(:user) } + let(:developer_user) { create(:user) } + + subject { described_class.new(target_project, user) } + + describe '#execute' do + before do + project_with_users.add_master(master_user) + project_with_users.add_developer(developer_user) + project_with_users.add_reporter(reporter_user) + end + + it 'moves the authorizations from one project to another' do + expect(project_with_users.authorized_users.count).to eq 4 + expect(target_project.authorized_users.count).to eq 1 + + subject.execute(project_with_users) + + expect(project_with_users.authorized_users.count).to eq 0 + expect(target_project.authorized_users.count).to eq 4 + end + + it 'does not move existent authorizations to the current project' do + target_project.add_master(developer_user) + target_project.add_developer(reporter_user) + + expect(project_with_users.authorized_users.count).to eq 4 + expect(target_project.authorized_users.count).to eq 3 + + subject.execute(project_with_users) + + expect(project_with_users.authorized_users.count).to eq 0 + expect(target_project.authorized_users.count).to eq 4 + end + + context 'when remove_remaining_elements is false' do + let(:options) { { remove_remaining_elements: false } } + + it 'does not remove remaining project authorizations' do + target_project.add_master(developer_user) + target_project.add_developer(reporter_user) + + subject.execute(project_with_users, options) + + expect(project_with_users.project_authorizations.count).not_to eq 0 + end + end + end +end diff --git a/spec/services/projects/move_project_group_links_service_spec.rb b/spec/services/projects/move_project_group_links_service_spec.rb new file mode 100644 index 00000000000..e3d06e6d3d7 --- /dev/null +++ b/spec/services/projects/move_project_group_links_service_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Projects::MoveProjectGroupLinksService do + let!(:user) { create(:user) } + let(:project_with_groups) { create(:project, namespace: user.namespace) } + let(:target_project) { create(:project, namespace: user.namespace) } + let(:master_group) { create(:group) } + let(:reporter_group) { create(:group) } + let(:developer_group) { create(:group) } + + subject { described_class.new(target_project, user) } + + describe '#execute' do + before do + project_with_groups.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER) + project_with_groups.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER) + project_with_groups.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER) + end + + it 'moves the group links from one project to another' do + expect(project_with_groups.project_group_links.count).to eq 3 + expect(target_project.project_group_links.count).to eq 0 + + subject.execute(project_with_groups) + + expect(project_with_groups.project_group_links.count).to eq 0 + expect(target_project.project_group_links.count).to eq 3 + end + + it 'does not move existent group links in the current project' do + target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER) + target_project.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER) + + expect(project_with_groups.project_group_links.count).to eq 3 + expect(target_project.project_group_links.count).to eq 2 + + subject.execute(project_with_groups) + + expect(project_with_groups.project_group_links.count).to eq 0 + expect(target_project.project_group_links.count).to eq 3 + end + + it 'rollbacks changes if transaction fails' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_groups) }.to raise_error(StandardError) + + expect(project_with_groups.project_group_links.count).to eq 3 + expect(target_project.project_group_links.count).to eq 0 + end + + context 'when remove_remaining_elements is false' do + let(:options) { { remove_remaining_elements: false } } + + it 'does not remove remaining project group links' do + target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER) + target_project.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER) + + subject.execute(project_with_groups, options) + + expect(project_with_groups.project_group_links.count).not_to eq 0 + end + end + end +end diff --git a/spec/services/projects/move_project_members_service_spec.rb b/spec/services/projects/move_project_members_service_spec.rb new file mode 100644 index 00000000000..9c9a2d2fde1 --- /dev/null +++ b/spec/services/projects/move_project_members_service_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Projects::MoveProjectMembersService do + let!(:user) { create(:user) } + let(:project_with_users) { create(:project, namespace: user.namespace) } + let(:target_project) { create(:project, namespace: user.namespace) } + let(:master_user) { create(:user) } + let(:reporter_user) { create(:user) } + let(:developer_user) { create(:user) } + + subject { described_class.new(target_project, user) } + + describe '#execute' do + before do + project_with_users.add_master(master_user) + project_with_users.add_developer(developer_user) + project_with_users.add_reporter(reporter_user) + end + + it 'moves the members from one project to another' do + expect(project_with_users.project_members.count).to eq 4 + expect(target_project.project_members.count).to eq 1 + + subject.execute(project_with_users) + + expect(project_with_users.project_members.count).to eq 0 + expect(target_project.project_members.count).to eq 4 + end + + it 'does not move existent members to the current project' do + target_project.add_master(developer_user) + target_project.add_developer(reporter_user) + + expect(project_with_users.project_members.count).to eq 4 + expect(target_project.project_members.count).to eq 3 + + subject.execute(project_with_users) + + expect(project_with_users.project_members.count).to eq 0 + expect(target_project.project_members.count).to eq 4 + end + + it 'rollbacks changes if transaction fails' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_users) }.to raise_error(StandardError) + + expect(project_with_users.project_members.count).to eq 4 + expect(target_project.project_members.count).to eq 1 + end + + context 'when remove_remaining_elements is false' do + let(:options) { { remove_remaining_elements: false } } + + it 'does not remove remaining project members' do + target_project.add_master(developer_user) + target_project.add_developer(reporter_user) + + subject.execute(project_with_users, options) + + expect(project_with_users.project_members.count).not_to eq 0 + end + end + end +end diff --git a/spec/services/projects/move_users_star_projects_service_spec.rb b/spec/services/projects/move_users_star_projects_service_spec.rb new file mode 100644 index 00000000000..e0545c5a21b --- /dev/null +++ b/spec/services/projects/move_users_star_projects_service_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe Projects::MoveUsersStarProjectsService do + let!(:user) { create(:user) } + let!(:project_with_stars) { create(:project, namespace: user.namespace) } + let!(:target_project) { create(:project, namespace: user.namespace) } + + subject { described_class.new(target_project, user) } + + describe '#execute' do + before do + create_list(:users_star_project, 2, project: project_with_stars) + end + + it 'moves the user\'s stars from one project to another' do + expect(project_with_stars.users_star_projects.count).to eq 2 + expect(project_with_stars.star_count).to eq 2 + expect(target_project.users_star_projects.count).to eq 0 + expect(target_project.star_count).to eq 0 + + subject.execute(project_with_stars) + project_with_stars.reload + target_project.reload + + expect(project_with_stars.users_star_projects.count).to eq 0 + expect(project_with_stars.star_count).to eq 0 + expect(target_project.users_star_projects.count).to eq 2 + expect(target_project.star_count).to eq 2 + end + + it 'rollbacks changes if transaction fails' do + allow(subject).to receive(:success).and_raise(StandardError) + + expect { subject.execute(project_with_stars) }.to raise_error(StandardError) + + expect(project_with_stars.users_star_projects.count).to eq 2 + expect(project_with_stars.star_count).to eq 2 + expect(target_project.users_star_projects.count).to eq 0 + expect(target_project.star_count).to eq 0 + end + end +end diff --git a/spec/services/projects/overwrite_project_service_spec.rb b/spec/services/projects/overwrite_project_service_spec.rb new file mode 100644 index 00000000000..252c61f4224 --- /dev/null +++ b/spec/services/projects/overwrite_project_service_spec.rb @@ -0,0 +1,198 @@ +require 'spec_helper' + +describe Projects::OverwriteProjectService do + include ProjectForksHelper + + let(:user) { create(:user) } + let(:project_from) { create(:project, namespace: user.namespace) } + let(:project_to) { create(:project, namespace: user.namespace) } + let!(:lvl1_forked_project_1) { fork_project(project_from, user) } + let!(:lvl1_forked_project_2) { fork_project(project_from, user) } + let!(:lvl2_forked_project_1_1) { fork_project(lvl1_forked_project_1, user) } + let!(:lvl2_forked_project_1_2) { fork_project(lvl1_forked_project_1, user) } + + subject { described_class.new(project_to, user) } + + before do + allow(project_to).to receive(:import_data).and_return(double(data: { 'original_path' => project_from.path })) + end + + describe '#execute' do + shared_examples 'overwrite actions' do + it 'moves deploy keys' do + deploy_keys_count = project_from.deploy_keys_projects.count + + subject.execute(project_from) + + expect(project_to.deploy_keys_projects.count).to eq deploy_keys_count + end + + it 'moves notification settings' do + notification_count = project_from.notification_settings.count + + subject.execute(project_from) + + expect(project_to.notification_settings.count).to eq notification_count + end + + it 'moves users stars' do + stars_count = project_from.users_star_projects.count + + subject.execute(project_from) + project_to.reload + + expect(project_to.users_star_projects.count).to eq stars_count + expect(project_to.star_count).to eq stars_count + end + + it 'moves project group links' do + group_links_count = project_from.project_group_links.count + + subject.execute(project_from) + + expect(project_to.project_group_links.count).to eq group_links_count + end + + it 'moves memberships and authorizations' do + members_count = project_from.project_members.count + project_authorizations = project_from.project_authorizations.count + + subject.execute(project_from) + + expect(project_to.project_members.count).to eq members_count + expect(project_to.project_authorizations.count).to eq project_authorizations + end + + context 'moves lfs objects relationships' do + before do + create_list(:lfs_objects_project, 3, project: project_from) + end + + it do + lfs_objects_count = project_from.lfs_objects.count + + subject.execute(project_from) + + expect(project_to.lfs_objects.count).to eq lfs_objects_count + end + end + + it 'removes the original project' do + subject.execute(project_from) + + expect { Project.find(project_from.id) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'renames the project' do + subject.execute(project_from) + + expect(project_to.full_path).to eq project_from.full_path + end + end + + context 'when project does not have any relation' do + it_behaves_like 'overwrite actions' + end + + context 'when project with elements' do + it_behaves_like 'overwrite actions' do + let(:master_user) { create(:user) } + let(:reporter_user) { create(:user) } + let(:developer_user) { create(:user) } + let(:master_group) { create(:group) } + let(:reporter_group) { create(:group) } + let(:developer_group) { create(:group) } + + before do + create_list(:deploy_keys_project, 2, project: project_from) + create_list(:notification_setting, 2, source: project_from) + create_list(:users_star_project, 2, project: project_from) + project_from.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER) + project_from.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER) + project_from.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER) + project_from.add_master(master_user) + project_from.add_developer(developer_user) + project_from.add_reporter(reporter_user) + end + end + end + + context 'forks' do + context 'when moving a root forked project' do + it 'moves the descendant forks' do + expect(project_from.forks.count).to eq 2 + expect(project_to.forks.count).to eq 0 + + subject.execute(project_from) + + expect(project_from.forks.count).to eq 0 + expect(project_to.forks.count).to eq 2 + expect(lvl1_forked_project_1.forked_from_project).to eq project_to + expect(lvl1_forked_project_1.fork_network_member.forked_from_project).to eq project_to + expect(lvl1_forked_project_2.forked_from_project).to eq project_to + expect(lvl1_forked_project_2.fork_network_member.forked_from_project).to eq project_to + end + + it 'updates the fork network' do + expect(project_from.fork_network.root_project).to eq project_from + expect(project_from.fork_network.fork_network_members.map(&:project)).to include project_from + + subject.execute(project_from) + + expect(project_to.reload.fork_network.root_project).to eq project_to + expect(project_to.fork_network.fork_network_members.map(&:project)).not_to include project_from + end + end + context 'when moving a intermediate forked project' do + let(:project_to) { create(:project, namespace: lvl1_forked_project_1.namespace) } + + it 'moves the descendant forks' do + expect(lvl1_forked_project_1.forks.count).to eq 2 + expect(project_to.forks.count).to eq 0 + + subject.execute(lvl1_forked_project_1) + + expect(lvl1_forked_project_1.forks.count).to eq 0 + expect(project_to.forks.count).to eq 2 + expect(lvl2_forked_project_1_1.forked_from_project).to eq project_to + expect(lvl2_forked_project_1_1.fork_network_member.forked_from_project).to eq project_to + expect(lvl2_forked_project_1_2.forked_from_project).to eq project_to + expect(lvl2_forked_project_1_2.fork_network_member.forked_from_project).to eq project_to + end + + it 'moves the ascendant fork' do + subject.execute(lvl1_forked_project_1) + + expect(project_to.reload.forked_from_project).to eq project_from + expect(project_to.fork_network_member.forked_from_project).to eq project_from + end + + it 'does not update fork network' do + subject.execute(lvl1_forked_project_1) + + expect(project_to.reload.fork_network.root_project).to eq project_from + end + end + end + + context 'if an exception is raised' do + it 'rollbacks changes' do + updated_at = project_from.updated_at + + allow(subject).to receive(:rename_project).and_raise(StandardError) + + expect { subject.execute(project_from) }.to raise_error(StandardError) + expect(Project.find(project_from.id)).not_to be_nil + expect(project_from.reload.updated_at.change(usec: 0)).to eq updated_at.change(usec: 0) + end + + it 'tries to restore the original project repositories' do + allow(subject).to receive(:rename_project).and_raise(StandardError) + + expect(subject).to receive(:attempt_restore_repositories).with(project_from) + + expect { subject.execute(project_from) }.to raise_error(StandardError) + end + end + end +end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 95a6771c59d..ff9b2372a35 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -37,6 +37,12 @@ describe Projects::TransferService do transfer_project(project, user, group) end + it 'invalidates the user\'s personal_project_count cache' do + expect(user).to receive(:invalidate_personal_projects_count) + + transfer_project(project, user, group) + end + it 'executes system hooks' do transfer_project(project, user, group) do |service| expect(service).to receive(:execute_system_hooks) diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index dd31a677dfe..1b6caeab15d 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -21,76 +21,72 @@ describe Projects::UpdatePagesService do end context 'legacy artifacts' do - %w(tar.gz zip).each do |format| - let(:extension) { format } + let(:extension) { 'zip' } - context "for valid #{format}" do + before do + build.update_attributes(legacy_artifacts_file: file) + build.update_attributes(legacy_artifacts_metadata: metadata) + end + + describe 'pages artifacts' do + context 'with expiry date' do before do - build.update_attributes(legacy_artifacts_file: file) - build.update_attributes(legacy_artifacts_metadata: metadata) + build.artifacts_expire_in = "2 days" + build.save! end - describe 'pages artifacts' do - context 'with expiry date' do - before do - build.artifacts_expire_in = "2 days" - build.save! - end - - it "doesn't delete artifacts" do - expect(execute).to eq(:success) - - expect(build.reload.artifacts?).to eq(true) - end - end - - context 'without expiry date' do - it "does delete artifacts" do - expect(execute).to eq(:success) + it "doesn't delete artifacts" do + expect(execute).to eq(:success) - expect(build.reload.artifacts?).to eq(false) - end - end + expect(build.reload.artifacts?).to eq(true) end + end - it 'succeeds' do - expect(project.pages_deployed?).to be_falsey + context 'without expiry date' do + it "does delete artifacts" do expect(execute).to eq(:success) - expect(project.pages_deployed?).to be_truthy - # Check that all expected files are extracted - %w[index.html zero .hidden/file].each do |filename| - expect(File.exist?(File.join(project.public_pages_path, filename))).to be_truthy - end + expect(build.reload.artifacts?).to eq(false) end + end + end - it 'limits pages size' do - stub_application_setting(max_pages_size: 1) - expect(execute).not_to eq(:success) - end + it 'succeeds' do + expect(project.pages_deployed?).to be_falsey + expect(execute).to eq(:success) + expect(project.pages_deployed?).to be_truthy - it 'removes pages after destroy' do - expect(PagesWorker).to receive(:perform_in) - expect(project.pages_deployed?).to be_falsey - expect(execute).to eq(:success) - expect(project.pages_deployed?).to be_truthy - project.destroy - expect(project.pages_deployed?).to be_falsey - end + # Check that all expected files are extracted + %w[index.html zero .hidden/file].each do |filename| + expect(File.exist?(File.join(project.public_pages_path, filename))).to be_truthy + end + end - it 'fails if sha on branch is not latest' do - build.update_attributes(ref: 'feature') + it 'limits pages size' do + stub_application_setting(max_pages_size: 1) + expect(execute).not_to eq(:success) + end - expect(execute).not_to eq(:success) - end + it 'removes pages after destroy' do + expect(PagesWorker).to receive(:perform_in) + expect(project.pages_deployed?).to be_falsey + expect(execute).to eq(:success) + expect(project.pages_deployed?).to be_truthy + project.destroy + expect(project.pages_deployed?).to be_falsey + end - it 'fails for empty file fails' do - build.update_attributes(legacy_artifacts_file: empty_file) + it 'fails if sha on branch is not latest' do + build.update_attributes(ref: 'feature') - expect { execute } - .to raise_error(Projects::UpdatePagesService::FailedToExtractError) - end - end + expect(execute).not_to eq(:success) + end + + it 'fails for empty file fails' do + build.update_attributes(legacy_artifacts_file: empty_file) + + expect { execute } + .to raise_error(Projects::UpdatePagesService::FailedToExtractError) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index beabba99cf5..83664bae046 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -66,6 +66,7 @@ RSpec.configure do |config| config.include MigrationsHelpers, :migration config.include StubFeatureFlags config.include StubENV + config.include ExpectOffense config.infer_spec_type_from_file_location! diff --git a/spec/support/helpers/expect_offense.rb b/spec/support/helpers/expect_offense.rb new file mode 100644 index 00000000000..35718ba90c5 --- /dev/null +++ b/spec/support/helpers/expect_offense.rb @@ -0,0 +1,20 @@ +require 'rubocop/rspec/support' + +# https://github.com/backus/rubocop-rspec/blob/master/spec/support/expect_offense.rb +# rubocop-rspec gem extension of RuboCop's ExpectOffense module. +# +# This mixin is the same as rubocop's ExpectOffense except the default +# filename ends with `_spec.rb` +module ExpectOffense + include RuboCop::RSpec::ExpectOffense + + DEFAULT_FILENAME = 'example_spec.rb'.freeze + + def expect_offense(source, filename = DEFAULT_FILENAME) + super + end + + def expect_no_offenses(source, filename = DEFAULT_FILENAME) + super + end +end diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb index 75982432ab4..e61983c60b4 100644 --- a/spec/support/issuables_list_metadata_shared_examples.rb +++ b/spec/support/issuables_list_metadata_shared_examples.rb @@ -5,9 +5,9 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| %w[fix improve/awesome].each do |source_branch| issuable = if issuable_type == :issue - create(issuable_type, project: project) + create(issuable_type, project: project, author: project.creator) else - create(issuable_type, source_project: project, source_branch: source_branch) + create(issuable_type, source_project: project, source_branch: source_branch, author: project.creator) end @issuable_ids << issuable.id @@ -16,7 +16,7 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| it "creates indexed meta-data object for issuable notes and votes count" do if action - get action + get action, author_id: project.creator.id else get :index, namespace_id: project.namespace, project_id: project end @@ -35,7 +35,7 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| it "doesn't execute any queries with false conditions" do get_action = if action - proc { get action } + proc { get action, author_id: project.creator.id } else proc { get :index, namespace_id: project2.namespace, project_id: project2 } end diff --git a/spec/support/issuables_requiring_filter_shared_examples.rb b/spec/support/issuables_requiring_filter_shared_examples.rb new file mode 100644 index 00000000000..439ef5ed92e --- /dev/null +++ b/spec/support/issuables_requiring_filter_shared_examples.rb @@ -0,0 +1,15 @@ +shared_examples 'issuables requiring filter' do |action| + it "doesn't load any issuables if no filter is set" do + expect_any_instance_of(described_class).not_to receive(:issuables_collection) + + get action + + expect(response).to render_template(action) + end + + it "loads issuables if at least one filter is set" do + expect_any_instance_of(described_class).to receive(:issuables_collection).and_call_original + + get action, author_id: user.id + end +end diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb index bad1d34df3a..a75a3eaefcb 100644 --- a/spec/support/stub_configuration.rb +++ b/spec/support/stub_configuration.rb @@ -45,6 +45,10 @@ module StubConfiguration allow(Gitlab.config.lfs).to receive_messages(to_settings(messages)) end + def stub_artifacts_setting(messages) + allow(Gitlab.config.artifacts).to receive_messages(to_settings(messages)) + end + def stub_storage_settings(messages) messages.deep_stringify_keys! diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb index 17d508d0146..16455e2517b 100644 --- a/spec/uploaders/object_storage_spec.rb +++ b/spec/uploaders/object_storage_spec.rb @@ -516,108 +516,46 @@ describe ObjectStorage do end end - describe '#store_workhorse_file!' do + describe '#cache!' do subject do - uploader.store_workhorse_file!(params, :file) + uploader.cache!(uploaded_file) end context 'when local file is used' do context 'when valid file is used' do - let(:target_path) do - File.join(uploader_class.root, uploader_class::TMP_UPLOAD_PATH) + let(:uploaded_file) do + fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') end - before do - FileUtils.mkdir_p(target_path) - end - - context 'when no filename is specified' do - let(:params) do - { "file.path" => "test/file" } - end - - it 'raises an error' do - expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Missing filename/) - end - end - - context 'when invalid file is specified' do - let(:file_path) do - File.join(target_path, "..", "test.file") - end - - before do - FileUtils.touch(file_path) - end - - let(:params) do - { "file.path" => file_path, - "file.name" => "my_file.txt" } - end - - it 'raises an error' do - expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Bad file path/) - end - end - - context 'when filename is specified' do - let(:params) do - { "file.path" => tmp_file, - "file.name" => "my_file.txt" } - end - - let(:tmp_file) { Tempfile.new('filename', target_path) } - - before do - FileUtils.touch(tmp_file) - end - - after do - FileUtils.rm_f(tmp_file) - end - - it 'succeeds' do - expect { subject }.not_to raise_error - - expect(uploader).to be_exists - end - - it 'proper path is being used' do - subject - - expect(uploader.path).to start_with(uploader_class.root) - expect(uploader.path).to end_with("my_file.txt") - end + it "properly caches the file" do + subject - it 'source file to not exist' do - subject - - expect(File.exist?(tmp_file.path)).to be_falsey - end + expect(uploader).to be_exists + expect(uploader.path).to start_with(uploader_class.root) + expect(uploader.filename).to eq('rails_sample.jpg') end end end context 'when remote file is used' do + let(:temp_file) { Tempfile.new("test") } + let!(:fog_connection) do stub_uploads_object_storage(uploader_class) end - context 'when valid file is used' do - context 'when no filename is specified' do - let(:params) do - { "file.remote_id" => "test/123123" } - end + before do + FileUtils.touch(temp_file) + end - it 'raises an error' do - expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Missing filename/) - end - end + after do + FileUtils.rm_f(temp_file) + end + context 'when valid file is used' do context 'when invalid file is specified' do - let(:params) do - { "file.remote_id" => "../test/123123", - "file.name" => "my_file.txt" } + let(:uploaded_file) do + UploadedFile.new(temp_file.path, remote_id: "../test/123123") end it 'raises an error' do @@ -626,9 +564,8 @@ describe ObjectStorage do end context 'when non existing file is specified' do - let(:params) do - { "file.remote_id" => "test/12312300", - "file.name" => "my_file.txt" } + let(:uploaded_file) do + UploadedFile.new(temp_file.path, remote_id: "test/123123") end it 'raises an error' do @@ -636,10 +573,9 @@ describe ObjectStorage do end end - context 'when filename is specified' do - let(:params) do - { "file.remote_id" => "test/123123", - "file.name" => "my_file.txt" } + context 'when valid file is specified' do + let(:uploaded_file) do + UploadedFile.new(temp_file.path, filename: "my_file.txt", remote_id: "test/123123") end let!(:fog_file) do @@ -649,36 +585,37 @@ describe ObjectStorage do ) end - it 'succeeds' do + it 'file to be cached and remote stored' do expect { subject }.not_to raise_error expect(uploader).to be_exists - end - - it 'path to not be temporary' do - subject - + expect(uploader).to be_cached expect(uploader.path).not_to be_nil - expect(uploader.path).not_to include('tmp/upload') - expect(uploader.url).to include('/my_file.txt') + expect(uploader.path).not_to include('tmp/cache') + expect(uploader.url).not_to be_nil + expect(uploader.path).not_to include('tmp/cache') + expect(uploader.object_store).to eq(described_class::Store::REMOTE) end - it 'url is used' do - subject + context 'when file is stored' do + subject do + uploader.store!(uploaded_file) + end - expect(uploader.url).not_to be_nil - expect(uploader.url).to include('/my_file.txt') + it 'file to be remotely stored in permament location' do + subject + + expect(uploader).to be_exists + expect(uploader).not_to be_cached + expect(uploader.path).not_to be_nil + expect(uploader.path).not_to include('tmp/upload') + expect(uploader.path).not_to include('tmp/cache') + expect(uploader.url).to include('/my_file.txt') + expect(uploader.object_store).to eq(described_class::Store::REMOTE) + end end end end end - - context 'when no file is used' do - let(:params) { {} } - - it 'raises an error' do - expect { subject }.to raise_error(uploader_class::RemoteStoreError, /Bad file/) - end - end end end diff --git a/spec/views/projects/jobs/show.html.haml_spec.rb b/spec/views/projects/jobs/show.html.haml_spec.rb index 6a67da79ec5..c93152b88e3 100644 --- a/spec/views/projects/jobs/show.html.haml_spec.rb +++ b/spec/views/projects/jobs/show.html.haml_spec.rb @@ -1,8 +1,10 @@ require 'spec_helper' describe 'projects/jobs/show' do + let(:user) { create(:user) } let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, pipeline: pipeline) } + let(:builds) { project.builds.present(current_user: user) } let(:pipeline) do create(:ci_pipeline, project: project, sha: project.commit.id) @@ -11,6 +13,7 @@ describe 'projects/jobs/show' do before do assign(:build, build.present) assign(:project, project) + assign(:builds, builds) allow(view).to receive(:can?).and_return(true) end @@ -18,7 +21,7 @@ describe 'projects/jobs/show' do describe 'environment info in job view' do context 'job with latest deployment' do let(:build) do - create(:ci_build, :success, environment: 'staging') + create(:ci_build, :success, :trace_artifact, environment: 'staging') end before do @@ -37,11 +40,11 @@ describe 'projects/jobs/show' do context 'job with outdated deployment' do let(:build) do - create(:ci_build, :success, environment: 'staging', pipeline: pipeline) + create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) end let(:second_build) do - create(:ci_build, :success, environment: 'staging', pipeline: pipeline) + create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) end let(:environment) do @@ -67,7 +70,7 @@ describe 'projects/jobs/show' do context 'job failed to deploy' do let(:build) do - create(:ci_build, :failed, environment: 'staging', pipeline: pipeline) + create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) end let!(:environment) do @@ -85,7 +88,7 @@ describe 'projects/jobs/show' do context 'job will deploy' do let(:build) do - create(:ci_build, :running, environment: 'staging', pipeline: pipeline) + create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) end context 'when environment exists' do @@ -133,7 +136,7 @@ describe 'projects/jobs/show' do context 'job that failed to deploy and environment has not been created' do let(:build) do - create(:ci_build, :failed, environment: 'staging', pipeline: pipeline) + create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) end let!(:environment) do @@ -151,7 +154,7 @@ describe 'projects/jobs/show' do context 'job that will deploy and environment has not been created' do let(:build) do - create(:ci_build, :running, environment: 'staging', pipeline: pipeline) + create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) end let!(:environment) do @@ -171,8 +174,9 @@ describe 'projects/jobs/show' do end context 'when job is running' do + let(:build) { create(:ci_build, :trace_live, :running, pipeline: pipeline) } + before do - build.run! render end diff --git a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb index 3ca67114558..b1c6565c08a 100644 --- a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb +++ b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb @@ -28,6 +28,6 @@ describe 'projects/merge_requests/_commits.html.haml' do commit = merge_request.commits.first # HEAD href = diffs_project_merge_request_path(target_project, merge_request, commit_id: commit) - expect(rendered).to have_link(Commit.truncate_sha(commit.sha), href: href) + expect(rendered).to have_link(href: href) end end |