deepl-rb 3.6.1 → 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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +8 -1
  3. data/CHANGELOG.md +35 -1
  4. data/Gemfile +0 -2
  5. data/README.md +165 -34
  6. data/Rakefile +2 -0
  7. data/VERSION +1 -1
  8. data/deepl-rb.gemspec +52 -20
  9. data/lib/deepl/requests/base.rb +16 -0
  10. data/lib/deepl/requests/document/upload.rb +6 -5
  11. data/lib/deepl/requests/rephrase.rb +3 -2
  12. data/lib/deepl/requests/style_rule/create.rb +46 -0
  13. data/lib/deepl/requests/style_rule/create_custom_instruction.rb +45 -0
  14. data/lib/deepl/requests/style_rule/destroy.rb +39 -0
  15. data/lib/deepl/requests/style_rule/destroy_custom_instruction.rb +40 -0
  16. data/lib/deepl/requests/style_rule/find.rb +40 -0
  17. data/lib/deepl/requests/style_rule/find_custom_instruction.rb +41 -0
  18. data/lib/deepl/requests/style_rule/update.rb +42 -0
  19. data/lib/deepl/requests/style_rule/update_configured_rules.rb +41 -0
  20. data/lib/deepl/requests/style_rule/update_custom_instruction.rb +47 -0
  21. data/lib/deepl/requests/translate.rb +17 -4
  22. data/lib/deepl/requests/translation_memory/list.rb +58 -0
  23. data/lib/deepl/resources/style_rule.rb +2 -1
  24. data/lib/deepl/resources/translation_memory.rb +25 -0
  25. data/lib/deepl/style_rule_api.rb +44 -0
  26. data/lib/deepl/translation_memory_api.rb +17 -0
  27. data/lib/deepl.rb +75 -55
  28. data/lib/version.rb +1 -1
  29. data/spec/api/deepl_spec.rb +134 -332
  30. data/spec/integration_tests/document_api_spec.rb +4 -18
  31. data/spec/integration_tests/document_error_paths_spec.rb +33 -0
  32. data/spec/integration_tests/glossary_api_spec.rb +114 -0
  33. data/spec/integration_tests/glossary_error_paths_spec.rb +107 -0
  34. data/spec/integration_tests/languages_api_spec.rb +54 -0
  35. data/spec/integration_tests/languages_error_paths_spec.rb +25 -0
  36. data/spec/integration_tests/rephrase_api_spec.rb +90 -0
  37. data/spec/integration_tests/rephrase_error_paths_spec.rb +53 -0
  38. data/spec/integration_tests/smoke_test_spec.rb +24 -0
  39. data/spec/integration_tests/style_rule_api_spec.rb +55 -17
  40. data/spec/integration_tests/style_rule_error_paths_spec.rb +45 -0
  41. data/spec/integration_tests/translate_api_spec.rb +98 -0
  42. data/spec/integration_tests/translate_error_paths_spec.rb +48 -0
  43. data/spec/integration_tests/translation_memory_api_spec.rb +54 -0
  44. data/spec/integration_tests/translation_memory_error_paths_spec.rb +19 -0
  45. data/spec/integration_tests/usage_api_spec.rb +29 -0
  46. data/spec/integration_tests/usage_error_paths_spec.rb +18 -0
  47. data/spec/requests/glossary/create_spec.rb +0 -21
  48. data/spec/requests/glossary/destroy_spec.rb +0 -39
  49. data/spec/requests/glossary/entries_spec.rb +0 -35
  50. data/spec/requests/glossary/find_spec.rb +0 -40
  51. data/spec/requests/glossary/language_pairs_spec.rb +0 -13
  52. data/spec/requests/glossary/list_spec.rb +0 -27
  53. data/spec/requests/languages_spec.rb +0 -41
  54. data/spec/requests/rephrase_spec.rb +13 -139
  55. data/spec/requests/style_rule/create_custom_instruction_spec.rb +30 -0
  56. data/spec/requests/style_rule/create_spec.rb +29 -0
  57. data/spec/requests/style_rule/destroy_custom_instruction_spec.rb +28 -0
  58. data/spec/requests/style_rule/destroy_spec.rb +27 -0
  59. data/spec/requests/style_rule/find_custom_instruction_spec.rb +29 -0
  60. data/spec/requests/style_rule/find_spec.rb +28 -0
  61. data/spec/requests/style_rule/list_spec.rb +27 -0
  62. data/spec/requests/style_rule/update_configured_rules_spec.rb +31 -0
  63. data/spec/requests/style_rule/update_custom_instruction_spec.rb +32 -0
  64. data/spec/requests/style_rule/update_spec.rb +29 -0
  65. data/spec/requests/translate_spec.rb +8 -218
  66. data/spec/requests/translation_memory/list_spec.rb +27 -0
  67. data/spec/requests/usage_spec.rb +0 -16
  68. data/spec/resources/custom_instruction_spec.rb +32 -0
  69. data/spec/resources/style_rule_spec.rb +68 -0
  70. data/spec/resources/translation_memory_spec.rb +35 -0
  71. data/spec/spec_helper.rb +15 -45
  72. data/spec/support/live_mock_server.rb +12 -0
  73. data/spec/support/managed_glossary.rb +17 -0
  74. data/spec/support/managed_style_rule.rb +17 -0
  75. data/spec/support/managed_translation_memory.rb +7 -0
  76. metadata +48 -19
  77. data/spec/fixtures/vcr_cassettes/deepl_document.yml +0 -95
  78. data/spec/fixtures/vcr_cassettes/deepl_document_download.yml +0 -1214
  79. data/spec/fixtures/vcr_cassettes/deepl_glossaries.yml +0 -1163
  80. data/spec/fixtures/vcr_cassettes/deepl_languages.yml +0 -54
  81. data/spec/fixtures/vcr_cassettes/deepl_rephrase.yml +0 -87
  82. data/spec/fixtures/vcr_cassettes/deepl_translate.yml +0 -358
  83. data/spec/fixtures/vcr_cassettes/deepl_usage.yml +0 -129
  84. data/spec/fixtures/vcr_cassettes/glossaries.yml +0 -1702
  85. data/spec/fixtures/vcr_cassettes/languages.yml +0 -229
  86. data/spec/fixtures/vcr_cassettes/rephrase_texts.yml +0 -401
  87. data/spec/fixtures/vcr_cassettes/style_rules.yml +0 -92
  88. data/spec/fixtures/vcr_cassettes/translate_texts.yml +0 -10630
  89. data/spec/fixtures/vcr_cassettes/usage.yml +0 -171
