metasploit-erd 0.0.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.
- checksums.yaml +15 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +26 -0
- data/.rspec +3 -0
- data/.simplecov +42 -0
- data/.travis.yml +7 -0
- data/.yardopts +1 -0
- data/Gemfile +26 -0
- data/LICENSE +28 -0
- data/README.md +44 -0
- data/Rakefile +9 -0
- data/lib/metasploit/erd.rb +35 -0
- data/lib/metasploit/erd/cluster.rb +49 -0
- data/lib/metasploit/erd/clusterable.rb +39 -0
- data/lib/metasploit/erd/diagram.rb +89 -0
- data/lib/metasploit/erd/entity.rb +11 -0
- data/lib/metasploit/erd/entity/class.rb +69 -0
- data/lib/metasploit/erd/entity/namespace.rb +66 -0
- data/lib/metasploit/erd/relationship.rb +57 -0
- data/lib/metasploit/erd/version.rb +31 -0
- data/lib/tasks/yard.rake +32 -0
- data/metasploit-erd.gemspec +33 -0
- data/spec/metasploit/erd/cluster_spec.rb +158 -0
- data/spec/metasploit/erd/diagram_spec.rb +238 -0
- data/spec/metasploit/erd/entity/class_spec.rb +236 -0
- data/spec/metasploit/erd/entity/namespace_spec.rb +130 -0
- data/spec/metasploit/erd/relationship_spec.rb +246 -0
- data/spec/metasploit/erd/version_spec.rb +125 -0
- data/spec/metasploit/erd_spec.rb +15 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/shared/contexts/active_record_base_connection.rb +12 -0
- data/spec/support/shared/contexts/active_record_base_descendants_cleaner.rb +11 -0
- data/spec/support/shared/examples/metasploit/erd/clusterable.rb +284 -0
- metadata +187 -0
@@ -0,0 +1,238 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::ERD::Diagram do
|
4
|
+
subject(:diagram) {
|
5
|
+
described_class.new(*arguments)
|
6
|
+
}
|
7
|
+
|
8
|
+
let(:arguments) {
|
9
|
+
[
|
10
|
+
domain
|
11
|
+
]
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:domain) {
|
15
|
+
RailsERD::Domain.new
|
16
|
+
}
|
17
|
+
|
18
|
+
it { should be_a RailsERD::Diagram::Graphviz }
|
19
|
+
|
20
|
+
context 'CONSTANTS' do
|
21
|
+
context 'ATTRIBUTES' do
|
22
|
+
subject(:attributes) {
|
23
|
+
described_class::ATTRIBUTES
|
24
|
+
}
|
25
|
+
|
26
|
+
it { should include :content }
|
27
|
+
it { should include :foreign_keys }
|
28
|
+
it { should include :primary_keys }
|
29
|
+
it { should include :timestamps }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'DEFAULT_OPTIONS' do
|
33
|
+
subject(:default_options) {
|
34
|
+
described_class::DEFAULT_OPTIONS
|
35
|
+
}
|
36
|
+
|
37
|
+
context '[:attributes]' do
|
38
|
+
subject(:attributes) {
|
39
|
+
default_options[:attributes]
|
40
|
+
}
|
41
|
+
|
42
|
+
it 'should be ATTRIBUTES' do
|
43
|
+
expect(attributes).to eq(described_class::ATTRIBUTES)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context '[:filetype]' do
|
48
|
+
subject(:filetype) {
|
49
|
+
default_options[:filetype]
|
50
|
+
}
|
51
|
+
|
52
|
+
it 'should be FILETYPE' do
|
53
|
+
expect(filetype).to eq(described_class::FILETYPE)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context '[:indirect]' do
|
58
|
+
subject(:indirect) {
|
59
|
+
default_options[:indirect]
|
60
|
+
}
|
61
|
+
|
62
|
+
it 'should be INDIRECT' do
|
63
|
+
expect(indirect).to eq(described_class::INDIRECT)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context '[:inheritance]' do
|
68
|
+
subject(:inheritance) {
|
69
|
+
default_options[:inheritance]
|
70
|
+
}
|
71
|
+
|
72
|
+
it 'should be INHERITANCE' do
|
73
|
+
expect(inheritance).to eq(described_class::INHERITANCE)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context '[:notation]' do
|
78
|
+
subject(:notation) {
|
79
|
+
default_options[:notation]
|
80
|
+
}
|
81
|
+
|
82
|
+
it 'should be NOTATION' do
|
83
|
+
expect(notation).to eq(described_class::NOTATION)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context '[:polymorphism]' do
|
88
|
+
subject(:polymorphism) {
|
89
|
+
default_options[:polymorphism]
|
90
|
+
}
|
91
|
+
|
92
|
+
it 'should be POLYMORPHISM' do
|
93
|
+
expect(polymorphism).to eq(described_class::POLYMORPHISM)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'FILETYPE' do
|
99
|
+
subject(:filetype) {
|
100
|
+
described_class::FILETYPE
|
101
|
+
}
|
102
|
+
|
103
|
+
it { should == :png }
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'INDIRECT' do
|
107
|
+
subject(:indirect) {
|
108
|
+
described_class::INDIRECT
|
109
|
+
}
|
110
|
+
|
111
|
+
it { should be_false }
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'INHERITANCE' do
|
115
|
+
subject(:inheritance) {
|
116
|
+
described_class::INHERITANCE
|
117
|
+
}
|
118
|
+
|
119
|
+
it { should be_true }
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'NOTATION' do
|
123
|
+
subject(:notation) {
|
124
|
+
described_class::NOTATION
|
125
|
+
}
|
126
|
+
|
127
|
+
it { should == :crowsfoot }
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'POLYMORPHISM' do
|
131
|
+
subject(:polymorphism) {
|
132
|
+
described_class::POLYMORPHISM
|
133
|
+
}
|
134
|
+
|
135
|
+
it { should be_true }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'callbacks' do
|
140
|
+
subject(:callbacks) {
|
141
|
+
described_class.send(:callbacks)
|
142
|
+
}
|
143
|
+
|
144
|
+
context '[:each_entity]' do
|
145
|
+
subject(:each_entity) {
|
146
|
+
callbacks[:each_entity]
|
147
|
+
}
|
148
|
+
|
149
|
+
it { should_not be_nil }
|
150
|
+
|
151
|
+
it 'uses RailsERD::Diagram::Graphviz.callbacks[:each_entity]' do
|
152
|
+
expect(each_entity).to eq(RailsERD::Diagram::Graphviz.send(:callbacks)[:each_entity])
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context '[:each_relationship]' do
|
157
|
+
subject(:each_relationship) {
|
158
|
+
callbacks[:each_relationship]
|
159
|
+
}
|
160
|
+
|
161
|
+
it { should_not be_nil }
|
162
|
+
|
163
|
+
it 'uses RailsERD::Diagram::Graphviz.callbacks[:each_relationship]' do
|
164
|
+
expect(each_relationship).to eq(RailsERD::Diagram::Graphviz.send(:callbacks)[:each_relationship])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context '[:each_specialization]' do
|
169
|
+
subject(:each_specialization) {
|
170
|
+
callbacks[:each_specialization]
|
171
|
+
}
|
172
|
+
|
173
|
+
it { should_not be_nil }
|
174
|
+
|
175
|
+
it 'uses RailsERD::Diagram::Graphviz.callbacks[:each_specialization]' do
|
176
|
+
expect(each_specialization).to eq(RailsERD::Diagram::Graphviz.send(:callbacks)[:each_specialization])
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
context '[:save]' do
|
181
|
+
subject(:save) {
|
182
|
+
callbacks[:save]
|
183
|
+
}
|
184
|
+
|
185
|
+
it { should_not be_nil }
|
186
|
+
|
187
|
+
it 'extends RailsERD::Diagram::Graphviz.callbacks[:save]' do
|
188
|
+
expect(save).not_to eq(RailsERD::Diagram::Graphviz.send(:callbacks)[:save])
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context '[:setup]' do
|
193
|
+
subject(:setup) {
|
194
|
+
callbacks[:setup]
|
195
|
+
}
|
196
|
+
|
197
|
+
it { should_not be_nil }
|
198
|
+
|
199
|
+
it 'uses RailsERD::Diagram::Graphviz.callbacks[:setup]' do
|
200
|
+
expect(setup).to eq(RailsERD::Diagram::Graphviz.send(:callbacks)[:setup])
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context '#initialize' do
|
206
|
+
context 'with domain' do
|
207
|
+
it 'uses first argument as domain' do
|
208
|
+
expect(diagram.domain).to eq(arguments.first)
|
209
|
+
end
|
210
|
+
|
211
|
+
context 'with options' do
|
212
|
+
let(:arguments) {
|
213
|
+
super() + [options]
|
214
|
+
}
|
215
|
+
|
216
|
+
let(:options) {
|
217
|
+
{
|
218
|
+
key: :value
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
it 'merges options with DEFAULT_OPTIONS' do
|
223
|
+
expect(described_class::DEFAULT_OPTIONS).to receive(:merge).with(options).and_call_original
|
224
|
+
|
225
|
+
diagram
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context 'without options' do
|
230
|
+
it 'uses DEFAULT_OPTIONS as #options' do
|
231
|
+
described_class::DEFAULT_OPTIONS.each do |key, value|
|
232
|
+
expect(diagram.options[key]).to eq(value)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Metasploit::ERD::Entity::Class do
|
4
|
+
include_context 'ActiveRecord::Base.descendants cleaner'
|
5
|
+
|
6
|
+
subject(:class_entity) {
|
7
|
+
described_class.new(klass)
|
8
|
+
}
|
9
|
+
|
10
|
+
#
|
11
|
+
# lets
|
12
|
+
#
|
13
|
+
|
14
|
+
let(:klass) {
|
15
|
+
Class.new(ActiveRecord::Base)
|
16
|
+
}
|
17
|
+
|
18
|
+
let(:klass_name) do
|
19
|
+
'Klass'
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Callbacks
|
24
|
+
#
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
stub_const(klass_name, klass)
|
28
|
+
end
|
29
|
+
|
30
|
+
it_should_behave_like 'Metasploit::ERD::Clusterable' do
|
31
|
+
let(:entity) {
|
32
|
+
described_class.new(Dummy::Widget)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
context '#class_set' do
|
37
|
+
include_context 'ActiveRecord::Base connection'
|
38
|
+
|
39
|
+
subject(:class_set) do
|
40
|
+
class_entity.class_set
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with belongs_to associations' do
|
44
|
+
context 'with same class twice' do
|
45
|
+
#
|
46
|
+
# lets
|
47
|
+
#
|
48
|
+
|
49
|
+
let(:target_name) do
|
50
|
+
'Target'
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:target) do
|
54
|
+
Class.new(ActiveRecord::Base)
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Callbacks
|
59
|
+
#
|
60
|
+
|
61
|
+
before(:each) do
|
62
|
+
target_name = self.target_name
|
63
|
+
|
64
|
+
stub_const(target_name, target)
|
65
|
+
|
66
|
+
klass.class_eval do
|
67
|
+
#
|
68
|
+
# Associations
|
69
|
+
#
|
70
|
+
|
71
|
+
# @!attribute first_target
|
72
|
+
# @return [Target]
|
73
|
+
belongs_to :first_target,
|
74
|
+
class_name: target_name,
|
75
|
+
inverse_of: :first_klasses
|
76
|
+
|
77
|
+
# @!attribute second_target
|
78
|
+
# @return [Target]
|
79
|
+
belongs_to :second_target,
|
80
|
+
class_name: target_name,
|
81
|
+
inverse_of: :second_klasses
|
82
|
+
end
|
83
|
+
|
84
|
+
klass_name = self.klass_name
|
85
|
+
|
86
|
+
target.class_eval do
|
87
|
+
#
|
88
|
+
# Associations
|
89
|
+
#
|
90
|
+
|
91
|
+
# @!attribute first_klasses
|
92
|
+
# @return [ActiveRecord::Relation<Klass>]
|
93
|
+
has_many :first_klasses,
|
94
|
+
class_name: klass_name,
|
95
|
+
inverse_of: :first_target
|
96
|
+
|
97
|
+
# @!attribute second_klasses
|
98
|
+
# @return [ActiveRecord::Relation<Klass>]
|
99
|
+
has_many :second_klasses,
|
100
|
+
class_name: klass_name,
|
101
|
+
inverse_of: :second_target
|
102
|
+
end
|
103
|
+
|
104
|
+
ActiveRecord::Migration.verbose = false
|
105
|
+
|
106
|
+
ActiveRecord::Migration.create_table :targets do |t|
|
107
|
+
t.timestamps
|
108
|
+
end
|
109
|
+
|
110
|
+
ActiveRecord::Migration.create_table :klasses do |t|
|
111
|
+
t.references :first_target
|
112
|
+
t.references :second_target
|
113
|
+
|
114
|
+
t.timestamps
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'includes class once' do
|
119
|
+
expect(class_set).to have(1).items
|
120
|
+
expect(class_set).to include(target)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'with has_many associations' do
|
125
|
+
let(:belongs_to_target) do
|
126
|
+
Class.new(ActiveRecord::Base)
|
127
|
+
end
|
128
|
+
|
129
|
+
let(:belongs_to_target_name) do
|
130
|
+
'BelongsToTarget'
|
131
|
+
end
|
132
|
+
|
133
|
+
let(:has_many_target) do
|
134
|
+
Class.new(ActiveRecord::Base)
|
135
|
+
end
|
136
|
+
|
137
|
+
let(:has_many_target_name) do
|
138
|
+
'HasManyTarget'
|
139
|
+
end
|
140
|
+
|
141
|
+
before(:each) do
|
142
|
+
belongs_to_target_name = self.belongs_to_target_name
|
143
|
+
klass_name = self.klass_name
|
144
|
+
has_many_target_name = self.has_many_target_name
|
145
|
+
|
146
|
+
stub_const(belongs_to_target_name, belongs_to_target)
|
147
|
+
stub_const(has_many_target_name, has_many_target)
|
148
|
+
|
149
|
+
belongs_to_target.class_eval do
|
150
|
+
#
|
151
|
+
# Associations
|
152
|
+
#
|
153
|
+
|
154
|
+
# @!attribute klasses
|
155
|
+
# @return [ActiveRecord::Relation<Klass>]
|
156
|
+
has_many :klasses,
|
157
|
+
class_name: klass_name,
|
158
|
+
inverse_of: :belongs_to_target
|
159
|
+
end
|
160
|
+
|
161
|
+
has_many_target.class_eval do
|
162
|
+
#
|
163
|
+
# Associations
|
164
|
+
#
|
165
|
+
|
166
|
+
# @!attribute klass
|
167
|
+
# @return [Klass]
|
168
|
+
belongs_to :klass,
|
169
|
+
class_name: klass_name,
|
170
|
+
inverse_of: :has_many_targets
|
171
|
+
end
|
172
|
+
|
173
|
+
klass.class_eval do
|
174
|
+
#
|
175
|
+
# Associations
|
176
|
+
#
|
177
|
+
|
178
|
+
# @!attribute belongs_to_target
|
179
|
+
# @return [BelongsToTarget]
|
180
|
+
belongs_to :belongs_to_target,
|
181
|
+
class_name: belongs_to_target_name,
|
182
|
+
inverse_of: :klasses
|
183
|
+
|
184
|
+
# @!attribute has_many_targets
|
185
|
+
# @return [HasManyTarget]
|
186
|
+
has_many :has_many_target,
|
187
|
+
class_name: has_many_target_name,
|
188
|
+
inverse_of: :klass
|
189
|
+
end
|
190
|
+
|
191
|
+
ActiveRecord::Migration.verbose = false
|
192
|
+
|
193
|
+
ActiveRecord::Migration.create_table :belongs_to_targets do |t|
|
194
|
+
t.timestamps
|
195
|
+
end
|
196
|
+
|
197
|
+
ActiveRecord::Migration.create_table :klass do |t|
|
198
|
+
t.references :belongs_to_target
|
199
|
+
|
200
|
+
t.timestamps
|
201
|
+
end
|
202
|
+
|
203
|
+
ActiveRecord::Migration.create_table :has_many_targets do |t|
|
204
|
+
t.references :klass
|
205
|
+
|
206
|
+
t.timestamps
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'includes belongs_to target classes' do
|
211
|
+
expect(class_set).to include(belongs_to_target)
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'does not include has_many target classes' do
|
215
|
+
expect(class_set).not_to include(has_many_target)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context 'without belongs_to associations' do
|
221
|
+
it { should be_empty }
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context '#cluster' do
|
226
|
+
subject(:cluster) {
|
227
|
+
class_entity.cluster
|
228
|
+
}
|
229
|
+
|
230
|
+
it 'creates a Metasploit::ERD::Cluster containing #klass' do
|
231
|
+
expect(Metasploit::ERD::Cluster).to receive(:new).with(klass)
|
232
|
+
|
233
|
+
cluster
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|