~launchpad-pqm/launchpad/devel

8687.15.45 by Karl Fogel
Add license header block to files brought in in the r8731 merge.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
8911.2.4 by Stuart Bishop
ShipIt load tester
3
4
"""Basic ShipIt usage test."""
5
6
__metaclass__ = type
7
__all__ = []
8
9
import re
8911.2.12 by Stuart Bishop
Use a lock to prevent race where multiple threads attempt to open the database connection
10
import threading
8911.2.4 by Stuart Bishop
ShipIt load tester
11
import unittest
12
13
from funkload.FunkLoadTestCase import FunkLoadTestCase
14
from funkload.Lipsum import Lipsum
15
import psycopg2
16
17
18
class ShipIt(FunkLoadTestCase):
19
20
    db_connection = None
8911.2.12 by Stuart Bishop
Use a lock to prevent race where multiple threads attempt to open the database connection
21
    db_connection_lock = threading.Lock()
8911.2.4 by Stuart Bishop
ShipIt load tester
22
23
    def setUp(self):
24
        """Setting up test."""
25
        self.logd("setUp")
26
27
        self.server_url = self.conf_get('main', 'url')
28
        self.database_conninfo = self.conf_get('main', 'database_conninfo')
29
30
        self.lipsum = Lipsum()
31
32
        if ShipIt.db_connection is None:
8911.2.5 by Stuart Bishop
Review feedback
33
            # We use a class variable for the database connection so the
34
            # same connection is shared between threads. This is important
35
            # for when we are running with hundreds of threads.
8911.2.12 by Stuart Bishop
Use a lock to prevent race where multiple threads attempt to open the database connection
36
            ShipIt.db_connection_lock.acquire()
37
            try:
38
                if ShipIt.db_connection is None:
39
                    assert psycopg2.threadsafety >= 2, (
40
                        "psycopg2 cannot share connections between threads")
41
                    self.logi(
42
                        'Opening database connection "%s".'
43
                        % self.database_conninfo)
44
                    ShipIt.db_connection = psycopg2.connect(
45
                        self.database_conninfo)
46
                    ShipIt.db_connection.set_isolation_level(0)
47
            finally:
48
                ShipIt.db_connection_lock.release()
8911.2.4 by Stuart Bishop
ShipIt load tester
49
50
    def cursor(self):
51
        return ShipIt.db_connection.cursor()
52
53
    def get_email_validation_token(self, email):
54
        cur = self.cursor()
55
        cur.execute("""
56
            SELECT token FROM AuthToken
57
            WHERE date_consumed IS NULL AND token_type=12 AND email=%s
58
            """, (email,))
59
        row = cur.fetchone()
60
        if row is None:
61
            return None
62
        return row[0]
63
64
    _error_re = re.compile('(?s)class="error">.*?class="message">(.*?)</')
65
66
    def assertNoFormErrors(self, response):
67
        self.assertEquals(response.code, 200)
68
        match = self._error_re.search(response.body)
69
        if match is not None:
70
            self.fail('Form contained error "%s"' % match.group(1))
71
72
    def test_shipit(self):
73
        server_url = self.server_url
74
75
        self.get(server_url + "/", description="GET /")
76
77
        response = self.get(server_url + "/login", description="Get /login")
78
        response = response.postForm(
79
            0, self.post, {'submit': 'Continue'},
80
            'Post /+openid - OpenID authentication request')
81
        self.assertNoFormErrors(response)
82
83
        # User is not logged on
84
        email = 'user_%s@lp%s.example.com' % (
85
            self.lipsum.getUniqWord(), self.lipsum.getUniqWord())
86
        # response.postForm fails here - looks like a bug. The action
87
        # on the form is a relative URL and response.postForm tries
88
        # to retrieve it from localhost.
89
        params = response.extractForm(include_submit=True)
90
        params['field.email'] = email
91
        params['field.action'] = 'createaccount'
92
        response = self.post(
8911.2.5 by Stuart Bishop
Review feedback
93
            self.absolute_url(response, '/+login-register'),
8911.2.4 by Stuart Bishop
ShipIt load tester
94
            params, "Post /+login-register - Create account")
95
        self.assertNoFormErrors(response)
96
97
        # Pull the email validation token from the database and
98
        # validate it.
99
        token = self.get_email_validation_token(email)
8911.2.7 by Stuart Bishop
Ensure email validation token was found
100
        self.assert_(token is not None, "No login token created")
8911.2.8 by Stuart Bishop
Cope with Bug #400610
101
        newaccount_url = self.absolute_url(
102
            response, '/token/%s/+newaccount' % token)
8911.2.4 by Stuart Bishop
ShipIt load tester
103
        response = self.get(
8911.2.8 by Stuart Bishop
Cope with Bug #400610
104
            newaccount_url, description="Get /token/[...]/+newaccount")
8911.2.4 by Stuart Bishop
ShipIt load tester
105
106
        # Complete the registration process.
107
        displayname = self.lipsum.getSubject(2)
108
        password = self.lipsum.getWord()
8911.2.8 by Stuart Bishop
Cope with Bug #400610
109
        params = response.extractForm(include_submit=True)
110
111
        params.update({
112
            'field.displayname': displayname,
113
            'field.hide_email_addresses': 'on',
114
            'field.password': password,
115
            'field.password_dupe': password,
116
            'field.actions.continue': 'Continue'})
117
        # At the moment, creating your account doesn't log you in
118
        # immidiately so you might end up at a 403 page - Bug #400610
