deepl-rb 2.5.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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 +3 -0
  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