maxmind-geoip2 1.4.0 → 1.5.1

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +1 -1
  4. data/lib/maxmind/geoip2/record/traits.rb +9 -1
  5. data/lib/maxmind/geoip2/version.rb +1 -1
  6. metadata +9 -91
  7. data/CLAUDE.md +0 -390
  8. data/Gemfile +0 -5
  9. data/Gemfile.lock +0 -126
  10. data/README.dev.md +0 -4
  11. data/Rakefile +0 -14
  12. data/maxmind-geoip2.gemspec +0 -41
  13. data/test/data/LICENSE-APACHE +0 -202
  14. data/test/data/LICENSE-MIT +0 -17
  15. data/test/data/MaxMind-DB-spec.md +0 -573
  16. data/test/data/README.md +0 -11
  17. data/test/data/bad-data/README.md +0 -7
  18. data/test/data/bad-data/libmaxminddb/libmaxminddb-offset-integer-overflow.mmdb +0 -0
  19. data/test/data/bad-data/maxminddb-golang/cyclic-data-structure.mmdb +0 -0
  20. data/test/data/bad-data/maxminddb-golang/invalid-bytes-length.mmdb +0 -1
  21. data/test/data/bad-data/maxminddb-golang/invalid-data-record-offset.mmdb +0 -0
  22. data/test/data/bad-data/maxminddb-golang/invalid-map-key-length.mmdb +0 -0
  23. data/test/data/bad-data/maxminddb-golang/invalid-string-length.mmdb +0 -1
  24. data/test/data/bad-data/maxminddb-golang/metadata-is-an-uint128.mmdb +0 -1
  25. data/test/data/bad-data/maxminddb-golang/unexpected-bytes.mmdb +0 -0
  26. data/test/data/bad-data/maxminddb-python/bad-unicode-in-map-key.mmdb +0 -0
  27. data/test/data/cmd/write-test-data/main.go +0 -68
  28. data/test/data/go.mod +0 -13
  29. data/test/data/go.sum +0 -16
  30. data/test/data/pkg/writer/decoder.go +0 -178
  31. data/test/data/pkg/writer/geoip2.go +0 -184
  32. data/test/data/pkg/writer/ip.go +0 -39
  33. data/test/data/pkg/writer/maxmind.go +0 -246
  34. data/test/data/pkg/writer/nestedstructures.go +0 -73
  35. data/test/data/pkg/writer/writer.go +0 -61
  36. data/test/data/source-data/GeoIP-Anonymous-Plus-Test.json +0 -175
  37. data/test/data/source-data/GeoIP2-Anonymous-IP-Test.json +0 -55
  38. data/test/data/source-data/GeoIP2-City-Test.json +0 -13272
  39. data/test/data/source-data/GeoIP2-Connection-Type-Test.json +0 -127
  40. data/test/data/source-data/GeoIP2-Country-Test.json +0 -15978
  41. data/test/data/source-data/GeoIP2-DensityIncome-Test.json +0 -14
  42. data/test/data/source-data/GeoIP2-Domain-Test.json +0 -457
  43. data/test/data/source-data/GeoIP2-Enterprise-Test.json +0 -1110
  44. data/test/data/source-data/GeoIP2-IP-Risk-Test.json +0 -31
  45. data/test/data/source-data/GeoIP2-ISP-Test.json +0 -12605
  46. data/test/data/source-data/GeoIP2-Precision-Enterprise-Sandbox-Test.json +0 -296
  47. data/test/data/source-data/GeoIP2-Precision-Enterprise-Test.json +0 -3189
  48. data/test/data/source-data/GeoIP2-Static-IP-Score-Test.json +0 -2147
  49. data/test/data/source-data/GeoIP2-User-Count-Test.json +0 -2855
  50. data/test/data/source-data/GeoLite2-ASN-Test.json +0 -4120
  51. data/test/data/source-data/GeoLite2-City-Test.json +0 -12969
  52. data/test/data/source-data/GeoLite2-Country-Test.json +0 -11369
  53. data/test/data/test-data/GeoIP-Anonymous-Plus-Test.mmdb +0 -0
  54. data/test/data/test-data/GeoIP2-Anonymous-IP-Test.mmdb +0 -0
  55. data/test/data/test-data/GeoIP2-City-Test-Broken-Double-Format.mmdb +0 -0
  56. data/test/data/test-data/GeoIP2-City-Test-Invalid-Node-Count.mmdb +0 -0
  57. data/test/data/test-data/GeoIP2-City-Test.mmdb +0 -0
  58. data/test/data/test-data/GeoIP2-Connection-Type-Test.mmdb +0 -0
  59. data/test/data/test-data/GeoIP2-Country-Test.mmdb +0 -0
  60. data/test/data/test-data/GeoIP2-DensityIncome-Test.mmdb +0 -0
  61. data/test/data/test-data/GeoIP2-Domain-Test.mmdb +0 -0
  62. data/test/data/test-data/GeoIP2-Enterprise-Test.mmdb +0 -0
  63. data/test/data/test-data/GeoIP2-IP-Risk-Test.mmdb +0 -0
  64. data/test/data/test-data/GeoIP2-ISP-Test.mmdb +0 -0
  65. data/test/data/test-data/GeoIP2-Precision-Enterprise-Test.mmdb +0 -0
  66. data/test/data/test-data/GeoIP2-Static-IP-Score-Test.mmdb +0 -0
  67. data/test/data/test-data/GeoIP2-User-Count-Test.mmdb +0 -0
  68. data/test/data/test-data/GeoLite2-ASN-Test.mmdb +0 -0
  69. data/test/data/test-data/GeoLite2-City-Test.mmdb +0 -0
  70. data/test/data/test-data/GeoLite2-Country-Test.mmdb +0 -0
  71. data/test/data/test-data/MaxMind-DB-no-ipv4-search-tree.mmdb +0 -0
  72. data/test/data/test-data/MaxMind-DB-string-value-entries.mmdb +0 -0
  73. data/test/data/test-data/MaxMind-DB-test-broken-pointers-24.mmdb +0 -0
  74. data/test/data/test-data/MaxMind-DB-test-broken-search-tree-24.mmdb +0 -0
  75. data/test/data/test-data/MaxMind-DB-test-decoder.mmdb +0 -0
  76. data/test/data/test-data/MaxMind-DB-test-ipv4-24.mmdb +0 -0
  77. data/test/data/test-data/MaxMind-DB-test-ipv4-28.mmdb +0 -0
  78. data/test/data/test-data/MaxMind-DB-test-ipv4-32.mmdb +0 -0
  79. data/test/data/test-data/MaxMind-DB-test-ipv6-24.mmdb +0 -0
  80. data/test/data/test-data/MaxMind-DB-test-ipv6-28.mmdb +0 -0
  81. data/test/data/test-data/MaxMind-DB-test-ipv6-32.mmdb +0 -0
  82. data/test/data/test-data/MaxMind-DB-test-metadata-pointers.mmdb +0 -0
  83. data/test/data/test-data/MaxMind-DB-test-mixed-24.mmdb +0 -0
  84. data/test/data/test-data/MaxMind-DB-test-mixed-28.mmdb +0 -0
  85. data/test/data/test-data/MaxMind-DB-test-mixed-32.mmdb +0 -0
  86. data/test/data/test-data/MaxMind-DB-test-nested.mmdb +0 -0
  87. data/test/data/test-data/MaxMind-DB-test-pointer-decoder.mmdb +0 -0
  88. data/test/data/test-data/README.md +0 -42
  89. data/test/data/test-data/maps-with-pointers.raw +0 -0
  90. data/test/data/tidyall.ini +0 -5
  91. data/test/test_client.rb +0 -467
  92. data/test/test_model_country.rb +0 -95
  93. data/test/test_model_names.rb +0 -50
  94. data/test/test_reader.rb +0 -589
