abbyy-cloud 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +24 -0
- data/README.md +60 -33
- data/abbyy-cloud.gemspec +1 -1
- data/lib/abbyy/cloud.rb +10 -16
- data/lib/abbyy/cloud/connection.rb +31 -17
- data/lib/abbyy/cloud/models/direction.rb +4 -2
- data/lib/abbyy/cloud/models/discount.rb +12 -0
- data/lib/abbyy/cloud/models/engine.rb +4 -1
- data/lib/abbyy/cloud/models/locale.rb +23 -0
- data/lib/abbyy/cloud/models/price.rb +22 -0
- data/lib/abbyy/cloud/models/source_segment.rb +14 -0
- data/lib/abbyy/cloud/models/source_tag.rb +15 -0
- data/lib/abbyy/cloud/models/transfer_data.rb +14 -0
- data/lib/abbyy/cloud/models/translation.rb +1 -1
- data/lib/abbyy/cloud/models/translation_segment.rb +16 -0
- data/lib/abbyy/cloud/models/unit_price.rb +13 -0
- data/lib/abbyy/cloud/namespaces/machine_translations.rb +47 -1
- data/lib/abbyy/cloud/namespaces/prices.rb +29 -0
- data/lib/abbyy/cloud/operations/base.rb +20 -6
- data/lib/abbyy/cloud/operations/engines.rb +3 -1
- data/lib/abbyy/cloud/operations/prices.rb +23 -0
- data/lib/abbyy/cloud/operations/translate.rb +5 -3
- data/lib/abbyy/cloud/operations/translate_segments.rb +22 -0
- data/lib/abbyy/cloud/settings.rb +4 -5
- data/lib/abbyy/cloud/types.rb +14 -10
- data/spec/abbyy/cloud/connection_spec.rb +1 -1
- data/spec/abbyy/cloud/models/discount_spec.rb +32 -0
- data/spec/abbyy/cloud/models/locale_spec.rb +55 -0
- data/spec/abbyy/cloud/models/price_spec.rb +107 -0
- data/spec/abbyy/cloud/models/source_segment_spec.rb +37 -0
- data/spec/abbyy/cloud/models/source_tag_spec.rb +56 -0
- data/spec/abbyy/cloud/models/transfer_data_spec.rb +40 -0
- data/spec/abbyy/cloud/models/translation_segment_spec.rb +38 -0
- data/spec/abbyy/cloud/models/unit_price_spec.rb +48 -0
- data/spec/abbyy/cloud/settings_spec.rb +2 -24
- data/spec/abbyy/cloud_spec.rb +3 -4
- data/spec/feature/abbyy/mt_default_engine_spec.rb +12 -0
- data/spec/feature/abbyy/mt_engine_spec.rb +15 -0
- data/spec/feature/abbyy/mt_translate_segments_spec.rb +136 -0
- data/spec/feature/abbyy/{order_translate_spec.rb → mt_translate_spec.rb} +2 -2
- data/spec/feature/abbyy/prices_details_spec.rb +70 -0
- metadata +39 -5
- data/lib/abbyy/cloud/namespaces/orders.rb +0 -25
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative "transfer_data"
|
2
|
+
|
3
|
+
class ABBYY::Cloud
|
4
|
+
module Models
|
5
|
+
# A segment of the translation
|
6
|
+
class TranslationSegment < Struct
|
7
|
+
attribute :id, Types::Strict::String
|
8
|
+
attribute :text, Types::Strict::String
|
9
|
+
attribute :tags_transfer_data,
|
10
|
+
Types::Array.member(Types::TransferData).default([])
|
11
|
+
end
|
12
|
+
|
13
|
+
# Registers type Types::TranslationSegment
|
14
|
+
Types.register_type TranslationSegment
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ABBYY::Cloud
|
2
|
+
module Models
|
3
|
+
# Price details
|
4
|
+
class UnitPrice < Struct
|
5
|
+
attribute :unit_type, Types::UnitType
|
6
|
+
attribute :currency, Types::Currency
|
7
|
+
attribute :amount, Types::Coercible::Float
|
8
|
+
end
|
9
|
+
|
10
|
+
# Registers type Types::UnitPrice
|
11
|
+
Types.register_type UnitPrice
|
12
|
+
end
|
13
|
+
end
|
@@ -1,17 +1,63 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
1
3
|
class ABBYY::Cloud
|
2
4
|
module Namespaces
|
3
5
|
# Namespace for operations with machine translations
|
4
6
|
# @see [https://api.abbyy.cloud/swagger/ui/index#!/MachineTranslation]
|
5
7
|
class MachineTranslations < Base
|
6
8
|
# Returns list of all available engines
|
9
|
+
# @return [Array<ABBYY::Cloud::Models::Engine>]
|
7
10
|
def engines
|
8
11
|
Operations::Engines.new(settings).call
|
9
12
|
end
|
10
13
|
|
11
|
-
# Returns
|
14
|
+
# Returns engine object selected by its name
|
15
|
+
# @return [ABBYY::Cloud::Models::Engine]
|
12
16
|
def engine(name)
|
13
17
|
engines.find { |engine| engine.name == name }
|
14
18
|
end
|
19
|
+
|
20
|
+
# Returns engine object for the default engine
|
21
|
+
# @return [ABBYY::Cloud::Models::Engine]
|
22
|
+
def default_engine
|
23
|
+
engine(settings.engine)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Instantly (synchronously) translates the text
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# translate "Hello world!", from: "en", to: "fr_FR"
|
30
|
+
#
|
31
|
+
# @param [String] text
|
32
|
+
# @option [ABBYY::Cloud::Types::Locale] :from Source language
|
33
|
+
# @option [ABBYY::Cloud::Types::Locale] :to Target language
|
34
|
+
# @return [ABBYY::Cloud::Models::Translation]
|
35
|
+
#
|
36
|
+
def translate(text, from:, to:, **opts)
|
37
|
+
Operations::Translate.new(settings).call source_text: text,
|
38
|
+
source_language: from,
|
39
|
+
target_language: to,
|
40
|
+
engine: settings.engine,
|
41
|
+
**opts
|
42
|
+
end
|
43
|
+
|
44
|
+
# Instantly (synchronously) translates array of texts
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# translate ["Hello", "world"], from: "en", to: "fr_FR"
|
48
|
+
#
|
49
|
+
# @param [Array<String>] texts
|
50
|
+
# @option [ABBYY::Cloud::Types::Locale] :from Source language
|
51
|
+
# @option [ABBYY::Cloud::Types::Locale] :to Target language
|
52
|
+
# @return [ABBYY::Cloud::Models::Translation]
|
53
|
+
#
|
54
|
+
def translate_segments(texts, from:, to:, **opts)
|
55
|
+
sources = texts.map { |text| { text: text } }
|
56
|
+
Operations::TranslateSegments
|
57
|
+
.new(settings)
|
58
|
+
.call sources: sources, from: from, to: to, engine: settings.engine,
|
59
|
+
**opts
|
60
|
+
end
|
15
61
|
end
|
16
62
|
end
|
17
63
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
class ABBYY::Cloud
|
4
|
+
module Namespaces
|
5
|
+
# Namespace for operations with prices
|
6
|
+
# @see [https://api.abbyy.cloud/swagger/ui/index#!/Prices] ABBYY Cloud API
|
7
|
+
class Prices < Base
|
8
|
+
MAX_PER_REQUEST = 1_000
|
9
|
+
|
10
|
+
# Returns prices data filtered by type of translated object,
|
11
|
+
# source and target language.
|
12
|
+
#
|
13
|
+
# If take option is NOT set, the method makes as many requests
|
14
|
+
# as necessary to get all prices.
|
15
|
+
#
|
16
|
+
def details(skip: 0, take: nil, **opts)
|
17
|
+
take_now = (take && take <= MAX_PER_REQUEST) ? take : MAX_PER_REQUEST
|
18
|
+
take_later = take - take_now if take
|
19
|
+
skip_later = skip + take_now
|
20
|
+
|
21
|
+
items = Operations::Prices.new(settings)
|
22
|
+
.call(skip: skip, take: take_now, **opts)
|
23
|
+
|
24
|
+
return items if (items.count < take_now) || take_later.zero?
|
25
|
+
items + details(skip: skip_later, take: take_later, **opts)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -27,6 +27,10 @@ class ABBYY::Cloud
|
|
27
27
|
provide_struct :@request_body, struct, &block
|
28
28
|
end
|
29
29
|
|
30
|
+
def request_query(struct = nil, &block)
|
31
|
+
provide_struct :@request_query, struct, &block
|
32
|
+
end
|
33
|
+
|
30
34
|
def response_body(struct = nil, &block)
|
31
35
|
provide_struct :@response_body, struct, &block
|
32
36
|
end
|
@@ -44,20 +48,24 @@ class ABBYY::Cloud
|
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
51
|
+
include Dry::Initializer.define -> do
|
52
|
+
param :settings
|
53
|
+
end
|
54
|
+
|
55
|
+
def_delegators :settings, :connection
|
47
56
|
def_delegators :"self.class",
|
48
57
|
:link,
|
49
58
|
:http_method,
|
50
59
|
:path,
|
51
60
|
:request_body,
|
61
|
+
:request_query,
|
52
62
|
:response_body
|
53
63
|
|
54
|
-
include Dry::Initializer.define -> do
|
55
|
-
param :settings
|
56
|
-
end
|
57
|
-
|
58
64
|
def call(**data)
|
59
|
-
body
|
60
|
-
|
65
|
+
body = prepare_request_body(data)
|
66
|
+
query = prepare_request_query(data)
|
67
|
+
res = connection.call(http_method, path, body: body, query: query)
|
68
|
+
|
61
69
|
handle_response_body(res)
|
62
70
|
end
|
63
71
|
|
@@ -69,6 +77,12 @@ class ABBYY::Cloud
|
|
69
77
|
raise ArgumentError.new(link, data, error.message)
|
70
78
|
end
|
71
79
|
|
80
|
+
def prepare_request_query(data)
|
81
|
+
request_query[data]
|
82
|
+
rescue => error
|
83
|
+
raise ArgumentError.new(link, data, error.message)
|
84
|
+
end
|
85
|
+
|
72
86
|
def handle_response_body(data)
|
73
87
|
response_body[data]
|
74
88
|
rescue => error
|
@@ -1,10 +1,12 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
1
3
|
class ABBYY::Cloud
|
2
4
|
module Operations
|
3
5
|
class Engines < Base
|
4
6
|
# rubocop: disable Metrics/LineLength
|
5
7
|
link "https://api.abbyy.cloud/swagger/ui/index#!/MachineTranslation/MachineTranslation_Engines"
|
6
8
|
# rubocop: enable Metrics/LineLength
|
7
|
-
path "mt/engines"
|
9
|
+
path "v0/mt/engines"
|
8
10
|
http_method "get"
|
9
11
|
|
10
12
|
response_body Types::Array.member(Types::Engine)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
class ABBYY::Cloud
|
4
|
+
module Operations
|
5
|
+
class Prices < Base
|
6
|
+
# rubocop: disable Metrics/LineLength
|
7
|
+
link "https://api.abbyy.cloud/swagger/ui/index#!/Prices/Prices_GetAccountPrices"
|
8
|
+
# rubocop: enable Metrics/LineLength
|
9
|
+
path "v0/prices/details"
|
10
|
+
http_method "get"
|
11
|
+
|
12
|
+
request_query do
|
13
|
+
attribute :skip, Types::Strict::Int
|
14
|
+
attribute :take, Types::Strict::Int
|
15
|
+
attribute :type, Types::Strict::String.optional
|
16
|
+
attribute :from, Types::Locale.optional
|
17
|
+
attribute :to, Types::Locale.optional
|
18
|
+
end
|
19
|
+
|
20
|
+
response_body Types::Array.member(Types::Price)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
1
3
|
class ABBYY::Cloud
|
2
4
|
module Operations
|
3
5
|
class Translate < Base
|
4
6
|
link "https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_Translate"
|
5
|
-
path "order/mt/sync"
|
7
|
+
path "v0/order/mt/sync"
|
6
8
|
http_method "post"
|
7
9
|
|
8
10
|
request_body do
|
9
11
|
attribute :engine, Types::Strict::String
|
10
|
-
attribute :source_language, Types::
|
11
|
-
attribute :target_language, Types::
|
12
|
+
attribute :source_language, Types::Locale
|
13
|
+
attribute :target_language, Types::Locale
|
12
14
|
attribute :source_text, Types::Strict::String
|
13
15
|
end
|
14
16
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
class ABBYY::Cloud
|
4
|
+
module Operations
|
5
|
+
class TranslateSegments < Base
|
6
|
+
# rubocop: disable Metrics/LineLength
|
7
|
+
link "https://api.abbyy.cloud/swagger/ui/index#!/Order/Order_TranslateSegments"
|
8
|
+
# rubocop: enable Metrics/LineLength
|
9
|
+
path "v1/order/mt/sync"
|
10
|
+
http_method "post"
|
11
|
+
|
12
|
+
request_body do
|
13
|
+
attribute :engine, Types::Strict::String
|
14
|
+
attribute :from, Types::Locale
|
15
|
+
attribute :to, Types::Locale
|
16
|
+
attribute :sources, Types::Array.member(Types::SourceSegment)
|
17
|
+
end
|
18
|
+
|
19
|
+
response_body Types::Array.member(Types::TranslationSegment)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/abbyy/cloud/settings.rb
CHANGED
@@ -3,14 +3,13 @@
|
|
3
3
|
class ABBYY::Cloud
|
4
4
|
class Settings
|
5
5
|
include Dry::Initializer.define -> do
|
6
|
-
option :id,
|
7
|
-
option :token,
|
8
|
-
option :
|
9
|
-
option :engine, type: Types::Strict::String, default: proc { "Sandbox" }
|
6
|
+
option :id, type: Types::Strict::String
|
7
|
+
option :token, type: Types::Strict::String
|
8
|
+
option :engine, type: Types::Strict::String, default: proc { "Sandbox" }
|
10
9
|
end
|
11
10
|
|
12
11
|
def connection
|
13
|
-
@connection ||= Connection.new(
|
12
|
+
@connection ||= Connection.new(id: id, token: token)
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
data/lib/abbyy/cloud/types.rb
CHANGED
@@ -2,27 +2,31 @@ module ABBYY::Cloud::Types
|
|
2
2
|
include Dry::Types.module
|
3
3
|
|
4
4
|
VERSIONS = [0].freeze
|
5
|
-
|
5
|
+
UNIT_TYPES = %w(Chars Words Pages Documents).freeze
|
6
|
+
DISCOUNT_TYPES = \
|
7
|
+
%w(TMTextMatch TMTaggedTextMatch TMHalfContextMatch TMFullContextMatch)
|
8
|
+
.freeze
|
6
9
|
|
7
10
|
# Gem-specific primitive types
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
Version = Coercible::Int.constrained(included_in: VERSIONS)
|
11
|
+
Version = Coercible::Int.constrained(included_in: VERSIONS)
|
12
|
+
UnitType = Strict::String.constrained(included_in: UNIT_TYPES)
|
13
|
+
DiscountType = Strict::String.constrained(included_in: DISCOUNT_TYPES)
|
14
|
+
Currency = Strict::String.constrained(format: /\A[A-Z]{3}\z/)
|
13
15
|
|
14
16
|
# Registers new coercible type from a struct class
|
15
|
-
def self.register_type(struct, as: nil)
|
16
|
-
type_name = as || struct.name.split("::").last
|
17
|
+
def self.register_type(struct, as: nil, constructor: :new)
|
18
|
+
type_name = Inflecto.underscore(as || struct.name.split("::").last)
|
17
19
|
definition = Dry::Types::Definition.new(struct).constructor do |value|
|
18
20
|
case value
|
19
|
-
when nil then
|
21
|
+
when nil then raise
|
20
22
|
when struct then value
|
21
|
-
else struct
|
23
|
+
else struct.send(constructor, value)
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
27
|
Dry::Types.register type_name, definition
|
26
28
|
Dry::Types.define_constants self, [type_name]
|
27
29
|
end
|
30
|
+
|
31
|
+
register_type ::Time, as: "coercible.time", constructor: :parse
|
28
32
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
RSpec.describe ABBYY::Cloud::Models::Discount do
|
2
|
+
let(:data) { { discount_type: "TMTextMatch", discount: 0.01 } }
|
3
|
+
|
4
|
+
subject { described_class.new(data) }
|
5
|
+
|
6
|
+
it { is_expected.to be_kind_of ABBYY::Cloud::Struct }
|
7
|
+
its(:to_h) { is_expected.to eq data }
|
8
|
+
|
9
|
+
context "with invalid discount_type:" do
|
10
|
+
before { data[:discount_type] = "Unknown" }
|
11
|
+
|
12
|
+
it "fails" do
|
13
|
+
expect { subject }.to raise_error(StandardError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "without discount_type:" do
|
18
|
+
before { data.delete :discount_type }
|
19
|
+
|
20
|
+
it "fails" do
|
21
|
+
expect { subject }.to raise_error(StandardError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "without discount:" do
|
26
|
+
before { data.delete :discount }
|
27
|
+
|
28
|
+
it "fails" do
|
29
|
+
expect { subject }.to raise_error(StandardError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
RSpec.describe ABBYY::Cloud::Models::Locale do
|
2
|
+
it "accepts simple language locales" do
|
3
|
+
%w(en sjp yds).each do |locale|
|
4
|
+
expect(described_class[locale]).to eq locale
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
it "accepts scripted locales" do
|
9
|
+
%w(ar-Aran).each do |locale|
|
10
|
+
expect(described_class[locale]).to eq locale
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "accepts regional locales" do
|
15
|
+
%w(en-GB igb-AA en-018).each do |locale|
|
16
|
+
expect(described_class[locale]).to eq locale
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "accepts scripted regional locales" do
|
21
|
+
%w(ar-Aran-YE ar-Aran-002).each do |locale|
|
22
|
+
expect(described_class[locale]).to eq locale
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "accepts scripted regional subtagged locales" do
|
27
|
+
%w(fr-Aran-FR-1606nict de-1996 de-DE-1996).each do |locale|
|
28
|
+
expect(described_class[locale]).to eq locale
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "accepts grandfarthered locales" do
|
33
|
+
%w(cel-gaulish i-ami en-GB-oed sgn-BE-NL).each do |locale|
|
34
|
+
expect(described_class[locale]).to eq locale
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "accepts redundant locales" do
|
39
|
+
%w(bs-Latn de-AT-1901 zh-Hans-TW).each do |locale|
|
40
|
+
expect(described_class[locale]).to eq locale
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "accepts private-use locales" do
|
45
|
+
%w(en-x-it).each do |locale|
|
46
|
+
expect(described_class[locale]).to eq locale
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "declines locales that mismatch IANA/IETF standards" do
|
51
|
+
%w(i en_EN).each do |locale|
|
52
|
+
expect { described_class[locale] }.to raise_error(StandardError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
RSpec.describe ABBYY::Cloud::Models::Price do
|
2
|
+
let(:data) do
|
3
|
+
{
|
4
|
+
id: "foo",
|
5
|
+
account_id: "bar",
|
6
|
+
type: "qux",
|
7
|
+
from: "ru",
|
8
|
+
to: "en",
|
9
|
+
unit_prices: [{ unit_type: "Words", currency: "USD", amount: 0.03 }],
|
10
|
+
discounts: [{ discount_type: "TMTextMatch", discount: 0.01 }],
|
11
|
+
created: Time.now
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
subject { described_class.new(data) }
|
16
|
+
|
17
|
+
it { is_expected.to be_kind_of ABBYY::Cloud::Struct }
|
18
|
+
its(:to_h) { is_expected.to eq data }
|
19
|
+
|
20
|
+
context "without id:" do
|
21
|
+
before { data.delete :id }
|
22
|
+
|
23
|
+
it "fails" do
|
24
|
+
expect { subject }.to raise_error(StandardError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "without account_id:" do
|
29
|
+
before { data.delete :account_id }
|
30
|
+
|
31
|
+
it "fails" do
|
32
|
+
expect { subject }.to raise_error(StandardError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "without type:" do
|
37
|
+
before { data.delete :type }
|
38
|
+
|
39
|
+
it "fails" do
|
40
|
+
expect { subject }.to raise_error(StandardError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "without source language:" do
|
45
|
+
before { data.delete :from }
|
46
|
+
|
47
|
+
it "fails" do
|
48
|
+
expect { subject }.to raise_error(StandardError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "without target language:" do
|
53
|
+
before { data.delete :to }
|
54
|
+
|
55
|
+
it "fails" do
|
56
|
+
expect { subject }.to raise_error(StandardError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with invalid unit_prices:" do
|
61
|
+
before { data[:unit_prices] = "foo" }
|
62
|
+
|
63
|
+
it "fails" do
|
64
|
+
expect { subject }.to raise_error(StandardError)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with invalid unit_price:" do
|
69
|
+
before { data[:unit_prices] = [{ id: "foo" }] }
|
70
|
+
|
71
|
+
it "fails" do
|
72
|
+
expect { subject }.to raise_error(StandardError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "without unit_prices:" do
|
77
|
+
before { data.delete :unit_prices }
|
78
|
+
|
79
|
+
it "fails" do
|
80
|
+
expect { subject }.to raise_error(StandardError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "with invalid discounts:" do
|
85
|
+
before { data[:discounts] = "foo" }
|
86
|
+
|
87
|
+
it "fails" do
|
88
|
+
expect { subject }.to raise_error(StandardError)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "with invalid discount:" do
|
93
|
+
before { data[:discounts] = [{ id: "foo" }] }
|
94
|
+
|
95
|
+
it "fails" do
|
96
|
+
expect { subject }.to raise_error(StandardError)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "without discounts:" do
|
101
|
+
before { data.delete :discounts }
|
102
|
+
|
103
|
+
it "fails" do
|
104
|
+
expect { subject }.to raise_error(StandardError)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|