@@ -266,227 +266,17 @@ describe DeepL::Requests::Translate do
266
266
  expect(request.options[:custom_instructions]).to eq(['Use informal language', 'Be concise'])
267
267
  end
268
268
  end
269
- end
270
-
271
- describe '#request' do
272
- around do |example|
273
- VCR.use_cassette('translate_texts') { example.call }
274
- end
275
-
276
- context 'when performing a valid request with one text' do
277
- it 'returns a text object' do
278
- text = translate.request
279
-
280
- expect(text).to be_a(DeepL::Resources::Text)
281
- expect(text.text).to eq('Texto de muestra')
282
- expect(text.detected_source_language).to eq('EN')
283
- end
284
- end
285
-
286
- context 'when performing a valid request with multiple texts' do
287
- let(:text) { %w[Sample Word] }
288
-
289
- it 'returns a text object' do
290
- texts = translate.request
291
-
292
- expect(texts).to be_a(Array)
293
- expect(texts.first.text).to eq('Muestra')
294
- expect(texts.first.detected_source_language).to eq('EN')
295
-
296
- expect(texts.last.text).to eq('Palabra')
297
- expect(texts.last.detected_source_language).to eq('EN')
298
- end
299
- end
300
-
301
- context 'when performing a valid request with tag handling' do
302
- let(:text) { '<p>Sample text</p>' }
303
- let(:options) { { tag_handling: 'xml' } }
304
-
305
- it 'returns a text object' do
306
- text = translate.request
307
-
308
- expect(text).to be_a(DeepL::Resources::Text)
309
- expect(text.text).to eq('<p>Texto de muestra</p>')
310
- expect(text.detected_source_language).to eq('EN')
311
- end
312
- end
313
-
314
- context 'when performing a valid request and passing a variable' do
315
- let(:text) { 'Welcome and <code>Hello great World</code> Good Morning!' }
316
- let(:options) { { tag_handling: 'xml', ignore_tags: %w[code span] } }
317
-
318
- it 'returns a text object' do
319
- text = translate.request
320
-
321
- expect(text).to be_a(DeepL::Resources::Text)
322
- expect(text.text).to eq('Bienvenido y <code>Hello great World</code> ¡Buenos días!')
323
- expect(text.detected_source_language).to eq('EN')
324
- end
325
- end
326
-
327
- context 'when performing a valid request with an HTML document' do
328
- let(:text) do
329
- <<~XML
330
- <document>
331
- <meta>
332
- <title>A document's title</title>
333
- </meta>
334
- <content>
335
- <par>This is the first sentence. Followed by a second one.</par>
336
- <par>This is the third sentence.</par>
337
- </content>
338
- </document>
339
- XML
340
- end
341
- let(:options) do
342
- {
343
- tag_handling: 'xml',
344
- split_sentences: 'nonewlines',
345
- outline_detection: false,
346
- splitting_tags: %w[title par]
347
- }
348
- end
349
-
350
- it 'returns a text object' do
351
- text = translate.request
352
-
353
- expect(text).to be_a(DeepL::Resources::Text)
354
- expect(text.text).to eq(
355
- <<~XML
356
- <document>
357
- <meta>
358
- <title>Título de un documento</title>
359
- </meta>
360
- <content>
361
- <par>Es la primera frase. Seguido de una segunda.</par>
362
- <par>Esta es la tercera frase.</par>
363
- </content>
364
- </document>
365
- XML
366
- )
367
- expect(text.detected_source_language).to eq('EN')
368
- end
369
- end
370
-
371
- context 'when performing a valid request with context' do
372
- let(:text) { 'That is hot!' }
373
-
374
- context 'when context is empty' do
375
- let(:options) { { context: '' } }
376
-
377
- it 'translates correctly with empty context' do
378
- res = translate.request
379
- expect(res).to be_a(DeepL::Resources::Text)
380
- expect(res.text).to eq('¡Eso está caliente!')
381
- end
382
- end
383
269
 
