deepl-rb 2.5.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/add_issues_to_kanban.yml +16 -0
  3. data/.gitlab-ci.yml +135 -0
  4. data/.rubocop.yml +27 -0
  5. data/CHANGELOG.md +34 -0
  6. data/CODE_OF_CONDUCT.md +132 -0
  7. data/CONTRIBUTING.md +37 -0
  8. data/Gemfile +3 -1
  9. data/LICENSE.md +1 -0
  10. data/README.md +115 -5
  11. data/Rakefile +7 -5
  12. data/SECURITY.md +58 -0
  13. data/VERSION +1 -1
  14. data/deepl-rb.gemspec +36 -20
  15. data/lib/deepl/api.rb +11 -1
  16. data/lib/deepl/configuration.rb +34 -3
  17. data/lib/deepl/document_api.rb +120 -0
  18. data/lib/deepl/exceptions/authorization_failed.rb +3 -0
  19. data/lib/deepl/exceptions/bad_request.rb +3 -0
  20. data/lib/deepl/exceptions/document_translation_error.rb +15 -0
  21. data/lib/deepl/exceptions/error.rb +6 -0
  22. data/lib/deepl/exceptions/limit_exceeded.rb +7 -0
  23. data/lib/deepl/exceptions/not_found.rb +3 -0
  24. data/lib/deepl/exceptions/not_supported.rb +3 -0
  25. data/lib/deepl/exceptions/quota_exceeded.rb +3 -0
  26. data/lib/deepl/exceptions/request_entity_too_large.rb +4 -1
  27. data/lib/deepl/exceptions/request_error.rb +4 -2
  28. data/lib/deepl/exceptions/server_error.rb +18 -0
  29. data/lib/deepl/glossary_api.rb +3 -0
  30. data/lib/deepl/requests/base.rb +89 -34
  31. data/lib/deepl/requests/document/download.rb +44 -0
  32. data/lib/deepl/requests/document/get_status.rb +44 -0
  33. data/lib/deepl/requests/document/upload.rb +64 -0
  34. data/lib/deepl/requests/glossary/create.rb +15 -1
  35. data/lib/deepl/requests/glossary/destroy.rb +8 -1
  36. data/lib/deepl/requests/glossary/entries.rb +8 -1
  37. data/lib/deepl/requests/glossary/find.rb +8 -1
  38. data/lib/deepl/requests/glossary/language_pairs.rb +9 -2
  39. data/lib/deepl/requests/glossary/list.rb +9 -2
  40. data/lib/deepl/requests/languages.rb +9 -2
  41. data/lib/deepl/requests/translate.rb +33 -11
  42. data/lib/deepl/requests/usage.rb +9 -2
  43. data/lib/deepl/resources/base.rb +3 -0
  44. data/lib/deepl/resources/document_handle.rb +57 -0
  45. data/lib/deepl/resources/document_translation_status.rb +54 -0
  46. data/lib/deepl/resources/glossary.rb +3 -0
  47. data/lib/deepl/resources/language.rb +3 -0
  48. data/lib/deepl/resources/language_pair.rb +3 -0
  49. data/lib/deepl/resources/text.rb +3 -0
  50. data/lib/deepl/resources/usage.rb +3 -0
  51. data/lib/deepl/utils/backoff_timer.rb +46 -0
  52. data/lib/deepl/utils/exception_builder.rb +18 -13
  53. data/lib/deepl.rb +47 -0
  54. data/lib/http_client_options.rb +22 -0
  55. data/license_checker.sh +8 -0
  56. data/spec/api/api_spec.rb +8 -4
  57. data/spec/api/configuration_spec.rb +92 -18
  58. data/spec/api/deepl_spec.rb +225 -86
  59. data/spec/fixtures/vcr_cassettes/deepl_document.yml +95 -0
  60. data/spec/fixtures/vcr_cassettes/deepl_document_download.yml +1214 -0
  61. data/spec/fixtures/vcr_cassettes/deepl_glossaries.yml +812 -23
  62. data/spec/fixtures/vcr_cassettes/deepl_languages.yml +28 -17
  63. data/spec/fixtures/vcr_cassettes/deepl_translate.yml +161 -53
  64. data/spec/fixtures/vcr_cassettes/deepl_usage.yml +93 -3
  65. data/spec/fixtures/vcr_cassettes/glossaries.yml +1237 -15
  66. data/spec/fixtures/vcr_cassettes/languages.yml +159 -44
  67. data/spec/fixtures/vcr_cassettes/translate_texts.yml +9742 -12
  68. data/spec/fixtures/vcr_cassettes/usage.yml +134 -2
  69. data/spec/integration_tests/document_api_spec.rb +143 -0
  70. data/spec/integration_tests/integration_test_utils.rb +170 -0
  71. data/spec/requests/glossary/create_spec.rb +23 -13
  72. data/spec/requests/glossary/destroy_spec.rb +33 -17
  73. data/spec/requests/glossary/entries_spec.rb +31 -17
  74. data/spec/requests/glossary/find_spec.rb +31 -17
  75. data/spec/requests/glossary/language_pairs_spec.rb +17 -7
  76. data/spec/requests/glossary/list_spec.rb +21 -11
  77. data/spec/requests/languages_spec.rb +31 -21
  78. data/spec/requests/translate_spec.rb +125 -131
  79. data/spec/requests/usage_spec.rb +17 -7
  80. data/spec/resources/glossary_spec.rb +15 -12
  81. data/spec/resources/language_pair_spec.rb +10 -7
  82. data/spec/resources/language_spec.rb +21 -18
  83. data/spec/resources/text_spec.rb +10 -7
  84. data/spec/resources/usage_spec.rb +16 -13
  85. data/spec/spec_helper.rb +63 -6
  86. metadata +32 -9
