candy_check 0.1.0.pre → 0.3.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 (76) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +23 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +7 -8
  5. data/Guardfile +42 -0
  6. data/MIGRATION_GUIDE_0_2_0.md +141 -0
  7. data/README.md +86 -26
  8. data/Rakefile +1 -1
  9. data/candy_check.gemspec +33 -25
  10. data/lib/candy_check/app_store/receipt_collection.rb +5 -3
  11. data/lib/candy_check/app_store/subscription_verification.rb +25 -1
  12. data/lib/candy_check/app_store/verification.rb +1 -1
  13. data/lib/candy_check/app_store/verifier.rb +11 -11
  14. data/lib/candy_check/cli/app.rb +16 -33
  15. data/lib/candy_check/cli/commands/play_store.rb +12 -13
  16. data/lib/candy_check/play_store.rb +20 -10
  17. data/lib/candy_check/play_store/acknowledger.rb +19 -0
  18. data/lib/candy_check/play_store/android_publisher_service.rb +6 -0
  19. data/lib/candy_check/play_store/product_acknowledgements/acknowledgement.rb +45 -0
  20. data/lib/candy_check/play_store/product_acknowledgements/response.rb +24 -0
  21. data/lib/candy_check/play_store/product_purchases/product_purchase.rb +90 -0
  22. data/lib/candy_check/play_store/product_purchases/product_verification.rb +53 -0
  23. data/lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb +154 -0
  24. data/lib/candy_check/play_store/subscription_purchases/subscription_verification.rb +55 -0
  25. data/lib/candy_check/play_store/verification_failure.rb +8 -6
  26. data/lib/candy_check/play_store/verifier.rb +24 -49
  27. data/lib/candy_check/utils/config.rb +5 -3
  28. data/lib/candy_check/version.rb +1 -1
  29. data/spec/app_store/receipt_collection_spec.rb +33 -0
  30. data/spec/app_store/subscription_verification_spec.rb +35 -2
  31. data/spec/app_store/verifier_spec.rb +24 -5
  32. data/spec/candy_check_spec.rb +2 -2
  33. data/spec/cli/commands/play_store_spec.rb +10 -43
  34. data/spec/fixtures/play_store/random_dummy_key.json +12 -0
  35. data/spec/fixtures/vcr_cassettes/play_store/product_acknowledgements/acknowledged.yml +105 -0
  36. data/spec/fixtures/vcr_cassettes/play_store/product_acknowledgements/already_acknowledged.yml +124 -0
  37. data/spec/fixtures/vcr_cassettes/play_store/product_acknowledgements/refunded.yml +122 -0
  38. data/spec/fixtures/vcr_cassettes/play_store/product_purchases/permission_denied.yml +196 -0
  39. data/spec/fixtures/vcr_cassettes/play_store/product_purchases/response_with_empty_body.yml +183 -0
  40. data/spec/fixtures/vcr_cassettes/play_store/product_purchases/valid_but_not_consumed.yml +122 -0
  41. data/spec/fixtures/vcr_cassettes/play_store/subscription_purchases/permission_denied.yml +196 -0
  42. data/spec/fixtures/vcr_cassettes/play_store/subscription_purchases/valid_but_expired.yml +127 -0
  43. data/spec/play_store/acknowledger_spec.rb +48 -0
  44. data/spec/play_store/product_acknowledgements/acknowledgement_spec.rb +54 -0
  45. data/spec/play_store/product_acknowledgements/response_spec.rb +66 -0
  46. data/spec/play_store/product_purchases/product_purchase_spec.rb +110 -0
  47. data/spec/play_store/product_purchases/product_verification_spec.rb +49 -0
  48. data/spec/play_store/subscription_purchases/subscription_purchase_spec.rb +237 -0
  49. data/spec/play_store/subscription_purchases/subscription_verification_spec.rb +65 -0
  50. data/spec/play_store/verification_failure_spec.rb +18 -18
  51. data/spec/play_store/verifier_spec.rb +32 -96
  52. data/spec/spec_helper.rb +32 -10
  53. metadata +175 -75
  54. data/lib/candy_check/play_store/client.rb +0 -126
  55. data/lib/candy_check/play_store/config.rb +0 -51
  56. data/lib/candy_check/play_store/discovery_repository.rb +0 -33
  57. data/lib/candy_check/play_store/receipt.rb +0 -81
  58. data/lib/candy_check/play_store/subscription.rb +0 -138
  59. data/lib/candy_check/play_store/subscription_verification.rb +0 -30
  60. data/lib/candy_check/play_store/verification.rb +0 -48
  61. data/spec/fixtures/api_cache.dump +0 -1
  62. data/spec/fixtures/play_store/api_cache.dump +0 -1
  63. data/spec/fixtures/play_store/auth_failure.txt +0 -18
  64. data/spec/fixtures/play_store/auth_success.txt +0 -20
  65. data/spec/fixtures/play_store/discovery.txt +0 -2841
  66. data/spec/fixtures/play_store/dummy.p12 +0 -0
  67. data/spec/fixtures/play_store/empty.txt +0 -17
  68. data/spec/fixtures/play_store/products_failure.txt +0 -29
  69. data/spec/fixtures/play_store/products_success.txt +0 -22
  70. data/spec/play_store/client_spec.rb +0 -125
  71. data/spec/play_store/config_spec.rb +0 -96
  72. data/spec/play_store/discovery_respository_spec.rb +0 -31
  73. data/spec/play_store/receipt_spec.rb +0 -88
  74. data/spec/play_store/subscription_spec.rb +0 -138
  75. data/spec/play_store/subscription_verification_spec.rb +0 -98
  76. data/spec/play_store/verification_spec.rb +0 -82
