abbyy-cloud 0.0.3 → 0.0.4
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/.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
|