@@ -1,42 +0,0 @@
1
- ## How to generate test data
2
- Use the [write-test-data](https://github.com/maxmind/MaxMind-DB/blob/main/cmd/write-test-data)
3
- go tool to create a small set of test databases with a variety of data and
4
- record sizes.
5
-
6
- These test databases are useful for testing code that reads MaxMind DB files.
7
-
8
- There are several ways to figure out what IP addresses are actually in the
9
- test databases. You can take a look at the
10
- [source-data directory](https://github.com/maxmind/MaxMind-DB/tree/main/source-data)
11
- in this repository. This directory contains JSON files which are used to
12
- generate many (but not all) of the database files.
13
-
14
- You can also use the
15
- [mmdb-dump-database script](https://github.com/maxmind/MaxMind-DB-Reader-perl/blob/main/eg/mmdb-dump-database)
16
- in the
17
- [MaxMind-DB-Reader-perl repository](https://github.com/maxmind/MaxMind-DB-Reader-perl).
18
-
19
- ## Static test data
20
- Some of the test files are remnants of the
21
- [old perl test data writer](https://github.com/maxmind/MaxMind-DB/blob/f0a85c671c5b6e9c5e514bd66162724ee1dedea3/test-data/write-test-data.pl)
22
- and cannot be generated with the go tool. These databases are intentionally broken,
23
- and exploited functionality simply not available in the go mmdbwriter:
24
-
25
- - MaxMind-DB-test-broken-pointers-24.mmdb
26
- - MaxMind-DB-test-broken-search-tree-24.mmdb
27
- - MaxMind-DB-test-pointer-decoder.mmdb
28
- - GeoIP2-City-Test-Broken-Double-Format.mmdb
29
- - GeoIP2-City-Test-Invalid-Node-Count.mmdb
30
- - maps-with-pointers.raw
31
-
32
- ## Usage
33
- ```
34
- Usage of ./write-test-data:
35
- -source string
36
- Source data directory
37
- -target string
38
- Destination directory for the generated mmdb files
39
- ```
40
-
41
- Example:
42
- `./write-test-data --source ../../source-data --target ../../test-data`
@@ -1,5 +0,0 @@
1
- [PerlTidy]
2
- select = **/*.{pl,pm,t}
3
-
4
- [JSON]
5
- select = **/*.json
data/test/test_client.rb DELETED
@@ -1,467 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'maxmind/geoip2'
5
- require 'minitest/autorun'
6
- require 'webmock/minitest'
7
-
8
- class ClientTest < Minitest::Test
9
- COUNTRY = {
10
- 'continent' => {
11
- 'code' => 'NA',
12
- 'geoname_id' => 42,
13
- 'names' => { 'en' => 'North America' },
14
- },
15
- 'country' => {
16
- 'geoname_id' => 1,
17
- 'iso_code' => 'US',
18
- 'names' => { 'en' => 'United States of America' },
19
- },
20
- 'maxmind' => {
21
- 'queries_remaining' => 11,
22
- },
23
- 'traits' => {
24
- 'ip_address' => '1.2.3.4',
25
- 'is_anycast' => true,
26
- 'network' => '1.2.3.0/24',
27
- },
28
- }.freeze
29
-
30
- INSIGHTS = {
31
- 'anonymizer' => {
32
- 'confidence' => 85,
33
- 'is_anonymous' => true,
34
- 'is_anonymous_vpn' => true,
35
- 'is_hosting_provider' => false,
36
- 'is_public_proxy' => false,
37
- 'is_residential_proxy' => true,
38
- 'is_tor_exit_node' => false,
39
- 'network_last_seen' => '2025-10-15',
40
- 'provider_name' => 'NordVPN',
41
- },
42
- 'continent' => {
43
- 'code' => 'NA',
44
- 'geoname_id' => 42,
45
- 'names' => { 'en' => 'North America' },
46
- },
47
- 'country' => {
48
- 'geoname_id' => 1,
49
- 'iso_code' => 'US',
50
- 'names' => { 'en' => 'United States of America' },
51
- },
52
- 'maxmind' => {
53
- 'queries_remaining' => 11,
54
- },
55
- 'traits' => {
56
- 'ip_address' => '1.2.3.40',
57
- 'ip_risk_snapshot' => 45.5,
58
- 'is_anycast' => true,
59
- 'is_residential_proxy' => true,
60
- 'network' => '1.2.3.0/24',
61
- 'static_ip_score' => 1.3,
62
- 'user_count' => 2,
63
- },
64
- }.freeze
65
-
66
- CONTENT_TYPES = {
67
- country: 'application/vnd.maxmind.com-country+json; charset=UTF-8; version=2.1',
68
- }.freeze
69
-
70
- def test_country
71
- record = request(:country, '1.2.3.4')
72
-
73
- assert_instance_of(MaxMind::GeoIP2::Model::Country, record)
74
-
75
- assert_equal(42, record.continent.geoname_id)
76
- assert_equal('NA', record.continent.code)
77
- assert_equal({ 'en' => 'North America' }, record.continent.names)
78
- assert_equal('North America', record.continent.name)
79
-
80
- assert_equal(1, record.country.geoname_id)
81
- refute(record.country.in_european_union?)
82
- assert_equal('US', record.country.iso_code)
83
- assert_equal({ 'en' => 'United States of America' }, record.country.names)
84
- assert_equal('United States of America', record.country.name)
85
-
86
- assert_equal(11, record.maxmind.queries_remaining)
87
-
88
- refute(record.registered_country.in_european_union?)
89
-
90
- assert(record.traits.anycast?)
91
- assert_equal('1.2.3.0/24', record.traits.network)
92
- end
93
-
94
- def test_insights
95
- record = request(:insights, '1.2.3.40')
96
-
97
- assert_instance_of(MaxMind::GeoIP2::Model::Insights, record)
98
-
99
- assert_equal(42, record.continent.geoname_id)
100
-
101
- # Test anonymizer object
102
- assert_equal(85, record.anonymizer.confidence)
103
- assert(record.anonymizer.anonymous?)
104
- assert(record.anonymizer.anonymous_vpn?)
105
- refute(record.anonymizer.hosting_provider?)
106
- refute(record.anonymizer.public_proxy?)
107
- assert(record.anonymizer.residential_proxy?)
108
- refute(record.anonymizer.tor_exit_node?)
109
- assert_equal(Date.parse('2025-10-15'), record.anonymizer.network_last_seen)
110
- assert_equal('NordVPN', record.anonymizer.provider_name)
111
-
112
- # Test traits
113
- assert(record.traits.anycast?)
114
- assert(record.traits.residential_proxy?)
115
- assert_equal('1.2.3.0/24', record.traits.network)
116
- assert_in_delta(1.3, record.traits.static_ip_score)
117
- assert_equal(2, record.traits.user_count)
118
- assert_in_delta(45.5, record.traits.ip_risk_snapshot)
119
- end
120
-
121
- def test_city
122
- record = request(:city, '1.2.3.4')
123
-
124
- assert_instance_of(MaxMind::GeoIP2::Model::City, record)
125
-
126
- assert_equal('1.2.3.0/24', record.traits.network)
127
- end
128
-
129
- def test_me
130
- record = request(:city, 'me')
131
-
132
- assert_instance_of(MaxMind::GeoIP2::Model::City, record)
133
- end
134
-
135
- def test_no_body_error
136
- assert_raises(
137
- JSON::ParserError,
138
- ) { request(:country, '1.2.3.5') }
139
- end
140
-
141
- def test_bad_body_error
142
- assert_raises(
143
- JSON::ParserError,
144
- ) { request(:country, '2.2.3.5') }
145
- end
146
-
147
- def test_non_json_success_response
148
- error = assert_raises(
149
- MaxMind::GeoIP2::HTTPError,
150
- ) { request(:country, '3.2.3.5') }
151
-
152
- assert_equal(
153
- 'Received a success response for country but it is not JSON: extra bad body',
154
- error.message,
155
- )
156
- end
157
-
158
- def test_invalid_ip_error_from_web_service
159
- error = assert_raises(
160
- MaxMind::GeoIP2::AddressInvalidError,
161
- ) { request(:country, '1.2.3.6') }
162
-
163
- assert_equal(
164
- 'The value "1.2.3" is not a valid IP address',
165
- error.message,
166
- )
167
- end
168
-
169
- def test_invalid_ip_error_from_client
170
- error = assert_raises(
171
- MaxMind::GeoIP2::AddressInvalidError,
172
- ) { request(:country, '1.2.3') }
173
-
174
- assert_equal(
175
- 'The value "1.2.3" is not a valid IP address',
176
- error.message,
177
- )
178
- end
179
-
180
- def test_no_error_body_ip_error
181
- assert_raises(
182
- JSON::ParserError,
183
- ) { request(:country, '1.2.3.7') }
184
- end
185
-
186
- def test_missing_key_ip_error
187
- error = assert_raises(
188
- MaxMind::GeoIP2::HTTPError,
189
- ) { request(:country, '1.2.3.71') }
190
-
191
- assert_equal(
192
- 'Received client error response (400) that is JSON but does not specify code or error keys: {"code":"HI"}',
193
- error.message,
194
- )
195
- end
196
-
197
- def test_weird_error_body_ip_error
198
- error = assert_raises(
199
- MaxMind::GeoIP2::HTTPError,
200
- ) { request(:country, '1.2.3.8') }
201
-
202
- assert_equal(
203
- 'Received client error response (400) that is JSON but does not specify code or error keys: {"weird":42}',
204
- error.message,
205
- )
206
- end
207
-
208
- def test_500_error
209
- error = assert_raises(
210
- MaxMind::GeoIP2::HTTPError,
211
- ) { request(:country, '1.2.3.10') }
212
-
213
- assert_equal(
214
- 'Received server error response (500) for country with body foo',
215
- error.message,
216
- )
217
- end
218
-
219
- def test_300_response
220
- error = assert_raises(
221
- MaxMind::GeoIP2::HTTPError,
222
- ) { request(:country, '1.2.3.11') }
223
-
224
- assert_equal(
225
- 'Received unexpected response (300) for country with body bar',
226
- error.message,
227
- )
228
- end
229
-
230
- def test_406_error
231
- error = assert_raises(
232
- MaxMind::GeoIP2::HTTPError,
233
- ) { request(:country, '1.2.3.12') }
234
-
235
- assert_equal(
236
- 'Received client error response (406) for country but it is not JSON: Cannot satisfy your Accept-Charset requirements',
237
- error.message,
238
- )
239
- end
240
-
241
- def test_address_not_found_error
242
- error = assert_raises(
243
- MaxMind::GeoIP2::AddressNotFoundError,
244
- ) { request(:country, '1.2.3.13') }
245
-
246
- assert_equal(
247
- 'The address "1.2.3.13" is not in our database.',
248
- error.message,
249
- )
250
- end
251
-
252
- def test_address_reserved_error
253
- error = assert_raises(
254
- MaxMind::GeoIP2::AddressReservedError,
255
- ) { request(:country, '1.2.3.14') }
256
-
257
- assert_equal(
258
- 'The address "1.2.3.14" is a private address.',
259
- error.message,
260
- )
261
- end
262
-
263
- def test_authorization_error
264
- error = assert_raises(
265
- MaxMind::GeoIP2::AuthenticationError,
266
- ) { request(:country, '1.2.3.15') }
267
-
268
- assert_equal(
269
- 'An account ID and license key are required to use this service.',
270
- error.message,
271
- )
272
- end
273
-
274
- def test_missing_license_key_error
275
- error = assert_raises(
276
- MaxMind::GeoIP2::AuthenticationError,
277
- ) { request(:country, '1.2.3.16') }
278
-
279
- assert_equal(
280
- 'A license key is required to use this service.',
281
- error.message,
282
- )
283
- end
284
-
285
- def test_missing_account_id_error
286
- error = assert_raises(
287
- MaxMind::GeoIP2::AuthenticationError,
288
- ) { request(:country, '1.2.3.17') }
289
-
290
- assert_equal(
291
- 'An account ID is required to use this service.',
292
- error.message,
293
- )
294
- end
295
-
296
- def test_insufficient_funds_error
297
- error = assert_raises(
298
- MaxMind::GeoIP2::InsufficientFundsError,
299
- ) { request(:country, '1.2.3.18') }
300
-
301
- assert_equal(
302
- 'The license key you have provided is out of queries.',
303
- error.message,
304
- )
305
- end
306
-
307
- def test_unexpected_code_error
308
- error = assert_raises(
309
- MaxMind::GeoIP2::InvalidRequestError,
310
- ) { request(:country, '1.2.3.19') }
311
-
312
- assert_equal(
313
- 'Whoa!',
314
- error.message,
315
- )
316
- end
317
-
318
- def request(method, ip_address)
319
- response = get_response(ip_address)
320
-
321
- stub_request(:get, /geoip/)
322
- .to_return(
323
- body: response[:body],
324
- headers: response[:headers],
325
- status: response[:status],
326
- )
327
-
328
- client = MaxMind::GeoIP2::Client.new(
329
- account_id: 42,
330
- license_key: 'abcdef123456',
331
- )
332
-
333
- client.send(method, ip_address)
334
- end
335
-
336
- def get_response(ip_address)
337
- responses = {
338
- 'me' => {
339
- body: JSON.generate(COUNTRY),
340
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
341
- status: 200,
342
- },
343
- '1.2.3' => {},
344
- '1.2.3.4' => {
345
- body: JSON.generate(COUNTRY),
346
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
347
- status: 200,
348
- },
349
- '1.2.3.5' => {
350
- body: '',
351
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
352
- status: 200,
353
- },
354
- '2.2.3.5' => {
355
- body: 'bad body',
356
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
357
- status: 200,
358
- },
359
- '3.2.3.5' => {
360
- body: 'extra bad body',
361
- headers: {},
362
- status: 200,
363
- },
364
- '1.2.3.40' => {
365
- body: JSON.generate(INSIGHTS),
366
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
367
- status: 200,
368
- },
369
- '1.2.3.6' => {
370
- body: JSON.generate({
371
- 'code' => 'IP_ADDRESS_INVALID',
372
- 'error' => 'The value "1.2.3" is not a valid IP address',
373
- }),
374
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
375
- status: 400,
376
- },
377
- '1.2.3.7' => {
378
- body: '',
379
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
380
- status: 400,
381
- },
382
- '1.2.3.71' => {
383
- body: JSON.generate({ code: 'HI' }),
384
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
385
- status: 400,
386
- },
387
- '1.2.3.8' => {
388
- body: JSON.generate({ weird: 42 }),
389
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
390
- status: 400,
391
- },
392
- '1.2.3.10' => {
393
- body: 'foo',
394
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
395
- status: 500,
396
- },
397
- '1.2.3.11' => {
398
- body: 'bar',
399
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
400
- status: 300,
401
- },
402
- '1.2.3.12' => {
403
- body: 'Cannot satisfy your Accept-Charset requirements',
404
- headers: {},
405
- status: 406,
406
- },
407
- '1.2.3.13' => {
408
- body: JSON.generate({
409
- 'code' => 'IP_ADDRESS_NOT_FOUND',
410
- 'error' => 'The address "1.2.3.13" is not in our database.',
411
- }),
412
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
413
- status: 400,
414
- },
415
- '1.2.3.14' => {
416
- body: JSON.generate({
417
- 'code' => 'IP_ADDRESS_RESERVED',
418
- 'error' => 'The address "1.2.3.14" is a private address.',
419
- }),
420
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
421
- status: 400,
422
- },
423
- '1.2.3.15' => {
424
- body: JSON.generate({
425
- 'code' => 'AUTHORIZATION_INVALID',
426
- 'error' => 'An account ID and license key are required to use this service.',
427
- }),
428
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
429
- status: 401,
430
- },
431
- '1.2.3.16' => {
432
- body: JSON.generate({
433
- 'code' => 'LICENSE_KEY_REQUIRED',
434
- 'error' => 'A license key is required to use this service.',
435
- }),
436
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
437
- status: 401,
438
- },
439
- '1.2.3.17' => {
440
- body: JSON.generate({
441
- 'code' => 'ACCOUNT_ID_REQUIRED',
442
- 'error' => 'An account ID is required to use this service.',
443
- }),
444
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
445
- status: 401,
446
- },
447
- '1.2.3.18' => {
448
- body: JSON.generate({
449
- 'code' => 'INSUFFICIENT_FUNDS',
450
- 'error' => 'The license key you have provided is out of queries.',
451
- }),
452
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
453
- status: 402,
454
- },
455
- '1.2.3.19' => {
456
- body: JSON.generate({
457
- 'code' => 'UNEXPECTED',
458
- 'error' => 'Whoa!',
459
- }),
460
- headers: { 'Content-Type': CONTENT_TYPES[:country] },
461
- status: 400,
462
- },
463
- }
464
-
465
- responses[ip_address]
466
- end
467
- end