gandi_v5 0.1.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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +20 -0
  5. data/.travis.yml +23 -0
  6. data/CHANGELOG.md +3 -0
  7. data/Gemfile +6 -0
  8. data/Guardfile +40 -0
  9. data/LICENSE.md +32 -0
  10. data/README.md +94 -0
  11. data/Rakefile +3 -0
  12. data/TODO.md +29 -0
  13. data/bin/console +13 -0
  14. data/gandi_v5.gemspec +41 -0
  15. data/lib/gandi_v5/billing/info/prepaid.rb +33 -0
  16. data/lib/gandi_v5/billing/info.rb +26 -0
  17. data/lib/gandi_v5/billing.rb +28 -0
  18. data/lib/gandi_v5/data/converter/array_of.rb +35 -0
  19. data/lib/gandi_v5/data/converter/symbol.rb +26 -0
  20. data/lib/gandi_v5/data/converter/time.rb +28 -0
  21. data/lib/gandi_v5/data/converter.rb +41 -0
  22. data/lib/gandi_v5/data.rb +244 -0
  23. data/lib/gandi_v5/domain/auto_renew.rb +64 -0
  24. data/lib/gandi_v5/domain/contact.rb +102 -0
  25. data/lib/gandi_v5/domain/contract.rb +22 -0
  26. data/lib/gandi_v5/domain/dates.rb +44 -0
  27. data/lib/gandi_v5/domain/renewal_information.rb +41 -0
  28. data/lib/gandi_v5/domain/restore_information.rb +18 -0
  29. data/lib/gandi_v5/domain/sharing_space.rb +21 -0
  30. data/lib/gandi_v5/domain.rb +431 -0
  31. data/lib/gandi_v5/email/mailbox/responder.rb +36 -0
  32. data/lib/gandi_v5/email/mailbox.rb +236 -0
  33. data/lib/gandi_v5/email/offer.rb +27 -0
  34. data/lib/gandi_v5/email/slot.rb +134 -0
  35. data/lib/gandi_v5/email.rb +11 -0
  36. data/lib/gandi_v5/error/gandi_error.rb +21 -0
  37. data/lib/gandi_v5/error.rb +9 -0
  38. data/lib/gandi_v5/live_dns/domain.rb +211 -0
  39. data/lib/gandi_v5/live_dns/record_set.rb +79 -0
  40. data/lib/gandi_v5/live_dns/zone/snapshot.rb +62 -0
  41. data/lib/gandi_v5/live_dns/zone.rb +301 -0
  42. data/lib/gandi_v5/live_dns.rb +30 -0
  43. data/lib/gandi_v5/organization.rb +66 -0
  44. data/lib/gandi_v5/version.rb +5 -0
  45. data/lib/gandi_v5.rb +178 -0
  46. data/spec/.rubocop.yml +4 -0
  47. data/spec/features/domain_spec.rb +45 -0
  48. data/spec/features/livedns_domain_spec.rb +8 -0
  49. data/spec/features/livedns_zone_spec.rb +45 -0
  50. data/spec/features/mailbox_spec.rb +18 -0
  51. data/spec/fixtures/bodies/GandiV5_Billing/info.yaml +10 -0
  52. data/spec/fixtures/bodies/GandiV5_Domain/availability.yaml +15 -0
  53. data/spec/fixtures/bodies/GandiV5_Domain/fetch_contacts.yaml +8 -0
  54. data/spec/fixtures/bodies/GandiV5_Domain/get.yaml +37 -0
  55. data/spec/fixtures/bodies/GandiV5_Domain/list.yaml +20 -0
  56. data/spec/fixtures/bodies/GandiV5_Domain/renewal_info.yaml +12 -0
  57. data/spec/fixtures/bodies/GandiV5_Domain/restore_info.yaml +5 -0
  58. data/spec/fixtures/bodies/GandiV5_Domain/tld.yaml +10 -0
  59. data/spec/fixtures/bodies/GandiV5_Domain/tlds.yaml +7 -0
  60. data/spec/fixtures/bodies/GandiV5_Email_Mailbox/get.yaml +16 -0
  61. data/spec/fixtures/bodies/GandiV5_Email_Mailbox/list.yaml +8 -0
  62. data/spec/fixtures/bodies/GandiV5_Email_Slot/get.yaml +10 -0
  63. data/spec/fixtures/bodies/GandiV5_Email_Slot/list.yaml +8 -0
  64. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/get.yaml +4 -0
  65. data/spec/fixtures/bodies/GandiV5_LiveDNS_Domain/list.yaml +2 -0
  66. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/get.yaml +11 -0
  67. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone/list.yaml +11 -0
  68. data/spec/fixtures/bodies/GandiV5_LiveDNS_Zone_Snapshot/get.yaml +9 -0
  69. data/spec/fixtures/bodies/GandiV5_Organization/get.yaml +17 -0
  70. data/spec/fixtures/vcr/Domain_features/List_domains.yml +54 -0
  71. data/spec/fixtures/vcr/Domain_features/Renew_domain.yml +133 -0
  72. data/spec/fixtures/vcr/LiveDNS_Domain_features/List_domains.yml +32 -0
  73. data/spec/fixtures/vcr/LiveDNS_Zone_features/List_zones.yml +42 -0
  74. data/spec/fixtures/vcr/LiveDNS_Zone_features/Make_and_save_snapshot.yml +72 -0
  75. data/spec/fixtures/vcr/LiveDNS_Zone_features/Save_zone_to_file.yml +28 -0
  76. data/spec/fixtures/vcr/Mailbox_features/List_mailboxes.yml +39 -0
  77. data/spec/spec_helper.rb +60 -0
  78. data/spec/test.env +1 -0
  79. data/spec/units/gandi_v5/billing/info/prepaid_spec.rb +20 -0
  80. data/spec/units/gandi_v5/billing/info_spec.rb +4 -0
  81. data/spec/units/gandi_v5/billing_spec.rb +41 -0
  82. data/spec/units/gandi_v5/data/converter/array_of_spec.rb +18 -0
  83. data/spec/units/gandi_v5/data/converter/symbol_spec.rb +16 -0
  84. data/spec/units/gandi_v5/data/converter/time_spec.rb +16 -0
  85. data/spec/units/gandi_v5/data/converter_spec.rb +31 -0
  86. data/spec/units/gandi_v5/data_spec.rb +340 -0
  87. data/spec/units/gandi_v5/domain/auto_renew_spec.rb +70 -0
  88. data/spec/units/gandi_v5/domain/contact_spec.rb +36 -0
  89. data/spec/units/gandi_v5/domain/contract_spec.rb +4 -0
  90. data/spec/units/gandi_v5/domain/dates_spec.rb +4 -0
  91. data/spec/units/gandi_v5/domain/renewal_information_spec.rb +81 -0
  92. data/spec/units/gandi_v5/domain/restore_information_spec.rb +4 -0
  93. data/spec/units/gandi_v5/domain/sharing_space_spec.rb +4 -0
  94. data/spec/units/gandi_v5/domain_spec.rb +451 -0
  95. data/spec/units/gandi_v5/email/mailbox/responder_spec.rb +131 -0
  96. data/spec/units/gandi_v5/email/mailbox_spec.rb +384 -0
  97. data/spec/units/gandi_v5/email/offer_spec.rb +17 -0
  98. data/spec/units/gandi_v5/email/slot_spec.rb +102 -0
  99. data/spec/units/gandi_v5/error/gandi_error_spec.rb +30 -0
  100. data/spec/units/gandi_v5/error_spec.rb +4 -0
  101. data/spec/units/gandi_v5/live_dns/domain_spec.rb +247 -0
  102. data/spec/units/gandi_v5/live_dns/record_set_spec.rb +74 -0
  103. data/spec/units/gandi_v5/live_dns/zone/snapshot_spec.rb +37 -0
  104. data/spec/units/gandi_v5/live_dns/zone_spec.rb +329 -0
  105. data/spec/units/gandi_v5/live_dns_spec.rb +17 -0
  106. data/spec/units/gandi_v5/organization_spec.rb +30 -0
  107. data/spec/units/gandi_v5_spec.rb +204 -0
  108. metadata +406 -0
