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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
|
"""
IGNORED tests
- all test files with hyphens in the filename are ignored
- filnames with _ are OK
$ python manage.py test cuy.photologue --parallel
only runs the photologue tests. Working.(well, it was working..)
$ python manage.py test cuy.mailman --parallel
$ python manage.py test paypal.standard --parallel
needs work: a very large test suite
$ python manage.py test tagging --parallel
a huge suite - needs a lot of work to with Django 1.11 & python3
$ python manage.py test cuy.club --parallel
Runs the tests in this file only
"""
import re
import unittest
from django.test import Client, SimpleTestCase, TestCase, TransactionTestCase
class ImportTest(TestCase):
def test_import_imports(self):
#ed to go through all modules and copy all imports here
from io import StringIO
from cuy.club.models import (Article, Event, Member, Webpage,
WebpageCategory)
from cuy.website.views.generic import PUBLIC_LOGIN
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.core import management
from django.db import connection, connections
from django.db.utils import IntegrityError
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.template.defaultfilters import slugify
from django.utils.timezone import get_current_timezone, make_aware
class SimpleTest(SimpleTestCase):
def test_arith_mult(self):
"""
Tests that 10 x 10 always equals 100.
"""
self.assertEqual(10*10, 100)
class DataTests(TestCase ):
'''These check that the NULL and NON-UNIQUE constraints are working in the database '''
@classmethod
def setUpTestData(cls):
pass
def setUp(self):
from cuy.club.models import Member
from django.contrib.auth.models import User
m = Member()
m.pk=8000
m.user_id = 9000 # not NULL constraint
m.save()
self.member = m
u = User()
u.pk = 9000
u.user_id = 8000
u.username, u.password ='stinker', 'secretword'
u.email='philip.sargent+SP@gmail.com'
u.first_name, u.last_name ='Stinker', 'Pinker'
u.save()
self.user = u
def tearDown(self):
#self.member.delete() # must delete member before user
#self.user.delete() # horrible crash, why?
pass
def test_member_not_null_field(self):
from cuy.club.models import Member
from django.db.utils import IntegrityError
n = Member()
try:
n.save()
except IntegrityError as ex:
t = re.search(r'NOT NULL constraint failed: club_member.user_id', str(ex))
self.assertIsNotNone(t, "Exception is not the expected 'NOT NULL constraint failed'")
n.user_id = 1000
try:
n.save
except:
return self.assertIsNotNone(None, "Failed to save valid Member to database")
def test_member_not_unique_field(self):
from cuy.club.models import Member
from django.db.utils import IntegrityError
m1 = Member()
m2 = Member()
m1.user_id = 1000
m2.user_id = m1.user_id
m1.save()
try:
m2.save()
except IntegrityError as ex:
t = re.search(r'UNIQUE constraint failed: club_member.user_id', str(ex))
return self.assertIsNotNone(t, "IntegrityError as expected but message is not the expected 'UNIQUE constraint failed'" )
self.assertIsNotNone(None, "Failed to enforce 'UNIQUE constraint' on saving two Member objects with same user_id")
def test_article_invalid_date(self):
from cuy.club.models import Article, Member
from django.core.exceptions import ValidationError
from django.db.utils import IntegrityError
a = Article()
m = self.member
a.author_id = m.user_id
a.publish="not a valid datetime"
try:
a.save()
except ValidationError as ex:
t = re.search(r'value has an invalid format. It must be in YYYY-MM-DD HH:MM', str(ex))
self.assertIsNotNone(t, "Exception is not the expected 'invalid format'")
def test_article_and_author_not_null(self):
from cuy.club.models import Article, Member
from django.core.exceptions import ValidationError
from django.db.utils import IntegrityError
a2 = Article()
a2.publish ="2021-02-17 17:25"
a2.author_id = None
try:
a2.save()
except IntegrityError as ex:
t = re.search(r'NOT NULL constraint failed: club_article.author_id', str(ex))
self.assertIsNotNone(t, "Exception is not the expected 'NOT NULL constraint failed'")
except:
self.assertIsNotNone(None, "Exception is not the expected 'NOT NULL constraint failed' IntegrityError")
def test_article_and_author_ok(self):
from cuy.club.models import Article, Member
from django.core.exceptions import ValidationError
from django.db.utils import IntegrityError
m = self.member
a3 = Article()
a3.pk = 5000
a3.publish ="2021-02-17 17:25"
a3.author_id = m.pk
try:
a3.save()
except:
return self.assertIsNotNone(None, "Failed to save valid Article to database")
def test_member_and_user(self):
u = self.user
m = self.member
m.user = u
self.assertEqual(m.user.last_name, 'Pinker')
m.save()
u.save()
class FixturePageTests(TestCase):
fixtures = ['cuyc_basic_data.json', 'test_data.json', 'auth_user_gussie']
def setUp(self):
from django.contrib.auth.models import User
self.user = User.objects.get(username='gussie')
self.member = self.user.profile
def tearDown(self):
pass
def test_fix_event_loaded(self):
from cuy.club.models import Event
e = Event.objects.get(slug='spring-in-the-med')
self.assertEqual(str(e.shore_contact.first_name()), 'Stiffy')
self.assertEqual(str(e.organiser.last_name()), 'Fittleworth')
def test_fix_page_all_trips(self):
response = self.client.get('/programme/')
content = response.content.decode()
t = re.search(r'Spring in the Arctic', content)
self.assertIsNotNone(t, "Failed to see Event loaded from fixture")
t = re.search(r'High Summer in the Irish Sea', content)
self.assertIsNotNone(t, "Failed to see Event loaded from fixture")
def test_fix_page_event(self):
response = self.client.get('/programme/events/spring-in-the-arctic/')
content = response.content.decode()
t = re.search(r'Spring in the Arctic', content)
self.assertIsNotNone(t, "Failed to see Event loaded from fixture")
def test_fix_admin_login_fail(self):
c = self.client
from cuy.club.models import Member
from django.contrib.auth.models import User
m = Member.objects.get(pk=9002)
u = User.objects.get(username='bingo')
self.assertTrue(u.is_active, 'User \'' + u.username + '\' is INACTIVE')
logged_in = c.login(username=u.username, password='secretword') # fails to work if password=u.password !
self.assertTrue(logged_in, 'FAILED to login as \'' + u.username + '\'')
response = c.get('/admin/')
content = response.content.decode()
with open('admin-op.html', 'w') as f:
f.write(content)
t = re.search(r'Site administration', content)
self.assertIsNone(t, 'Logged in as \'' + u.username + '\' (not staff) but still managed to get the Admin page' )
class ComplexLoginTests(TestCase):
'''These test the login and capabilities of logged-in users'''
def setUp(self):
'''setUp runs once for each test in this class'''
from cuy.club.models import AFFILIATION, MEMBER_TYPES, Member
from django.contrib.auth.models import User
m = Member()
m.pk=8000
m.user_id = 9000 # not NULL constraint
m.email = "philip.sargent+HG@gmail.com"
m.member_type = MEMBER_TYPES[1]
m.affiliation = AFFILIATION[3]
m.committee_email_prefix = 'honoria'
u = User()
u.pk = 9000
u.user_id = 8000
u.username, u.password ='honoria', 'secretword'
u.email='philip.sargent+HG@gmail.com'
u.first_name, u.last_name ='Honoria', 'Glossop'
u.is_staff = True
u.is_superuser = True
u.set_password(u.password) # This creates a new salt and thus a new key for EACH test
u.save() # vital that we save all this before attempting login
#print ('\n',u.password)
m.save()
self.user = u
self.member = m
from cuy.club.models import ClubRole, Elected
cr = ClubRole()
cr.id = 7000
cr.title = 'Skipper'
cr.short_description = 'Club skipper who can lead trips'
cr.committee_position = True
cr.rank = 8
cr.save()
self.clubrole = cr
e = Elected()
e.member = m
e.club_role = cr
e.save()
self.elected = e
def tearDown(self):
self.client.logout() # not needed as each test creates a new self.client
#self.member.delete()
##self.user.delete() # id attribute set to None !
pass
def test_login_redirect_for_non_logged_on_user(self):
c = self.client
# Need to login first. Tests that we are redirected to login page if not logged in
response = c.get('/committee/appointments/')
self.assertRedirects(response, "/login/?next=/committee/appointments/")
def test_ordinary_login(self):
c = self.client
u = self.user
self.assertTrue(u.is_active, 'User \'' + u.username + '\' is INACTIVE')
logged_in = c.login(username=u.username, password='secretword') # fails to work if password=u.password !
self.assertTrue(logged_in, 'FAILED to login as \'' + u.username + '\'')
response = c.get('/')
content = response.content.decode()
t = re.search(r'Hello Honoria', content)
self.assertIsNotNone(t, 'Logged in as \'' + u.username + '\' but failed to get personal greeting' )
def test_authentication_login(self):
c = self.client
u = self.user
self.assertTrue(u.is_active, 'User \'' + u.username + '\' is INACTIVE')
# This is weird. I thought that the user had to login before she was in the authenticated state
self.assertTrue(u.is_authenticated, 'User \'' + u.username + '\' is NOT AUTHENTICATED before login')
logged_in = c.login(username=u.username, password='secretword') # fails to work if password=u.password !
self.assertTrue(logged_in, 'FAILED to login as \'' + u.username + '\'')
self.assertTrue(u.is_authenticated, 'User \'' + u.username + '\' is NOT AUTHENTICATED after login')
c.logout()
self.assertFalse(u.is_authenticated, 'User \'' + u.username + '\' is STILL AUTHENTICATED after logout')
def test_admin_login(self):
c = self.client
u = self.user
m = self.member
m.user = u
logged_in = c.login(username=u.username, password='secretword') # fails to work if password=u.password !
self.assertTrue(logged_in, 'FAILED to login as \'' + u.username + '\'')
response = c.get('/admin/')
content = response.content.decode()
# with open('admin-op.html', 'w') as f:
# f.write(content)
t = re.search(r'Site administration', content)
self.assertIsNotNone(t, 'Logged in as \'' + u.username + '\' but failed to get the Admin page' )
def test_user_account_login(self):
# User must be associated with a Member for whom is_committee() is True
c = self.client
u = self.user
m = self.member
m.user = u
logged_in = c.login(username=u.username, password='secretword') # fails if password=u.password !
self.assertTrue(logged_in, 'FAILED to login as \'' + u.username + '\'')
response = c.get('/accounts/profile/')
content = response.content.decode()
# with open('account-profile-op.html', 'w') as f:
# f.write(content)
t = re.search(r'CUYC Member Profile - Cambridge University Yacht Club', content)
self.assertIsNotNone(t, 'Logged in as \'' + u.username + '\' but failed to get /accounts/profile/ content')
def test_committee_login(self):
from django.contrib.auth.models import User
# User must be associated with a Member for whom is_committee() is True
c = self.client # inherited from TestCase
u = self.user
m = self.member
cr = self.clubrole
e = self.elected
m.user = u
logged_in = c.login(username=u.username, password='secretword') # fails if password=u.password !
self.assertTrue(logged_in, 'FAILED to login as \'' + u.username + '\'')
response = c.get('/')
content = response.content.decode()
t = re.search(r'Hello Honoria', content)
self.assertIsNotNone(t, 'Logged in as \'' + u.username + '\' but failed to get personal greeting' )
response = c.get('/committee/appointments/')
content = response.content.decode()
# with open('cmttee-op.html', 'w') as f:
# f.write(content)
t = re.search(r'A word of warning...', content)
self.assertIsNotNone(t, 'Logged in as \'' + u.username + '\' but failed to get /committee/ content')
def test_user_force(self):
from django.conf import settings
c = self.client
u = self.user
m = self.member
m.user = u
try:
c.force_login(u)
except:
self.assertIsNotNone(None, 'Unexpected exception trying to force_login as \'' + u.username + '\' but failed (Bad Django documentation?)')
response = c.get('/')
content = response.content.decode()
t = re.search(r'Hello Honoria', content)
self.assertIsNotNone(t, 'Forced logged in as \'' + u.username + '\' but failed to get personal greeting' )
response = c.get('/accounts/profile/')
content = response.content.decode()
t = re.search(r'From here you can update your', content)
self.assertIsNotNone(t, 'Forced logged in as \'' + u.username + '\' but failed to get /accounts/profile/ content')
class DynamicPageTests(TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_empty_yachts(self):
# no page there initially
response = self.client.get('/yachts/')
content = response.content.decode()
self.assertEqual(response.status_code, 404)
def test_full_yachts(self):
'''Creating a WebpageCategory and an index webpage creates a valid url
'''
from cuy.club.models import Webpage, WebpageCategory
wc = WebpageCategory()
wc.pk = 8000
wc.id = 8000
wc.name, wc.slug ='Yachts', 'yachts'
wc.save()
self.webcategory = wc
p = Webpage()
p.pk = 9000
p.id = 9000
p.category_id = wc.id
p.description = "Current Yacht"
p.edited = 1
p.event_id = None
p.index = 1
p.markup = "<h1>Skylark</h1>"
p.ordering = 10
p.slug = "yacht"
p.title = "Skylark Yacht"
p.save()
self.webpage = p
response = self.client.get('/yachts/')
content = response.content.decode()
self.assertEqual(response.status_code, 200)
class PageTests(TestCase):
def setUp(self):
# Every test needs a client.
# new in Django 1.5 no need to create self.client first
# https://docs.djangoproject.com/en/dev/topics/testing/tools/#django.test.LiveServerTestCase
#self.client = Client()
pass
def tearDown(self):
pass
def test_basic_admin(self):
response = self.client.get('/admin/login/')
self.assertEqual(response.status_code, 200)
def test_basic_admindoc(self):
# Need to login first. Tests that we are redirected
response = self.client.get('/admin/doc/models/')
self.assertRedirects(response, "/admin/login/?next=/admin/doc/models/")
def test_basic_programme(self):
response = self.client.get('/programme/')
self.assertEqual(response.status_code, 200)
def test_basic_login (self):
# Need to login first
response = self.client.post('/login/', {'username': 'gussie', 'password': 'secretword'})
if response.status_code == 302:
print(response['location'])
self.assertEqual(response.status_code, 200) # fails because user does not exist
def test_basic_committee(self):
# Need to login first. Tests that we are redirected to login page
response = self.client.get('/committee/')
self.assertRedirects(response, "/login/?next=/committee/")
# --- Check non-logged-in users cannot see these
def test_basic_gallery(self):
response = self.client.get('/gallery/')
self.assertEqual(response.status_code, 200)
def test_basic_sitemap(self):
response = self.client.get('/site-map/')
self.assertEqual(response.status_code, 200)
# --- public club pages created by content in templates/*.html
def test_basic_club(self):
response = self.client.get('/club/')
content = response.content.decode()
t = re.search(r'offers opportunities for members of the university to sail yachts', content)
self.assertIsNotNone(t)
def test_basic_programme(self):
response = self.client.get('/programme/')
content = response.content.decode()
t = re.search(r'If you would like to go on any of these events', content)
self.assertIsNotNone(t)
def test_basic_programme_onshore(self):
response = self.client.get('/programme/on_shore/')
content = response.content.decode()
t = re.search(r'All Upcoming Shore Based Events', content)
self.assertIsNotNone(t)
def test_page_equal_opps(self):
response = self.client.get('/club/equal-opps/')
content = response.content.decode()
t = re.search(r'commitment to a policy of equal opportunities', content)
self.assertIsNotNone(t)
def test_page_safety(self):
response = self.client.get('/club/safety/')
content = response.content.decode()
t = re.search(r'endeavour to maintain the highest levels of safety', content)
self.assertIsNotNone(t)
def test_page_safety_risk(self):
response = self.client.get('/club/safety/risk/')
content = response.content.decode()
t = re.search(r'rules for the use of safety lines to be described and monitored by the skipper.', content)
self.assertIsNotNone(t)
def test_page_safetypolicy(self):
response = self.client.get('/club/safetypolicy/')
content = response.content.decode()
t = re.search(r'should be capable of swimming at least fifty meters in clothing and keeping afloat for at least five minutes', content)
self.assertIsNotNone(t)
def test_page_safety_rules(self):
response = self.client.get('/club/safety/rules/')
content = response.content.decode()
t = re.search(r'Safety Officer is responsible for the maintenance of safety records', content)
self.assertIsNotNone(t)
def test_page_regulations(self):
response = self.client.get('/club/regulations/')
content = response.content.decode()
t = re.search(r'Sanger Institute, the Babraham Institute, Wellcome and MRC Research Laboratories', content)
self.assertIsNotNone(t)
def test_page_constitution(self):
response = self.client.get('/club/constitution/')
content = response.content.decode()
t = re.search(r'to provide a wide variety of safe and affordable yacht sailing', content)
self.assertIsNotNone(t)
def test_page_clubcommittee(self):
response = self.client.get('/club/committee/')
content = response.content.decode()
t = re.search(r'CUYC elects new officers as needed, usually at the beginning of each term', content)
self.assertIsNotNone(t)
def test_page_damages(self):
response = self.client.get('/club/damages/')
content = response.content.decode()
t = re.search(r'all crew participants may be required to contribute to the payment of damages', content)
self.assertIsNotNone(t)
def test_page_training(self):
response = self.client.get('/training/')
content = response.content.decode()
t = re.search(r'members of the club are always happy to pass on informal training tips', content)
self.assertIsNotNone(t)
def test_page_racing(self):
response = self.client.get('/racing/')
content = response.content.decode()
t = re.search(r'CUYC Racing Squad', content)
self.assertIsNotNone(t)
def test_page_blog(self):
response = self.client.get('/blog/')
content = response.content.decode()
t = re.search(r'Latest Posts', content)
self.assertIsNotNone(t)
def test_page_gallery(self):
response = self.client.get('/gallery/')
content = response.content.decode()
t = re.search(r'Photo Galleries', content)
self.assertIsNotNone(t)
def test_page_about_photos(self):
response = self.client.get('/about_photos/')
content = response.content.decode()
t = re.search(r'have been supplied by members of CUYC', content)
self.assertIsNotNone(t)
def test_page_loginhelp(self):
response = self.client.get('/login/help/')
content = response.content.decode()
t = re.search(r'Existing CUYC Member, without an account?', content)
self.assertIsNotNone(t)
def test_page_loginregister(self):
response = self.client.get('/login/register/')
content = response.content.decode()
t = re.search(r'If you are, or have ever been, a CUYC or CUCrC member', content)
self.assertIsNotNone(t)
# --- These pages are not connected to top level public menus but are in fact public
def test_page_club_tripinformation(self):
response = self.client.get('/club/trip-information/')
content = response.content.decode()
t = re.search(r'organisers have a choice to add a sum to the trip fee quoted on the website to cover expenses', content)
self.assertIsNotNone(t)
def test_page_club_trippayment(self):
response = self.client.get('/club/trip-information/payment/')
content = response.content.decode()
t = re.search(r'All payments to the club should be sent via Paypal', content)
self.assertIsNotNone(t)
def test_page_club_trip_typical_day(self):
response = self.client.get('/club/trip-information/typical-day/')
content = response.content.decode()
t = re.search(r'Skipper and first mate crawl out of their sleeping bags early', content)
self.assertIsNotNone(t)
def test_page_club_trip_faq(self):
response = self.client.get('/club/trip-information/faq/')
content = response.content.decode()
t = re.search(r'Different people are seasick in different ways', content)
self.assertIsNotNone(t)
def test_page_club_trip_kit(self):
response = self.client.get('/club/trip-information/kit/')
content = response.content.decode()
t = re.search(r'appropriate quantity of base layer clothes to match the duration', content)
self.assertIsNotNone(t)
|