deepl-rb 3.7.0 → 3.8.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +3 -1
  3. data/CHANGELOG.md +17 -1
  4. data/Gemfile +0 -2
  5. data/README.md +12 -12
  6. data/VERSION +1 -1
  7. data/deepl-rb.gemspec +27 -22
  8. data/lib/deepl/requests/rephrase.rb +3 -2
  9. data/lib/deepl/requests/translate.rb +2 -2
  10. data/lib/deepl.rb +7 -4
  11. data/lib/version.rb +1 -1
  12. data/spec/api/deepl_spec.rb +134 -332
  13. data/spec/integration_tests/document_api_spec.rb +4 -18
  14. data/spec/integration_tests/document_error_paths_spec.rb +33 -0
  15. data/spec/integration_tests/glossary_api_spec.rb +114 -0
  16. data/spec/integration_tests/glossary_error_paths_spec.rb +107 -0
  17. data/spec/integration_tests/languages_api_spec.rb +54 -0
  18. data/spec/integration_tests/languages_error_paths_spec.rb +25 -0
  19. data/spec/integration_tests/rephrase_api_spec.rb +90 -0
  20. data/spec/integration_tests/rephrase_error_paths_spec.rb +53 -0
  21. data/spec/integration_tests/smoke_test_spec.rb +24 -0
  22. data/spec/integration_tests/style_rule_api_spec.rb +1 -19
  23. data/spec/integration_tests/style_rule_error_paths_spec.rb +45 -0
  24. data/spec/integration_tests/translate_api_spec.rb +98 -0
  25. data/spec/integration_tests/translate_error_paths_spec.rb +48 -0
  26. data/spec/integration_tests/translation_memory_api_spec.rb +1 -17
  27. data/spec/integration_tests/translation_memory_error_paths_spec.rb +19 -0
  28. data/spec/integration_tests/usage_api_spec.rb +29 -0
  29. data/spec/integration_tests/usage_error_paths_spec.rb +18 -0
  30. data/spec/requests/glossary/create_spec.rb +0 -21
  31. data/spec/requests/glossary/destroy_spec.rb +0 -39
  32. data/spec/requests/glossary/entries_spec.rb +0 -35
  33. data/spec/requests/glossary/find_spec.rb +0 -40
  34. data/spec/requests/glossary/language_pairs_spec.rb +0 -13
  35. data/spec/requests/glossary/list_spec.rb +0 -27
  36. data/spec/requests/languages_spec.rb +0 -41
  37. data/spec/requests/rephrase_spec.rb +13 -139
  38. data/spec/requests/style_rule/create_custom_instruction_spec.rb +0 -24
  39. data/spec/requests/style_rule/create_spec.rb +0 -16
  40. data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +0 -26
  41. data/spec/requests/style_rule/destroy_spec.rb +0 -27
  42. data/spec/requests/style_rule/find_custom_instruction_spec.rb +0 -27
  43. data/spec/requests/style_rule/find_spec.rb +0 -28
  44. data/spec/requests/style_rule/list_spec.rb +0 -31
  45. data/spec/requests/style_rule/update_configured_rules_spec.rb +0 -21
  46. data/spec/requests/style_rule/update_custom_instruction_spec.rb +0 -26
  47. data/spec/requests/style_rule/update_spec.rb +0 -19
  48. data/spec/requests/translate_spec.rb +8 -217
  49. data/spec/requests/translation_memory/list_spec.rb +0 -34
  50. data/spec/requests/usage_spec.rb +0 -16
  51. data/spec/resources/custom_instruction_spec.rb +32 -0
  52. data/spec/resources/style_rule_spec.rb +68 -0
  53. data/spec/spec_helper.rb +15 -45
  54. data/spec/support/live_mock_server.rb +12 -0
  55. data/spec/support/managed_glossary.rb +17 -0
  56. data/spec/support/managed_style_rule.rb +17 -0
  57. data/spec/support/managed_translation_memory.rb +7 -0
  58. metadata +23 -21
  59. data/spec/fixtures/vcr_cassettes/deepl_document.yml +0 -95
  60. data/spec/fixtures/vcr_cassettes/deepl_document_download.yml +0 -1214
  61. data/spec/fixtures/vcr_cassettes/deepl_glossaries.yml +0 -1163
  62. data/spec/fixtures/vcr_cassettes/deepl_languages.yml +0 -54
  63. data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +0 -87
  64. data/spec/fixtures/vcr_cassettes/deepl_translate.yml +0 -358
  65. data/spec/fixtures/vcr_cassettes/deepl_usage.yml +0 -129
  66. data/spec/fixtures/vcr_cassettes/glossaries.yml +0 -1702
  67. data/spec/fixtures/vcr_cassettes/languages.yml +0 -229
  68. data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +0 -401
  69. data/spec/fixtures/vcr_cassettes/style_rules.yml +0 -92
  70. data/spec/fixtures/vcr_cassettes/style_rules_crud.yml +0 -926
  71. data/spec/fixtures/vcr_cassettes/translate_texts.yml +0 -10630
  72. data/spec/fixtures/vcr_cassettes/translation_memories.yml +0 -74
  73. data/spec/fixtures/vcr_cassettes/usage.yml +0 -171
