metasploit_data_models 0.7.0 → 0.11.2
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.
- data/.gitignore +3 -0
- data/.travis.yml +1 -0
- data/app/models/mdm/host.rb +352 -26
- data/app/models/mdm/loot.rb +72 -7
- data/app/models/mdm/{module_action.rb → module/action.rb} +3 -3
- data/app/models/mdm/{module_arch.rb → module/arch.rb} +3 -3
- data/app/models/mdm/{module_author.rb → module/author.rb} +3 -3
- data/app/models/mdm/module/detail.rb +280 -0
- data/app/models/mdm/{module_mixin.rb → module/mixin.rb} +3 -3
- data/app/models/mdm/{module_platform.rb → module/platform.rb} +3 -3
- data/app/models/mdm/module/ref.rb +48 -0
- data/app/models/mdm/{module_target.rb → module/target.rb} +3 -3
- data/app/models/mdm/note.rb +61 -6
- data/app/models/mdm/ref.rb +39 -1
- data/app/models/mdm/service.rb +85 -7
- data/app/models/mdm/session.rb +100 -6
- data/app/models/mdm/vuln.rb +104 -24
- data/db/migrate/20130228214900_change_required_columns_to_null_false_in_web_vulns.rb +1 -17
- data/db/migrate/20130412154159_change_foreign_key_in_module_actions.rb +25 -0
- data/db/migrate/20130412171844_change_foreign_key_in_module_archs.rb +25 -0
- data/db/migrate/20130412173121_change_foreign_key_in_module_authors.rb +25 -0
- data/db/migrate/20130412173640_change_foreign_key_in_module_mixins.rb +25 -0
- data/db/migrate/20130412174254_change_foreign_key_in_module_platforms.rb +25 -0
- data/db/migrate/20130412174719_change_foreign_key_in_module_refs.rb +25 -0
- data/db/migrate/20130412175040_change_foreign_key_in_module_targets.rb +25 -0
- data/db/migrate/20130430151353_change_required_columns_to_null_false_in_hosts.rb +11 -0
- data/db/migrate/20130430162145_enforce_address_uniqueness_in_workspace_in_hosts.rb +23 -0
- data/lib/mdm/module.rb +4 -0
- data/lib/metasploit_data_models.rb +1 -0
- data/lib/metasploit_data_models/change_required_columns_to_null_false.rb +23 -0
- data/lib/metasploit_data_models/version.rb +1 -1
- data/spec/app/models/mdm/host_spec.rb +411 -0
- data/spec/app/models/mdm/host_tag_spec.rb +13 -0
- data/spec/app/models/mdm/{module_action_spec.rb → module/action_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_arch_spec.rb → module/arch_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_author_spec.rb → module/author_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_detail_spec.rb → module/detail_spec.rb} +101 -11
- data/spec/app/models/mdm/{module_mixin_spec.rb → module/mixin_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_platform_spec.rb → module/platform_spec.rb} +6 -6
- data/spec/app/models/mdm/module/ref_spec.rb +62 -0
- data/spec/app/models/mdm/{module_target_spec.rb → module/target_spec.rb} +6 -6
- data/spec/app/models/mdm/ref_spec.rb +62 -0
- data/spec/app/models/mdm/tag_spec.rb +13 -0
- data/spec/app/models/mdm/vuln_ref_spec.rb +13 -0
- data/spec/app/models/mdm/vuln_spec.rb +231 -0
- data/spec/dummy/db/schema.rb +20 -20
- data/spec/factories/mdm/host_tags.rb +9 -0
- data/spec/factories/mdm/hosts.rb +65 -0
- data/spec/factories/mdm/module/actions.rb +14 -0
- data/spec/factories/mdm/module/archs.rb +14 -0
- data/spec/factories/mdm/{module_authors.rb → module/authors.rb} +4 -4
- data/spec/factories/mdm/module/details.rb +66 -0
- data/spec/factories/mdm/module/mixins.rb +14 -0
- data/spec/factories/mdm/module/platforms.rb +14 -0
- data/spec/factories/mdm/module/refs.rb +14 -0
- data/spec/factories/mdm/{module_targets.rb → module/targets.rb} +3 -3
- data/spec/factories/mdm/refs.rb +9 -0
- data/spec/factories/mdm/tags.rb +14 -0
- data/spec/factories/mdm/vuln_refs.rb +4 -0
- data/spec/factories/mdm/vulns.rb +20 -0
- metadata +78 -45
- data/app/models/mdm/module_detail.rb +0 -59
- data/app/models/mdm/module_ref.rb +0 -24
- data/spec/app/models/mdm/module_ref_spec.rb +0 -38
- data/spec/factories/mdm/module_actions.rb +0 -14
- data/spec/factories/mdm/module_archs.rb +0 -14
- data/spec/factories/mdm/module_details.rb +0 -9
- data/spec/factories/mdm/module_mixins.rb +0 -14
- data/spec/factories/mdm/module_platforms.rb +0 -14
- data/spec/factories/mdm/module_refs.rb +0 -14
@@ -4,5 +4,5 @@ module MetasploitDataModels
|
|
4
4
|
# metasploit-framework/data/sql/migrate to db/migrate in this project, not all models have specs that verify the
|
5
5
|
# migrations (with have_db_column and have_db_index) and certain models may not be shared between metasploit-framework
|
6
6
|
# and pro, so models may be removed in the future. Because of the unstable API the version should remain below 1.0.0
|
7
|
-
VERSION = '0.
|
7
|
+
VERSION = '0.11.2'
|
8
8
|
end
|
@@ -0,0 +1,411 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mdm::Host do
|
4
|
+
subject(:host) do
|
5
|
+
FactoryGirl.build(:mdm_host)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:architectures) do
|
9
|
+
[
|
10
|
+
'armbe',
|
11
|
+
'armle',
|
12
|
+
'cbea',
|
13
|
+
'cbea64',
|
14
|
+
'cmd',
|
15
|
+
'java',
|
16
|
+
'mips',
|
17
|
+
'mipsbe',
|
18
|
+
'mipsle',
|
19
|
+
'php',
|
20
|
+
'ppc',
|
21
|
+
'ppc64',
|
22
|
+
'ruby',
|
23
|
+
'sparc',
|
24
|
+
'tty',
|
25
|
+
'x64',
|
26
|
+
'x86',
|
27
|
+
'x86_64'
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:operating_system_names) do
|
32
|
+
[
|
33
|
+
'FreeBSD',
|
34
|
+
'Linux',
|
35
|
+
'Mac OS X',
|
36
|
+
'Microsoft Windows',
|
37
|
+
'NetBSD',
|
38
|
+
'OpenBSD',
|
39
|
+
'Unknown',
|
40
|
+
'VMWare'
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:states) do
|
45
|
+
[
|
46
|
+
'alive',
|
47
|
+
'down',
|
48
|
+
'unknown'
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'associations' do
|
53
|
+
it { should have_many(:creds).class_name('Mdm::Cred').through(:services) }
|
54
|
+
it { should have_many(:exploit_attempts).class_name('Mdm::ExploitAttempt').dependent(:destroy) }
|
55
|
+
it { should have_many(:exploited_hosts).class_name('Mdm::ExploitedHost').dependent(:destroy) }
|
56
|
+
it { should have_many(:host_details).class_name('Mdm::HostDetail').dependent(:destroy) }
|
57
|
+
it { should have_many(:hosts_tags).class_name('Mdm::HostTag') }
|
58
|
+
it { should have_many(:loots).class_name('Mdm::Loot').dependent(:destroy).order('loots.created_at DESC') }
|
59
|
+
|
60
|
+
context 'module_details' do
|
61
|
+
it { should have_many(:module_details).class_name('Mdm::Module::Detail').through(:module_refs) }
|
62
|
+
|
63
|
+
context 'with Mdm::Vulns' do
|
64
|
+
let!(:vulns) do
|
65
|
+
FactoryGirl.create_list(
|
66
|
+
:mdm_vuln,
|
67
|
+
2,
|
68
|
+
:host => host
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with Mdm::Ref' do
|
73
|
+
let(:name) do
|
74
|
+
FactoryGirl.generate :mdm_ref_name
|
75
|
+
end
|
76
|
+
|
77
|
+
let!(:ref) do
|
78
|
+
FactoryGirl.create(:mdm_ref, :name => name)
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with Mdm::VulnRefs' do
|
82
|
+
let!(:vuln_refs) do
|
83
|
+
vulns.collect { |vuln|
|
84
|
+
FactoryGirl.create(:mdm_vuln_ref, :ref => ref, :vuln => vuln)
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with Mdm::Module::Detail' do
|
89
|
+
let!(:module_detail) do
|
90
|
+
FactoryGirl.create(
|
91
|
+
:mdm_module_detail
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'with Mdm::Module::Ref with same name as Mdm::Ref' do
|
96
|
+
let!(:module_ref) do
|
97
|
+
FactoryGirl.create(
|
98
|
+
:mdm_module_ref,
|
99
|
+
:detail => module_detail,
|
100
|
+
:name => name
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should list unique Mdm::Module::Detail' do
|
105
|
+
host.module_details.should =~ [module_detail]
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should have duplicate Mdm::Module::Details if collected through chain' do
|
109
|
+
vuln_refs = []
|
110
|
+
|
111
|
+
host.vulns.each do |vuln|
|
112
|
+
# @todo https://www.pivotaltracker.com/story/show/49004623
|
113
|
+
vuln_refs += vuln.vulns_refs
|
114
|
+
end
|
115
|
+
|
116
|
+
refs = []
|
117
|
+
|
118
|
+
vuln_refs.each do |vuln_ref|
|
119
|
+
refs << vuln_ref.ref
|
120
|
+
end
|
121
|
+
|
122
|
+
module_refs = []
|
123
|
+
|
124
|
+
refs.each do |ref|
|
125
|
+
module_refs += ref.module_refs
|
126
|
+
end
|
127
|
+
|
128
|
+
module_details = []
|
129
|
+
|
130
|
+
module_refs.each do |module_ref|
|
131
|
+
module_details << module_ref.detail
|
132
|
+
end
|
133
|
+
|
134
|
+
host.module_details.count.should < module_details.length
|
135
|
+
module_details.uniq.count.should == host.module_details.count
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
it { should have_many(:module_refs).class_name('Mdm::Module::Ref').through(:refs) }
|
145
|
+
it { should have_many(:notes).class_name('Mdm::Note').dependent(:delete_all).order('notes.created_at') }
|
146
|
+
it { should have_many(:refs).class_name('Mdm::Ref').through(:vuln_refs) }
|
147
|
+
it { should have_many(:services).class_name('Mdm::Service').dependent(:destroy).order('services.port, services.proto') }
|
148
|
+
it { should have_many(:service_notes).through(:services) }
|
149
|
+
it { should have_many(:sessions).class_name('Mdm::Session').dependent(:destroy).order('sessions.opened_at') }
|
150
|
+
it { should have_many(:tags).class_name('Mdm::Tag').through(:hosts_tags) }
|
151
|
+
it { should have_many(:vulns).class_name('Mdm::Vuln').dependent(:delete_all) }
|
152
|
+
it { should have_many(:vuln_refs).class_name('Mdm::VulnRef') }
|
153
|
+
it { should have_many(:web_sites).class_name('Mdm::WebSite').through(:services) }
|
154
|
+
it { should belong_to(:workspace).class_name('Mdm::Workspace') }
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'callbacks' do
|
158
|
+
context 'before destroy' do
|
159
|
+
context 'cleanup_tags' do
|
160
|
+
context 'with tags' do
|
161
|
+
let!(:tag) do
|
162
|
+
FactoryGirl.create(:mdm_tag)
|
163
|
+
end
|
164
|
+
|
165
|
+
let!(:host) do
|
166
|
+
FactoryGirl.create(:mdm_host)
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'with only this host' do
|
170
|
+
before(:each) do
|
171
|
+
FactoryGirl.create(
|
172
|
+
:mdm_host_tag,
|
173
|
+
:host => host,
|
174
|
+
:tag => tag
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should destroy the tags' do
|
179
|
+
expect {
|
180
|
+
host.destroy
|
181
|
+
}.to change(Mdm::Tag, :count).by(-1)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should destroy the host tags' do
|
185
|
+
expect {
|
186
|
+
host.destroy
|
187
|
+
}.to change(Mdm::HostTag, :count).by(-1)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'with additional hosts' do
|
192
|
+
let(:other_host) do
|
193
|
+
FactoryGirl.create(:mdm_host)
|
194
|
+
end
|
195
|
+
|
196
|
+
before(:each) do
|
197
|
+
FactoryGirl.create(:mdm_host_tag, :host => host, :tag => tag)
|
198
|
+
FactoryGirl.create(:mdm_host_tag, :host => other_host, :tag => tag)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should not destroy the tag' do
|
202
|
+
expect {
|
203
|
+
host.destroy
|
204
|
+
}.to_not change(Mdm::Tag, :count)
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should destroy the host tags' do
|
208
|
+
expect {
|
209
|
+
host.destroy
|
210
|
+
}.to change(Mdm::HostTag, :count).by(-1)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should not destroy the other host's tags" do
|
214
|
+
host.destroy
|
215
|
+
|
216
|
+
other_host.hosts_tags.count.should == 1
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'CONSTANTS' do
|
225
|
+
context 'ARCHITECTURES' do
|
226
|
+
subject(:architectures) do
|
227
|
+
described_class::ARCHITECTURES
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should be an Array<String>' do
|
231
|
+
architectures.should be_an Array
|
232
|
+
|
233
|
+
architectures.each do |architecture|
|
234
|
+
architecture.should be_a String
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should include both endians of ARM' do
|
239
|
+
architectures.should include('armbe')
|
240
|
+
architectures.should include('armle')
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should include 32-bit and 64-bit versions of Cell Broadband Engine Architecture' do
|
244
|
+
architectures.should include('cbea')
|
245
|
+
architectures.should include('cbea64')
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should include cmd for command shell' do
|
249
|
+
architectures.should include('cmd')
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should include java for Java Virtual Machine' do
|
253
|
+
architectures.should include('java')
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should include plain and endian-ware MIPS' do
|
257
|
+
architectures.should include('mips')
|
258
|
+
architectures.should include('mipsbe')
|
259
|
+
architectures.should include('mipsle')
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should include php for PHP code' do
|
263
|
+
architectures.should include('php')
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'should include 32-bit and 64-bit PowerPC' do
|
267
|
+
architectures.should include('ppc')
|
268
|
+
architectures.should include('ppc64')
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'should include ruby for Ruby code' do
|
272
|
+
architectures.should include('ruby')
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'should include sparc for Sparc' do
|
276
|
+
architectures.should include('sparc')
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should include tty for Terminals' do
|
280
|
+
architectures.should include('tty')
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'should include 32-bit and 64-bit x86' do
|
284
|
+
architectures.should include('x64')
|
285
|
+
architectures.should include('x86')
|
286
|
+
architectures.should include('x86_64')
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'should define OPERATING_SYSTEM_NAMES in any order' do
|
291
|
+
described_class::OPERATING_SYSTEM_NAMES.should =~ operating_system_names
|
292
|
+
end
|
293
|
+
|
294
|
+
context 'SEARCH_FIELDS' do
|
295
|
+
subject(:search_fields) do
|
296
|
+
described_class::SEARCH_FIELDS
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should be an Array<String>' do
|
300
|
+
search_fields.should be_an Array
|
301
|
+
|
302
|
+
search_fields.each { |search_field|
|
303
|
+
search_field.should be_a String
|
304
|
+
}
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'should cast address to text' do
|
308
|
+
search_fields.should include('address::text')
|
309
|
+
end
|
310
|
+
|
311
|
+
it { should include('comments') }
|
312
|
+
it { should include('mac') }
|
313
|
+
it { should include('name') }
|
314
|
+
it { should include('os_flavor') }
|
315
|
+
it { should include('os_name') }
|
316
|
+
it { should include('os_sp') }
|
317
|
+
it { should include('purpose') }
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'should define STATES in any order' do
|
321
|
+
described_class::STATES.should =~ states
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context 'database' do
|
326
|
+
context 'columns' do
|
327
|
+
it { should have_db_column(:address).of_type(:string).with_options(:null => false) }
|
328
|
+
it { should have_db_column(:arch).of_type(:string) }
|
329
|
+
it { should have_db_column(:comm).of_type(:string) }
|
330
|
+
it { should have_db_column(:comments).of_type(:text) }
|
331
|
+
it { should have_db_column(:info).of_type(:string).with_options(:limit => 2 ** 16) }
|
332
|
+
it { should have_db_column(:mac).of_type(:string) }
|
333
|
+
it { should have_db_column(:name).of_type(:string) }
|
334
|
+
it { should have_db_column(:os_flavor).of_type(:string) }
|
335
|
+
it { should have_db_column(:os_lang).of_type(:string) }
|
336
|
+
it { should have_db_column(:os_name).of_type(:string) }
|
337
|
+
it { should have_db_column(:os_sp).of_type(:string) }
|
338
|
+
it { should have_db_column(:purpose).of_type(:text) }
|
339
|
+
it { should have_db_column(:scope).of_type(:text) }
|
340
|
+
it { should have_db_column(:state).of_type(:string) }
|
341
|
+
it { should have_db_column(:virtual_host).of_type(:text) }
|
342
|
+
it { should have_db_column(:workspace_id).of_type(:integer).with_options(:null => false) }
|
343
|
+
|
344
|
+
context 'counter caches' do
|
345
|
+
it { should have_db_column(:exploit_attempt_count).of_type(:integer).with_options(:default => 0) }
|
346
|
+
it { should have_db_column(:host_detail_count).of_type(:integer).with_options(:default => 0) }
|
347
|
+
it { should have_db_column(:note_count).of_type(:integer).with_options(:default => 0) }
|
348
|
+
it { should have_db_column(:service_count).of_type(:integer).with_options(:default => 0) }
|
349
|
+
it { should have_db_column(:vuln_count).of_type(:integer).with_options(:default => 0) }
|
350
|
+
end
|
351
|
+
|
352
|
+
context 'timestamps' do
|
353
|
+
it { should have_db_column(:created_at).of_type(:datetime) }
|
354
|
+
it { should have_db_column(:updated_at).of_type(:datetime) }
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
context 'indices' do
|
359
|
+
it { should have_db_index([:workspace_id, :address]).unique(true) }
|
360
|
+
it { should have_db_index(:name) }
|
361
|
+
it { should have_db_index(:os_flavor) }
|
362
|
+
it { should have_db_index(:os_name) }
|
363
|
+
it { should have_db_index(:purpose) }
|
364
|
+
it { should have_db_index(:state) }
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context 'factories' do
|
369
|
+
context 'full_mdm_host' do
|
370
|
+
subject(:full_mdm_host) do
|
371
|
+
FactoryGirl.build(:full_mdm_host)
|
372
|
+
end
|
373
|
+
|
374
|
+
it { should be_valid }
|
375
|
+
end
|
376
|
+
|
377
|
+
context 'mdm_host' do
|
378
|
+
subject(:mdm_host) do
|
379
|
+
FactoryGirl.build(:mdm_host)
|
380
|
+
end
|
381
|
+
|
382
|
+
it { should be_valid }
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
context 'validations' do
|
387
|
+
context 'address' do
|
388
|
+
it { should ensure_exclusion_of(:address).in_array(['127.0.0.1']) }
|
389
|
+
it { should validate_presence_of(:address) }
|
390
|
+
|
391
|
+
# can't use validate_uniqueness_of(:address).scoped_to(:workspace_id) because it will attempt to set workspace_id
|
392
|
+
# to `nil`, which will make the `:null => false` constraint on hosts.workspace_id to fail.
|
393
|
+
it 'should validate uniqueness of address scoped to workspace_id' do
|
394
|
+
address = '192.168.0.1'
|
395
|
+
|
396
|
+
workspace = FactoryGirl.create(:mdm_workspace)
|
397
|
+
FactoryGirl.create(:mdm_host, :address => address, :workspace => workspace)
|
398
|
+
|
399
|
+
duplicate_host = FactoryGirl.build(:mdm_host, :address => address, :workspace => workspace)
|
400
|
+
|
401
|
+
duplicate_host.should_not be_valid
|
402
|
+
duplicate_host.errors[:address].should include('has already been taken')
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
it { should ensure_inclusion_of(:arch).in_array(architectures).allow_nil }
|
407
|
+
it { should ensure_inclusion_of(:os_name).in_array(operating_system_names).allow_nil }
|
408
|
+
it { should ensure_inclusion_of(:state).in_array(states).allow_nil }
|
409
|
+
it { should validate_presence_of(:workspace) }
|
410
|
+
end
|
411
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Mdm::
|
3
|
+
describe Mdm::Module::Action do
|
4
4
|
context 'associations' do
|
5
|
-
it { should belong_to(:
|
5
|
+
it { should belong_to(:detail).class_name('Mdm::Module::Detail') }
|
6
6
|
end
|
7
7
|
|
8
8
|
context 'database' do
|
9
9
|
context 'columns' do
|
10
|
-
it { should have_db_column(:
|
10
|
+
it { should have_db_column(:detail_id).of_type(:integer) }
|
11
11
|
it { should have_db_column(:name).of_type(:text) }
|
12
12
|
end
|
13
13
|
|
14
14
|
context 'indices' do
|
15
|
-
it { should have_db_index(:
|
15
|
+
it { should have_db_index(:detail_id) }
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -27,12 +27,12 @@ describe Mdm::ModuleAction do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
context 'mass assignment security' do
|
30
|
-
it { should_not allow_mass_assignment_of(:
|
30
|
+
it { should_not allow_mass_assignment_of(:detail_id) }
|
31
31
|
it { should allow_mass_assignment_of(:name) }
|
32
32
|
end
|
33
33
|
|
34
34
|
context 'validations' do
|
35
|
-
it { should validate_presence_of(:
|
35
|
+
it { should validate_presence_of(:detail) }
|
36
36
|
it { should validate_presence_of(:name) }
|
37
37
|
end
|
38
38
|
end
|