File: bug_0002.t

package info (click to toggle)
libdbd-mock-perl 1.43-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 416 kB
  • sloc: perl: 1,135; makefile: 2
file content (127 lines) | stat: -rwxr-xr-x 3,855 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/perl
use Test::More tests => 15; 
use strict;
use warnings;
use Test::Exception;
use DBI;
use DBD::Mock;

# This test is designed to expose the bug found in the DBD::Mock
# methods begin_work, commit and rollback (RT #66815), where a failing
# ->prepare invocation (returning nothing) is not detected and the
# undefined value resulting is used anyway. In this test, as in the
# example found in the wild, the failure is triggered by exhaustion of
# the session states.
#
# This is a list of sessions designed to engineer the right condition
# to trigger the bug.  They all start with a dummy statement (so that
# there are at least two states) then the final statement is removed
# before it is passed to DBD::Mock::Session->new (which requires at
# least one state).  The final statements are 'BEGIN WORK', 'COMMIT'
# and 'ROLLBACK', respectively.
#
# Hence, when the test tries to invoke the final state, the session
# will have run out and DBD::Mock->verify_statement will cause the
# prepare method to fail.

my @cases = (
    'begin_work' => [
        {
            statement => 'SELECT something FROM somewhere',
            results => [],
        },
        {
            statement => 'BEGIN WORK',
            results => [],
        },
    ],

    'commit' => [
        {
            statement => 'SELECT something FROM somewhere',
            results => [],
        },
        {
            statement => 'BEGIN WORK',
            results => [],
        },
        {
            statement => 'INSERT INTO foo (bar) VALUES (?);',
            results => [],
            bound_params => [1],
        },
        {
            statement => 'COMMIT',
            results => [],
        },
    ],

    'rollback' => [
        {
            statement => 'SELECT something FROM somewhere',
            results => [],
        },
        {
            statement => 'BEGIN WORK',
            results => [],
        },
        {
            statement => 'INSERT INTO foo (bar) VALUES (?);',
            results => [],
            bound_params => [1],
        },
        {
            statement => 'ROLLBACK',
            results => [],
        },
    ],
);

while(@cases) {
    my ($name, $states) = splice @cases, 0, 2;
    my $case_name = "case $name";

    my $dbh = DBI->connect('dbi:Mock:', '',  '', 
                           { PrintError => 0, 
                             RaiseError => 1 });

    # Add all but the last state of the expected session
    my $missing_state = pop @$states;
    my $num_states = @$states;
    $dbh->{mock_session} = DBD::Mock::Session->new($name => @$states);

    # Execute the initial dummy statement.
    my $state = $states->[0];
    my $sth = $dbh->prepare($state->{statement});
    ok $sth, 
        "$case_name: prepare statement";
        
    ok $sth->execute(),
        "$case_name: execute statement";
    
    # Now try and do the next steps in @session, but using the
    # appropriate transaction methods directly.  This should fail when
    # the session is exhausted with a useful message. (The original
    # bug meant that the message got clobbered by "Can't call method
    # 'execute' on an undefined value".)
    throws_ok {
        # This stlibatement is always the same.
        ok $dbh->begin_work,
            "$case_name: start transaction";

        my $state = $states->[2];
        my $sth = $dbh->prepare($state->{statement});
        ok $sth, 
            "$case_name: prepare statement";
        
        ok $sth->execute(@{$state->{bound_params}}),
            "$case_name: execute statement";

        # get the final operation from the session
        my $operation = lc $missing_state->{statement};

        ok $dbh->$operation,
            "$case_name: $operation transaction";        
    } qr/\QSession states exhausted, only '$num_states' in DBD::Mock::Session\E/;

}