384
- context 'when context is set' do
385
- let(:options) { { context: 'He did not like the jalapenos in his meal.' } }
386
-
387
- it 'translates correctly with context taken into account' do
388
- res = translate.request
389
- expect(res).to be_a(DeepL::Resources::Text)
390
- expect(res.text).to eq('¡Eso es picante!')
391
- end
392
- end
393
- end
394
-
395
- context 'when performing a request with a model type' do
396
- let(:target_lang) { 'DE' }
397
-
398
- %w[quality_optimized latency_optimized prefer_quality_optimized].each do |model_type_str|
399
- it "translates correctly with #{model_type_str} models" do
400
- options = { model_type: model_type_str }
401
- translate = described_class.new(api, text, source_lang, target_lang, options)
402
- res = translate.request
403
- expect(res).to be_a(DeepL::Resources::Text)
404
- expected_model_type = model_type_str.sub(/^prefer_/, '')
405
- expect(res.model_type_used).to eq(expected_model_type)
406
- end
407
- end
408
- end
409
-
410
- context 'when performing a request with extra_body_parameters' do
411
- it 'allows extra parameters to override standard parameters' do
412
- extra_options = { extra_body_parameters: { target_lang: 'FR', debug: '1' } }
413
- translate = described_class.new(api, text, source_lang, target_lang, extra_options)
414
- res = translate.request
415
-
416
- expect(res).to be_a(DeepL::Resources::Text)
417
- expect(res.text).to eq('Texte modèle')
418
- expect(res.detected_source_language).to eq('EN')
419
- end
420
- end
421
-
422
- context 'when performing a request with custom_instructions' do
423
- let(:text) { 'Hello world' }
424
- let(:target_lang) { 'DE' }
425
-
426
- it 'includes custom_instructions in the payload' do
427
- options = { custom_instructions: ['Use formal language', 'Be concise'] }
428
- translate = described_class.new(api, text, source_lang, target_lang, options)
429
-
430
- # Verify the options are properly stored
431
- expect(translate.options[:custom_instructions]).to eq(['Use formal language', 'Be concise'])
270
+ context 'when passing additional headers' do
271
+ it 'merges the headers into the request headers' do
272
+ request = described_class.new(api, nil, nil, nil, {},
273
+ { 'X-DeepL-Reporting-Tag' => 'my-tag' })
274
+ expect(request.send(:headers)).to include('X-DeepL-Reporting-Tag' => 'my-tag')
432
275
  end