@@ -0,0 +1,54 @@
1
+ # Copyright 2024 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Resources
8
+ class DocumentTranslationStatus < Base
9
+ attr_reader :document_id, :status, :seconds_remaining, :billed_characters, :error_message
10
+
11
+ def initialize(response, *args)
12
+ super(*args)
13
+
14
+ @document_id = response['document_id']
15
+ @status = response['status']
16
+ @seconds_remaining = response['seconds_remaining']
17
+ @billed_characters = response['billed_characters']
18
+ @error_message = response['error_message']
19
+ end
20
+
21
+ def to_s
22
+ "DocumentTranslationStatus: ID: #{document_id} - Status: #{status} " \
23
+ "- Error message: #{error_message}"
24
+ end
25
+
26
+ ##
27
+ # Checks if the translation finished successfully
28
+ #
29
+ # @return [true] if so
30
+
31
+ def successful?
32
+ status == 'done'
33
+ end
34
+
35
+ ##
36
+ # Checks if there was an error during translation
37
+ #
38
+ # @return [true] if so
39
+
40
+ def error?
41
+ status == 'error'
42
+ end
43
+
44
+ ##
45
+ # Checks if the translation process terminated. Note that this could be due to an error as
46
+ # well, but means no further waiting is necessary.
47
+ #
48
+ # @return [true] if so
49
+ def finished?
50
+ successful? || error?
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  module DeepL
@@ -1,3 +1,6 @@
1
+ # Copyright 2021 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  module DeepL
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  module DeepL
@@ -1,3 +1,6 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  module DeepL
@@ -1,3 +1,6 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  module DeepL
@@ -0,0 +1,46 @@
1
+ # Copyright 2024 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ module Utils
8
+ class BackoffTimer
9
+ # Implements exponential-backoff strategy.
10
+ # This strategy is based on the GRPC Connection Backoff Protocol:
11
+ # https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
12
+
13
+ BACKOFF_INITIAL = 1.0
14
+ BACKOFF_MAX = 120.0
15
+ BACKOFF_JITTER = 0.23
16
+ BACKOFF_MULTIPLIER = 1.6
17
+
18
+ attr_reader :num_retries
19
+
20
+ def initialize(min_connection_timeout = 10.0)
21
+ @num_retries = 0
22
+ @backoff = BACKOFF_INITIAL
23
+ @deadline = Time.now.to_f + @backoff
24
+ @min_connection_timeout = min_connection_timeout
25
+ end
26
+
27
+ def current_request_timeout
28
+ [time_until_deadline, @min_connection_timeout].max
29
+ end
30
+
31
+ def time_until_deadline
32
+ [@deadline - Time.now.to_f, 0.0].max
33
+ end
34
+
35
+ def sleep_until_deadline
36
+ sleep(time_until_deadline)
37
+ # Apply multiplier to current backoff time
38
+ @backoff = [@backoff * BACKOFF_MULTIPLIER, BACKOFF_MAX].min
39
+ # Get deadline by applying jitter as a proportion of backoff:
40
+ # if jitter is 0.1, then multiply backoff by random value in [0.9, 1.1]
41
+ @deadline = Time.now.to_f + (@backoff * (1 + (BACKOFF_JITTER * rand(-1.0..1.0))))
42
+ @num_retries += 1
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,6 @@
1
+ # Copyright 2022 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  module DeepL
@@ -5,24 +8,26 @@ module DeepL
5
8
  class ExceptionBuilder
