· 9 years ago · Jul 31, 2017, 05:44 AM
1require File.join(File.dirname(__FILE__), '..', 'CONFIG.rb')
2
3require 'rubygems'
4require 'test/unit'
5require 'pp'
6
7require 'og'
8
9class Logger
10 class ObsoleteField < RuntimeError; end
11 class MissingField < RuntimeError; end
12 @@boom = false
13
14 def self.warn(str, boom = @@boom)
15
16 @@global_logger.warn(str)
17
18 @@global_logger.warn('I WILL BOOM!') if boom
19
20 if boom && str =~ /(Obsolete|Missing) field/
21 case $1
22 when 'Obsolete'
23 raise ObsoleteField.new(str)
24 when 'Missing'
25 raise MissingField.new(str)
26 end
27 end
28
29 end # end self.warn()
30
31 def self.boom=(bool)
32 bool
33 end
34end # end Logger
35
36class TC_Evolution < Test::Unit::TestCase
37
38 # Sets up a new store with given options
39
40 def self.setup_og(options = {})
41 Og.create_schema = options[:create_schema] || Og.create_schema
42 Logger.boom = options[:boom] || false
43
44 opt = $og1.instance_variable_get('@options')
45 opt = opt.update(options)
46 opt[:classes] = []
47 Og.setup(opt)
48 end
49
50 # Initialize a set of stores with differing options
51
52 STORES = {
53 :full => setup_og(:destroy => false, :evolve_schema => :full),
54 :warn => setup_og(:destroy => false, :evolve_schema => :warn),
55 false => setup_og(:destroy => false, :evolve_schema => false),
56 true => setup_og(:destroy => false, :evolve_schema => true),
57 :add => setup_og(:destroy => false, :evolve_schema => :add)
58 }.freeze
59
60 def setup
61
62 end
63
64 def teardown
65 store = $og1.get_store
66 destroy_class('User')
67 destroy_class('Admin')
68
69 if store.table_exists?("ogtc_evolution_user")
70 store.exec("DROP TABLE ogtc_evolution_user")
71 end
72 ensure
73 $og1.put_store
74 end
75
76 User = nil
77 Admin = nil
78
79 # workaround, can't change constants in method scope. Usage: CHUser.call()
80
81 MKClass = proc do |klass, x|
82 case klass
83 when 'User'
84 User = x
85 when 'Admin'
86 Admin = x
87 else
88 raise "No such class #{klass}"
89 end
90 end
91
92 def test_testcase
93 assert_table_no_exists 'ogtc_evolution_user'
94 create_class('User', :name => String)
95 assert_not_nil User
96 check_fields(User, ['oid', 'name'])
97
98 destroy_class
99 assert_nil User
100 assert !Og::Manager.managed?(User)
101
102 std_setup
103 assert Og::Manager.managed?(User)
104 end
105
106 def test_evolve_full_simple
107 assert_nil User
108 o = STORES[:full]
109 create_class('User', :name => String, :store => :full)
110
111 check_fields(User, ['oid', 'name'])
112 check_db_fields(User, ['oid', 'name'])
113
114 User.property :pass, String
115
116 check_fields(User, ['oid', 'name', 'pass'])
117 check_db_fields(User, ['oid', 'name'])
118
119 store = o.get_store
120 store.evolve_schema(User)
121 o.put_store
122
123 check_fields(User, ['oid', 'name', 'pass'])
124 check_db_fields(User, ['oid', 'name', 'pass'])
125 end
126
127 def test_evolve_warn_simple
128 o = STORES[:warn]
129 create_class('User', :name => String, :store => :warn)
130
131 check_fields(User, ['oid', 'name'])
132 check_db_fields(User, ['oid', 'name'])
133
134 User.property :pass, String
135
136 check_fields(User, ['oid', 'name', 'pass'])
137 check_db_fields(User, ['oid', 'name'])
138
139 store = o.get_store
140 store.evolve_schema(User)
141 o.put_store
142
143 check_fields(User, ['oid', 'name', 'pass'])
144 check_db_fields(User, ['oid', 'name'])
145 end
146
147 def test_evolve_full
148 std_setup(:store => :full)
149
150 check_db_fields(User, ['oid', 'nick'])
151 end
152
153 def test_evolve_add
154 std_setup(:store => :add)
155
156 check_db_fields(User, ['oid', 'nick', 'name', 'password'])
157 end
158
159 def test_evolve_warn
160 std_setup(:store => :warn)
161
162 check_db_fields(User, ['oid', 'name', 'password'])
163 end
164
165 def test_evolve_false
166 std_setup(:store => false)
167
168 check_db_fields(User, ['oid', 'name', 'password'])
169 end
170
171 def test_evolve_true
172 std_setup(:store => true)
173
174 check_db_fields(User, ['oid', 'nick', 'name', 'password'])
175 end
176
177
178 ###################################################################
179 # Only support stuff after here
180 ###################################################################
181
182private
183
184 # Create standard setup.
185 # First creates:
186 # class User
187 # property :name, String
188 # property :password, String
189 # end
190 # Then it deletes that class after enchanting it and recreates it as
191 # following:
192 # class User
193 # property :nick, String
194 # end
195
196 def std_setup(options = {})
197 options = {:store => :full}.update(options)
198 o = STORES[options[:store]]
199 raise "No store for #{options[:store].inspect}?}" unless o
200
201 create_class('User', :name => String, :password => String, :store => options[:store])
202
203 check_fields(User, ['oid', 'name', 'password'])
204
205 destroy_class
206
207 create_class('User', :nick => String, :store => options[:store])
208 end
209
210 # Creates:
211 # class User
212 # is Og::SchemaInheritanceBase
213 # property :name, String
214 # property :password, String
215 # end
216 # class Admin < User
217 # property :protected, TrueClass
218 # end
219
220 def prepare_sti_classes(options = {})
221 options = {:store => :full}.update(options)
222 o = STORES[options[:store]]
223 raise "No store for #{options[:store].inspect}?}" unless o
224
225 create_class('User', :name => String, :password => String, :store => options[:store])
226 User.include Og::SchemaInheritanceBase
227 create_class('Admin', :superclass => User, :protected => TrueClass, :store => options[:store])
228 end
229
230 # Checks if attributes of a class are available as expected
231
232 def check_fields(klass, expected)
233 attrs = klass.serializable_attributes.map {|x| x.to_s }.sort
234
235 assert_equal expected.sort, attrs, "Does not have required fields."
236 end
237
238 # Checks if database fields of a given class are as expected
239
240 def check_db_fields(klass, expected)
241 fields = klass.ogmanager.store.create_field_map(klass).keys.map {|x| x.to_s }.sort
242 klass.ogmanager.put_store
243 assert_equal expected.sort, fields, "Does not have required db fields."
244 end
245
246 # Creates a new class using the MKClass facility
247 # Usage:
248 # create_class([klass, [options]])
249 # create_class('User', :superclass => Object, :store => :full, props...)
250 # props will map to properties withing that class:
251 # :name => String will map to Klass.property :name, String
252
253 def create_class(klass = 'User', options = {})
254 options = {:store => :full}.update(options)
255 st = STORES[options.delete(:store)]
256 raise "No such store for #{options[:store].inspect}?" unless st
257
258 sc = options.delete(:superclass)
259 MKClass.call(klass, Class.new(sc || Object))
260
261 options.each do |prop, type|
262 User.property prop, type
263 end
264 st.manage_classes User
265 end
266
267 # Destroys the given class using the given classname
268
269 def destroy_class(klass = 'User')
270 kl = constant(klass)
271 return unless kl
272 kl.ogmanager.unmanage_class kl
273 kl.ogmanager.put_store
274 MKClass.call(klass, nil)
275 end
276
277 ###################################################################
278 # Only assertions stuff after here
279 ###################################################################
280
281 def assert_table_no_exists(table, message = '')
282 store = $og1.get_store
283
284 message = build_message message, "<?> exists.", table
285
286 assert_block message do
287 if store.table_exists?("ogtc_evolution_user")
288 false
289 else
290 true
291 end
292 end
293 ensure
294 $og1.put_store
295 end
296
297end