433
- end
434
-
435
- context 'when performing a request with tag_handling_version' do
436
- let(:text) { '<p>Hello world</p>' }
437
- let(:target_lang) { 'DE' }
438
-
439
- %w[v1 v2].each do |version|
440
- it "translates correctly with tag_handling_version #{version}" do
441
- options = { tag_handling: 'html', tag_handling_version: version }
442
- translate = described_class.new(api, text, source_lang, target_lang, options)
443
- res = translate.request
444
-
445
- expect(res).to be_a(DeepL::Resources::Text)
446
- expect(res.text).not_to be_nil
447
- expect(res.text).not_to be_empty
448
- end
449
- end
450
- end
451
-
452
- context 'when performing a bad request' do
453
- context 'when using an invalid token' do
454
- let(:api) do
455
- api = build_deepl_api
456
- api.configuration.auth_key = 'invalid'
457
- api
458
- end
459
-
460
- it 'raises an unauthorized error' do
461
- expect { translate.request }.to raise_error(DeepL::Exceptions::AuthorizationFailed)
462
- end
463
- end
464
-
465
- context 'when using an invalid text' do
466
- let(:text) { nil }
467
-
468
- it 'raises a bad request error' do
469
- message = "Invalid request: Expected 'text' parameter to be an array of strings"
470
- expect { translate.request }.to raise_error(DeepL::Exceptions::BadRequest, message)
471
- end
472
- end
473
-
474
- context 'when using an invalid target language' do
475
- let(:target_lang) { nil }
476
-
477
- it 'raises a bad request error' do
478
- message = "Value for 'target_lang' not supported."
479
- expect { translate.request }.to raise_error(DeepL::Exceptions::BadRequest, message)
480
- end
481
- end
482
- end
483
-
484
- context 'when performing a request with too many texts' do
485
- let(:text) { Array.new(10_000) { |i| "This is the sentence number #{i}" } }
486
276
 
487
- it 'raises a request entity too large error' do
488
- expect { translate.request }.to raise_error(DeepL::Exceptions::RequestEntityTooLarge,
489
- /request size has reached the supported limit/)
277
+ it 'defaults to no additional headers' do
278
+ request = described_class.new(api, nil, nil, nil)
279
+ expect(request.send(:headers).keys).to contain_exactly('Authorization', 'User-Agent')
490
280
  end
491
281
  end
492
282
  end
@@ -0,0 +1,27 @@
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::Requests::TranslationMemory::List do
9
+ subject(:translation_memory_list) { described_class.new(api, options) }
10
+
11
+ around do |tests|
12
+ tmp_env = replace_env_preserving_deepl_vars_except_mock_server
13
+ tests.call
14
+ ENV.replace(tmp_env)
15
+ end
16
+
17
+ let(:api) { build_deepl_api }
18
+ let(:options) { {} }
19
+
20
+ describe '#initialize' do
21
+ context 'when building a request' do
22
+ it 'creates a request object' do
23
+ expect(translation_memory_list).to be_a(described_class)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -24,20 +24,4 @@ describe DeepL::Requests::Usage do
24
24
  end
25
25
  end
26
26
  end
27
-
28
- describe '#request' do
29
- around do |example|
30
- VCR.use_cassette('usage') { example.call }
31
- end
32
-
33
- context 'when performing a valid request' do
34
- it 'returns an usage object' do
35
- usage = usage_req.request
36
-
37
- expect(usage).to be_a(DeepL::Resources::Usage)
38
- expect(usage.character_count).to be_a(Numeric)
39
- expect(usage.character_limit).to be_a(Numeric)
40
- end
41
- end
42
- end
43
27
  end