6
9
  attr_reader :request, :response
7
10
 
8
- ERROR_CODE_CLASS_MAP = {
9
- '400' => Exceptions::BadRequest,
10
- '401' => Exceptions::AuthorizationFailed,
11
- '403' => Exceptions::AuthorizationFailed,
12
- '404' => Exceptions::NotFound,
13
- '413' => Exceptions::RequestEntityTooLarge,
14
- '429' => Exceptions::LimitExceeded,
15
- '456' => Exceptions::QuotaExceeded
16
- }.freeze
11
+ def self.error_class_from_response_code(code) # rubocop:disable Metrics/CyclomaticComplexity
12
+ case code
13
+ when 400 then Exceptions::BadRequest
14
+ when 401, 403 then Exceptions::AuthorizationFailed
15
+ when 404 then Exceptions::NotFound
16
+ when 413 then Exceptions::RequestEntityTooLarge
17
+ when 429 then Exceptions::LimitExceeded
18
+ when 456 then Exceptions::QuotaExceeded
19
+ when 500..599 then Exceptions::ServerError
20
+ else Exceptions::RequestError
21
+ end
22
+ end
17
23
 
18
- def initialize(request, response)
19
- @request = request
24
+ def initialize(response)
20
25
  @response = response
21
26
  end
22
27
 
23
28
  def build
24
- error_class = ERROR_CODE_CLASS_MAP[response.code.to_s] || Exceptions::RequestError
25
- error_class.new(request, response)
29
+ error_class = self.class.error_class_from_response_code(response.code.to_i)
30
+ error_class.new(response)
26
31
  end
27
32
  end
28
33
  end
data/lib/deepl.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  # -- Dependencies
@@ -14,9 +17,14 @@ require 'deepl/exceptions/quota_exceeded'
14
17
  require 'deepl/exceptions/not_found'
15
18
  require 'deepl/exceptions/not_supported'
16
19
  require 'deepl/exceptions/request_entity_too_large'
20
+ require 'deepl/exceptions/document_translation_error'
21
+ require 'deepl/exceptions/server_error'
17
22
 
18
23
  # -- Requests
19
24
  require 'deepl/requests/base'
25
+ require 'deepl/requests/document/download'
26
+ require 'deepl/requests/document/get_status'
27
+ require 'deepl/requests/document/upload'
20
28
  require 'deepl/requests/glossary/create'
21
29
  require 'deepl/requests/glossary/destroy'
22
30
  require 'deepl/requests/glossary/entries'