@@ -0,0 +1,329 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5::LiveDNS::Zone do
4
+ subject do
5
+ described_class.new(
6
+ name: 'Zone Name',
7
+ uuid: 'zone-uuid',
8
+ soa_retry: 1,
9
+ soa_minimum: 2,
10
+ soa_refresh: 3,
11
+ soa_expire: 4,
12
+ soa_serial: 5,
13
+ soa_email: 'admin.example.com',
14
+ soa_primary_ns: '192.168.0.1'
15
+ )
16
+ end
17
+
18
+ it '#to_s' do
19
+ expect(subject.to_s).to eq [
20
+ '@ IN SOA 192.168.0.1 admin.example.com (',
21
+ "\t5\t;Serial",
22
+ "\t3\t\t;Refresh",
23
+ "\t1\t\t;Retry",
24
+ "\t4\t\t;Expire",
25
+ "\t2\t\t;Minimum & Negative TTL",
26
+ ')'
27
+ ].join("\n")
28
+ end
29
+
30
+ describe '#fetch_records' do
31
+ it 'All of them' do
32
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/records')
33
+ .and_return([])
34
+
35
+ expect(subject.fetch_records).to eq []
36
+ end
37
+
38
+ it 'All for a name' do
39
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name')
40
+ .and_return([])
41
+
42
+ expect(subject.fetch_records('name')).to eq []
43
+ end
44
+
45
+ it 'A type for a name' do
46
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT')
47
+ .and_return(
48
+ [
49
+ {
50
+ 'rrset_type' => 'TXT',
51
+ 'rrset_name' => 'name',
52
+ 'rrset_ttl' => 600,
53
+ 'rrset_values' => %w[a b]
54
+ }
55
+ ]
56
+ )
57
+
58
+ records = subject.fetch_records('name', 'TXT')
59
+ expect(records.count).to eq 1
60
+ expect(records.first.type).to eq 'TXT'
61
+ expect(records.first.ttl).to eq 600
62
+ expect(records.first.name).to eq 'name'
63
+ expect(records.first.values).to match_array %w[a b]
64
+ end
65
+
66
+ it 'Invalid type' do
67
+ expect { subject.fetch_records 'name', 'INVALID-TYPE' }.to raise_error ArgumentError
68
+ end
69
+ end
70
+
71
+ describe '#fetch_zone_lines' do
72
+ it 'All of them' do
73
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
74
+ expect(GandiV5).to receive(:get).with(url, accept: 'text/plain').and_return('returned')
75
+ expect(subject.fetch_zone_lines).to eq 'returned'
76
+ end
77
+
78
+ it 'All for a name' do
79
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name'
80
+ expect(GandiV5).to receive(:get).with(url, accept: 'text/plain').and_return('returned')
81
+ expect(subject.fetch_zone_lines('name')).to eq 'returned'
82
+ end
83
+
84
+ it 'A type for a name' do
85
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT'
86
+ expect(GandiV5).to receive(:get).with(url, accept: 'text/plain').and_return('returned')
87
+ expect(subject.fetch_zone_lines('name', 'TXT')).to eq 'returned'
88
+ end
89
+
90
+ it 'Invalid type' do
91
+ expect { subject.fetch_zone_lines 'name', 'INVALID-TYPE' }.to raise_error ArgumentError
92
+ end
93
+ end
94
+
95
+ describe '#add_record' do
96
+ it 'Success' do
97
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
98
+ body = '{"rrset_name":"name","rrset_type":"TXT","rrset_ttl":900,"rrset_values":["a","b"]}'
99
+ expect(GandiV5).to receive(:post).with(url, body).and_return('message' => 'Confirmation message.')
100
+ expect(subject.add_record('name', 'TXT', 900, 'a', 'b')).to eq 'Confirmation message.'
101
+ end
102
+
103
+ it 'Bad type' do
104
+ expect { subject.add_record 'name', 'INVALID-TYPE', 900, 'a' }.to raise_error ArgumentError
105
+ end
106
+
107
+ it 'Bad TTL' do
108
+ expect { subject.add_record 'name', 'TXT', -900, 'a' }.to raise_error(
109
+ ArgumentError,
110
+ 'ttl must be positive and non-zero'
111
+ )
112
+ end
113
+
114
+ it 'No values' do
115
+ expect { subject.add_record 'name', 'TXT', 900 }.to raise_error(
116
+ ArgumentError,
117
+ 'there must be at least one value'
118
+ )
119
+ end
120
+ end
121
+
122
+ describe '#delete_records' do
123
+ it 'All of them' do
124
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
125
+ expect(GandiV5).to receive(:delete).with(url)
126
+ subject.delete_records
127
+ end
128
+
129
+ it 'All for a name' do
130
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name'
131
+ expect(GandiV5).to receive(:delete).with(url)
132
+ subject.delete_records 'name'
133
+ end
134
+
135
+ it 'A type for a name' do
136
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT'
137
+ expect(GandiV5).to receive(:delete).with(url)
138
+ subject.delete_records 'name', 'TXT'
139
+ end
140
+
141
+ it 'Invalid type' do
142
+ expect { subject.delete_records 'name', 'INVALID-TYPE' }.to raise_error ArgumentError
143
+ end
144
+ end
145
+
146
+ describe '#replace_records' do
147
+ it 'Given array of GandiV5::LiveDNS::RecordSet' do
148
+ records = [
149
+ { name: 'name', ttl: 600, type: 'TXT', values: ['a'] }
150
+ ]
151
+
152
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
153
+ body = '{"items":[{"rrset_name":"name","rrset_ttl":600,"rrset_type":"TXT","rrset_values":["a"]}]}'
154
+ expect(GandiV5).to receive(:put).with(url, body)
155
+ .and_return('message' => 'Confirmation message.')
156
+ expect(subject.replace_records(records: records)).to eq 'Confirmation message.'
157
+ end
158
+
159
+ it 'Given string' do
160
+ records = [
161
+ '@ 86400 IN A 192.168.0.1',
162
+ '* 86400 IN A 192.168.0.1'
163
+ ].join("\n")
164
+
165
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records'
166
+ expect(GandiV5).to receive(:put).with(url, records, 'content-type': 'text/plain')
167
+ .and_return('message' => 'Confirmation message.')
168
+ expect(subject.replace_records(text: records)).to eq 'Confirmation message.'
169
+ end
170
+
171
+ it 'Given both' do
172
+ expect { subject.replace_records text: '', records: [] }.to raise_error(
173
+ ArgumentError,
174
+ 'you must pass ONE of records: or text:'
175
+ )
176
+ end
177
+
178
+ it 'Given nothing' do
179
+ expect { subject.replace_records }.to raise_error ArgumentError, 'you must pass ONE of records: or text:'
180
+ end
181
+ end
182
+
183
+ it '#replace_records_for' do
184
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name'
185
+ body = '{"items":[{"rrset_type":"TXT","rrset_values":["a"]}]}'
186
+ expect(GandiV5).to receive(:put).with(url, body)
187
+ .and_return('message' => 'Confirmation message.')
188
+ records = { type: 'TXT', values: ['a'] }
189
+ expect(subject.replace_records_for('name', records)).to eq 'Confirmation message.'
190
+ end
191
+
192
+ describe '#replace_??_records_for' do
193
+ it '#replace_a_records_for' do
194
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/A'
195
+ body = '{"rrset_ttl":600,"rrset_values":["192.168.0.1","192.168.0.2"]}'
196
+ expect(GandiV5).to receive(:put).with(url, body)
197
+ .and_return('message' => 'Confirmation message.')
198
+ expect(subject.replace_a_records_for('name', 600, '192.168.0.1', '192.168.0.2')).to eq 'Confirmation message.'
199
+ end
200
+
201
+ it '#replace_txt_records_for' do
202
+ url = 'https://dns.api.gandi.net/api/v5/zones/zone-uuid/records/name/TXT'
203
+ body = '{"rrset_ttl":600,"rrset_values":["a","b"]}'
204
+ expect(GandiV5).to receive(:put).with(url, body)
205
+ .and_return('message' => 'Confirmation message.')
206
+ expect(subject.replace_txt_records_for('name', 600, 'a', 'b')).to eq 'Confirmation message.'
207
+ end
208
+ end
209
+
210
+ it '#list_domains' do
211
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/domains')
212
+ .and_return([{ 'fqdn' => 'example.com' }])
213
+ expect(subject.list_domains).to match_array ['example.com']
214
+ end
215
+
216
+ describe '#attach_domain' do
217
+ it 'Given a fqdn' do
218
+ url = 'https://dns.api.gandi.net/api/v5/domains/example.com'
219
+ body = '{"zone_uuid":"zone-uuid"}'
220
+ expect(GandiV5).to receive(:patch).with(url, body)
221
+ .and_return('message' => 'Confirmation message.')
222
+ expect(subject.attach_domain('example.com')).to eq 'Confirmation message.'
223
+ end
224
+
225
+ it 'Given something with a fqdn method' do
226
+ url = 'https://dns.api.gandi.net/api/v5/domains/example.com'
227
+ body = '{"zone_uuid":"zone-uuid"}'
228
+ expect(GandiV5).to receive(:patch).with(url, body)
229
+ .and_return('message' => 'Confirmation message.')
230
+ domain = double GandiV5::LiveDNS::Domain, fqdn: 'example.com'
231
+ expect(subject.attach_domain(domain)).to eq 'Confirmation message.'
232
+ end
233
+ end
234
+
235
+ it '#snapshots' do
236
+ returns = [{ 'uuid' => 'snapshot-uuid', 'date_created' => '2016-12-16T16:51:26Z' }]
237
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/snapshots')
238
+ .and_return(returns)
239
+ expect(subject.snapshots).to eq('snapshot-uuid' => Time.new(2016, 12, 16, 16, 51, 26, 0))
240
+ end
241
+
242
+ it '#snapshot' do
243
+ returns = double GandiV5::LiveDNS::Zone::Snapshot
244
+ expect(GandiV5::LiveDNS::Zone::Snapshot).to receive(:fetch).with('zone-uuid', 'snapshot-uuid')
245
+ .and_return(returns)
246
+ expect(subject.snapshot('snapshot-uuid')).to be returns
247
+ end
248
+
249
+ it '#take_snapshot' do
250
+ returns = double GandiV5::LiveDNS::Zone::Snapshot
251
+ expect(GandiV5).to receive(:post).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid/snapshots')
252
+ .and_return('message' => 'Confirmation message.', 'uuid' => 'snapshot-uuid')
253
+ expect(GandiV5::LiveDNS::Zone::Snapshot).to receive(:fetch).with('zone-uuid', 'snapshot-uuid')
254
+ .and_return(returns)
255
+ expect(subject.take_snapshot).to be returns
256
+ end
257
+
258
+ it '#update' do
259
+ body = '{"name":"new-name"}'
260
+ expect(GandiV5).to receive(:patch).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid', body)
261
+ .and_return('message' => 'Confirmation message.')
262
+ expect(subject.update(name: 'new-name')).to eq 'Confirmation message.'
263
+ end
264
+
265
+ it '#delete' do
266
+ expect(GandiV5).to receive(:delete).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid')
267
+ subject.delete
268
+ end
269
+
270
+ describe '.create' do
271
+ it 'With sharing-id' do
272
+ body = '{"name":"Name"}'
273
+ params = { sharing_id: 'sharing-id' }
274
+ expect(GandiV5).to receive(:post).with('https://dns.api.gandi.net/api/v5/zones', body, params: params)
275
+ .and_return('message' => 'Confirmation message.')
276
+ expect(described_class.create('Name', sharing_id: 'sharing-id')).to eq 'Confirmation message.'
277
+ end
278
+
279
+ it 'Without sharing-id' do
280
+ body = '{"name":"Name"}'
281
+ expect(GandiV5).to receive(:post).with('https://dns.api.gandi.net/api/v5/zones', body, params: {})
282
+ .and_return('message' => 'Confirmation message.')
283
+ expect(described_class.create('Name')).to eq 'Confirmation message.'
284
+ end
285
+ end
286
+
287
+ describe '.list' do
288
+ subject { described_class.list }
289
+
290
+ before :each do
291
+ body_fixture = File.expand_path(File.join('spec', 'fixtures', 'bodies', 'GandiV5_LiveDNS_Zone', 'list.yaml'))
292
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones')
293
+ .and_return(YAML.load_file(body_fixture))
294
+ end
295
+
296
+ its('count') { should eq 1 }
297
+ its('first.uuid') { should eq 'zone-uuid' }
298
+ its('first.name') { should eq 'Name' }
299
+ its('first.sharing_uuid') { should be nil }
300
+ its('first.soa_retry') { should eq 3_600 }
301
+ its('first.soa_minimum') { should eq 900 }
302
+ its('first.soa_refresh') { should eq 10_800 }
303
+ its('first.soa_expire') { should eq 604_800 }
304
+ its('first.soa_serial') { should eq 1_432_798_405 }
305
+ its('first.soa_email') { should eq 'hostmaster.gandi.net.' }
306
+ its('first.soa_primary_ns') { should eq 'a.dns.gandi.net.' }
307
+ end
308
+
309
+ describe '.fetch' do
310
+ subject { described_class.fetch 'zone-uuid' }
311
+
312
+ before :each do
313
+ body_fixture = File.expand_path(File.join('spec', 'fixtures', 'bodies', 'GandiV5_LiveDNS_Zone', 'get.yaml'))
314
+ expect(GandiV5).to receive(:get).with('https://dns.api.gandi.net/api/v5/zones/zone-uuid')
315
+ .and_return(YAML.load_file(body_fixture))
316
+ end
317
+
318
+ its('uuid') { should eq 'zone-uuid' }
319
+ its('name') { should eq 'Name' }
320
+ its('sharing_uuid') { should be nil }
321
+ its('soa_retry') { should eq 3_600 }
322
+ its('soa_minimum') { should eq 900 }
323
+ its('soa_refresh') { should eq 10_800 }
324
+ its('soa_expire') { should eq 604_800 }
325
+ its('soa_serial') { should eq 1_432_798_405 }
326
+ its('soa_email') { should eq 'hostmaster.gandi.net.' }
327
+ its('soa_primary_ns') { should eq 'a.dns.gandi.net.' }
328
+ end
329
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5::LiveDNS do
4
+ describe '.require_valid_record_type' do
5
+ it 'Does nothing for valid type' do
6
+ expect { described_class.require_valid_record_type 'A' }.to_not raise_error
7
+ end
8
+
9
+ it 'Errors on invalid type' do
10
+ expect { described_class.require_valid_record_type 'invalid' }.to raise_error(
11
+ ArgumentError,
12
+ 'type must be one of A, AAAA, CNAME, MX, NS, TXT, ALIAS, WKS, ' \
13
+ 'SRV, LOC, SPF, CAA, DS, SSHFP, PTR, KEY, DNAME, TLSA, OPENPGPKEY, CDS'
14
+ )
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5::Organization do
4
+ let(:body_fixtures) { File.expand_path(File.join('spec', 'fixtures', 'bodies', 'GandiV5_Organization')) }
5
+
6
+ describe '.fetch' do
7
+ subject { described_class.fetch }
8
+ before :each do
9
+ expect(GandiV5).to receive(:get).with('https://api.gandi.net/v5/organization/user-info')
10
+ .and_return(YAML.load_file(File.join(body_fixtures, 'get.yaml')))
11
+ end
12
+
13
+ its('uuid') { should eq 'organization-uuid' }
14
+ its('username') { should eq 'UserName' }
15
+ its('name') { should eq 'FirstLast' }
16
+ its('first_name') { should eq 'First' }
17
+ its('last_name') { should eq 'Last' }
18
+ its('lang') { should eq 'en' }
19
+ its('street_address') { should eq 'Street Address' }
20
+ its('city') { should eq 'City' }
21
+ its('zip') { should eq 'Post Code' }
22
+ its('country') { should eq 'GB' }
23
+ its('email') { should eq 'username@example.com' }
24
+ its('phone') { should eq '+12.34567890' }
25
+ its('security_email') { should eq 'security@example.com' }
26
+ its('security_phone') { should eq '+23.45678901' }
27
+ its('security_email_validated') { should eq true }
28
+ its('security_email_validation_deadline') { should eq Time.new(2017, 11, 22, 17, 13, 33) }
29
+ end
30
+ end
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe GandiV5 do
4
+ let(:api_key) { ENV['GANDI_API_KEY'] }
5
+
6
+ it 'Has a version' do
7
+ expect(defined?(GandiV5::VERSION)).to eq 'constant'
8
+ expect(GandiV5::VERSION).to_not be_empty
9
+ end
10
+
11
+ describe 'Attributes' do
12
+ describe 'api_key' do
13
+ it '.api_key=' do
14
+ described_class.api_key = '1a'
15
+ expect(described_class.send(:api_key)).to eq '1a'
16
+ described_class.api_key = api_key
17
+ end
18
+
19
+ it 'is write only' do
20
+ expect { described_class.api_key }.to raise_error NoMethodError
21
+ end
22
+ end
23
+ end
24
+
25
+ describe 'Uses RestClient' do
26
+ %i[get delete].each do |method|
27
+ describe ":#{method}" do
28
+ it 'As JSON' do
29
+ response_data = { 'hello' => 'world' }
30
+ response = double RestClient::Response, body: response_data.to_json, headers: { content_type: 'application/json' }
31
+ expect(described_class).to receive(:prepare_headers)
32
+ expect(RestClient).to receive(method).with('url', hash_including(accept: 'application/json')).and_return(response)
33
+ expect(described_class.send(method, 'url', accept: 'application/json')).to eq response_data
34
+ end
35
+
36
+ it 'As text' do
37
+ response_data = 'Hello world!'
38
+ response = double RestClient::Response, body: response_data, headers: { content_type: 'text/plain' }
39
+ expect(described_class).to receive(:prepare_headers)
40
+ expect(RestClient).to receive(method).with('url', hash_including(accept: 'text/plain')).and_return(response)
41
+ expect(described_class.send(method, 'url', accept: 'text/plain')).to eq response_data
42
+ end
43
+
44
+ it 'Passes request headers' do
45
+ expect(described_class).to receive(:prepare_headers)
46
+ expect(described_class).to receive(:parse_response)
47
+ expect(RestClient).to receive(method).with(anything, header: 'value')
48
+ expect(described_class.send(method, 'url', header: 'value')).to be_nil
49
+ end
50
+
51
+ it 'Adds authentication header' do
52
+ expect(RestClient).to receive(method).with(anything, hash_including(Authorization: 'Apikey abdce12345'))
53
+ expect(described_class).to receive(:parse_response)
54
+ expect(described_class.send(method, 'https://api.gandi.net/v5/')).to be_nil
55
+ end
56
+
57
+ it 'Default accept header' do
58
+ expect(RestClient).to receive(method).with(any_args, hash_including(accept: 'application/json'))
59
+ expect(described_class).to receive(:parse_response)
60
+ expect(described_class.send(method, 'https://api.gandi.net/v5/')).to be_nil
61
+ end
62
+
63
+ it 'Converts a 406 (bad request) exception' do
64
+ expect(RestClient).to receive(method).and_raise(RestClient::BadRequest)
65
+ expect(described_class).to receive(:handle_bad_request)
66
+ described_class.send(method, 'https://api.gandi.net/v5/')
67
+ end
68
+ end
69
+ end
70
+
71
+ %i[patch post put].each do |method|
72
+ describe ":#{method}" do
73
+ let(:payload) { '{"say":"hello world"}' }
74
+
75
+ it 'As JSON' do
76
+ response_data = { 'said' => 'hello world' }
77
+ response = double RestClient::Response, body: response_data.to_json, headers: { content_type: 'application/json' }
78
+ expect(described_class).to receive(:prepare_headers)
79
+ expect(RestClient).to receive(method).with('url', payload, hash_including(accept: 'application/json'))
80
+ .and_return(response)
81
+ expect(described_class.send(method, 'url', payload, accept: 'application/json')).to eq response_data
82
+ end
83
+
84
+ it 'As text' do
85
+ response_data = 'hello world'
86
+ response = double RestClient::Response, body: response_data, headers: { content_type: 'text/plain' }
87
+ expect(described_class).to receive(:prepare_headers)
88
+ expect(RestClient).to receive(method).with('url', payload, hash_including(accept: 'text/plain'))
89
+ .and_return(response)
90
+ expect(described_class.send(method, 'url', payload, accept: 'text/plain')).to eq response_data
91
+ end
92
+
93
+ it 'Passes payload' do
94
+ expect(described_class).to receive(:prepare_headers)
95
+ expect(described_class).to receive(:parse_response)
96
+ expect(RestClient).to receive(method).with(anything, payload, any_args)
97
+ expect(described_class.send(method, 'url', payload)).to be_nil
98
+ end
99
+
100
+ it 'Passes request headers' do
101
+ expect(described_class).to receive(:prepare_headers)
102
+ expect(described_class).to receive(:parse_response)
103
+ expect(RestClient).to receive(method).with(any_args, hash_including(header: 'value'))
104
+ expect(described_class.send(method, 'url', payload, header: 'value')).to be_nil
105
+ end
106
+
107
+ it 'Adds content type header' do
108
+ expect(RestClient).to receive(method).with(any_args, hash_including('content-type': 'application/json'))
109
+ expect(described_class).to receive(:parse_response)
110
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to be_nil
111
+ end
112
+
113
+ it 'Adds authentication header' do
114
+ expect(RestClient).to receive(method).with(any_args, hash_including(Authorization: 'Apikey abdce12345'))
115
+ expect(described_class).to receive(:parse_response)
116
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to be_nil
117
+ end
118
+
119
+ it 'Default accept header' do
120
+ expect(RestClient).to receive(method).with(any_args, hash_including(accept: 'application/json'))
121
+ expect(described_class).to receive(:parse_response)
122
+ expect(described_class.send(method, 'https://api.gandi.net/v5/', payload)).to be_nil
123
+ end
124
+
125
+ it 'Converts a 406 (bad request) exception' do
126
+ expect(RestClient).to receive(method).and_raise(RestClient::BadRequest)
127
+ expect(described_class).to receive(:handle_bad_request)
128
+ described_class.send(method, 'https://api.gandi.net/v5/', payload)
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ describe 'Generates correct authorisation header' do
135
+ it 'When requesting from main V5 API' do
136
+ expect(described_class.send(:authorisation_header, 'https://api.gandi.net/v5/example'))
137
+ .to eq(Authorization: "Apikey #{api_key}")
138
+ end
139
+
140
+ it 'When requesting from main LiveDNS V5 API' do
141
+ expect(described_class.send(:authorisation_header, 'https://dns.api.gandi.net/api/v5/example'))
142
+ .to eq('X-Api-Key': api_key)
143
+ end
144
+
145
+ it 'Raises ArgumentError when requesting an unknown url' do
146
+ expect { described_class.send(:authorisation_header, 'https://unknown.example.com') }
147
+ .to raise_error ArgumentError, 'Don\'t know how to authorise for url: https://unknown.example.com'
148
+ end
149
+ end
150
+
151
+ describe 'Parses response' do
152
+ it 'Text' do
153
+ response = double RestClient::Response, body: 'Hello World', headers: { content_type: 'text/plain' }
154
+ expect(described_class.send(:parse_response, response)).to eq 'Hello World'
155
+ end
156
+
157
+ describe 'JSON' do
158
+ it 'Response is parsable' do
159
+ response = double RestClient::Response, body: '{"hello":"world"}', headers: { content_type: 'application/json' }
160
+ expect(described_class.send(:parse_response, response)).to eq('hello' => 'world')
161
+ end
162
+
163
+ it 'Response is an error message' do
164
+ response = double RestClient::Response, body: '{"status":"error"}', headers: { content_type: 'application/json' }
165
+ expect { described_class.send(:parse_response, response) }.to raise_error GandiV5::Error::GandiError
166
+ end
167
+ end
168
+
169
+ it 'Raises ArgumentError when requesting an unknown parser' do
170
+ response = double RestClient::Response, headers: { content_type: 'unknown/unknown' }
171
+ expect { described_class.send(:parse_response, response) }
172
+ .to raise_error ArgumentError, 'Don\'t know how to parse a unknown/unknown response'
173
+ end
174
+ end
175
+
176
+ describe 'Handles HTTP error 406 (Bad Request)' do
177
+ it 'Passes on exception if body is not JSON' do
178
+ response = double RestClient::Response,
179
+ code: '406',
180
+ body: 'Something went wrong!'
181
+ exception = RestClient::BadRequest.new response
182
+ expect { described_class.send(:handle_bad_request, exception) }
183
+ .to raise_error exception
184
+ end
185
+
186
+ it 'Passes on exception if body is JSON but not an error hash' do
187
+ response = double RestClient::Response,
188
+ code: '406',
189
+ body: '{"status":"not error"}'
190
+ exception = RestClient::BadRequest.new response
191
+ expect { described_class.send(:handle_bad_request, exception) }
192
+ .to raise_error RestClient::BadRequest
193
+ end
194
+
195
+ it 'Raises GandiV5::Error::GandiError if response is an error hash' do
196
+ response = double RestClient::Response,
197
+ code: '406',
198
+ body: '{"status":"error","errors":[{"name":"field","description":"message"}]}'
199
+ exception = RestClient::BadRequest.new response
200
+ expect { described_class.send(:handle_bad_request, exception) }
201
+ .to raise_error GandiV5::Error::GandiError, 'field: message'
202
+ end
203
+ end
204
+ end