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.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +3 -1
- data/CHANGELOG.md +17 -1
- data/Gemfile +0 -2
- data/README.md +12 -12
- data/VERSION +1 -1
- data/deepl-rb.gemspec +27 -22
- data/lib/deepl/requests/rephrase.rb +3 -2
- data/lib/deepl/requests/translate.rb +2 -2
- data/lib/deepl.rb +7 -4
- data/lib/version.rb +1 -1
- data/spec/api/deepl_spec.rb +134 -332
- data/spec/integration_tests/document_api_spec.rb +4 -18
- data/spec/integration_tests/document_error_paths_spec.rb +33 -0
- data/spec/integration_tests/glossary_api_spec.rb +114 -0
- data/spec/integration_tests/glossary_error_paths_spec.rb +107 -0
- data/spec/integration_tests/languages_api_spec.rb +54 -0
- data/spec/integration_tests/languages_error_paths_spec.rb +25 -0
- data/spec/integration_tests/rephrase_api_spec.rb +90 -0
- data/spec/integration_tests/rephrase_error_paths_spec.rb +53 -0
- data/spec/integration_tests/smoke_test_spec.rb +24 -0
- data/spec/integration_tests/style_rule_api_spec.rb +1 -19
- data/spec/integration_tests/style_rule_error_paths_spec.rb +45 -0
- data/spec/integration_tests/translate_api_spec.rb +98 -0
- data/spec/integration_tests/translate_error_paths_spec.rb +48 -0
- data/spec/integration_tests/translation_memory_api_spec.rb +1 -17
- data/spec/integration_tests/translation_memory_error_paths_spec.rb +19 -0
- data/spec/integration_tests/usage_api_spec.rb +29 -0
- data/spec/integration_tests/usage_error_paths_spec.rb +18 -0
- data/spec/requests/glossary/create_spec.rb +0 -21
- data/spec/requests/glossary/destroy_spec.rb +0 -39
- data/spec/requests/glossary/entries_spec.rb +0 -35
- data/spec/requests/glossary/find_spec.rb +0 -40
- data/spec/requests/glossary/language_pairs_spec.rb +0 -13
- data/spec/requests/glossary/list_spec.rb +0 -27
- data/spec/requests/languages_spec.rb +0 -41
- data/spec/requests/rephrase_spec.rb +13 -139
- data/spec/requests/style_rule/create_custom_instruction_spec.rb +0 -24
- data/spec/requests/style_rule/create_spec.rb +0 -16
- data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +0 -26
- data/spec/requests/style_rule/destroy_spec.rb +0 -27
- data/spec/requests/style_rule/find_custom_instruction_spec.rb +0 -27
- data/spec/requests/style_rule/find_spec.rb +0 -28
- data/spec/requests/style_rule/list_spec.rb +0 -31
- data/spec/requests/style_rule/update_configured_rules_spec.rb +0 -21
- data/spec/requests/style_rule/update_custom_instruction_spec.rb +0 -26
- data/spec/requests/style_rule/update_spec.rb +0 -19
- data/spec/requests/translate_spec.rb +8 -217
- data/spec/requests/translation_memory/list_spec.rb +0 -34
- data/spec/requests/usage_spec.rb +0 -16
- data/spec/resources/custom_instruction_spec.rb +32 -0
- data/spec/resources/style_rule_spec.rb +68 -0
- data/spec/spec_helper.rb +15 -45
- data/spec/support/live_mock_server.rb +12 -0
- data/spec/support/managed_glossary.rb +17 -0
- data/spec/support/managed_style_rule.rb +17 -0
- data/spec/support/managed_translation_memory.rb +7 -0
- metadata +23 -21
- data/spec/fixtures/vcr_cassettes/deepl_document.yml +0 -95
- data/spec/fixtures/vcr_cassettes/deepl_document_download.yml +0 -1214
- data/spec/fixtures/vcr_cassettes/deepl_glossaries.yml +0 -1163
- data/spec/fixtures/vcr_cassettes/deepl_languages.yml +0 -54
- data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +0 -87
- data/spec/fixtures/vcr_cassettes/deepl_translate.yml +0 -358
- data/spec/fixtures/vcr_cassettes/deepl_usage.yml +0 -129
- data/spec/fixtures/vcr_cassettes/glossaries.yml +0 -1702
- data/spec/fixtures/vcr_cassettes/languages.yml +0 -229
- data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +0 -401
- data/spec/fixtures/vcr_cassettes/style_rules.yml +0 -92
- data/spec/fixtures/vcr_cassettes/style_rules_crud.yml +0 -926
- data/spec/fixtures/vcr_cassettes/translate_texts.yml +0 -10630
- data/spec/fixtures/vcr_cassettes/translation_memories.yml +0 -74
- 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
|