@@ -29,6 +37,8 @@ require 'deepl/requests/usage'
29
37
 
30
38
  # -- Responses and resources
31
39
  require 'deepl/resources/base'
40
+ require 'deepl/resources/document_handle'
41
+ require 'deepl/resources/document_translation_status'
32
42
  require 'deepl/resources/glossary'
33
43
  require 'deepl/resources/language'
34
44
  require 'deepl/resources/language_pair'
@@ -37,10 +47,15 @@ require 'deepl/resources/usage'
37
47
 
38
48
  # -- Utils
39
49
  require 'deepl/utils/exception_builder'
50
+ require 'deepl/utils/backoff_timer'
51
+
52
+ # -- HTTP Utils
53
+ require 'http_client_options'
40
54
 
41
55
  # -- Other wrappers
42
56
  require 'deepl/api'
43
57
  require 'deepl/configuration'
58
+ require 'deepl/document_api'
44
59
  require 'deepl/glossary_api'
45
60
 
46
61
  # -- Gem interface
@@ -62,6 +77,11 @@ module DeepL
62
77
  Requests::Translate.new(api, text, source_lang, target_lang, options).request
63
78
  end
64
79
 
80
+ def document(options = {})
81
+ configure if @configuration.nil?
82
+ DocumentApi.new(api, options)
83
+ end
84
+
65
85
  def glossaries(options = {})
66
86
  configure if @configuration.nil?
67
87
  GlossaryApi.new(api, options)
@@ -72,6 +92,33 @@ module DeepL
72
92
  Requests::Usage.new(api, options).request
73
93
  end
74
94
 
95
+ def http_client
96
+ @http_client
97
+ end
98
+
99
+ def with_session(client_options = HTTPClientOptions.new()) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
100
+ raise ArgumentError 'This method requires a block to be passed in which contains the actual API calls, see README for example usage.' unless block_given? # rubocop:disable Layout/LineLength
101
+
102
+ has_proxy = client_options.proxy.key?('proxy_addr') and client_options.proxy.key?('proxy_port')
103
+ begin
104
+ uri = URI(configuration.host)
105
+ http = Net::HTTP.new(uri.host, uri.port, has_proxy ? client_options.proxy['proxy_addr'] : nil,
106
+ has_proxy ? client_options.proxy['proxy_port'] : nil)
107
+ http.use_ssl = client_options.enable_ssl_verification
108
+ http.ca_file = client_options.cert_path if client_options.cert_path
109
+ http.open_timeout = client_options.open_timeout unless client_options.open_timeout.nil?
110
+ http.read_timeout = client_options.read_timeout unless client_options.read_timeout.nil?
111
+ http.write_timeout = client_options.write_timeout unless client_options.write_timeout.nil?
112
+ http.ssl_timeout = client_options.ssl_timeout unless client_options.ssl_timeout.nil?
113
+ http.start
114
+ @http_client = http
115
+ api.update_http_client(http)
116
+ yield
117
+ ensure
118
+ http.finish
119
+ end
120
+ end
121
+
75
122
  # -- Configuration
76
123
 
77
124
  def configuration
