File Coverage

blib/lib/Eve/DbiStub.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Eve::DbiStub;
2              
3 3     3   7347 use strict;
  3         5  
  3         84  
4 3     3   16 use warnings;
  3         3  
  3         59  
5              
6 3     3   3333 use DateTime::Format::Pg;
  3         620829  
  3         37  
7 3     3   2001 use DBD::Pg ();
  0            
  0            
8              
9             use Test::MockObject;
10             use Test::MockObject::Extends;
11              
12             =head1 NAME
13              
14             B - a stub for replacing the database in unit tests.
15              
16             =head1 SYNOPSIS
17              
18             my $data_hash = Eve::DbiStub->get_compiled_data();
19              
20             my $function_return_data = $data_hash->{'test_function_1'}->{'data'};
21              
22             =head1 METHODS
23              
24             =head2 B
25              
26             Returns compiled data that is to be returned from stored procedures
27             after being processed by a database adapter.
28              
29             =cut
30              
31             sub get_compiled_data {
32             my ($data_hash, $result) = (get_data(), undef);
33              
34             my $callback_hash = {
35             'created' => sub {
36             return DateTime::Format::Pg->parse_timestamp_with_time_zone($_[0]);
37             },
38             'modified' => sub {
39             return DateTime::Format::Pg->parse_timestamp_with_time_zone($_[0]);
40             }};
41              
42             my $compiled_data = {};
43              
44             for my $function (keys %{$data_hash}) {
45             my $compiled_result_list = [];
46              
47             for my $result_list (@{$data_hash->{$function}->{'data'}}) {
48             my $data = [];
49              
50             if (ref($result_list)) {
51             for my $entry (@{$result_list}) {
52             my $processed_entry = {};
53              
54             for my $field_key (keys %{$entry}) {
55              
56             if (defined $callback_hash->{$field_key}
57             and defined $entry->{$field_key}) {
58              
59             $processed_entry->{$field_key} =
60             $callback_hash->{$field_key}->(
61             $entry->{$field_key});
62             } else {
63              
64             $processed_entry->{$field_key} =
65             $entry->{$field_key};
66             }
67             }
68              
69             push (@{$data}, $processed_entry);
70             }
71             }
72              
73             push (@{$compiled_result_list}, $data);
74             }
75              
76             $compiled_data->{$function} =
77             {%{$data_hash->{$function}}, 'data' => $compiled_result_list};
78             }
79              
80             return $compiled_data;
81             }
82              
83             =head2 B
84              
85             Returns raw data that is to be returned from stored procedures prior
86             to being processed by the database adapter.
87              
88             =cut
89              
90             sub get_data {
91              
92             my $data_template_hash = {
93             'entry_undef' => {
94             'id' => undef,
95             'created' => undef,
96             'modified' => undef,
97             'status' => undef},
98             'entry_inactive' => {
99             'id' => 12345,
100             'created' => '2012-04-02 00:00:00',
101             'modified' => '2012-04-02 00:00:00',
102             'status' => 0},
103             'entry_1' => {
104             'id' => 123456789,
105             'created' => '2011-07-01 00:00:00',
106             'modified' => '2011-07-01 00:00:00',
107             'status' => 1},
108             'entry_2' => {
109             'id' => 987654321,
110             'created' => '2011-04-22 00:00:00',
111             'modified' => '2011-04-22 00:00:00',
112             'status' => 1},
113             'entry_3' => {
114             'id' => 12345,
115             'created' => '2012-04-02 00:00:00',
116             'modified' => '2012-04-02 00:00:00',
117             'status' => 1},
118             'profile_undef' => {
119             'provider_name' => undef,
120             'identifier' => undef,
121             'display_name' => undef,
122             'email' => undef},
123             'profile_1' => {
124             'provider_name' => 'facebook',
125             'identifier' => '100000123812',
126             'display_name' => 'John Q. Doe',
127             'email' => 'john.q.doe@example.net'},
128             'profile_2' => {
129             'provider_name' => 'twitter',
130             'identifier' => '1000001211812',
131             'display_name' => 'John M. Doe',
132             'email' => 'john.m.doe@example.net'},
133             'profile_3' => {
134             'provider_name' => 'facebook',
135             'identifier' => '100000123812',
136             'display_name' => 'John Q. Deere',
137             'email' => 'john.q.deere@example.net'},
138             'profile_4' => {
139             'provider_name' => 'twitter',
140             'identifier' => '100000123813',
141             'display_name' => 'John M. Smith',
142             'email' => 'john.m.smith@example.com'}};
143              
144             my $data_hash_list = {
145             'test_function_1' => {
146             'sql_pattern' => 'pgsql_function_test\.test_function_1',
147             'data' => [[
148             {'id' => 1, 'memo' => 'text 1'},
149             {'id' => 2, 'memo' => 'text 2'}]]},
150             'test_function_2' => {
151             'sql_pattern' => 'pgsql_function_test\.test_function_2',
152             'data' => [[{'memo' => 'text'}]]},
153             'account_add' => {
154             'sql_pattern' => 'account_add',
155             'input_type_list' => [],
156             'data' => [
157             [$data_template_hash->{'entry_1'}],
158             [$data_template_hash->{'entry_2'}]]},
159             'account_get' => {
160             'sql_pattern' => 'account_get',
161             'input_type_list' => [DBD::Pg::PG_INT8],
162             'data' => [
163             [{%{$data_template_hash->{'entry_1'}}, 'id' => 987654321}],
164             [{%{$data_template_hash->{'entry_2'}}, 'id' => 123456789}]]},
165             'account_get_by_userid' => {
166             'sql_pattern' => 'account_get_by_userid',
167             'input_type_list' => [DBD::Pg::PG_TEXT],
168             'data' => [
169             [{%{$data_template_hash->{'entry_1'}}, 'id' => 987654321}],
170             [{%{$data_template_hash->{'entry_2'}}, 'id' => 123456789}]]},
171             'external_profile_add' => {
172             'sql_pattern' => 'external_profile_add',
173             'input_type_list' => [DBD::Pg::PG_INT8, (DBD::Pg::PG_TEXT) x 4],
174             'data' => [
175             [{%{$data_template_hash->{'entry_1'}},
176             'account_id' => 123123123,
177             %{$data_template_hash->{'profile_1'}}}],
178             [{%{$data_template_hash->{'entry_2'}},
179             'account_id' => 123123,
180             %{$data_template_hash->{'profile_2'}}}]]},
181             'external_profile_get_by_credentials' => {
182             'sql_pattern' => 'external_profile_get_by_credentials',
183             'input_type_list' => [DBD::Pg::PG_TEXT, DBD::Pg::PG_TEXT],
184             'data' => [
185             [{%{$data_template_hash->{'entry_1'}},
186             'id' => 987654321,
187             'account_id' => 123123123,
188             %{$data_template_hash->{'profile_1'}}}],
189             [{%{$data_template_hash->{'entry_3'}},
190             'account_id' => 123123,
191             %{$data_template_hash->{'profile_2'}},
192             'identifier' => '100000123813'}],
193             [{%{$data_template_hash->{'entry_undef'}},
194             'account_id' => undef,
195             %{$data_template_hash->{'profile_undef'}}}]]},
196             'external_profile_update' => {
197             'sql_pattern' => 'external_profile_update',
198             'input_type_list' => [DBD::Pg::PG_INT8, (DBD::Pg::PG_TEXT) x 4],
199             'data' => [
200             [{%{$data_template_hash->{'entry_1'}},
201             'modified' => '2011-03-24 00:00:00',
202             'account_id' => 123123123,
203             %{$data_template_hash->{'profile_3'}}}],
204             [{%{$data_template_hash->{'entry_3'}},
205             'modified' => '2012-04-15 00:00:00',
206             'account_id' => 123123,
207             %{$data_template_hash->{'profile_4'}}}]]}};
208              
209             return $data_hash_list;
210             }
211              
212             =head2 B
213              
214             Returns a mock statement handle.
215              
216             =cut
217              
218             sub mock_sth {
219             my $sql = shift;
220              
221             my $sth_mock = Test::MockObject->new();
222              
223             $sth_mock->{'input_type_list'} = [];
224             $sth_mock->{'data_hash_list'} = get_data();
225              
226             $sth_mock->mock(
227             'bind_param',
228             sub {
229             my ($self, $index, undef, $attr_hash) = @_;
230              
231             push(@{$self->{'input_type_list'}}, $attr_hash->{'pg_type'});
232             return;
233             });
234              
235             $sth_mock->mock(
236             'execute',
237             sub {
238             my $self = shift;
239              
240             $self->set_always('value_list', [@_]);
241              
242             $self->{'return_list'} = [];
243              
244             my $match_found;
245              
246             for my $key (keys %{$self->{'data_hash_list'}}) {
247             my $data_hash = $self->{'data_hash_list'}->{$key};
248             my $function_name = defined $data_hash->{'real_function_name'} ?
249             $data_hash->{'real_function_name'} : $key;
250              
251             my $pattern = qr/$data_hash->{'sql_pattern'}/;
252              
253             if ($sql =~ $pattern) {
254             my $result;
255             if (
256             exists $data_hash->{'constant'}
257             and $data_hash->{'constant'}) {
258             $result = $data_hash->{'data'}->[0];
259             } else {
260             $result = shift(@{$data_hash->{'data'}});
261             }
262              
263             if (exists $data_hash->{'cyclical'}
264             and $data_hash->{'cyclical'}) {
265              
266             my $result_copy;
267              
268             if (ref($result)) {
269             $result_copy = [];
270              
271             for my $item (@{$result}) {
272             push (@{$result_copy}, {%{$item}});
273             }
274             }
275              
276             push (@{$data_hash->{'data'}}, $result_copy);
277             }
278              
279             if (not ref ($result) and defined $result) {
280             die($result);
281             } elsif (not defined $result) {
282             die('No more results for this function.');
283             }
284              
285             if (defined $data_hash->{'input_type_list'} and
286             not $data_hash->{'input_type_list'} ~~
287             $self->{'input_type_list'}) {
288             next;
289             $match_found = 0;
290             } else {
291             $match_found = 1;
292             $self->{'return_list'} = $result;
293             last;
294             }
295             }
296             }
297              
298             if (defined $match_found and not $match_found) {
299             $sql =~ m/([\w_]+)\s*\(/;
300              
301             die('DBD::Pg::st execute failed: ERROR: function ' .
302             $sql . '(' .
303             join(', ', @{$self->{'input_type_list'}}) . ') ' .
304             'does not exist');
305             }
306              
307             return;
308             });
309              
310             $sth_mock->mock(
311             'fetchall_arrayref',
312             sub {
313             my $self = shift;
314              
315             return $self->{'return_list'};
316             });
317              
318             return $sth_mock;
319             }
320              
321             =head2 B
322              
323             Returns a mock database connection handle.
324              
325             =cut
326              
327             sub mock_dbh {
328             my @arg_list = @_;
329              
330             my $dbh_mock = Test::MockObject::Extends->new('DBI');
331              
332             $dbh_mock->mock('connect', sub { return shift; });
333              
334             $dbh_mock->mock(
335             'prepare',
336             sub {
337             shift;
338              
339             return mock_sth(@_);
340             });
341              
342             return $dbh_mock->connect(@arg_list);
343             }
344              
345             =head2 B
346              
347             Main procedure that is being run when the module is being
348             required. Fakes the DBI package by replacing it with a
349             B object.
350              
351             =cut
352              
353             sub main {
354             Test::MockObject::Extends->new('DBI')->fake_module(
355             'DBI', 'connect' => sub { return mock_dbh(@_); });
356             }
357              
358             main();
359              
360             =head1 SEE ALSO
361              
362             =over 4
363              
364             =item L
365              
366             =item L
367              
368             =item L
369              
370             =item L
371              
372             =item L
373              
374             =item L
375              
376             =back
377              
378             =head1 LICENSE AND COPYRIGHT
379              
380             Copyright 2010-2013 Sergey Konoplev, Igor Zinovyev.
381              
382             This program is free software; you can redistribute it and/or modify it
383             under the terms of either: the GNU General Public License as published
384             by the Free Software Foundation; or the Artistic License.
385              
386             See http://dev.perl.org/licenses/ for more information.
387              
388             =head1 AUTHOR
389              
390             =over 4
391              
392             =item L
393              
394             =item L
395              
396             =back
397              
398             =cut
399              
400             1;