@@ -0,0 +1,32 @@
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::Resources::CustomInstruction do
9
+ subject(:custom_instruction) do
10
+ described_class.new({
11
+ 'id' => 'ci-1',
12
+ 'label' => 'Formal',
13
+ 'prompt' => 'Always use formal language',
14
+ 'source_language' => 'en'
15
+ })
16
+ end
17
+
18
+ describe '#initialize' do
19
+ context 'when building a basic object' do
20
+ it 'creates a resource' do
21
+ expect(custom_instruction).to be_a(described_class)
22
+ end
23
+
24
+ it 'assigns the attributes' do
25
+ expect(custom_instruction.id).to eq('ci-1')
26
+ expect(custom_instruction.label).to eq('Formal')
27
+ expect(custom_instruction.prompt).to eq('Always use formal language')
28
+ expect(custom_instruction.source_language).to eq('en')
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,68 @@
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::Resources::StyleRule do
9
+ subject(:style_rule) do
10
+ described_class.new({
11
+ 'style_id' => 'dca2e053-8ae5-45e6-a0d2-881156e7f4e4',
12
+ 'name' => 'Default Style Rule',
13
+ 'creation_time' => '2025-01-01T00:00:00Z',
14
+ 'updated_time' => '2025-01-02T00:00:00Z',
15
+ 'language' => 'en',
16
+ 'version' => 1
17
+ }, nil, nil)
18
+ end
19
+
20
+ describe '#initialize' do
21
+ context 'when building a basic object' do
22
+ it 'creates a resource' do
23
+ expect(style_rule).to be_a(described_class)
24
+ end
25
+
26
+ it 'assigns the attributes' do
27
+ expect(style_rule.style_id).to eq('dca2e053-8ae5-45e6-a0d2-881156e7f4e4')
28
+ expect(style_rule.name).to eq('Default Style Rule')
29
+ expect(style_rule.language).to eq('en')
30
+ expect(style_rule.version).to eq(1)
31
+ expect(style_rule.creation_time).to eq(Time.parse('2025-01-01T00:00:00Z'))
32
+ expect(style_rule.updated_time).to eq(Time.parse('2025-01-02T00:00:00Z'))
33
+ expect(style_rule.configured_rules).to be_nil
34
+ expect(style_rule.custom_instructions).to be_nil
35
+ end
36
+ end
37
+
38
+ context 'when building an object with configured rules and custom instructions' do
39
+ subject(:style_rule) do
40
+ described_class.new({
41
+ 'style_id' => 'dca2e053-8ae5-45e6-a0d2-881156e7f4e4',
42
+ 'name' => 'Detailed Style Rule',
43
+ 'language' => 'en',
44
+ 'version' => 1,
45
+ 'configured_rules' => {
46
+ 'dates_and_times' => { 'calendar_era' => 'use_bc_and_ad' }
47
+ },
48
+ 'custom_instructions' => [
49
+ { 'id' => 'ci-1', 'label' => 'Test', 'prompt' => 'Be formal' }
50
+ ]
51
+ }, nil, nil)
52
+ end
53
+
54
+ it 'maps configured_rules into a ConfiguredRules resource' do
55
+ expect(style_rule.configured_rules).to be_a(DeepL::Resources::ConfiguredRules)
56
+ expect(style_rule.configured_rules.dates_and_times)
57
+ .to eq({ 'calendar_era' => 'use_bc_and_ad' })
58
+ end
59
+
60
+ it 'maps custom_instructions into an array of CustomInstruction resources' do
61
+ expect(style_rule.custom_instructions).to all(be_a(DeepL::Resources::CustomInstruction))
62
+ expect(style_rule.custom_instructions.first.id).to eq('ci-1')
63
+ expect(style_rule.custom_instructions.first.label).to eq('Test')
64
+ expect(style_rule.custom_instructions.first.prompt).to eq('Be formal')
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,35 @@
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::Resources::TranslationMemory do
9
+ subject(:translation_memory) do
10
+ described_class.new({
11
+ 'translation_memory_id' => 'a74d88fb-ed2a-4943-a664-a4512398b994',
12
+ 'name' => 'Legal',
13
+ 'source_language' => 'en',
14
+ 'target_languages' => %w[es de],
15
+ 'segment_count' => 3542
16
+ }, nil, nil)
17
+ end
18
+
19
+ describe '#initialize' do
20
+ context 'when building a basic object' do
21
+ it 'creates a resource' do
22
+ expect(translation_memory).to be_a(described_class)
23
+ end
24
+
25
+ it 'assigns the attributes' do
26
+ expect(translation_memory.translation_memory_id)
27
+ .to eq('a74d88fb-ed2a-4943-a664-a4512398b994')
28
+ expect(translation_memory.name).to eq('Legal')
29
+ expect(translation_memory.source_language).to eq('en')
30
+ expect(translation_memory.target_languages).to eq(%w[es de])
31
+ expect(translation_memory.segment_count).to eq(3542)
32
+ end
33
+ end
34
+ end
35
+ end
data/spec/spec_helper.rb CHANGED
@@ -14,59 +14,29 @@ SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
14
14
  require_relative '../lib/deepl'
15
15
  require_relative 'integration_tests/integration_test_utils'
16
16
 
17
+ # Auto-load test helpers under spec/support
18
+ Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f }
19
+
17
20
  # Lib config
18
21
  ENV['DEEPL_AUTH_KEY'] ||= 'TEST-TOKEN'
