OSDN Git Service

Initial commit
[hdboo/hdboo.git] / test / tc_sql.rb
1 #!/usr/bin/ruby -Ku
2 # -*- encoding: UTF-8 -*-
3
4 require 'test/unit'
5 require 'hdboo/sql'
6
7 include Hdboo
8
9 module TestSQL
10   USER_SQL = <<-END_SQL
11   -- user.sql
12   -- @Module(Model)
13   -- @Include(Pivot)
14
15   DELIMITER //
16   
17   CREATE TABLE UserType (
18     user_type_code
19       CHAR(24) NOT NULL,
20       CONSTRAINT  PK_UserType
21         PRIMARY KEY (user_type_code),
22         
23     name
24       VARCHAR(256) NOT NULL
25   ) ENGINE = INNODB
26   //
27   
28   CREATE PROCEDURE UserType_load_master_data()
29   -- @MasterData
30   BEGIN
31     INSERT INTO UserType
32     VALUES
33       ('User',           '通常ユーザー'),
34       ('PrivilegedUser', '特権ユーザー');
35   END
36   //
37   
38   CREATE PROCEDURE UserType_list_all_types()
39   -- @DefineConstants(user_type_code)
40   BEGIN
41     SELECT *
42     FROM   UserType;
43   END
44   //
45   
46   CREATE TABLE User (
47   -- @ClassifiedBy(user_type_code)
48   
49     user_id
50       INTEGER AUTO_INCREMENT,
51       CONSTRAINT
52         PK_User PRIMARY KEY(user_id),
53     
54     user_type_code
55       CHAR(24) NOT NULL DEFAULT 'User',
56       CONSTRAINT FK_User_user_type_code
57         FOREIGN KEY(user_type_code) REFERENCES UserType(user_type_code),
58       
59     is_active
60       -- @Bool
61       INTEGER NOT NULL DEFAULT 0, 
62
63     name
64       -- @KanaKanji @NotBlank
65       CHAR(32) NOT NULL,
66
67     nickname
68       -- @Word @NotBlank
69       CHAR(32) NOT NULL,
70       INDEX IND_User_nickname (nickname),
71
72     mail_address
73       -- @MailAddress @NotBlank
74       CHAR(128) NOT NULL,
75       CONSTRAINT UQ_User_mail_address
76         UNIQUE(mail_address),
77       
78     comment
79       TEXT NOT NULL
80       
81   ) ENGINE=INNODB
82   //
83            
84   CREATE PROCEDURE User_count_active_users()
85   -- @ReturnsFirst(INTEGER)
86   BEGIN
87     SELECT COUNT(*)
88     FROM   User
89     WHERE  is_active;
90   END
91   //
92
93   CREATE PROCEDURE User_list_active_users()
94   -- @Returns(User)
95   BEGIN
96     SELECT *
97     FROM   User
98     WHERE  is_active;
99   END
100   // 
101
102   CREATE PROCEDURE User_find_by_id(
103     _user_id    INTEGER  -- @ColumnValue
104   )
105   -- @ReturnsFirst(User)
106   BEGIN
107     SELECT *
108     FROM   User 
109     WHERE  user_id = _user_id;
110   END
111   //
112
113   CREATE PROCEDURE User_activate(
114     _mail_address CHAR(128) -- @ColumnValue
115   )
116   -- @ReturnsFirst(User)
117   -- @OnNoData(mail_address, 'メールアドレスが誤っているか、まだ登録されていません。')
118   BEGIN
119     UPDATE User
120     SET    is_active = 1
121     WHERE  mail_address = _mail_address
122     AND    NOT is_active;
123
124     SELECT *
125     FROM   User
126     WHERE  mail_address = _mail_address
127     AND    is_active;
128   END
129   //
130
131   CREATE PROCEDURE User_register(
132     _name         TEXT, -- @ColumnValue
133     _nickname     TEXT, -- @ColumnValue
134     _mail_address TEXT, -- @ColumnValue
135     _comment      TEXT  -- @ColumnValue @Optional
136   )
137   -- @ReturnsFirst(User)
138   -- @OnConflict(UQ_User_mail_address, mail_address, 'このメールアドレスは既に登録されています。')
139   BEGIN
140     SET @comment := COALESCE(_comment, '');
141
142     INSERT INTO User
143     VALUES (
144       DEFAULT,
145       DEFAULT,
146       DEFAULT,
147       _name,
148       _nickname,
149       _mail_address,
150       @comment
151     );
152
153     SELECT *
154     FROM   User
155     WHERE  user_id = LAST_INSERT_ID();
156   END
157   //
158   
159   CREATE PROCEDURE User_edit_comment(
160     _user_id INTEGER,  -- @ColumnValue
161     _comment TEXT      -- @ColumnValue
162   )
163   -- @ReturnsFirst(User)
164   -- @OnNoData(user_id, 'このユーザは登録されていないか、一時的に使用できなくなっています。')
165   BEGIN
166     UPDATE User
167     SET    comment = _comment
168     WHERE  user_id = _user_id
169     AND    is_active;
170     
171     SELECT *
172     FROM   User
173     WHERE  user_id = _user_id
174     AND    is_active;
175   END
176   //
177
178   CREATE TABLE PrivilegedUser (
179     user_id
180       INTEGER NOT NULL,
181       CONSTRAINT PK_PrivilegedUser
182         PRIMARY KEY(user_id),
183       CONSTRAINT FK_PrivilegedUser_user_id
184         FOREIGN KEY(user_id) REFERENCES User(user_id)
185         ON DELETE CASCADE,
186     
187     access_level
188       -- @Min(1) @Max(5)
189       INTEGER NOT NULL DEFAULT 1
190       
191   ) ENGINE = INNODB
192   //
193   
194   CREATE PROCEDURE User_make_privileged(
195     _user_id INTEGER -- @ColumnValue
196   )
197   -- @ReturnsFirst(User)
198   -- @OnNoData(user_id, 'このユーザはすでに特権ユーザとなっているか、一時的に使用できなくなっています。')
199   BEGIN
200     UPDATE User
201     SET    user_type_code = 'PrivilegedUser'
202     WHERE  user_id = _user_id
203     AND    is_active
204     AND    user_type_code = 'User';
205     
206     INSERT INTO PrivilegedUser
207     SELECT user_id, 1
208     FROM   User
209     WHERE  user_id = _user_id
210     AND    is_active
211     AND    user_type_code = 'PrivilegedUser';
212     
213     SELECT *
214     FROM   User JOIN PrivilegedUser USING (user_id)
215     WHERE  user_id = _user_id
216     AND    is_active;
217   END
218   //
219   
220   CREATE PROCEDURE PrivilegedUser_find_by_id(
221     _user_id INTEGER
222   )
223   -- @ReturnsFirst(User)
224   BEGIN
225     SELECT *
226     FROM   User JOIN PrivilegedUser USING (user_id)
227     WHERE  user_id = _user_id
228     AND    is_active
229     AND    user_type_code = 'PrivilegedUser';
230   END
231   //
232   
233   CREATE PROCEDURE PrivilegedUser_acquire_top_secret(
234     _user_id INTEGER -- @ColumnValue
235   )
236   -- @ReturnsFirst(User)
237   -- @OnNoData('*', 'このユーザには権限がありません。')
238   BEGIN
239     SELECT *, '王様の耳は、ろばタイプ' AS top_secret
240     FROM   User JOIN PrivilegedUser USING(user_id)
241     WHERE  user_id = _user_id
242     AND    is_active;
243   END
244   //
245   END_SQL
246 end
247
248 class TestSQLLoad < Test::Unit::TestCase
249   include Hdboo
250   
251   def test_query
252     SQL.sql_eval!('test_hdboo_temp_db', TestSQL::USER_SQL)
253     db = SQL.connect('test_hdboo_temp_db')
254     sql = <<-SQL_END
255       DELETE
256       FROM   UserType
257       WHERE  user_type_code = 'User';
258     SQL_END
259     db.transaction do
260       assert_equal 2, Model::UserType.list_all_types.size
261       SQL.query(sql)
262       assert_equal 1, Model::UserType.list_all_types.size\r    end
263   end
264   
265   def test_equality_of_record_object
266     SQL.sql_eval!('test_hdboo_temp_db', TestSQL::USER_SQL)
267     hogeo = Model::User.new(
268       :user_id => 1,
269       :email   => 'hogeta.hogeo@example.com'
270     )
271     
272     nise_hogeo = Model::User.new(
273       :user_id => 1,
274       :email   => 'nise.hogeo@example.com'
275     )
276     
277     assert_equal [1], hogeo.identifier
278     assert_equal [:user_id], hogeo.class.identifier_keys 
279     assert  hogeo == nise_hogeo
280     assert !hogeo.eql?(nise_hogeo)
281     assert  hogeo == {:user_id => 1, :email => 'hogeta.hogeo@example.com'}
282     assert  hogeo != {:user_id => 1, :email => ''}
283     assert  hogeo.eql?({:user_id => 1, :email => 'hogeta.hogeo@example.com'})
284     assert  ({:user_id => 1, :email => 'hogeta.hogeo@example.com'}) == hogeo
285     assert  ({:user_id => 1, :email => 'hogeta.hogeo@example.com'}).eql?(hogeo)
286     assert  ({:user_id => 1, :email => ''}) != hogeo
287   end
288   
289   def test_require_sql!
290     $:.unshift File.join(File.dirname(__FILE__))
291     require_sql 'testmodel/user@test_hdboo_temp_db'
292     assert ::Fuga::Model::User.is_a?(Class)
293   end
294   
295   def test_sql_eval!
296     SQL.sql_eval!('test_hdboo_temp_db', TestSQL::USER_SQL)
297     db = SQL.connect('test_hdboo_temp_db')
298     
299     #procedure call
300     assert Model::User.respond_to?(:list_active_users)
301     active_users = db.transaction {Model::User.list_active_users}
302     assert active_users.empty?
303     
304     assert Model::User.respond_to?(:find_by_id)
305     nobody = db.transaction {Model::User.find_by_id(:user_id => 9999)}
306     assert nobody.nil?
307     
308     nobody = db.transaction {Model::User.find_by_id(9999)}
309     assert nobody.nil?
310     
311     hogeo = Model::User.new(
312       :name         => 'ホゲ田ほげ夫',
313       :nickname     => 'hogeo',
314       :mail_address => 'invalid..address@example.com'
315     )
316     assert_raise(InvalidArgument) {hogeo.validate}
317     
318     hogeo.mail_address = 'hogeta.hogeo@example.com'
319     assert_nothing_raised {hogeo.validate}
320     
321     assert hogeo.respond_to?(:register)
322     assert hogeo.respond_to?(:register!)
323     
324     db.transaction { hogeo.register! }
325     assert_equal 1, hogeo.user_id
326     assert !     hogeo.active?
327     
328     assert_equal 0, db.transaction { Model::User.count_active_users }
329     db.transaction { hogeo.activate! }
330     assert       hogeo.active?
331     assert_equal 1, db.transaction { Model::User.count_active_users }
332       
333     all_users = db.transaction { Model::User.list_active_users }
334     assert_equal 1, all_users.size
335     assert       all_users[0].is_a?(Model::User)
336     
337     assert_equal '', hogeo.comment
338     db.transaction { hogeo.edit_comment!(:comment => "multi\nline\ndata") }
339     assert_equal "multi\nline\ndata", hogeo.comment
340     
341       
342     #unique constraint violation
343     nise_hogeo = Model::User.new(
344       :name         => 'にせホゲ田ほげ夫',
345       :nickname     => 'nise_hogeo',
346       :mail_address => 'hogeta.hogeo@example.com' #must be unique
347     )
348     
349     assert_raise(DataConflict) do
350       db.transaction { nise_hogeo.register! }\r    end
351     
352     assert_raise(NoDataFound) do
353       db.transaction do
354         Model::User.activate(:mail_address => 'nobody@example.com')
355       end\r    end
356     
357     #initialize master data & load as class constants 
358     assert Model::UserType.is_a?(Class)
359     assert Model::UserType.const_defined?('User')
360     assert Model::UserType.const_defined?('PrivilegedUser')
361     
362     assert_equal 'User', Model::UserType::User.user_type_code
363     assert_equal '通常ユーザー', Model::UserType::User.name
364     
365     assert_equal 'PrivilegedUser',
366                  Model::UserType::PrivilegedUser.user_type_code
367     assert_equal '特権ユーザー',
368                  Model::UserType::PrivilegedUser.name
369     
370     #inheritance
371     assert Model::PrivilegedUser.is_a?(Class)
372     assert Model::PrivilegedUser < Model::User
373     
374     super_hogeo = Model::PrivilegedUser.new(
375       :user_id      => 1,
376       :name         => '超ホゲ田ほげ夫',
377       :nickname     => 'hogeo',
378       :mail_address => 'hogeta.hogeo@example.com'
379     )
380     
381     assert  super_hogeo.is_a?(Model::PrivilegedUser)
382     assert !      hogeo.respond_to?(:acquire_top_secret)
383     assert  super_hogeo.respond_to?(:acquire_top_secret)
384     
385     assert_raise(NoDataFound) do
386       db.transaction {super_hogeo.acquire_top_secret!}\r    end
387     
388     #overridden method
389     assert_not_nil db.transaction {Model::User.find_by_id(:user_id => 1)}
390     assert_not_nil db.transaction {Model::User.find_by_id(1)}
391     assert_nil db.transaction {Model::PrivilegedUser.find_by_id(:user_id => 1)}
392     assert_nil db.transaction {Model::PrivilegedUser.find_by_id(1)}
393    
394     db.transaction { super_hogeo = hogeo.make_privileged }  
395     assert  super_hogeo.is_a?(Model::PrivilegedUser)
396     
397     assert_not_nil db.transaction {Model::PrivilegedUser.find_by_id(1)}
398     assert  super_hogeo.respond_to?(:access_level)
399     assert_equal 1, super_hogeo.access_level
400     
401     db.transaction { super_hogeo.acquire_top_secret! }
402     assert_equal '王様の耳は、ろばタイプ', super_hogeo.top_secret
403   end
404   
405   def test_load_sql
406     SQL.sql_eval('test_hdboo_temp_db', TestSQL::USER_SQL)
407     
408     assert Model::User.is_a?(Class)
409     
410     hogeo = Model::User.new(
411       :name     => 'ホゲ田ほげ夫',
412       :nickname => 'hogeo',
413       :mail_address => 'hogeta.hogeo@example.com',
414       :is_active => 0
415     )
416     
417     #accessors
418     assert hogeo.respond_to?(:mail_address)
419     assert hogeo.respond_to?(:mail_address=)
420     assert_equal 'hogeta.hogeo@example.com', hogeo.mail_address
421     assert hogeo.respond_to?(:nickname)
422     assert hogeo.respond_to?(:nickname=)
423     assert_equal 'hogeo', hogeo.nickname
424     
425     #accessors (boolean)
426     assert hogeo.respond_to?(:is_active)
427     assert hogeo.respond_to?(:is_active=)
428     assert hogeo.respond_to?(:active?)
429     assert_equal 0, hogeo.is_active
430     assert_equal false, hogeo.active?
431     
432     #instance method
433     assert hogeo.respond_to?(:count_active_users)
434     assert hogeo.respond_to?(:list_active_users)
435     
436     #class method
437     assert hogeo.class.respond_to?(:count_active_users)
438     assert hogeo.class.respond_to?(:list_active_users)
439     
440     #validation
441     assert  hogeo.respond_to?(:invalid?)
442     assert  hogeo.respond_to?(:validate)
443     assert !hogeo.invalid?
444     assert_nothing_raised do
445       hogeo.validate
446     end
447     
448     assert hogeo.validate.is_a?(Model::User)
449   
450
451     raised_error = nil
452     begin
453       hogeo.is_active = 'treu'
454     rescue
455       raised_error = $!
456     end
457     assert !     raised_error.nil?
458     assert       raised_error.is_a?(InvalidArgument)
459     assert_equal 1, raised_error.messages.size
460     assert_match /treu/, raised_error.messages[:is_active].to_s
461     
462     hogeo.is_active = true
463     assert hogeo.active?
464   end
465   
466   def test_csv_util
467     SQL.sql_eval!('test_hdboo_temp_db', <<-END_DDL)
468     -- @Module(CSVTest)
469     -- @Include(Pivot)
470     
471     DELIMITER //
472     
473     CREATE TABLE Dummy (
474       id INTEGER NOT NULL AUTO_INCREMENT,
475       PRIMARY KEY (id)
476     ) ENGINE=MYISAM
477     //
478     
479     CREATE PROCEDURE Dummy_parse(csv VARCHAR(1024))
480     BEGIN
481       SELECT csv_get(csv, pos) AS val
482       FROM   pivot
483       WHERE  pos <= csv_length(csv)
484       ;
485     END
486     //
487     END_DDL
488     
489     db = SQL.connect('test_hdboo_temp_db')
490     
491     result = db.transaction do
492       CSVTest::Dummy.parse(:csv => 'hoge,fuga,piyo')
493     end
494     assert_equal 3, result.length
495     assert_equal 'hoge', result[0]['val']
496     assert_equal 'fuga', result[1]['val']
497     assert_equal 'piyo', result[2]['val']
498   end
499 end
500
501 class TestSQLParser < Test::Unit::TestCase  
502   def test_parse
503     ast = SQL::Parser.new.parse(TestSQL::USER_SQL)
504
505     create_table_statements     = []
506     create_procedure_statements = []
507     
508     SQL::Parser::ASTWalker.new\
509       .visit(:CREATE_TABLE) do |statement|
510         create_table_statements << statement
511       end\
512       .visit(:CREATE_PROCEDURE) do |statement|
513         create_procedure_statements << statement
514       end\
515       .walk(ast)
516     
517     assert_equal 3,  create_table_statements.length
518     assert_equal 11, create_procedure_statements.length
519     
520     user_table_source = create_table_statements[0].source.collect do |line|
521                           line.strip
522                         end.join("\n")
523   end
524 end
525
526 class TestTable < Test::Unit::TestCase
527   include Hdboo::SQL
528   def test_new
529     sql_module = SQL::Module.new('TestModule.Model')
530     sql_module.elements << Table.new(:User, [
531       Table::Column.new(:id, DataType.new(:INTEGER)),
532       Table::Constraint::PrimaryKey.new(:User_id_PK, [:id]),
533       Table::Column.new(:mail_address, DataType.new(:CHAR, 128)),
534       Table::Column.new(:password,     DataType.new(:CHAR, 32))
535     ])
536     sql_module.eval
537     
538     assert TestModule::Model::User.is_a?(Class)
539   end
540 end