From 18cfd1b24dde08febb7292fbf90cd9e5ad4a4f42 Mon Sep 17 00:00:00 2001 From: J2EEbbesen Date: Mon, 14 Oct 2024 14:48:35 -0500 Subject: [PATCH] VCR refactor (#22) * refactoring tests to eliminate need for real environment variables during testing * defaulting required environment variables in test --- README.md | 2 +- lib/sheet_zoukas.rb | 7 ++++- lib/sheet_zoukas/google_sheets.rb | 9 ++++++- .../get_spreadsheet_values_no_range.yml | 8 +++--- .../get_spreadsheet_values_range.yml | 8 +++--- .../vcr_cassettes/retrieve_sheet_json.yml | 2 +- spec/sheet_zoukas/google_sheets_spec.rb | 6 +++-- spec/sheet_zoukas_spec.rb | 10 ++++--- spec/spec_helper.rb | 27 ++++++++++++++----- 9 files changed, 55 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 70871ab..e3e233b 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ GitHub Action runs ### testing -RSpec tests require the environment variables checked in [spec_helper.rb](spec/spec_helper.rb). Out of the box your tests that call Google (mocked with VCR) will fail because you do (should) not have access to my test account credentials. + $ rake spec ## Release Build diff --git a/lib/sheet_zoukas.rb b/lib/sheet_zoukas.rb index f0a724d..f8ef258 100644 --- a/lib/sheet_zoukas.rb +++ b/lib/sheet_zoukas.rb @@ -12,11 +12,16 @@ class Error < StandardError; end class << self def retrieve_sheet_json(sheet_id, tab_name, range = nil) - exit 1 unless SheetZoukas::Utils.vars_present?(REQUIRED_VARS, 'required for Google Sheets API calls') + SheetZoukas.exit_program unless SheetZoukas::Utils.vars_present?(REQUIRED_VARS, + 'required for Google Sheets API calls') sheet = GoogleSheets.new data = sheet.retrieve_sheet(sheet_id, tab_name, range) DataConverter.new(data.values).convert end + + def exit_program + raise StandardError, 'Error encountered' + end end end diff --git a/lib/sheet_zoukas/google_sheets.rb b/lib/sheet_zoukas/google_sheets.rb index 7f1f946..0823d53 100644 --- a/lib/sheet_zoukas/google_sheets.rb +++ b/lib/sheet_zoukas/google_sheets.rb @@ -9,7 +9,7 @@ class GoogleSheets DEFAULT_SCOPE = 'https://www.googleapis.com/auth/spreadsheets.readonly' def initialize(scope = DEFAULT_SCOPE) - @authorizer = Google::Auth::ServiceAccountCredentials.from_env(scope: scope) + init_authorizer(scope) end def retrieve_sheet(sheet_id, tab_name, range = nil) @@ -25,5 +25,12 @@ def retrieve_sheet(sheet_id, tab_name, range = nil) "#{tab_name}!#{range}" end + + private + + # overriden in spec_helper so we don't try to authenticate with Google for tests + def init_authorizer(scope) + @authorizer = Google::Auth::ServiceAccountCredentials.from_env(scope: scope) + end end end diff --git a/spec/fixtures/vcr_cassettes/get_spreadsheet_values_no_range.yml b/spec/fixtures/vcr_cassettes/get_spreadsheet_values_no_range.yml index 6288e39..62b85ca 100644 --- a/spec/fixtures/vcr_cassettes/get_spreadsheet_values_no_range.yml +++ b/spec/fixtures/vcr_cassettes/get_spreadsheet_values_no_range.yml @@ -5,7 +5,7 @@ http_interactions: uri: https://www.googleapis.com/oauth2/v4/token body: encoding: ASCII-8BIT - string: grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJzdHAtZm9vZC1zZXJ2aWNlQHN0cC1mb29kLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiYXVkIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3Y0L3Rva2VuIiwiZXhwIjoxNzI4NzcyNjE4LCJpYXQiOjE3Mjg3NzI0OTgsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9kcml2ZS5yZWFkb25seSJ9.C7iPR9Lavlv44wtQcNs2orahR_Er7QjjBn7TJTMfd7lI5eE3tRC2ixwuS1oGEXPG1ColjAc3gojKFtJxTJsWUYiCTwdFk1Kx6U8lBJV_sNY8F8Y_LxiDfS8zGPldXJ5RBabHRzJofRfXPOK1q6M8IpPgoK1jV3ilynfnUajlwpmhIcHUOeGmu0h2XxHzcXgKhJFBxVRw5PjOmdC0EOqyGIC6KVu2JJKPDhZ-LE4WCNuiFZIEfNY2huzxKYxhTz-C9dWkmlS2TDeGkWLWsD1QLN3p5D2TCO3V3jmQ3SQpncbSZQ475OcpRdCnAnByo49qVxR8U6lR6XEdqQbwKInibg + string: grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion= headers: User-Agent: - Faraday v2.12.0 @@ -44,11 +44,11 @@ http_interactions: - chunked body: encoding: ASCII-8BIT - string: '{"access_token":"ya29.c.c0ASRK0GYEVGz-_kslN9gKdl0NhSMljNMSKv-F2fBcXarlK3Itsq5TzaHMHf-opp_pMdKmgG5FZp7s3iATwMYaRTjbi50PjyYk17kmZKZPf079yhIw68aqnMLfZ8UeRBHN1Uvby2RXh9ExZjcGWBTQt7tRF0n-nOHwr0e7rnEQuyNeSDHTTvsp7ckSJ6BwnErQUIdI0B6rCU1-V6pl7nlV60rnKg0O23MtYjThWjmtZAdC6OB-DHbfjkrpaFB5ZqhJljDpbDixfVqqiX-W6sv0RXZuaHlR3Z-Soxhk0isUyXZg7fOXm5WlsxleesaaPyOLsGT8YNcuNDNvOcSN14K0dkGY9IXMypt67F9U6bPpSimxFgSebiugVIAN384PakY4_Ut97ol-n4M8m1kbwf5Q5pRwj7S9WWmBZjFi43hsMwiq-RjyZfRc17yQR776Bpnv0sSoZzacZ4tRtfmc4k-uZIXXmeQgyjM4niOW7v2BZ1RyuxcdskmWWgssM1cM18wWecpk4eBoWnwzcx6lZQ0h_aSaJmrFQvMURqwZp1eQg426vyaOs7xw61hF7Se4z5X08laBwZe6lBaXUXdyyRBB8j9eiSsgB9sXBO04sYzUMSVw1qzlc9atWIbczvjupQYpmlysyoz_feviOpjwn1lXU2j6URbmbc1iSBleOwf5nb5W_cveUc7jmkl128-pn6pnl2RS_v3dyyUhV4wj8FBpgvWMyBYRWb3q3WiR820_9c-MiQtBxURos01wYRyXcurulcWatrz_bd6VnOZBjpQRBYdvVyeqWQFpxFjJgyV-J2Xq5jy5eh8a-ahc0tonYr14R37hU7um5wgz31QQhI3c0hVZmzV00-aod0tg53g3BZ01conosyoaZj-axdjsB8zlIcO3dQtvo-xf6igxfbWxOa416vJkI1oSnot0fQeW87qyMW7bofi27MicXgfUIhrkOyVuuR2SOmtRUj_esm_r6O-mW_icS-gpR9uoZ2x1OvegOqmB7XviR1k","expires_in":3599,"token_type":"Bearer"}' + string: '{"access_token":"","expires_in":3599,"token_type":"Bearer"}' recorded_at: Sat, 12 Oct 2024 22:35:58 GMT - request: method: get - uri: https://sheets.googleapis.com/v4/spreadsheets//values/Log + uri: https://sheets.googleapis.com/v4/spreadsheets/test_id/values/Log body: encoding: UTF-8 string: '' @@ -64,7 +64,7 @@ http_interactions: X-Goog-Api-Client: - gl-ruby/3.3.5 gdcl/1.36.0 Authorization: - - Bearer ya29.c.c0ASRK0GYEVGz-_kslN9gKdl0NhSMljNMSKv-F2fBcXarlK3Itsq5TzaHMHf-opp_pMdKmgG5FZp7s3iATwMYaRTjbi50PjyYk17kmZKZPf079yhIw68aqnMLfZ8UeRBHN1Uvby2RXh9ExZjcGWBTQt7tRF0n-nOHwr0e7rnEQuyNeSDHTTvsp7ckSJ6BwnErQUIdI0B6rCU1-V6pl7nlV60rnKg0O23MtYjThWjmtZAdC6OB-DHbfjkrpaFB5ZqhJljDpbDixfVqqiX-W6sv0RXZuaHlR3Z-Soxhk0isUyXZg7fOXm5WlsxleesaaPyOLsGT8YNcuNDNvOcSN14K0dkGY9IXMypt67F9U6bPpSimxFgSebiugVIAN384PakY4_Ut97ol-n4M8m1kbwf5Q5pRwj7S9WWmBZjFi43hsMwiq-RjyZfRc17yQR776Bpnv0sSoZzacZ4tRtfmc4k-uZIXXmeQgyjM4niOW7v2BZ1RyuxcdskmWWgssM1cM18wWecpk4eBoWnwzcx6lZQ0h_aSaJmrFQvMURqwZp1eQg426vyaOs7xw61hF7Se4z5X08laBwZe6lBaXUXdyyRBB8j9eiSsgB9sXBO04sYzUMSVw1qzlc9atWIbczvjupQYpmlysyoz_feviOpjwn1lXU2j6URbmbc1iSBleOwf5nb5W_cveUc7jmkl128-pn6pnl2RS_v3dyyUhV4wj8FBpgvWMyBYRWb3q3WiR820_9c-MiQtBxURos01wYRyXcurulcWatrz_bd6VnOZBjpQRBYdvVyeqWQFpxFjJgyV-J2Xq5jy5eh8a-ahc0tonYr14R37hU7um5wgz31QQhI3c0hVZmzV00-aod0tg53g3BZ01conosyoaZj-axdjsB8zlIcO3dQtvo-xf6igxfbWxOa416vJkI1oSnot0fQeW87qyMW7bofi27MicXgfUIhrkOyVuuR2SOmtRUj_esm_r6O-mW_icS-gpR9uoZ2x1OvegOqmB7XviR1k + - Bearer Content-Type: - application/x-www-form-urlencoded response: diff --git a/spec/fixtures/vcr_cassettes/get_spreadsheet_values_range.yml b/spec/fixtures/vcr_cassettes/get_spreadsheet_values_range.yml index 01b2b2c..4e94686 100644 --- a/spec/fixtures/vcr_cassettes/get_spreadsheet_values_range.yml +++ b/spec/fixtures/vcr_cassettes/get_spreadsheet_values_range.yml @@ -5,7 +5,7 @@ http_interactions: uri: https://www.googleapis.com/oauth2/v4/token body: encoding: ASCII-8BIT - string: grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJzdHAtZm9vZC1zZXJ2aWNlQHN0cC1mb29kLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiYXVkIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3Y0L3Rva2VuIiwiZXhwIjoxNzI4NzcyNjE4LCJpYXQiOjE3Mjg3NzI0OTgsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9kcml2ZS5yZWFkb25seSJ9.C7iPR9Lavlv44wtQcNs2orahR_Er7QjjBn7TJTMfd7lI5eE3tRC2ixwuS1oGEXPG1ColjAc3gojKFtJxTJsWUYiCTwdFk1Kx6U8lBJV_sNY8F8Y_LxiDfS8zGPldXJ5RBabHRzJofRfXPOK1q6M8IpPgoK1jV3ilynfnUajlwpmhIcHUOeGmu0h2XxHzcXgKhJFBxVRw5PjOmdC0EOqyGIC6KVu2JJKPDhZ-LE4WCNuiFZIEfNY2huzxKYxhTz-C9dWkmlS2TDeGkWLWsD1QLN3p5D2TCO3V3jmQ3SQpncbSZQ475OcpRdCnAnByo49qVxR8U6lR6XEdqQbwKInibg + string: grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion= headers: User-Agent: - Faraday v2.12.0 @@ -44,11 +44,11 @@ http_interactions: - chunked body: encoding: ASCII-8BIT - string: '{"access_token":"ya29.c.c0ASRK0GbFIkrrzSnGUpIS3wiTPBIJGQZLMy78KWhu-et3-yUzcuy-219qqUZoHTfuXUB9IkW-qgQn9my7j5KYKaENaHK3H--SbiBCIU0u4NnEUz_ydHIDDwmPD12jykdTvlah89EdPm3ITP00Lwd-TmFBtsRCPnHhcqGxE_qYivEliNHIoiSnuzOnle43iiopj4rFCKaeIer8U5rBzix8dzpN9c3ktGXhy3jXKvxSItiz180Z-l7mFulztaB-09nHG4BxdxAFDDdi9Z3jdx4J6sQa9sgV0k0ULvawiGr0g-EdPOc-EBmoUPtRGahrKepkslgMDma_JsmwO-Lskv79JtpYN6KsuRv1oiLm25J0zAao0JclzMXlc3VwN385DJFrl_uFqVMkaetr3RvBnUdU3xnxRM93arssih09Ulwv9BFl2WdWb6rXmQ6f09q8JU0aW-7noSYZobfs5Ob-3Q6WS3VzppzFFoFnF9wx40iU8Z619ckuZy429haWyj1U52XmBpwux_mWjZQatJFZ0yeqhbof9vVdmFY_ncjgojQfe54_srtm3S2lnexejs4q6ozJaZqItcqjtsu9pr_JwF-myfJjS54IaOue82p34Mx_zz4vnvuUI7Ieapid6XqfxMwUqJS6luQxf1U8-9l5qOwl-qx29wSi0WWbkQVj-8n2Rj7pn33V48JihwOMjIJW6i5oRUwyBgkQMif2y-xWSW4fRrlVBc417020B9tt4imnmelcwsX-vfWBUuIlO35w0S1bmMixgwdbX8Yn9lZr1Uv3f5VeFev48k59q7gwSR9uZZdg7Z3qfw_ZX7YBttFvXFjn_nwy8Va0aoRRw9o3r0euJ0nv_fSBywpYb_wjSVb9kmOdbqa5mzeV4jiv8x_xqo-vOy5ZmVyyv6qsjSWtfBq3U62xOUQYhZzxj5yXFh1yBYJv38zb_7JkSoZcQ4SS8-qUp4pf7zd-MUum2jmq6FsFlUre5U9lXZlY5bxI760cSbpzMXQerMVWcj4","expires_in":3599,"token_type":"Bearer"}' + string: '{"access_token":"","expires_in":3599,"token_type":"Bearer"}' recorded_at: Sat, 12 Oct 2024 22:35:58 GMT - request: method: get - uri: https://sheets.googleapis.com/v4/spreadsheets//values/Log!A:Z + uri: https://sheets.googleapis.com/v4/spreadsheets/test_id/values/Log!A:Z body: encoding: UTF-8 string: '' @@ -64,7 +64,7 @@ http_interactions: X-Goog-Api-Client: - gl-ruby/3.3.5 gdcl/1.36.0 Authorization: - - Bearer ya29.c.c0ASRK0GbFIkrrzSnGUpIS3wiTPBIJGQZLMy78KWhu-et3-yUzcuy-219qqUZoHTfuXUB9IkW-qgQn9my7j5KYKaENaHK3H--SbiBCIU0u4NnEUz_ydHIDDwmPD12jykdTvlah89EdPm3ITP00Lwd-TmFBtsRCPnHhcqGxE_qYivEliNHIoiSnuzOnle43iiopj4rFCKaeIer8U5rBzix8dzpN9c3ktGXhy3jXKvxSItiz180Z-l7mFulztaB-09nHG4BxdxAFDDdi9Z3jdx4J6sQa9sgV0k0ULvawiGr0g-EdPOc-EBmoUPtRGahrKepkslgMDma_JsmwO-Lskv79JtpYN6KsuRv1oiLm25J0zAao0JclzMXlc3VwN385DJFrl_uFqVMkaetr3RvBnUdU3xnxRM93arssih09Ulwv9BFl2WdWb6rXmQ6f09q8JU0aW-7noSYZobfs5Ob-3Q6WS3VzppzFFoFnF9wx40iU8Z619ckuZy429haWyj1U52XmBpwux_mWjZQatJFZ0yeqhbof9vVdmFY_ncjgojQfe54_srtm3S2lnexejs4q6ozJaZqItcqjtsu9pr_JwF-myfJjS54IaOue82p34Mx_zz4vnvuUI7Ieapid6XqfxMwUqJS6luQxf1U8-9l5qOwl-qx29wSi0WWbkQVj-8n2Rj7pn33V48JihwOMjIJW6i5oRUwyBgkQMif2y-xWSW4fRrlVBc417020B9tt4imnmelcwsX-vfWBUuIlO35w0S1bmMixgwdbX8Yn9lZr1Uv3f5VeFev48k59q7gwSR9uZZdg7Z3qfw_ZX7YBttFvXFjn_nwy8Va0aoRRw9o3r0euJ0nv_fSBywpYb_wjSVb9kmOdbqa5mzeV4jiv8x_xqo-vOy5ZmVyyv6qsjSWtfBq3U62xOUQYhZzxj5yXFh1yBYJv38zb_7JkSoZcQ4SS8-qUp4pf7zd-MUum2jmq6FsFlUre5U9lXZlY5bxI760cSbpzMXQerMVWcj4 + - Bearer Content-Type: - application/x-www-form-urlencoded response: diff --git a/spec/fixtures/vcr_cassettes/retrieve_sheet_json.yml b/spec/fixtures/vcr_cassettes/retrieve_sheet_json.yml index 518c23e..c3c72f5 100644 --- a/spec/fixtures/vcr_cassettes/retrieve_sheet_json.yml +++ b/spec/fixtures/vcr_cassettes/retrieve_sheet_json.yml @@ -48,7 +48,7 @@ http_interactions: recorded_at: Sun, 13 Oct 2024 11:03:14 GMT - request: method: get - uri: https://sheets.googleapis.com/v4/spreadsheets//values/Log + uri: https://sheets.googleapis.com/v4/spreadsheets/test_id/values/Log body: encoding: UTF-8 string: '' diff --git a/spec/sheet_zoukas/google_sheets_spec.rb b/spec/sheet_zoukas/google_sheets_spec.rb index ce1a84f..0b70756 100644 --- a/spec/sheet_zoukas/google_sheets_spec.rb +++ b/spec/sheet_zoukas/google_sheets_spec.rb @@ -4,6 +4,8 @@ require 'sheet_zoukas/google_sheets' RSpec.describe SheetZoukas::GoogleSheets do + before { ENV.store('GOOGLE_API_SPREADSHEET_ID_TEST', 'test_id') } + describe '#initialize' do it 'uses default scope' do ret = described_class.new.instance_variable_get(:@authorizer).scope @@ -32,14 +34,14 @@ describe '#retrieve_sheet' do it 'retrieves sheet with range' do VCR.use_cassette('get_spreadsheet_values_range') do - sheet = described_class.new.retrieve_sheet(ENV.fetch('GOOGLE_API_SPREADSHEET_ID', nil), 'Log', 'A:Z') + sheet = described_class.new.retrieve_sheet(ENV.fetch('GOOGLE_API_SPREADSHEET_ID_TEST', nil), 'Log', 'A:Z') expect(sheet.values.first).to include('Reward Type') end end it 'retrieves sheet without range' do VCR.use_cassette('get_spreadsheet_values_no_range') do - sheet = described_class.new.retrieve_sheet(ENV.fetch('GOOGLE_API_SPREADSHEET_ID', nil), 'Log') + sheet = described_class.new.retrieve_sheet(ENV.fetch('GOOGLE_API_SPREADSHEET_ID_TEST', nil), 'Log') expect(sheet.values.first).to include('Reward Type') end end diff --git a/spec/sheet_zoukas_spec.rb b/spec/sheet_zoukas_spec.rb index 0cd6725..4cdc612 100644 --- a/spec/sheet_zoukas_spec.rb +++ b/spec/sheet_zoukas_spec.rb @@ -6,9 +6,11 @@ end describe '.retrieve_sheet_json' do + before { ENV.store('GOOGLE_API_SPREADSHEET_ID_TEST', 'test_id') } + it 'retrieves sheet data' do # rubocop:disable RSpec/ExampleLength VCR.use_cassette('retrieve_sheet_json') do - data = described_class.retrieve_sheet_json(ENV.fetch('GOOGLE_API_SPREADSHEET_ID', nil), 'Log') + data = described_class.retrieve_sheet_json(ENV.fetch('GOOGLE_API_SPREADSHEET_ID_TEST', nil), 'Log') expect(data[0]).to eq('Place' => 'Slice Brothers', 'Deal' => '2 slices for $5.99', 'Deal Earned' => '', @@ -26,9 +28,9 @@ ENV.delete(missing) expect do expect do - described_class.retrieve_sheet_json(ENV.fetch('GOOGLE_API_SPREADSHEET_ID', nil), 'Log') - end.to raise_error(SystemExit) do |error| # rubocop:disable Style/MultilineBlockChain - expect(error.status).to eq(1) + described_class.retrieve_sheet_json(ENV.fetch('GOOGLE_API_SPREADSHEET_ID_TEST', nil), 'Log') + end.to raise_error(StandardError) do |error| # rubocop:disable Style/MultilineBlockChain + expect(error.message).to eq('Error encountered') end end.to output("⛔️ #{missing} required for Google Sheets API calls.\n").to_stdout end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1d79510..7a215b6 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,11 +26,26 @@ VCR.configure do |config| config.cassette_library_dir = 'spec/fixtures/vcr_cassettes' config.hook_into :webmock - config.filter_sensitive_data('') { ENV.fetch('GOOGLE_API_KEY', nil) } - config.filter_sensitive_data('') { ENV.fetch('GOOGLE_API_SPREADSHEET_ID', nil) } end -# make sure all vars required for testing are present -require 'sheet_zoukas/utils' -REQUIRED_VARS_TEST = (SheetZoukas::REQUIRED_VARS + ['GOOGLE_API_SPREADSHEET_ID']).freeze -exit 1 unless SheetZoukas::Utils.vars_present?(REQUIRED_VARS_TEST, 'required for tests to run') +# initialize required environment variables +ENV.store('GOOGLE_ACCOUNT_TYPE', 'service_account') +ENV.store('GOOGLE_API_KEY', 'fake_google_api_key') +ENV.store('GOOGLE_CLIENT_EMAIL', 'sheet@zoukas.zoukas') +ENV.store('GOOGLE_CLIENT_ID', 'fake_google_client_id') +ENV.store('GOOGLE_PRIVATE_KEY', "----BEGIN PRIVATE KEY-----\nfake_google_private_key==\n-----END PRIVATE KEY-----\n") + +module SheetZoukas + # for testing don't try to authenticate with Google + # will need to remove when recording new VCR cassettes + class GoogleSheets + Authorizer = Struct.new(:scope) + + private + + def init_authorizer(scope) + scopes = scope.is_a?(Array) ? scope : [scope] + @authorizer = Authorizer.new(scopes) + end + end +end