19
22
 
20
- # General helpers
21
- def build_deepl_api
22
- DeepL::API.new(DeepL::Configuration.new)
23
- end
23
+ RSpec.configure do |config|
24
+ config.include ManagedGlossary
25
+ config.include ManagedStyleRule
26
+ config.include ManagedTranslationMemory
24
27
 
25
- uri_ignoring_deepl_api_subdomain = lambda do |request1, request2|
26
- deepl_api_regexp = %r{https?://api.*\.deepl\.com/}
27
- uri1 = request1.uri
28
- uri2 = request2.uri
29
- uri1_match = uri1.match(deepl_api_regexp)
30
- uri2_match = uri2.match(deepl_api_regexp)
31
- if uri1_match && uri2_match
32
- uri1_without_deepl_domain = uri1.gsub(uri1_match[0], '')
33
- uri2_without_deepl_domain = uri2.gsub(uri2_match[0], '')
34
- uri1_without_deepl_domain == uri2_without_deepl_domain
35
- else
36
- uri1 == uri2
28
+ config.before(:each, :mock_server_only) do
29
+ skip 'Only runs on mock server' unless ENV.key?('DEEPL_MOCK_SERVER_PORT')
37
30
  end
38
- end
39
-
40
- headers_ignoring_user_agent = lambda do |request1, request2|
41
- user_agent_key = 'User-Agent'
42
- # Pass by reference, so we need to use a copy of the headers
43
- headers1 = request1.headers.dup
44
- headers2 = request2.headers.dup
45
- headers1_has_user_agent = headers1.key?(user_agent_key)
46
- headers2_has_user_agent = headers2.key?(user_agent_key)
47
- return false if headers1_has_user_agent != headers2_has_user_agent
48
31
 
49
- headers1.delete(user_agent_key)
50
- headers2.delete(user_agent_key)
51
- headers1 == headers2
32
+ config.before(:each, :real_server_only) do
33
+ skip 'Only runs on real server' if ENV.key?('DEEPL_MOCK_SERVER_PORT')
34
+ end
52
35
  end
53
36
 
54
- # VCR tapes configuration
55
- require 'vcr'
56
- VCR.configure do |config|
57
- config.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
58
- config.hook_into :webmock
59
- config.filter_sensitive_data('VALID_TOKEN') { ENV.fetch('DEEPL_AUTH_KEY', nil) }
60
-
61
- # to record new or missing VCR cassettes, call rspec like this:
62
- # $ VCR_RECORD_MODE=new_episodes bundle exec rspec
63
-
64
- record_mode = ENV['VCR_RECORD_MODE'] ? ENV['VCR_RECORD_MODE'].to_sym : :none
65
- config.default_cassette_options = {
66
- record: record_mode,
67
- match_requests_on: [:method, uri_ignoring_deepl_api_subdomain, :body,
68
- headers_ignoring_user_agent]
69
- }
37
+ # General helpers
38
+ def build_deepl_api
39
+ DeepL::API.new(DeepL::Configuration.new)
70
40
  end
71
41
 
72
42
  # Test helpers
@@ -0,0 +1,12 @@
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
+ # Shared setup for integration specs that hit the live mock server: expose an
7
+ # API the server rejects as unauthorized without mutating global DeepL state.
8
+ RSpec.shared_context 'with a live mock server' do
9
+ let(:unauthorized_api) do
10
+ DeepL::API.new(DeepL::Configuration.new(auth_key: 'invalid'))
11
+ end
12
+ end
@@ -0,0 +1,17 @@
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
+ module ManagedGlossary
7
+ def with_managed_glossary(name:, source_lang:, target_lang:, entries:, options: {})
8
+ glossary = DeepL.glossaries.create(name, source_lang, target_lang, entries, options)
9
+ yield glossary
10
+ ensure
11
+ begin
12
+ DeepL.glossaries.destroy(glossary.id) if glossary
13
+ rescue StandardError
14
+ nil
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
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
+ module ManagedStyleRule
7
+ def with_managed_style_rule(name:, language:, options: {})
8
+ style_rule = DeepL.style_rules.create(name, language, options)
9
+ yield style_rule
10
+ ensure
11
+ begin
12
+ DeepL.style_rules.destroy(style_rule.style_id) if style_rule
13
+ rescue StandardError
14
+ nil
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
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
+ module ManagedTranslationMemory
7
+ end