@@ -0,0 +1,114 @@
1
+ # Copyright 2026 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
+ require 'spec_helper'
7
+
8
+ describe DeepL::GlossaryApi do
9
+ let(:default_glossary_args) do
10
+ {
11
+ name: 'Integration Test Glossary',
12
+ source_lang: 'en',
13
+ target_lang: 'de',
14
+ entries: [%w[Hello Hallo], %w[World Welt]]
15
+ }
16
+ end
17
+
18
+ describe 'happy path lifecycle' do
19
+ it 'creates, lists, and finds a glossary' do # rubocop:disable RSpec/ExampleLength,RSpec/MultipleExpectations
20
+ with_managed_glossary(**default_glossary_args) do |glossary|
21
+ expect(glossary).to be_a(DeepL::Resources::Glossary)
22
+ expect(glossary.id).to be_a(String)
23
+ expect(glossary.name).to eq(default_glossary_args[:name])
24
+ expect(glossary.entry_count).to eq(2)
25
+ expect(glossary.source_lang).to eq(default_glossary_args[:source_lang])
26
+ expect(glossary.target_lang).to eq(default_glossary_args[:target_lang])
27
+ expect(glossary.ready).to be(true).or be(false)
28
+ expect { Time.iso8601(glossary.creation_time) }.not_to raise_error
29
+
30
+ listed = DeepL.glossaries.list
31
+ expect(listed).to be_an(Array)
32
+ expect(listed.map(&:id)).to include(glossary.id)
33
+
34
+ found = DeepL.glossaries.find(glossary.id)
35
+ expect(found).to be_a(DeepL::Resources::Glossary)
36
+ expect(found.id).to eq(glossary.id)
37
+ expect(found.name).to eq(glossary.name)
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '#language_pairs' do
43
+ it 'returns a non-empty list of supported source/target combos' do
44
+ pairs = DeepL.glossaries.language_pairs
45
+ expect(pairs).to be_an(Array)
46
+ expect(pairs).not_to be_empty
47
+ expect(pairs.first).to be_a(DeepL::Resources::LanguagePair)
48
+ expect(pairs.first.source_lang).to be_a(String)
49
+ expect(pairs.first.target_lang).to be_a(String)
50
+ end
51
+ end
52
+
53
+ describe 'error handling' do
54
+ it 'raises when creating a glossary with an invalid source language code' do
55
+ expect do
56
+ DeepL.glossaries.create(
57
+ 'Invalid Lang Glossary',
58
+ 'zz',
59
+ 'de',
60
+ [%w[Hello Hallo]]
61
+ )
62
+ end.to raise_error(DeepL::Exceptions::RequestError)
63
+ end
64
+ end
65
+
66
+ describe '#destroy' do
67
+ it 'returns the id of the destroyed glossary' do
68
+ glossary = DeepL.glossaries.create(
69
+ default_glossary_args[:name],
70
+ default_glossary_args[:source_lang],
71
+ default_glossary_args[:target_lang],
72
+ default_glossary_args[:entries]
73
+ )
74
+
75
+ expect(DeepL.glossaries.destroy(glossary.id)).to eq(glossary.id)
76
+ end
77
+ end
78
+
79
+ describe '#entries' do
80
+ it 'returns the glossary entries as an array of [source, target] pairs' do # rubocop:disable RSpec/ExampleLength
81
+ with_managed_glossary(**default_glossary_args) do |glossary|
82
+ entries = DeepL.glossaries.entries(glossary.id)
83
+
84
+ expect(entries).to be_an(Array)
85
+ expect(entries).to all(be_an(Array))
86
+ expect(entries.size).to eq(default_glossary_args[:entries].size)
87
+ entries.each do |entry|
88
+ expect(entry.size).to eq(2)
89
+ expect(entry.first).to be_a(String)
90
+ expect(entry.last).to be_a(String)
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ describe 'edge cases' do
97
+ it 'accepts Unicode keys and values in glossary entries' do # rubocop:disable RSpec/ExampleLength
98
+ unicode_entries = [
99
+ ['Schöne Grüße', 'Best regards'],
100
+ ['🚀 rocket', '🚀 fusée'],
101
+ ['日本語', 'Japanese'],
102
+ ['café', 'kafē']
103
+ ]
104
+ args = default_glossary_args.merge(
105
+ name: 'Unicode Glossary',
106
+ entries: unicode_entries
107
+ )
108
+
109
+ with_managed_glossary(**args) do |glossary|
110
+ expect(glossary.entry_count).to eq(unicode_entries.length)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,107 @@
1
+ # Copyright 2026 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
+ require 'spec_helper'
7
+
8
+ describe DeepL::GlossaryApi do # rubocop:disable RSpec/SpecFilePathFormat
9
+ include_context 'with a live mock server'
10
+
11
+ let(:nonexistent_glossary_id) { '00000000-0000-0000-0000-000000000000' }
12
+
13
+ let(:valid_glossary_args) do
14
+ {
15
+ name: 'Error Path Test Glossary',
16
+ source_lang: 'en',
17
+ target_lang: 'de',
18
+ entries: [%w[Hello Hallo]]
19
+ }
20
+ end
21
+
22
+ describe 'AuthorizationFailed (401/403)' do
23
+ let(:unauthorized_glossaries) { described_class.new(unauthorized_api) }
24
+
25
+ it 'is raised by #list when the auth key is invalid' do
26
+ expect { unauthorized_glossaries.list }
27
+ .to raise_error(DeepL::Exceptions::AuthorizationFailed)
28
+ end
29
+
30
+ it 'is raised by #language_pairs when the auth key is invalid' do
31
+ expect { unauthorized_glossaries.language_pairs }
32
+ .to raise_error(DeepL::Exceptions::AuthorizationFailed)
33
+ end
34
+
35
+ it 'is raised by #create when the auth key is invalid' do
36
+ expect do
37
+ unauthorized_glossaries.create(
38
+ valid_glossary_args[:name],
39
+ valid_glossary_args[:source_lang],
40
+ valid_glossary_args[:target_lang],
41
+ valid_glossary_args[:entries]
42
+ )
43
+ end.to raise_error(DeepL::Exceptions::AuthorizationFailed)
44
+ end
45
+
46
+ it 'is raised by #find when the auth key is invalid' do
47
+ expect { unauthorized_glossaries.find(nonexistent_glossary_id) }
48
+ .to raise_error(DeepL::Exceptions::AuthorizationFailed)
49
+ end
50
+
51
+ it 'is raised by #destroy when the auth key is invalid' do
52
+ expect { unauthorized_glossaries.destroy(nonexistent_glossary_id) }
53
+ .to raise_error(DeepL::Exceptions::AuthorizationFailed)
54
+ end
55
+ end
56
+
57
+ describe 'NotFound (404)' do
58
+ it 'is raised by #find for a well-formed but unknown glossary id' do
59
+ expect { DeepL.glossaries.find(nonexistent_glossary_id) }
60
+ .to raise_error(DeepL::Exceptions::NotFound)
61
+ end
62
+
63
+ it 'is raised by #destroy for a well-formed but unknown glossary id' do
64
+ expect { DeepL.glossaries.destroy(nonexistent_glossary_id) }
65
+ .to raise_error(DeepL::Exceptions::NotFound)
66
+ end
67
+ end
68
+
69
+ describe 'BadRequest (400) from #create' do
70
+ it 'is raised for an invalid source language code' do
71
+ expect do
72
+ DeepL.glossaries.create(
73
+ valid_glossary_args[:name],
74
+ 'zz',
75
+ valid_glossary_args[:target_lang],
76
+ valid_glossary_args[:entries]
77
+ )
78
+ end.to raise_error(DeepL::Exceptions::BadRequest)
79
+ end
80
+ end
81
+
82
+ describe 'BadRequest (400) from a malformed glossary id' do
83
+ let(:malformed_uuid) { 'invalid-uuid' }
84
+
85
+ it 'is raised by #find for a malformed glossary id' do
86
+ expect { DeepL.glossaries.find(malformed_uuid) }
87
+ .to raise_error(DeepL::Exceptions::BadRequest)
88
+ end
89
+
90
+ it 'is raised by #destroy for a malformed glossary id' do
91
+ expect { DeepL.glossaries.destroy(malformed_uuid) }
92
+ .to raise_error(DeepL::Exceptions::BadRequest)
93
+ end
94
+
95
+ it 'is raised by #entries for a malformed glossary id' do
96
+ expect { DeepL.glossaries.entries(malformed_uuid) }
97
+ .to raise_error(DeepL::Exceptions::BadRequest)
98
+ end
99
+ end
100
+
101
+ describe 'NotFound (404) from #entries' do
102
+ it 'is raised for a well-formed but unknown glossary id' do
103
+ expect { DeepL.glossaries.entries(nonexistent_glossary_id) }
104
+ .to raise_error(DeepL::Exceptions::NotFound)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,54 @@
1
+ # Copyright 2026 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
+ require 'spec_helper'
7
+
8
+ # Covers GET /v2/languages; the SDK has no v3 languages endpoint.
9
+ describe 'DeepL.languages' do # rubocop:disable RSpec/DescribeClass
10
+ describe 'source languages' do
11
+ it 'returns a non-empty array of Language resources with common codes' do
12
+ languages = DeepL.languages
13
+
14
+ expect(languages).to be_an(Array)
15
+ expect(languages).not_to be_empty
16
+ expect(languages).to all(be_a(DeepL::Resources::Language))
17
+ expect(languages.first).to respond_to(:code, :name)
18
+
19
+ codes = languages.map(&:code)
20
+ expect(codes).to include('EN', 'DE')
21
+ end
22
+ end
23
+
24
+ describe 'target languages' do
25
+ it 'returns target entries that include both formality-supporting and non-supporting members' do
26
+ languages = DeepL.languages(type: :target)
27
+
28
+ expect(languages).to be_an(Array)
29
+ expect(languages).not_to be_empty
30
+ expect(languages).to all(be_a(DeepL::Resources::Language))
31
+
32
+ formalities = languages.map(&:supports_formality?)
33
+ expect(formalities).to include(true)
34
+ expect(formalities).to include(false)
35
+ end
36
+
37
+ it 'raises NotSupported when supports_formality? is called on a source-language entry' do
38
+ source_languages = DeepL.languages
39
+
40
+ expect { source_languages.first.supports_formality? }
41
+ .to raise_error(DeepL::Exceptions::NotSupported)
42
+ end
43
+ end
44
+
45
+ describe 'source vs target counts' do
46
+ it 'returns sensible (>20) counts for both source and target listings' do
47
+ source_languages = DeepL.languages
48
+ target_languages = DeepL.languages(type: :target)
49
+
50
+ expect(source_languages.size).to be > 20
51
+ expect(target_languages.size).to be > 20
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ # Copyright 2026 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
+ require 'spec_helper'
7
+
8
+ describe 'DeepL.languages error paths' do # rubocop:disable RSpec/DescribeClass
9
+ include_context 'with a live mock server'
10
+
11
+ describe 'authorization failures' do
12
+ it 'raises AuthorizationFailed when the auth key is invalid' do
13
+ request = DeepL::Requests::Languages.new(unauthorized_api)
14
+
15
+ expect { request.request }.to raise_error(DeepL::Exceptions::AuthorizationFailed)
16
+ end
17
+ end
18
+
19
+ describe 'bad request errors' do
20
+ it 'raises BadRequest for an unsupported languages type' do
21
+ expect { DeepL.languages(type: :invalid) }
22
+ .to raise_error(DeepL::Exceptions::BadRequest)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,90 @@
1
+ # Copyright 2026 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
+ require 'spec_helper'
7
+
8
+ describe 'DeepL.rephrase' do # rubocop:disable RSpec/DescribeClass
9
+ describe 'happy path' do
10
+ it 'rephrases an English sentence into English and returns a Text resource' do
11
+ result = DeepL.rephrase('As Gregor Samsa awoke one morning he found himself transformed.',
12
+ 'en')
13
+
14
+ expect(result).to be_a(DeepL::Resources::Text)
15
+ expect(result.text).to be_a(String)
16
+ expect(result.text).not_to be_empty
17
+ end
18
+ end
19
+
20
+ describe 'error path' do
21
+ it 'raises a bad request error for an invalid writing_style value' do
22
+ expect do
23
+ DeepL.rephrase('As Gregor Samsa awoke one morning he found himself transformed.',
24
+ 'en', 'totally_invalid_style')
25
+ end.to raise_error(DeepL::Exceptions::BadRequest)
26
+ end
27
+ end
28
+
29
+ describe 'edge case' do
30
+ it 'rephrases with a writing_style option applied' do
31
+ result = DeepL.rephrase('As Gregor Samsa awoke one morning he found himself transformed.',
32
+ 'en', 'business')
33
+
34
+ expect(result).to be_a(DeepL::Resources::Text)
35
+ expect(result.text).to be_a(String)
36
+ expect(result.text).not_to be_empty
37
+ end
38
+
39
+ it 'rephrases with a tone option applied' do
40
+ result = DeepL.rephrase('As Gregor Samsa awoke one morning he found himself transformed.',
41
+ 'en', nil, 'friendly')
42
+
43
+ expect(result).to be_a(DeepL::Resources::Text)
44
+ expect(result.text).to be_a(String)
45
+ expect(result.text).not_to be_empty
46
+ end
47
+
48
+ it 'rephrases an array of texts with a tone applied' do
49
+ texts = [
50
+ 'As Gregor Samsa awoke one morning he found himself transformed.',
51
+ 'He lay on his armour-like back, and if he lifted his head a little.'
52
+ ]
53
+
54
+ results = DeepL.rephrase(texts, 'en', nil, 'friendly')
55
+
56
+ expect(results).to be_an(Array)
57
+ expect(results.size).to eq(2)
58
+ expect(results).to all(be_a(DeepL::Resources::Text))
59
+ expect(results.map(&:text)).to all(be_a(String).and(satisfy { |t| !t.empty? }))
60
+ end
61
+
62
+ it 'rephrases an array of texts with a writing_style applied' do
63
+ texts = [
64
+ 'As Gregor Samsa awoke one morning he found himself transformed.',
65
+ 'He lay on his armour-like back, and if he lifted his head a little.'
66
+ ]
67
+
68
+ results = DeepL.rephrase(texts, 'en', 'business')
69
+
70
+ expect(results).to be_an(Array)
71
+ expect(results.size).to eq(2)
72
+ expect(results).to all(be_a(DeepL::Resources::Text))
73
+ expect(results.map(&:text)).to all(be_a(String).and(satisfy { |t| !t.empty? }))
74
+ end
75
+
76
+ it 'rephrases an array of texts and returns an array of Text resources' do
77
+ texts = [
78
+ 'As Gregor Samsa awoke one morning he found himself transformed.',
79
+ 'He lay on his armour-like back, and if he lifted his head a little.'
80
+ ]
81
+
82
+ results = DeepL.rephrase(texts, 'en')
83
+
84
+ expect(results).to be_an(Array)
85
+ expect(results.size).to eq(2)
86
+ expect(results).to all(be_a(DeepL::Resources::Text))
87
+ expect(results.map(&:text)).to all(be_a(String).and(satisfy { |t| !t.empty? }))
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,53 @@
1
+ # Copyright 2026 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
+ require 'securerandom'
7
+ require 'spec_helper'
8
+
9
+ describe 'DeepL.rephrase error paths' do # rubocop:disable RSpec/DescribeClass
10
+ include IntegrationTestUtils
11
+
12
+ include_context 'with a live mock server'
13
+
14
+ let(:sample_text) { 'As Gregor Samsa awoke one morning he found himself transformed.' }
15
+
16
+ def rephrase_with_fresh_user(headers)
17
+ fresh_auth_key = "rephrase-err-#{SecureRandom.uuid}"
18
+ config = DeepL::Configuration.new(auth_key: fresh_auth_key)
19
+ config.max_network_retries = 0
20
+ api = DeepL::API.new(config)
21
+ DeepL::Requests::Rephrase.new(api, sample_text, 'en', nil, nil, {}, headers).request
22
+ end
23
+
24
+ describe 'authorization failures' do
25
+ it 'raises AuthorizationFailed when called with an invalid auth_key' do
26
+ request = DeepL::Requests::Rephrase.new(unauthorized_api, sample_text, 'en')
27
+
28
+ expect { request.request }.to raise_error(DeepL::Exceptions::AuthorizationFailed)
29
+ end
30
+ end
31
+
32
+ describe 'bad request errors' do
33
+ it 'raises BadRequest for an unsupported target_lang code' do
34
+ expect do
35
+ DeepL.rephrase(sample_text, 'zzz')
36
+ end.to raise_error(DeepL::Exceptions::BadRequest)
37
+ end
38
+
39
+ it 'raises BadRequest when both writing_style and tone are provided' do
40
+ expect do
41
+ DeepL.rephrase(sample_text, 'en', 'business', 'friendly')
42
+ end.to raise_error(DeepL::Exceptions::BadRequest)
43
+ end
44
+ end
45
+
46
+ describe 'rate limiting' do
47
+ it 'raises LimitExceeded when the server responds with 429', :mock_server_only do
48
+ expect do
49
+ rephrase_with_fresh_user(respond_with_429_header(1))
50
+ end.to raise_error(DeepL::Exceptions::LimitExceeded)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright 2025 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
+ require 'spec_helper'
7
+
8
+ describe 'SDK smoke test', :integration do # rubocop:disable RSpec/DescribeClass
9
+ it 'exercises translate, glossaries.list, and usage end-to-end' do # rubocop:disable RSpec/ExampleLength
10
+ source_text = 'Hello, world!'
11
+ result = DeepL.translate(source_text, 'EN', 'DE')
12
+ expect(result).to be_a(DeepL::Resources::Text)
13
+ expect(result.text).to be_a(String)
14
+ expect(result.text).not_to be_empty
15
+ expect(result.text).not_to eq(source_text)
16
+
17
+ glossaries = DeepL.glossaries.list
18
+ expect(glossaries).to be_an(Array)
19
+
20
+ usage = DeepL.usage
21
+ expect(usage).to be_a(DeepL::Resources::Usage)
22
+ expect(usage).to respond_to(:character_count, :character_limit)
23
+ end
24
+ end
@@ -5,21 +5,9 @@
5
5
 