@@ -0,0 +1,22 @@
1
+ # Copyright 2024 DeepL SE (https://www.deepl.com)
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE file.
4
+ # frozen_string_literal: true
5
+
6
+ module DeepL
7
+ class HTTPClientOptions
8
+ attr_reader :proxy, :send_platform_info, :enable_ssl_verification, :cert_path, :open_timeout,
9
+ :read_timeout, :write_timeout, :ssl_timeout
10
+
11
+ def initialize(proxy, cert_path, enable_ssl_verification: true, open_timeout: nil, # rubocop:disable Metrics/ParameterLists
12
+ read_timeout: nil, write_timeout: nil, ssl_timeout: nil)
13
+ @proxy = proxy
14
+ @enable_ssl_verification = enable_ssl_verification
15
+ @cert_path = cert_path
16
+ @open_timeout = open_timeout
17
+ @read_timeout = read_timeout
18
+ @write_timeout = write_timeout
19
+ @ssl_timeout = ssl_timeout
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Usage: ./license_checker.sh source_code_pattern
4
+ # Example: ./license_checker.sh '*.rb'
5
+ # This will search all .rb files, ignoring anything not tracked in your git tree
6
+
7
+ git ls-files -z $1 | xargs -0 -n 10 -I{} sh -c 'RES=$(head -n 3 "{}" | grep "Copyright 20[0-9][0-9] DeepL SE (https://www.deepl.com)\|Copyright 20[0-9][0-9] Daniel Herzog"); if [ ! "${RES}" ] ; then echo "Lacking copyright header in" "{}" ; fi'
8
+
data/spec/api/api_spec.rb CHANGED
@@ -1,15 +1,19 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
3
6
  require 'spec_helper'
4
7
 
5
8
  describe DeepL::API do
9
+ subject(:api) { described_class.new(configuration) }
10
+
6
11
  let(:configuration) { DeepL::Configuration.new }
7
- subject { DeepL::API.new(configuration) }
8
12
 
9
13
  describe '#initialize' do
10
- context 'When building an API object' do
11
- it 'should save the configuration' do
12
- expect(subject.configuration).to be(configuration)
14
+ context 'when building an API object' do
15
+ it 'saves the configuration' do
16
+ expect(api.configuration).to be(configuration)
13
17
  end
14
18
  end
15
19
  end
@@ -1,27 +1,101 @@
1
+ # Copyright 2018 Daniel Herzog
2
+ # Use of this source code is governed by an MIT
3
+ # license that can be found in the LICENSE.md file.
1
4
  # frozen_string_literal: true
2
5
 
6
+ require 'logger'
3
7
  require 'spec_helper'
4
8
 
5
9
  describe DeepL::Configuration do
10
+ subject(:config) { described_class.new(attributes) }
11
+
6
12
  let(:attributes) { {} }
7
- subject { DeepL::Configuration.new(attributes) }
13
+
14
+ around do |tests|
15
+ tmp_env = ENV.to_hash
16
+ ENV.clear
17
+ tests.call
18
+ ENV.replace(tmp_env)
19
+ end
8
20
 
9
21
  describe '#initialize' do