@@ -0,0 +1,196 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://www.googleapis.com/oauth2/v4/token
6
+ body:
7
+ encoding: ASCII-8BIT
8
+ string: params
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.15.4
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Content-Type:
24
+ - application/json; charset=utf-8
25
+ Vary:
26
+ - Origin
27
+ - Referer
28
+ - X-Origin
29
+ Date:
30
+ - Thu, 10 Oct 2019 14:07:52 GMT
31
+ Server:
32
+ - scaffolding on HTTPServer2
33
+ Cache-Control:
34
+ - private
35
+ X-Xss-Protection:
36
+ - '0'
37
+ X-Frame-Options:
38
+ - SAMEORIGIN
39
+ X-Content-Type-Options:
40
+ - nosniff
41
+ Alt-Svc:
42
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
43
+ ma=2592000,h3-Q043=":443"; ma=2592000
44
+ Transfer-Encoding:
45
+ - chunked
46
+ body:
47
+ encoding: ASCII-8BIT
48
+ string: |-
49
+ {
50
+ "access_token": "my_token",
51
+ "expires_in": 3600,
52
+ "token_type": "Bearer"
53
+ }
54
+ http_version:
55
+ recorded_at: Thu, 10 Oct 2019 14:07:52 GMT
56
+ - request:
57
+ method: get
58
+ uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/subscriptions/my_subscription_id/tokens/my_token
59
+ body:
60
+ encoding: UTF-8
61
+ string: ''
62
+ headers:
63
+ User-Agent:
64
+ - unknown/0.0.0 google-api-ruby-client/0.28.7 Linux/5.0.0-31-generic (gzip)
65
+ Accept:
66
+ - "*/*"
67
+ Accept-Encoding:
68
+ - gzip,deflate
69
+ Date:
70
+ - Thu, 10 Oct 2019 14:07:52 GMT
71
+ Authorization:
72
+ - Bearer my_bearer
73
+ Content-Type:
74
+ - application/x-www-form-urlencoded
75
+ response:
76
+ status:
77
+ code: 401
78
+ message: Unauthorized
79
+ headers:
80
+ Vary:
81
+ - Origin
82
+ - X-Origin
83
+ Www-Authenticate:
84
+ - Bearer realm="https://accounts.google.com/", error=invalid_token
85
+ Content-Type:
86
+ - application/json; charset=UTF-8
87
+ Content-Encoding:
88
+ - gzip
89
+ Date:
90
+ - Thu, 10 Oct 2019 14:07:52 GMT
91
+ Expires:
92
+ - Thu, 10 Oct 2019 14:07:52 GMT
93
+ Cache-Control:
94
+ - private, max-age=0
95
+ X-Content-Type-Options:
96
+ - nosniff
97
+ X-Frame-Options:
98
+ - SAMEORIGIN
99
+ X-Xss-Protection:
100
+ - 1; mode=block
101
+ Server:
102
+ - GSE
103
+ Alt-Svc:
104
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
105
+ ma=2592000,h3-Q043=":443"; ma=2592000
106
+ Transfer-Encoding:
107
+ - chunked
108
+ body:
109
+ encoding: UTF-8
110
+ string: |
111
+ {
112
+ "error": {
113
+ "errors": [
114
+ {
115
+ "domain": "androidpublisher",
116
+ "reason": "permissionDenied",
117
+ "message": "The current user has insufficient permissions to perform the requested operation."
118
+ }
119
+ ],
120
+ "code": 401,
121
+ "message": "The current user has insufficient permissions to perform the requested operation."
122
+ }
123
+ }
124
+ http_version:
125
+ recorded_at: Thu, 10 Oct 2019 14:07:52 GMT
126
+ - request:
127
+ method: get
128
+ uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/subscriptions/my_subscription_id/tokens/my_token
129
+ body:
130
+ encoding: UTF-8
131
+ string: ''
132
+ headers:
133
+ User-Agent:
134
+ - unknown/0.0.0 google-api-ruby-client/0.28.7 Linux/5.0.0-31-generic (gzip)
135
+ Accept:
136
+ - "*/*"
137
+ Accept-Encoding:
138
+ - gzip,deflate
139
+ Date:
140
+ - Thu, 10 Oct 2019 14:07:53 GMT
141
+ Authorization:
142
+ - Bearer my_bearer
143
+ Content-Type:
144
+ - application/x-www-form-urlencoded
145
+ response:
146
+ status:
147
+ code: 401
148
+ message: Unauthorized
149
+ headers:
150
+ Vary:
151
+ - Origin
152
+ - X-Origin
153
+ Www-Authenticate:
154
+ - Bearer realm="https://accounts.google.com/", error=invalid_token
155
+ Content-Type:
156
+ - application/json; charset=UTF-8
157
+ Content-Encoding:
158
+ - gzip
159
+ Date:
160
+ - Thu, 10 Oct 2019 14:07:53 GMT
161
+ Expires:
162
+ - Thu, 10 Oct 2019 14:07:53 GMT
163
+ Cache-Control:
164
+ - private, max-age=0
165
+ X-Content-Type-Options:
166
+ - nosniff
167
+ X-Frame-Options:
168
+ - SAMEORIGIN
169
+ X-Xss-Protection:
170
+ - 1; mode=block
171
+ Server:
172
+ - GSE
173
+ Alt-Svc:
174
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
175
+ ma=2592000,h3-Q043=":443"; ma=2592000
176
+ Transfer-Encoding:
177
+ - chunked
178
+ body:
179
+ encoding: UTF-8
180
+ string: |
181
+ {
182
+ "error": {
183
+ "errors": [
184
+ {
185
+ "domain": "androidpublisher",
186
+ "reason": "permissionDenied",
187
+ "message": "The current user has insufficient permissions to perform the requested operation."
188
+ }
189
+ ],
190
+ "code": 401,
191
+ "message": "The current user has insufficient permissions to perform the requested operation."
192
+ }
193
+ }
194
+ http_version:
195
+ recorded_at: Thu, 10 Oct 2019 14:07:53 GMT
196
+ recorded_with: VCR 5.0.0
@@ -0,0 +1,127 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://www.googleapis.com/oauth2/v4/token
6
+ body:
7
+ encoding: ASCII-8BIT
8
+ string: some_grant
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.15.4
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Content-Type:
24
+ - application/json; charset=utf-8
25
+ Vary:
26
+ - Origin
27
+ - Referer
28
+ - X-Origin
29
+ Date:
30
+ - Thu, 24 Oct 2019 10:43:13 GMT
31
+ Server:
32
+ - scaffolding on HTTPServer2
33
+ Cache-Control:
34
+ - private
35
+ X-Xss-Protection:
36
+ - '0'
37
+ X-Frame-Options:
38
+ - SAMEORIGIN
39
+ X-Content-Type-Options:
40
+ - nosniff
41
+ Alt-Svc:
42
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
43
+ ma=2592000,h3-Q043=":443"; ma=2592000
44
+ Transfer-Encoding:
45
+ - chunked
46
+ body:
47
+ encoding: ASCII-8BIT
48
+ string: |-
49
+ {
50
+ "access_token": "some token",
51
+ "expires_in": 3600,
52
+ "token_type": "Bearer"
53
+ }
54
+ http_version:
55
+ recorded_at: Thu, 24 Oct 2019 10:43:13 GMT
56
+ - request:
57
+ method: get
58
+ uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/subscriptions/my_subscription_id/tokens/my_token
59
+ body:
60
+ encoding: UTF-8
61
+ string: ''
62
+ headers:
63
+ User-Agent:
64
+ - unknown/0.0.0 google-api-ruby-client/0.28.7 Linux/5.0.0-32-generic (gzip)
65
+ Accept:
66
+ - "*/*"
67
+ Accept-Encoding:
68
+ - gzip,deflate
69
+ Date:
70
+ - Thu, 24 Oct 2019 10:43:13 GMT
71
+ Authorization:
72
+ - Bearer some_token
73
+ Content-Type:
74
+ - application/x-www-form-urlencoded
75
+ response:
76
+ status:
77
+ code: 200
78
+ message: OK
79
+ headers:
80
+ Expires:
81
+ - Thu, 24 Oct 2019 10:43:13 GMT
82
+ Date:
83
+ - Thu, 24 Oct 2019 10:43:13 GMT
84
+ Cache-Control:
85
+ - private, max-age=0, must-revalidate, no-transform
86
+ Etag:
87
+ - ''
88
+ Vary:
89
+ - Origin
90
+ - X-Origin
91
+ Content-Type:
92
+ - application/json; charset=UTF-8
93
+ Content-Encoding:
94
+ - gzip
95
+ X-Content-Type-Options:
96
+ - nosniff
97
+ X-Frame-Options:
98
+ - SAMEORIGIN
99
+ X-Xss-Protection:
100
+ - 1; mode=block
101
+ Server:
102
+ - GSE
103
+ Alt-Svc:
104
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
105
+ ma=2592000,h3-Q043=":443"; ma=2592000
106
+ Transfer-Encoding:
107
+ - chunked
108
+ body:
109
+ encoding: UTF-8
110
+ string: |
111
+ {
112
+ "kind": "androidpublisher#subscriptionPurchase",
113
+ "startTimeMillis": "123456789",
114
+ "expiryTimeMillis": "123456789",
115
+ "autoRenewing": false,
116
+ "priceCurrencyCode": "EUR",
117
+ "priceAmountMicros": "123",
118
+ "countryCode": "DE",
119
+ "developerPayload": "",
120
+ "cancelReason": 1,
121
+ "orderId": "123",
122
+ "purchaseType": 0,
123
+ "acknowledgementState": 1
124
+ }
125
+ http_version:
126
+ recorded_at: Thu, 24 Oct 2019 10:43:13 GMT
127
+ recorded_with: VCR 5.0.0
@@ -0,0 +1,48 @@
1
+ require "spec_helper"
2
+
3
+ describe CandyCheck::PlayStore::Acknowledger do
4
+ let(:json_key_file) { File.expand_path("../fixtures/play_store/random_dummy_key.json", __dir__) }
5
+ subject { CandyCheck::PlayStore::Acknowledger.new(authorization: authorization) }
6
+
7
+ let(:package_name) { "fake_package_name" }
8
+ let(:product_id) { "fake_product_id" }
9
+ let(:token) { "fake_token" }
10
+
11
+ let(:authorization) { CandyCheck::PlayStore.authorization(json_key_file) }
12
+
13
+ describe "#acknowledge_product_purchase" do
14
+ it "when acknowledgement succeeds" do
15
+ VCR.use_cassette("play_store/product_acknowledgements/acknowledged") do
16
+ result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token)
17
+
18
+ result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
19
+ result.acknowledged?.must_be_true
20
+ result.error.must_be_nil
21
+ end
22
+ end
23
+ it "when already acknowledged" do
24
+ error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"errors\": [\n {\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"invalidPurchaseState\",\n \"location\": \"token\",\n \"locationType\": \"parameter\"\n }\n ]\n }\n}\n"
25
+
26
+ VCR.use_cassette("play_store/product_acknowledgements/already_acknowledged") do
27
+ result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token)
28
+
29
+ result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
30
+ result.acknowledged?.must_be_false
31
+ result.error[:body].must_equal(error_body)
32
+ result.error[:status_code].must_equal(400)
33
+ end
34
+ end
35
+ it "when it has been refunded" do
36
+ error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The product purchase is not owned by the user.\",\n \"errors\": [\n {\n \"message\": \"The product purchase is not owned by the user.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"productNotOwnedByUser\"\n }\n ]\n }\n}\n"
37
+
38
+ VCR.use_cassette("play_store/product_acknowledgements/refunded") do
39
+ result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token)
40
+
41
+ result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
42
+ result.acknowledged?.must_be_false
43
+ result.error[:body].must_equal(error_body)
44
+ result.error[:status_code].must_equal(400)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ describe CandyCheck::PlayStore::ProductAcknowledgements::Acknowledgement do
4
+ subject do
5
+ CandyCheck::PlayStore::ProductAcknowledgements::Acknowledgement.new(
6
+ package_name: package_name,
7
+ product_id: product_id,
8
+ token: token,
9
+ authorization: authorization,
10
+ )
11
+ end
12
+
13
+ let(:package_name) { "fake_package_name" }
14
+ let(:product_id) { "fake_product_id" }
15
+ let(:token) { "fake_token" }
16
+ let(:json_key_file) { File.expand_path("../../fixtures/play_store/random_dummy_key.json", __dir__) }
17
+ let(:authorization) { CandyCheck::PlayStore.authorization(json_key_file) }
18
+
19
+ describe "#call!" do
20
+ it "when acknowledgement succeeds" do
21
+ VCR.use_cassette("play_store/product_acknowledgements/acknowledged") do
22
+ result = subject.call!
23
+
24
+ result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
25
+ result.acknowledged?.must_be_true
26
+ result.error.must_be_nil
27
+ end
28
+ end
29
+ it "when already acknowledged" do
30
+ error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"errors\": [\n {\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"invalidPurchaseState\",\n \"location\": \"token\",\n \"locationType\": \"parameter\"\n }\n ]\n }\n}\n"
31
+
32
+ VCR.use_cassette("play_store/product_acknowledgements/already_acknowledged") do
33
+ result = subject.call!
34
+
35
+ result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
36
+ result.acknowledged?.must_be_false
37
+ result.error[:body].must_equal(error_body)
38
+ result.error[:status_code].must_equal(400)
39
+ end
40
+ end
41
+ it "when it has been refunded" do
42
+ error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The product purchase is not owned by the user.\",\n \"errors\": [\n {\n \"message\": \"The product purchase is not owned by the user.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"productNotOwnedByUser\"\n }\n ]\n }\n}\n"
43
+
44
+ VCR.use_cassette("play_store/product_acknowledgements/refunded") do
45
+ result = subject.call!
46
+
47
+ result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
48
+ result.acknowledged?.must_be_false
49
+ result.error[:body].must_equal(error_body)
50
+ result.error[:status_code].must_equal(400)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,66 @@
1
+ require "spec_helper"
2
+
3
+ describe CandyCheck::PlayStore::ProductAcknowledgements::Response do
4
+ subject do
5
+ CandyCheck::PlayStore::ProductAcknowledgements::Response.new(result: result, error_data: error_data)
6
+ end
7
+
8
+ describe '#acknowledged?' do
9
+ context 'when result present' do
10
+ let(:result) { '' }
11
+ let(:error_data) { nil }
12
+
13
+ it 'returns true' do
14
+ result = subject.acknowledged?
15
+
16
+ result.must_be_true
17
+ end
18
+ end
19
+
20
+ context 'when result is not present' do
21
+ let(:result) { nil }
22
+ let(:error_data) { nil }
23
+
24
+ it 'returns false' do
25
+ result = subject.acknowledged?
26
+
27
+ result.must_be_false
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#error' do
33
+ context 'when error present' do
34
+ let(:result) { nil }
35
+ let(:error_data) do
36
+ Module.new do
37
+ def status_code
38
+ 400
39
+ end
40
+ def body
41
+ 'A String describing the issue'
42
+ end
43
+ module_function :status_code, :body
44
+ end
45
+ end
46
+
47
+ it 'returns the expected data' do
48
+ result = subject.error
49
+
50
+ result[:status_code].must_equal(400)
51
+ result[:body].must_equal('A String describing the issue')
52
+ end
53
+ end
54
+
55
+ context 'when error is not present' do
56
+ let(:result) { '' }
57
+ let(:error_data) { nil }
58
+
59
+ it 'returns false' do
60
+ result = subject.error
61
+
62
+ result.must_be_nil
63
+ end
64
+ end
65
+ end
66
+ end