ronin-db-activerecord 0.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.github/workflows/ruby.yml +31 -0
  4. data/.gitignore +13 -0
  5. data/.rspec +1 -0
  6. data/.ruby-version +1 -0
  7. data/.yardopts +1 -0
  8. data/COPYING.txt +165 -0
  9. data/ChangeLog.md +39 -0
  10. data/Gemfile +27 -0
  11. data/README.md +143 -0
  12. data/Rakefile +72 -0
  13. data/db/migrate/0001_create_ronin_ip_address_mac_addresses_table.rb +43 -0
  14. data/db/migrate/0002_create_ronin_vulnerabilities_table.rb +61 -0
  15. data/db/migrate/0003_create_ronin_url_schemes_table.rb +32 -0
  16. data/db/migrate/0004_create_ronin_url_query_param_names_table.rb +32 -0
  17. data/db/migrate/0005_create_ronin_user_names_table.rb +33 -0
  18. data/db/migrate/0006_create_ronin_software_vendors_table.rb +32 -0
  19. data/db/migrate/0007_create_ronin_advisories_table.rb +42 -0
  20. data/db/migrate/0008_create_ronin_host_name_ip_addresses_table.rb +43 -0
  21. data/db/migrate/0009_create_ronin_host_names_table.rb +34 -0
  22. data/db/migrate/0010_create_ronin_arches_table.rb +37 -0
  23. data/db/migrate/0011_create_ronin_email_addresses_table.rb +44 -0
  24. data/db/migrate/0012_create_ronin_oses_table.rb +36 -0
  25. data/db/migrate/0013_create_ronin_organizations_table.rb +31 -0
  26. data/db/migrate/0014_create_ronin_ip_addresses_table.rb +35 -0
  27. data/db/migrate/0015_create_ronin_os_guesses_table.rb +40 -0
  28. data/db/migrate/0016_create_ronin_url_query_params_table.rb +42 -0
  29. data/db/migrate/0017_create_ronin_passwords_table.rb +32 -0
  30. data/db/migrate/0018_create_ronin_open_ports_table.rb +46 -0
  31. data/db/migrate/0019_create_ronin_urls_table.rb +50 -0
  32. data/db/migrate/0020_create_ronin_softwares_table.rb +39 -0
  33. data/db/migrate/0021_create_ronin_mac_addresses_table.rb +33 -0
  34. data/db/migrate/0022_create_ronin_countries_table.rb +34 -0
  35. data/db/migrate/0023_create_ronin_services_table.rb +32 -0
  36. data/db/migrate/0024_create_ronin_credentials_table.rb +44 -0
  37. data/db/migrate/0025_create_ronin_ports_table.rb +33 -0
  38. data/db/migrate/0026_create_ronin_asns_table.rb +44 -0
  39. data/db/migrate/0027_create_ronin_http_query_param_names_table.rb +32 -0
  40. data/db/migrate/0028_create_ronin_http_query_params_table.rb +42 -0
  41. data/db/migrate/0029_create_ronin_http_header_names_table.rb +31 -0
  42. data/db/migrate/0030_create_ronin_http_request_headers_table.rb +41 -0
  43. data/db/migrate/0031_create_ronin_http_response_headers_table.rb +41 -0
  44. data/db/migrate/0032_create_ronin_http_requests_table.rb +41 -0
  45. data/db/migrate/0033_create_ronin_http_responses_table.rb +36 -0
  46. data/db/migrate/0034_create_ronin_service_credentials_table.rb +41 -0
  47. data/db/migrate/0035_create_ronin_web_credentials_table.rb +41 -0
  48. data/gemspec.yml +28 -0
  49. data/lib/ronin/db/address.rb +105 -0
  50. data/lib/ronin/db/advisory.rb +169 -0
  51. data/lib/ronin/db/arch.rb +160 -0
  52. data/lib/ronin/db/asn.rb +212 -0
  53. data/lib/ronin/db/credential.rb +248 -0
  54. data/lib/ronin/db/email_address.rb +225 -0
  55. data/lib/ronin/db/host_name.rb +224 -0
  56. data/lib/ronin/db/host_name_ip_address.rb +65 -0
  57. data/lib/ronin/db/http_header_name.rb +75 -0
  58. data/lib/ronin/db/http_query_param.rb +79 -0
  59. data/lib/ronin/db/http_query_param_name.rb +76 -0
  60. data/lib/ronin/db/http_request.rb +120 -0
  61. data/lib/ronin/db/http_request_header.rb +78 -0
  62. data/lib/ronin/db/http_response.rb +91 -0
  63. data/lib/ronin/db/http_response_header.rb +78 -0
  64. data/lib/ronin/db/ip_address.rb +351 -0
  65. data/lib/ronin/db/ip_address_mac_address.rb +62 -0
  66. data/lib/ronin/db/mac_address.rb +91 -0
  67. data/lib/ronin/db/migrations.rb +137 -0
  68. data/lib/ronin/db/model/has_name.rb +102 -0
  69. data/lib/ronin/db/model/has_unique_name.rb +82 -0
  70. data/lib/ronin/db/model/importable.rb +85 -0
  71. data/lib/ronin/db/model/last_scanned_at.rb +48 -0
  72. data/lib/ronin/db/model.rb +37 -0
  73. data/lib/ronin/db/models.rb +108 -0
  74. data/lib/ronin/db/open_port.rb +148 -0
  75. data/lib/ronin/db/organization.rb +50 -0
  76. data/lib/ronin/db/os.rb +183 -0
  77. data/lib/ronin/db/os_guess.rb +67 -0
  78. data/lib/ronin/db/password.rb +167 -0
  79. data/lib/ronin/db/port.rb +123 -0
  80. data/lib/ronin/db/root.rb +28 -0
  81. data/lib/ronin/db/schema_migration.rb +34 -0
  82. data/lib/ronin/db/service.rb +48 -0
  83. data/lib/ronin/db/service_credential.rb +66 -0
  84. data/lib/ronin/db/software.rb +85 -0
  85. data/lib/ronin/db/software_vendor.rb +42 -0
  86. data/lib/ronin/db/url.rb +497 -0
  87. data/lib/ronin/db/url_query_param.rb +79 -0
  88. data/lib/ronin/db/url_query_param_name.rb +76 -0
  89. data/lib/ronin/db/url_scheme.rb +80 -0
  90. data/lib/ronin/db/user_name.rb +96 -0
  91. data/lib/ronin/db/vulnerability.rb +81 -0
  92. data/lib/ronin/db/web_credential.rb +69 -0
  93. data/ronin-db-activerecord.gemspec +61 -0
  94. data/spec/advisory_spec.rb +277 -0
  95. data/spec/arch_spec.rb +228 -0
  96. data/spec/asn_spec.rb +504 -0
  97. data/spec/credential_spec.rb +362 -0
  98. data/spec/email_address_spec.rb +372 -0
  99. data/spec/host_name_ip_address_spec.rb +8 -0
  100. data/spec/host_name_spec.rb +207 -0
  101. data/spec/http_header_name_spec.rb +25 -0
  102. data/spec/http_query_param_name_spec.rb +25 -0
  103. data/spec/http_query_param_spec.rb +104 -0
  104. data/spec/http_request_header_spec.rb +72 -0
  105. data/spec/http_request_spec.rb +168 -0
  106. data/spec/http_response_header_spec.rb +74 -0
  107. data/spec/http_response_spec.rb +103 -0
  108. data/spec/ip_address_mac_addresses_spec.rb +8 -0
  109. data/spec/ip_address_spec.rb +386 -0
  110. data/spec/mac_address_spec.rb +67 -0
  111. data/spec/migrations_spec.rb +122 -0
  112. data/spec/model/has_name_spec.rb +65 -0
  113. data/spec/model/has_unique_name_spec.rb +61 -0
  114. data/spec/model/importable_spec.rb +105 -0
  115. data/spec/models_spec.rb +60 -0
  116. data/spec/open_port_spec.rb +87 -0
  117. data/spec/organization_spec.rb +10 -0
  118. data/spec/os_guess_spec.rb +43 -0
  119. data/spec/os_spec.rb +114 -0
  120. data/spec/password_spec.rb +81 -0
  121. data/spec/port_spec.rb +102 -0
  122. data/spec/schema_migration_spec.rb +8 -0
  123. data/spec/service_credential_spec.rb +43 -0
  124. data/spec/service_spec.rb +39 -0
  125. data/spec/software_spec.rb +76 -0
  126. data/spec/software_vendor_spec.rb +33 -0
  127. data/spec/spec_helper.rb +13 -0
  128. data/spec/url_query_param_name_spec.rb +25 -0
  129. data/spec/url_query_param_spec.rb +110 -0
  130. data/spec/url_scheme_spec.rb +39 -0
  131. data/spec/url_spec.rb +951 -0
  132. data/spec/user_name_spec.rb +54 -0
  133. data/spec/vulnerability_spec.rb +8 -0
  134. data/spec/web_credential_spec.rb +72 -0
  135. metadata +266 -0