119
        response = self.post(
8911.2.9 by Stuart Bishop
Cope with Bug #400610
120
            newaccount_url, params, ok_codes=[200, 302, 303, 403],
8911.2.8 by Stuart Bishop
Cope with Bug #400610
121
            description="Post /token/[...]/+newaccount")
122
        # Keep hitting the /login link until it works.
8911.2.9 by Stuart Bishop
Cope with Bug #400610
123
        while response.code == 403:
8911.2.8 by Stuart Bishop
Cope with Bug #400610
124
            login_url = self.absolute_url(response, '/login')
125
            response = self.get(login_url, description="Get /login")
8911.2.10 by Stuart Bishop
More login race conditions, and ensure CD order fields are allowed lengths
126
            if response.get_base_url() == '/myrequest':
127
                break
8911.2.9 by Stuart Bishop
Cope with Bug #400610
128
            response = response.postForm(
129
                0, self.post, {}, "Post /+openid")
130
            if response.get_base_url() == '/+openid':
131
                params = response.extractForm()
132
                params['field.actions.auth'] = 'Sign In'
133
                response = self.post(
134
                    self.absolute_url(response, '/+decide'), params,
135
                    description = "Post /+decide",
136
                    ok_codes=[200, 302, 303, 403])
8911.2.4 by Stuart Bishop
ShipIt load tester
137
138
        # Registration succeeded - should be on the order details page now.
139
        self.assertEquals(response.get_base_url(), '/myrequest')
140
141
        # Request some CDs.
142
        params = response.extractForm(include_submit=True)
143
        params.update({
8911.2.11 by Stuart Bishop
Ensure CD order fields are allowed lengths better
144
            'field.recipientdisplayname': displayname[:20],
145
            'field.addressline1': self.lipsum.getSubject(3)[:30],
146
            'field.addressline2': self.lipsum.getSubject(3)[:30],
147
            'field.city': self.lipsum.getSubject(1)[:30],
148
            'field.province': self.lipsum.getSubject(2)[:30],
149
            'field.postcode': self.lipsum.getSubject(1)[:20],
8911.2.4 by Stuart Bishop
ShipIt load tester
150
            'field.country': '212',
151
            'field.country-empty-marker': '1',
8911.2.11 by Stuart Bishop
Ensure CD order fields are allowed lengths better
152
            'field.phone': self.lipsum.getPhoneNumber()[:16],
8911.2.4 by Stuart Bishop
ShipIt load tester
153
            'field.actions.continue': 'Submit Request'})
154
        response = self.post(
8911.2.5 by Stuart Bishop
Review feedback
155
            self.absolute_url(response, '/myrequest'),
8911.2.4 by Stuart Bishop
ShipIt load tester
156
            params, "Post /myrequest - Request CDs")
157
        self.assertNoFormErrors(response)
158
8911.2.11 by Stuart Bishop
Ensure CD order fields are allowed lengths better
159
        if response.body.find('Cancel Request') == -1:
160
            open('dud.html', 'w').write(response.body)
161
8911.2.4 by Stuart Bishop
ShipIt load tester
162
        # Confirm the request worked.
163
        self.assert_(
164
            response.body.find('Cancel Request') != -1,
165
            "Cancel button not found.")
166
167
        # Logout.
168
        response = self.post(
169
            server_url + "/+logout", description="Post /+logout")
170
        self.assert_(
171
            response.body.find('You have been logged out') != -1,
172
            "No logged out notification.")
173
174
        # Attempt to login again, bringing up the login form.
175
        response = self.get(server_url, description="Get /")
176
        response = self.get(
177
            server_url + "/login", description="Get /login")
178
        response = response.postForm(
179
            0, self.post, {'submit': 'Continue'},
180
            'Post /+openid - OpenID authentication request')
181
        self.assertNoFormErrors(response)
182
183
        # Submit the login form.
184
        params = response.extractForm(include_submit=True)
185
        params['field.email'] = email
186
        params['field.password'] = password
187
        params['field.action'] = 'login'
188
        response = self.post(
8911.2.5 by Stuart Bishop
Review feedback
189
            self.absolute_url(response, '/+login-register'),
8911.2.4 by Stuart Bishop
ShipIt load tester
190
            params, "Post /+login-register - Login to existing account")
191
        self.assertNoFormErrors(response)
192
        self.assertEquals(response.url, '/myrequest')
193
194
        # Cancel the CD request.
195
        params = response.extractForm([('form', 1)], include_submit=True)
196
        response = self.post(
8911.2.5 by Stuart Bishop
Review feedback
197
            self.absolute_url(response, '/myrequest'),
8911.2.4 by Stuart Bishop
ShipIt load tester
198
            params, description="Post /myrequest - Cancel CD order.")
199
        self.assertNoFormErrors(response)
200
        self.assert_(
201
            response.body.find('Cancel Request') == -1,
202
            "Cancel button found.")
203
204
        # Don't log out - leave the session dangling like most real users
205
        # do.
206
8911.2.5 by Stuart Bishop
Review feedback
207
    def absolute_url(self, response, path):
208
        """Calculate an absolute URL using the response and the path."""
8911.2.4 by Stuart Bishop
ShipIt load tester
209
        return '%s://%s:%s%s' % (
210
            response.protocol, response.server, response.port, path)
211
212
    def tearDown(self):
213
        """Setting up test."""
214
        self.logd("tearDown.\n")
215
216
217
218
if __name__ in ('main', '__main__'):
219
    unittest.main()