6
6
  require 'spec_helper'
7
7
 
8
- describe DeepL::StyleRuleApi do
9
- before do
10
- VCR.turn_off!
11
- WebMock.allow_net_connect!
12
- end
13
-
14
- after do
15
- VCR.turn_on!
16
- WebMock.disable_net_connect!
17
- end
18
-
8
+ describe DeepL::StyleRuleApi, :mock_server_only do
19
9
  describe '#translate_with_style_rules' do
20
10
  it 'when performing a request with style_id' do
21
- skip 'Only runs on mock server' if real_server?
22
-
23
11
  source_lang = 'DE'
24
12
  target_lang = 'EN-US'
25
13
  text = 'Hallo, Welt!'
@@ -30,8 +18,6 @@ describe DeepL::StyleRuleApi do
30
18
  end
31
19
 
32
20
  it 'when performing a request with style_rule object' do
33
- skip 'Only runs on mock server' if real_server?
34
-
35
21
  source_lang = 'DE'
36
22
  target_lang = 'EN-US'
37
23
  text = 'Hallo, Welt!'
@@ -44,7 +30,6 @@ describe DeepL::StyleRuleApi do
44
30
 
45
31
  describe '#list_style_rules' do
46
32
  it 'when requesting a list of all style rules' do
47
- skip 'Only runs on mock server' if real_server?
48
33
  style_rules = DeepL.style_rules.list(page: 0, page_size: 10, detailed: true)