@@ -0,0 +1,386 @@
1
+ require 'spec_helper'
2
+
3
+ require 'ronin/db/ip_address'
4
+
5
+ describe Ronin::DB::IPAddress do
6
+ it "must use the 'ronin_ip_addresses' table" do
7
+ expect(described_class.table_name).to eq('ronin_ip_addresses')
8
+ end
9
+
10
+ let(:address) { '127.0.0.1' }
11
+
12
+ let(:ipv4_address) { '93.184.216.34' }
13
+ let(:ipv6_address) { '2606:2800:220:1:248:1893:25c8:1946' }
14
+
15
+ subject { described_class.new(address: address) }
16
+
17
+ describe "validations" do
18
+ describe "address" do
19
+ it "must require an address" do
20
+ ip_address = described_class.new
21
+ expect(ip_address).to_not be_valid
22
+
23
+ ip_address = described_class.new(address: address)
24
+ expect(ip_address).to be_valid
25
+ end
26
+
27
+ it "must accept IPv4 addresses" do
28
+ ip_address = described_class.new(address: ipv4_address)
29
+ expect(ip_address).to be_valid
30
+ end
31
+
32
+ it "must accept IPv6 addresses" do
33
+ ip_address = described_class.new(address: ipv6_address)
34
+ expect(ip_address).to be_valid
35
+ end
36
+
37
+ it "must not accept non-IP addresses" do
38
+ ip_address = described_class.new(address: '0')
39
+ expect(ip_address).to_not be_valid
40
+ expect(ip_address.errors[:address]).to eq(
41
+ ["Must be a valid IP address"]
42
+ )
43
+ end
44
+ end
45
+ end
46
+
47
+ describe ".v4" do
48
+ subject { described_class }
49
+
50
+ before do
51
+ subject.create(address: ipv4_address)
52
+ subject.create(address: ipv6_address)
53
+ end
54
+
55
+ it "must query all #{described_class} with IPv4 addresses" do
56
+ ip_addresses = subject.v4
57
+ expect(ip_addresses.count).to eq(1)
58
+
59
+ ip_address = ip_addresses.first
60
+ expect(ip_address).to be_kind_of(described_class)
61
+ expect(ip_address.address).to eq(ipv4_address)
62
+ end
63
+
64
+ after do
65
+ subject.destroy_all
66
+ end
67
+ end
68
+
69
+ describe ".v6" do
70
+ subject { described_class }
71
+
72
+ before do
73
+ subject.create(address: ipv4_address)
74
+ subject.create(address: ipv6_address)
75
+ end
76
+
77
+ it "must query all #{described_class} with IPv6 addresses" do
78
+ ip_addresses = subject.v6
79
+ expect(ip_addresses.count).to eq(1)
80
+
81
+ ip_address = ip_addresses.first
82
+ expect(ip_address).to be_kind_of(described_class)
83
+ expect(ip_address.address).to eq(ipv6_address)
84
+ end
85
+
86
+ after do
87
+ subject.destroy_all
88
+ end
89
+ end
90
+
91
+ describe ".between" do
92
+ subject { described_class }
93
+
94
+ let(:address1) { '4.1.1.1' }
95
+ let(:address2) { '4.2.2.2' }
96
+ let(:address3) { '4.3.3.3' }
97
+
98
+ before do
99
+ described_class.create(address: '4.0.0.0')
100
+ described_class.create(address: address1)
101
+ described_class.create(address: address2)
102
+ described_class.create(address: address3)
103
+ described_class.create(address: '4.4.4.4')
104
+ end
105
+
106
+ it "must query all IP addresses that are in between the first and last IP address" do
107
+ ip_addresses = subject.between(address1,address3)
108
+
109
+ expect(ip_addresses.length).to eq(3)
110
+ expect(ip_addresses).to all(be_kind_of(described_class))
111
+ expect(ip_addresses[0].address).to eq(address1)
112
+ expect(ip_addresses[1].address).to eq(address2)
113
+ expect(ip_addresses[2].address).to eq(address3)
114
+ end
115
+
116
+ after { described_class.destroy_all }
117
+ end
118
+
119
+ describe ".in_range" do
120
+ subject { described_class }
121
+
122
+ let(:address1) { '4.1.1.1' }
123
+ let(:address2) { '4.2.2.2' }
124
+ let(:address3) { '4.3.3.3' }
125
+ let(:range) { address1..address3 }
126
+
127
+ before do
128
+ described_class.create(address: '4.0.0.0')
129
+ described_class.create(address: address1)
130
+ described_class.create(address: address2)
131
+ described_class.create(address: address3)
132
+ described_class.create(address: '4.4.4.4')
133
+ end
134
+
135
+ it "must query all IP addresses that are in between the first and last IP address" do
136
+ ip_addresses = subject.in_range(range)
137
+
138
+ expect(ip_addresses.length).to eq(3)
139
+ expect(ip_addresses).to all(be_kind_of(described_class))
140
+ expect(ip_addresses[0].address).to eq(address1)
141
+ expect(ip_addresses[1].address).to eq(address2)
142
+ expect(ip_addresses[2].address).to eq(address3)
143
+ end
144
+
145
+ after { described_class.destroy_all }
146
+ end
147
+
148
+ describe ".with_mac_address" do
149
+ subject { described_class }
150
+
151
+ let(:mac_address) { '00:01:02:03:04:05' }
152
+
153
+ before do
154
+ ip_address = subject.create(address: address)
155
+ ip_address.mac_addresses.create(address: mac_address)
156
+ end
157
+
158
+ it "must query all #{described_class} with the associated MAC address" do
159
+ ip_addresses = subject.with_mac_address(mac_address)
160
+ expect(ip_addresses.count).to eq(1)
161
+
162
+ ip_address = ip_addresses.first
163
+ expect(ip_address.mac_addresses.first.address).to eq(mac_address)
164
+ end
165
+
166
+ after do
167
+ Ronin::DB::IPAddressMACAddress.destroy_all
168
+ Ronin::DB::IPAddress.destroy_all
169
+ Ronin::DB::MACAddress.destroy_all
170
+ end
171
+ end
172
+
173
+ describe ".with_host_name" do
174
+ subject { described_class }
175
+
176
+ let(:host) { 'example.com' }
177
+
178
+ before do
179
+ ip_address = subject.create(address: address)
180
+ ip_address.host_names.create(name: host)
181
+ end
182
+
183
+ it "must query all #{described_class} with the associated host name" do
184
+ ip_addresses = subject.with_host_name(host)
185
+ expect(ip_addresses.count).to eq(1)
186
+
187
+ ip_address = ip_addresses.first
188
+ expect(ip_address.host_names.first.name).to eq(host)
189
+ end
190
+
191
+ after do
192
+ Ronin::DB::HostNameIPAddress.destroy_all
193
+ Ronin::DB::IPAddress.destroy_all
194
+ Ronin::DB::HostName.destroy_all
195
+ end
196
+ end
197
+
198
+ describe ".with_port_number" do
199
+ subject { described_class }
200
+
201
+ let(:port) { 443 }
202
+
203
+ before do
204
+ ip_address = subject.create(address: address)
205
+ ip_address.ports.create(protocol: :tcp, number: 80)
206
+ ip_address.ports.create(protocol: :tcp, number: port)
207
+ ip_address.ports.create(protocol: :tcp, number: 8080)
208
+ end
209
+
210
+ it "must query all #{described_class} with the associated port number" do
211
+ ip_addresses = subject.with_port_number(port)
212
+ expect(ip_addresses.count).to eq(1)
213
+
214
+ ip_address = ip_addresses.first
215
+ expect(ip_address.ports[1].number).to eq(port)
216
+ end
217
+
218
+ after do
219
+ Ronin::DB::OpenPort.destroy_all
220
+ Ronin::DB::IPAddress.destroy_all
221
+ Ronin::DB::Port.destroy_all
222
+ end
223
+ end
224
+
225
+ describe ".lookup" do
226
+ before do
227
+ described_class.create(address: '1.2.4.5')
228
+ described_class.create(address: address)
229
+ described_class.create(address: '6.7.8.9')
230
+ end
231
+
232
+ it "must query the #{described_class} with the matching IP address" do
233
+ ip_address = described_class.lookup(address)
234
+
235
+ expect(ip_address).to be_kind_of(described_class)
236
+ expect(ip_address.address).to eq(address)
237
+ end
238
+
239
+ after { described_class.destroy_all }
240
+ end
241
+
242
+ describe ".import" do
243
+ subject { described_class.import(address) }
244
+
245
+ it "must parse and import the IP address and set #address" do
246
+ expect(subject).to be_kind_of(described_class)
247
+ expect(subject.id).to_not be(nil)
248
+ expect(subject.address).to eq(address)
249
+ end
250
+
251
+ after { described_class.destroy_all }
252
+ end
253
+
254
+ describe "#ipaddr" do
255
+ it "must automatically parse #address and return an IPAddr" do
256
+ expect(subject.ipaddr).to be_kind_of(IPAddr)
257
+ expect(subject.ipaddr.to_s).to eq(address)
258
+ end
259
+
260
+ context "when the address is an IPv4 address" do
261
+ let(:address) { ipv4_address }
262
+
263
+ it "must return an IPv4 IPAddr" do
264
+ expect(subject.ipaddr.ipv4?).to be(true)
265
+ end
266
+ end
267
+
268
+ context "when the address is an IPv6 address" do
269
+ let(:address) { ipv6_address }
270
+
271
+ it "must return an IPv6 IPAddr" do
272
+ expect(subject.ipaddr.ipv6?).to be(true)
273
+ end
274
+ end
275
+
276
+ context "when the address is not a valid IP address" do
277
+ let(:address) { '0' }
278
+
279
+ it "must return nil" do
280
+ expect(subject.ipaddr).to be(nil)
281
+ end
282
+ end
283
+ end
284
+
285
+ describe "#version" do
286
+ it "should only accept 4 or 6" do
287
+ ip_address = described_class.new(address: '1.1.1.1', version: 7)
288
+
289
+ expect(ip_address).not_to be_valid
290
+ end
291
+
292
+ context "with IPv4 address" do
293
+ subject { described_class.new(address: '127.0.0.1') }
294
+
295
+ it { expect(subject.version).to be == 4 }
296
+ end
297
+
298
+ context "with IPv6 address" do
299
+ subject { described_class.new(address: '::1') }
300
+
301
+ it { expect(subject.version).to be == 6 }
302
+ end
303
+ end
304
+
305
+ describe "#asn" do
306
+ let(:asn_version) { 4 }
307
+ let(:asn_range_start) { '4.0.0.0' }
308
+ let(:asn_range_end) { '4.7.168.255' }
309
+ let(:asn_number) { 3356 }
310
+ let(:asn_country_code) { 'US' }
311
+ let(:asn_name) { 'LEVEL3' }
312
+
313
+ before do
314
+ Ronin::DB::ASN.create(
315
+ version: 6,
316
+ range_start: '64:ff9b::1:0:0',
317
+ range_end: '100::ffff:ffff:ffff:ffff',
318
+ number: 0
319
+ )
320
+
321
+ Ronin::DB::ASN.create(
322
+ version: asn_version,
323
+ range_start: asn_range_start,
324
+ range_end: asn_range_end,
325
+ number: asn_number,
326
+ country_code: asn_country_code,
327
+ name: asn_name
328
+ )
329
+
330
+ Ronin::DB::ASN.create(
331
+ version: 6,
332
+ range_start: '::',
333
+ range_end: '::1',
334
+ number: 0
335
+ )
336
+ end
337
+
338
+ let(:version) { 4 }
339
+ let(:address) { '4.4.4.4' }
340
+
341
+ subject do
342
+ described_class.new(
343
+ version: version,
344
+ address: address
345
+ )
346
+ end
347
+
348
+ it "must lookup the ASN record containing the IP address" do
349
+ asn = subject.asn
350
+
351
+ expect(asn).to be_kind_of(Ronin::DB::ASN)
352
+ expect(asn.version).to eq(version)
353
+ expect(asn.range_start).to eq('4.0.0.0')
354
+ expect(asn.range_end).to eq('4.7.168.255')
355
+ end
356
+
357
+ after do
358
+ Ronin::DB::ASN.destroy_all
359
+ end
360
+ end
361
+
362
+ describe "#recent_mac_address" do
363
+ end
364
+
365
+ describe "#recent_host_name" do
366
+ end
367
+
368
+ describe "#recent_os_guess" do
369
+ end
370
+
371
+ describe "#save" do
372
+ subject { described_class.create(address: address) }
373
+
374
+ it "must set hton" do
375
+ expect(subject.hton).to eq(subject.ipaddr.hton)
376
+ end
377
+
378
+ after { subject.destroy }
379
+ end
380
+
381
+ describe "#to_ip" do
382
+ end
383
+
384
+ describe "#to_i" do
385
+ end
386
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'ronin/db/mac_address'
3
+
4
+ describe Ronin::DB::MACAddress do
5
+ it "must use the 'ronin_mac_addresses' table" do
6
+ expect(described_class.table_name).to eq('ronin_mac_addresses')
7
+ end
8
+
9
+ let(:address) { '00:01:02:03:04:05' }
10
+
11
+ subject { described_class.new(address: address) }
12
+
13
+ describe "validations" do
14
+ describe "address" do
15
+ it "should require an address" do
16
+ mac_address = described_class.new
17
+ expect(mac_address).not_to be_valid
18
+ expect(mac_address.errors[:address]).to include(
19
+ "can't be blank"
20
+ )
21
+
22
+ mac_address = described_class.new(address: address)
23
+ expect(mac_address).to be_valid
24
+ end
25
+ end
26
+ end
27
+
28
+ describe ".lookup" do
29
+ before do
30
+ described_class.create(address: '11:12:13:14:15:16')
31
+ described_class.create(address: address)
32
+ described_class.create(address: '21:22:23:24:25:26')
33
+ end
34
+
35
+ it "must query the #{described_class} with the matching MAC address" do
36
+ ip_address = described_class.lookup(address)
37
+
38
+ expect(ip_address).to be_kind_of(described_class)
39
+ expect(ip_address.address).to eq(address)
40
+ end
41
+
42
+ after { described_class.destroy_all }
43
+ end
44
+
45
+ describe ".import" do
46
+ subject { described_class.import(address) }
47
+
48
+ it "must parse and import the MAC address and set #address" do
49
+ expect(subject).to be_kind_of(described_class)
50
+ expect(subject.id).to_not be(nil)
51
+ expect(subject.address).to eq(address)
52
+ end
53
+
54
+ after { described_class.destroy_all }
55
+ end
56
+
57
+ describe "#recent_ip_address" do
58
+ end
59
+
60
+ describe "#to_i" do
61
+ let(:integer) { 0x000102030405 }
62
+
63
+ it "should convert the MAC Address to an Integer" do
64
+ expect(subject.to_i).to eq(integer)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+ require 'ronin/db/migrations'
3
+
4
+ describe Ronin::DB::Migrations do
5
+ let(:migration_context) { subject.send(:context) }
6
+
7
+ describe ".current_version" do
8
+ it "must call .current_version on the MigrationContext object" do
9
+ expect(migration_context).to receive(:current_version)
10
+
11
+ subject.current_version
12
+ end
13
+ end
14
+
15
+ describe ".needs_migration?" do
16
+ it "must call .needs_migration? on the MigrationContext object" do
17
+ expect(migration_context).to receive(:needs_migration?)
18
+
19
+ subject.needs_migration?
20
+ end
21
+ end
22
+
23
+ describe ".migrate" do
24
+ context "when called with no arguments" do
25
+ it "must call .migrate(nil) on the MigrationContext object" do
26
+ expect(migration_context).to receive(:migrate).with(nil)
27
+
28
+ subject.migrate
29
+ end
30
+ end
31
+
32
+ context "when given a target version" do
33
+ let(:target_version) { 42 }
34
+
35
+ it "must call .migrate(target_version) on the MigrationContext object" do
36
+ expect(migration_context).to receive(:migrate).with(target_version)
37
+
38
+ subject.migrate(target_version)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe ".up" do
44
+ context "when called with no arguments" do
45
+ it "must call .up(nil) on the MigrationContext object" do
46
+ expect(migration_context).to receive(:up).with(nil)
47
+
48
+ subject.up
49
+ end
50
+ end
51
+
52
+ context "when given a target version" do
53
+ let(:target_version) { 42 }
54
+
55
+ it "must call .up(target_version) on the MigrationContext object" do
56
+ expect(migration_context).to receive(:up).with(target_version)
57
+
58
+ subject.up(target_version)
59
+ end
60
+ end
61
+ end
62
+
63
+ describe ".down" do
64
+ context "when called with no arguments" do
65
+ it "must call .down(nil) on the MigrationContext object" do
66
+ expect(migration_context).to receive(:down).with(nil)
67
+
68
+ subject.down
69
+ end
70
+ end
71
+
72
+ context "when given a target version" do
73
+ let(:target_version) { 42 }
74
+
75
+ it "must call .down(target_version) on the MigrationContext object" do
76
+ expect(migration_context).to receive(:down).with(target_version)
77
+
78
+ subject.down(target_version)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe ".rollback" do
84
+ context "when called with no arguments" do
85
+ it "must call .rollback(1) on the MigrationContext object" do
86
+ expect(migration_context).to receive(:rollback).with(1)
87
+
88
+ subject.rollback
89
+ end
90
+ end
91
+
92
+ context "when given a target version" do
93
+ let(:steps) { 2 }
94
+
95
+ it "must call .rollback(steps) on the MigrationContext object" do
96
+ expect(migration_context).to receive(:rollback).with(steps)
97
+
98
+ subject.rollback(steps)
99
+ end
100
+ end
101
+ end
102
+
103
+ describe ".foreward" do
104
+ context "when called with no arguments" do
105
+ it "must call .foreward(1) on the MigrationContext object" do
106
+ expect(migration_context).to receive(:foreward).with(1)
107
+
108
+ subject.foreward
109
+ end
110
+ end
111
+
112
+ context "when given a target version" do
113
+ let(:steps) { 2 }
114
+
115
+ it "must call .foreward(steps) on the MigrationContext object" do
116
+ expect(migration_context).to receive(:foreward).with(steps)
117
+
118
+ subject.foreward(steps)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'ronin/db/model/has_name'
3
+
4
+ describe Ronin::DB::Model::HasName do
5
+ class TestModelHasName < ActiveRecord::Base
6
+ include Ronin::DB::Model::HasName
7
+
8
+ self.table_name = 'test_model_has_name'
9
+ end
10
+
11
+ let(:model) { TestModelHasName }
12
+
13
+ before(:all) do
14
+ ActiveRecord::Base.connection.create_table :test_model_has_name do |t|
15
+ t.string :name
16
+ end
17
+ end
18
+
19
+ describe ".included" do
20
+ subject { model }
21
+
22
+ it "should include Ronin::DB::Model" do
23
+ expect(subject.ancestors).to include(Ronin::DB::Model)
24
+ end
25
+
26
+ it "should define a name attribute" do
27
+ expect(subject.new).to respond_to(:name)
28
+ expect(subject.new).to respond_to(:name=)
29
+ end
30
+ end
31
+
32
+ describe "validations" do
33
+ subject { model }
34
+
35
+ it "should require a name" do
36
+ resource = subject.new
37
+ expect(resource).not_to be_valid
38
+
39
+ resource.name = 'foo'
40
+ expect(resource).to be_valid
41
+ end
42
+ end
43
+
44
+ describe ".named" do
45
+ subject { model }
46
+
47
+ let(:name1) { 'foo1' }
48
+ let(:name2) { 'foo2' }
49
+
50
+ before do
51
+ subject.create(name: name1)
52
+ subject.create(name: name2)
53
+ end
54
+
55
+ it "should be able to find resources with similar names" do
56
+ resources = subject.named('foo')
57
+
58
+ expect(resources.length).to eq(2)
59
+ expect(resources[0].name).to be == name1
60
+ expect(resources[1].name).to be == name2
61
+ end
62
+
63
+ after { subject.destroy_all }
64
+ end
65
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require 'ronin/db/model/has_unique_name'
3
+
4
+ describe Ronin::DB::Model::HasUniqueName do
5
+ class TestModelHasUniqueName < ActiveRecord::Base
6
+ include Ronin::DB::Model::HasUniqueName
7
+
8
+ self.table_name = 'test_model_has_unique_name'
9
+ end
10
+
11
+ let(:model) { TestModelHasUniqueName }
12
+
13
+ before(:all) do
14
+ ActiveRecord::Base.connection.create_table :test_model_has_unique_name do |t|
15
+ t.string :name
16
+ end
17
+ end
18
+
19
+ describe ".included" do
20
+ subject { model }
21
+
22
+ it "should include Ronin::DB::Model" do
23
+ expect(subject.ancestors).to include(Ronin::DB::Model)
24
+ end
25
+
26
+ it "should include Ronin::DB::Model::HasName::InstanceMethods" do
27
+ expect(subject.ancestors).to include(Ronin::DB::Model::HasName::InstanceMethods)
28
+ end
29
+
30
+ it "should define a name attribute" do
31
+ expect(subject.new).to respond_to(:name)
32
+ expect(subject.new).to respond_to(:name=)
33
+ end
34
+ end
35
+
36
+ describe "validations" do
37
+ subject { model }
38
+
39
+ it "should require a name" do
40
+ record = subject.new
41
+ expect(record).to_not be_valid
42
+
43
+ record.name = 'foo'
44
+ expect(record).to be_valid
45
+ end
46
+
47
+ context "when the given name already exists in the table" do
48
+ let(:name) { 'foo' }
49
+
50
+ before { model.create(name: name) }
51
+
52
+ it "must require a unique name" do
53
+ record = subject.new(name: name)
54
+
55
+ expect(record).to_not be_valid
56
+ end
57
+
58
+ after { model.destroy_all }
59
+ end
60
+ end
61
+ end