10
- context 'When using default configuration attributes' do
11
- it 'should use default attributes' do
12
- expect(subject.auth_key).to eq(ENV.fetch('DEEPL_AUTH_KEY', nil))
13
- expect(subject.host).to eq('https://api.deepl.com')
14
- expect(subject.version).to eq('v2')
22
+ context 'when using default configuration attributes' do
23
+ it 'uses default attributes' do
24
+ expect(config.auth_key).to eq(ENV.fetch('DEEPL_AUTH_KEY', nil))
25
+ expect(config.host).to eq('https://api.deepl.com')
26
+ expect(config.max_doc_status_queries).to be_nil
27
+ expect(config.version).to eq('v2')
28
+ end
29
+
30
+ it 'sends platform information' do
31
+ expect(config.user_agent).to include('deepl-ruby/')
32
+ expect(config.user_agent).to include('(')
33
+ expect(config.user_agent).to include(')')
34
+ expect(config.user_agent).to include(' ruby/')
35
+ end
36
+ end
37
+
38
+ context 'when using custom configuration attributes' do
39
+ let(:logger) { Logger.new($stdout) }
40
+ let(:attributes) { { auth_key: 'SAMPLE', host: 'https://api-free.deepl.com', logger: logger, max_doc_status_queries: 42, max_network_retries: 43, version: 'v1' } }
41
+
42
+ it 'uses custom attributes' do
43
+ expect(config.auth_key).to eq(attributes[:auth_key])
44
+ expect(config.host).to eq(attributes[:host])
45
+ expect(config.max_doc_status_queries).to eq(attributes[:max_doc_status_queries])
46
+ expect(config.max_network_retries).to eq(attributes[:max_network_retries])
47
+ expect(config.version).to eq(attributes[:version])
48
+ end
49
+ end
50
+
51
+ context 'when using a free key' do
52
+ let(:attributes) { { auth_key: '123e4567-e89b-12d3-a456-426614174000:fx' } }
53
+
54
+ it 'uses the free API URL' do
55
+ expect(config.host).to eq('https://api-free.deepl.com')
56
+ end
57
+ end
58
+
59
+ context 'when using a pro key' do
60
+ let(:attributes) { { auth_key: '123e4567-e89b-12d3-a456-426614174000' } }
61
+
62
+ it 'uses the pro API URL' do
63
+ expect(config.host).to eq('https://api.deepl.com')
64
+ end
65
+ end
66
+
67
+ context 'when opting out of sending platform info' do
68
+ subject(:config) { described_class.new(attributes, nil, nil, false) }
69
+
70
+ it 'does not send platform information' do
71
+ expect(config.user_agent).to include('deepl-ruby/')
72
+ expect(config.user_agent).not_to include('(')
73
+ expect(config.user_agent).not_to include(')')
74
+ expect(config.user_agent).not_to include(' ruby/')
75
+ end
76
+ end
77
+
78
+ context 'when registering an app and sending platform info' do
79
+ subject(:config) { described_class.new(attributes, 'MyTestApp', '0.1.3', true) }
80
+
81
+ it 'sends platform and app info' do
82
+ expect(config.user_agent).to include('deepl-ruby/')
83
+ expect(config.user_agent).to include('(')
84
+ expect(config.user_agent).to include(')')
85
+ expect(config.user_agent).to include(' ruby/')
86
+ expect(config.user_agent).to include('MyTestApp/0.1.3')
15
87
  end
16
88
  end
17
89
 
18
- context 'When using custom configuration attributes' do
19
- let(:attributes) { { auth_key: 'SAMPLE', host: 'https://api-free.deepl.com', version: 'v1' } }
90
+ context 'when registering an app and not sending platform info' do
91
+ subject(:config) { described_class.new(attributes, 'MyTestApp', '0.1.3', false) }
20
92
 
21
- it 'should use custom attributes' do
22
- expect(subject.auth_key).to eq(attributes[:auth_key])
23
- expect(subject.host).to eq(attributes[:host])
24
- expect(subject.version).to eq(attributes[:version])
93
+ it 'sends app, but not platform info' do
94
+ expect(config.user_agent).to include('deepl-ruby/')
95
+ expect(config.user_agent).not_to include('(')
96
+ expect(config.user_agent).not_to include(')')
97
+ expect(config.user_agent).not_to include(' ruby/')
98
+ expect(config.user_agent).to include('MyTestApp/0.1.3')
25
99
  end
26
100
  end
27
101
  end
@@ -29,19 +103,19 @@ describe DeepL::Configuration do
29
103
  describe '#validate!' do
30
104
  let(:auth_message) { 'auth_key not provided' }
31
105
 
32
- context 'When providing a valid auth key' do
106
+ context 'when providing a valid auth key' do
33
107
  let(:attributes) { { auth_key: '' } }
34
108
 
35
- it 'should raise an error' do
36
- expect { subject.validate! }.to raise_error(DeepL::Exceptions::Error, auth_message)
109
+ it 'raises an error' do
110
+ expect { config.validate! }.to raise_error(DeepL::Exceptions::Error, auth_message)
37
111
  end
38
112
  end
39
113
 
40
- context 'When providing an invalid auth key' do
114
+ context 'when providing an invalid auth key' do
41
115
  let(:attributes) { { auth_key: 'not-empty' } }
42
116
 
43
- it 'should not raise an error' do
44
- expect { subject.validate! }.not_to raise_error
117
+ it 'does not raise an error' do
118
+ expect { config.validate! }.not_to raise_error
45
119
  end
46
120
  end
47
121
  end