49
34
  expect(style_rules).to be_an(Array)
50
35
  expect(style_rules.length).to eq(1)
@@ -55,7 +40,6 @@ describe DeepL::StyleRuleApi do
55
40
  end
56
41
 
57
42
  it 'when requesting a list of all style rules without detailed' do
58
- skip 'Only runs on mock server' if real_server?
59
43
  style_rules = DeepL.style_rules.list
60
44
  expect(style_rules).to be_an(Array)
61
45
  expect(style_rules.length).to eq(1)
@@ -67,8 +51,6 @@ describe DeepL::StyleRuleApi do
67
51
 
68
52
  describe 'style rule management operations' do
69
53
  it 'when performing all management operations on style rules' do # rubocop:disable RSpec/ExampleLength,RSpec/MultipleExpectations
70
- skip 'Only runs on mock server' if real_server?
71
-
72
54
  # Create a style rule
73
55
  style_rule = DeepL.style_rules.create('Test Style Rule', 'en')
74
56
  expect(style_rule).to be_a(DeepL::Resources::StyleRule)
@@ -0,0 +1,45 @@
1
+ # Copyright 2026 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.md file.
4
+ # frozen_string_literal: true
5
+
6
+ require 'spec_helper'
7
+
8
+ describe DeepL::StyleRuleApi, :mock_server_only do # rubocop:disable RSpec/SpecFilePathFormat
9
+ include_context 'with a live mock server'
10
+
11
+ let(:missing_uuid) { '00000000-0000-0000-0000-000000000000' }
12
+
13
+ describe 'authorization failures' do
14
+ let(:unauthorized_style_rules) { described_class.new(unauthorized_api) }
15
+
16
+ it 'raises AuthorizationFailed on a read operation (list)' do
17
+ expect { unauthorized_style_rules.list }
18
+ .to raise_error(DeepL::Exceptions::AuthorizationFailed)
19
+ end
20
+
21
+ it 'raises AuthorizationFailed on a write operation (create)' do
22
+ expect { unauthorized_style_rules.create('Auth Failure Test', 'en') }
23
+ .to raise_error(DeepL::Exceptions::AuthorizationFailed)
24
+ end
25
+ end
26
+
27
+ describe 'not-found failures' do
28
+ it 'raises NotFound when #find is called with a missing UUID' do
29
+ expect { DeepL.style_rules.find(missing_uuid) }
30
+ .to raise_error(DeepL::Exceptions::NotFound)
31
+ end
32
+ end
33
+
34
+ describe 'bad-request failures' do
35
+ it 'raises BadRequest when create is called with a nil language' do
36
+ expect { DeepL.style_rules.create('Nil Lang', nil) }
37
+ .to raise_error(DeepL::Exceptions::BadRequest)
38
+ end
39
+
40
+ it 'raises an error when #destroy is called with a malformed UUID' do
41
+ expect { DeepL.style_rules.destroy('invalid-uuid') }
42
+ .to raise_error(DeepL::Exceptions::Error)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,98 @@
1
+ # Copyright 2026 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
+ require 'spec_helper'
7
+
8
+ describe 'DeepL.translate' do # rubocop:disable RSpec/DescribeClass
9
+ include_context 'with a live mock server'
10
+
11
+ let(:text) { 'proton beam' }
12
+
13
+ describe 'happy path' do
14
+ it 'translates a single string into a Text resource' do
15
+ result = DeepL.translate(text, 'EN', 'DE')
16
+
17
+ expect(result).to be_a(DeepL::Resources::Text)
18
+ expect(result.text).to be_a(String)
19
+ expect(result.text).not_to be_empty
20
+ end
21
+
22
+ it 'translates an array of strings into an array of Text resources' do
23
+ results = DeepL.translate(%w[Sample Word], 'EN', 'ES')
24
+
25
+ expect(results).to be_an(Array)
26
+ expect(results.size).to eq(2)
27
+ expect(results).to all(be_a(DeepL::Resources::Text))
28
+ end
29
+ end
30
+
31
+ describe 'options' do
32
+ it 'accepts a formality setting' do
33
+ result = DeepL.translate('How are you?', 'EN', 'DE', { formality: 'more' })
34
+
35
+ expect(result).to be_a(DeepL::Resources::Text)
36
+ end
37
+
38
+ it 'translates with XML tag handling and ignored tags' do
39
+ result = DeepL.translate('<p>Sample <code>x</code></p>', 'EN', 'ES',
40
+ { tag_handling: 'xml', ignore_tags: %w[code] })
41
+
42
+ expect(result.text).to be_a(String)
43
+ expect(result.text).not_to be_empty
44
+ end
45
+
46
+ it 'accepts a context option' do
47
+ result = DeepL.translate('That is hot!', 'EN', 'ES', { context: 'about the weather' })
48
+
49
+ expect(result).to be_a(DeepL::Resources::Text)
50
+ end
51
+
52
+ it 'accepts an empty context option' do
53
+ result = DeepL.translate('That is hot!', 'EN', 'ES', { context: '' })
54
+
55
+ expect(result).to be_a(DeepL::Resources::Text)
56
+ end
57
+
58
+ it 'translates an HTML document with split_sentences, outline_detection and splitting_tags' do
59
+ document = '<document><meta><title>Title</title></meta>' \
60
+ '<content><par>First sentence. Second one.</par></content></document>'
61
+ result = DeepL.translate(document, 'EN', 'ES',
62
+ { tag_handling: 'xml', split_sentences: 'nonewlines',
63
+ outline_detection: false, splitting_tags: %w[title par] })
64
+
65
+ expect(result).to be_a(DeepL::Resources::Text)
66
+ expect(result.text).not_to be_empty
67
+ end
68
+
69
+ it 'translates using a glossary' do
70
+ with_managed_glossary(name: 'Translate Glossary', source_lang: 'en', target_lang: 'de',
71
+ entries: [%w[Hello Hallo]]) do |glossary|
72
+ result = DeepL.translate('Hello', 'EN', 'DE', { glossary_id: glossary.id })
73
+
74
+ expect(result).to be_a(DeepL::Resources::Text)
75
+ expect(result.text).not_to be_empty
76
+ end
77
+ end
78
+
79
+ %w[quality_optimized latency_optimized prefer_quality_optimized].each do |model_type|
80
+ it "returns model_type_used for #{model_type}" do
81
+ result = DeepL.translate(text, 'EN', 'DE', { model_type: model_type })
82
+
83
+ expect(result).to be_a(DeepL::Resources::Text)
84
+ expect(result.model_type_used).not_to be_nil
85
+ end
86
+ end
87
+
88
+ %w[v1 v2].each do |version|
89
+ it "translates with tag_handling_version #{version}" do
90
+ result = DeepL.translate('<p>Hello world</p>', 'EN', 'DE',
91
+ { tag_handling: 'html', tag_handling_version: version })
92
+
93
+ expect(result).to be_a(DeepL::Resources::Text)
94
+ expect(result.text).not_to be_empty
95
+ end
96
+ end
97
+ end
98
+ end