diff options
Diffstat (limited to 'core/TESTS/test_logins.py')
-rw-r--r-- | core/TESTS/test_logins.py | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/core/TESTS/test_logins.py b/core/TESTS/test_logins.py new file mode 100644 index 0000000..f50e292 --- /dev/null +++ b/core/TESTS/test_logins.py @@ -0,0 +1,449 @@ +""" +Originally written for CUYC +Philip Sargent (Feb.2021) + +Modified for Expo April 2021. +""" + +import pathlib +import re +from http import HTTPStatus + +from django.test import Client, TestCase + +import troggle.settings as settings +from troggle.core.models.wallets import Wallet +from troggle.core.models.troggle import Expedition + + +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 django.contrib.auth.models import User + + 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 + + +class FixturePageTests(TestCase): + # The fixtures have a password hash which is compatible with plain-text password 'secretword' + fixtures = ["auth_users"] + + def setUp(self): + from django.contrib.auth.models import User + + self.user = User.objects.get(username="expotest") + + def tearDown(self): + pass + + def test_fix_admin_login_fail(self): + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + + 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"Troggle administration", content) + self.assertIsNone(t, "Logged in as '" + u.username + "' (not staff) but still managed to get the Admin page") + + +class PostTests(TestCase): + """Tests walletedit form""" + + fixtures = ["auth_users"] + + @classmethod + def setUpTestData(cls): + pass + + def setUp(self): + from django.contrib.auth.models import User + + self.user = User.objects.get(username="expotest") + self.client = Client() + + testyear = "2022" + wname = f"{testyear}:00" + self.testyear = testyear + w = Wallet() + w.pk = 9100 + w.fpath = str(pathlib.Path(settings.SCANS_ROOT, wname)) + w.walletname = wname + w.save() + self.wallet = w + + e = Expedition() + e.year = testyear + e.save() + self.expedition = e + + def test_file_permissions(self): + """Expect to be allowed to write to expofiles + Need to login first. + """ + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + testyear = self.testyear + + self.assertTrue(u.is_active, "User '" + u.username + "' is INACTIVE") + c.login(username=u.username, password="secretword") + + for p in [settings.SCANS_ROOT, + settings.DRAWINGS_DATA / "walletjson", + settings.EXPOWEB / "documents", + settings.SURVEX_DATA / "docs" + ]: + + _test_file_path = pathlib.Path(p, "_created_by_test_suite.txt") + self.assertEqual(_test_file_path.is_file(), False) + + with open(_test_file_path, "w") as f: + f.write("test string: can we write to this directory?") + self.assertEqual(_test_file_path.is_file(), True) + _test_file_path.unlink() + + def test_scan_upload(self): + """Expect scan upload to wallet to work on any file + Need to login first. + + This upload form looks for the Cave and the Wallet, so the test fails if the database is not loaded with the cave + identified in the wallet + """ + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + testyear = self.testyear + + self.assertTrue(u.is_active, "User '" + u.username + "' is INACTIVE") + c.login(username=u.username, password="secretword") + + with open("core/fixtures/test_upload_file.txt", "r") as testf: + response = self.client.post( + f"/walletedit/{testyear}:00", data={"name": "test_upload_file.txt", "uploadfiles": testf} + ) + content = response.content.decode() + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(response.status_code, HTTPStatus.OK) + with open("_test_response.html", "w") as f: + f.write(content) + for ph in [ + r"test_upload_", + rf"← {testyear}#00 →", + r"description written", + r"Plan not required", + r"edit settings or upload a file", + ]: + phmatch = re.search(ph, content) + self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph + "'") + + # # Does not use the filename Django actually uses, assumes it is unchanged. Potential bug. + # remove_file = pathlib.Path(settings.SCANS_ROOT) / f'{testyear}' / f'{testyear}#00'/ 'test_upload_file.txt' + # remove_file.unlink() + # # Undo the auto create and commit of a new wallet + # cwd = settings.DRAWINGS_DATA + # sp = subprocess.run([settings.GIT, "reset", "--hard", "master^"], cwd=cwd, capture_output=True, text=True) + # print(f'git output: {cwd}:\n # {sp.stderr=}\n # {sp.stdout=} \n # return code: {str(sp.returncode)}') + # if sp.returncode != 0: + # print(f'git output: {cwd}:\n # {sp.stderr=}\n # {sp.stdout=} \n # return code: {str(sp.returncode)}') + + def test_photo_upload(self): + """Expect photo upload to work on any file (contrary to msg on screen) + Upload into current default year. settings.PHOTOS_YEAR + Deletes file afterwards + Need to login first. + """ + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + + self.assertTrue(u.is_active, "User '" + u.username + "' is INACTIVE") + c.login(username=u.username, password="secretword") + + with open("core/fixtures/test_upload_file.txt", "r") as testf: + response = self.client.post( + "/photoupload/", data={"name": "test_upload_file.txt", "renameto": "", "uploadfiles": testf} + ) + content = response.content.decode() + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(response.status_code, HTTPStatus.OK) + # with open('_test_response.html', 'w') as f: + # f.write(content) + for ph in [ + r"test_upload_", + r"Upload photos into /photos/" + str(settings.PHOTOS_YEAR), + r" you can create a new folder in your name", + r"Create new Photographer folder", + r"only photo image files are accepted", + ]: + phmatch = re.search(ph, content) + self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph + "'") + + # Does not use the filename Django actually uses, assumes it is unchanged. Potential bug. + remove_file = pathlib.Path(settings.PHOTOS_ROOT, settings.PHOTOS_YEAR) / "test_upload_file.txt" + remove_file.unlink() + + def test_photo_upload_rename(self): + """Expect photo upload to work on any file (contrary to msg on screen) + Upload into current default year. settings.PHOTOS_YEAR + Deletes file afterwards + Need to login first. + """ + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + + self.assertTrue(u.is_active, "User '" + u.username + "' is INACTIVE") + c.login(username=u.username, password="secretword") + + rename = "RENAMED-FILE.JPG" + with open("core/fixtures/test_upload_file.txt", "r") as testf: + response = self.client.post( + "/photoupload/", data={"name": "test_upload_file.txt", "renameto": rename, "uploadfiles": testf} + ) + content = response.content.decode() + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(response.status_code, HTTPStatus.OK) + # with open('_test_response.html', 'w') as f: + # f.write(content) + for ph in [rename]: + phmatch = re.search(ph, content) + self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph + "'") + + # Does not use the filename Django actually uses, assumes it is unchanged. Potential bug. + remove_file = pathlib.Path(settings.PHOTOS_ROOT, settings.PHOTOS_YEAR) / rename + remove_file.unlink() + + def test_photo_folder_create(self): + """Create folder for new user + Create in current default year. settings.PHOTOS_YEAR + Deletes folder afterwards + Need to login first. + """ + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + + self.assertTrue(u.is_active, "User '" + u.username + "' is INACTIVE") + c.login(username=u.username, password="secretword") + + response = self.client.post("/photoupload/", data={"photographer": "GussieFinkNottle"}) + content = response.content.decode() + self.assertEqual(response.status_code, HTTPStatus.OK) + self.assertEqual(response.status_code, HTTPStatus.OK) + # with open('_test_response.html', 'w') as f: + # f.write(content) + for ph in [r"/GussieFinkNottle/", r"Create new Photographer folder"]: + phmatch = re.search(ph, content) + self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph + "'") + + # Does not use the filename Django actually uses, assumes it is unchanged. Potential bug. + remove_dir = pathlib.Path(settings.PHOTOS_ROOT, settings.PHOTOS_YEAR) / "GussieFinkNottle" + remove_dir.rmdir() + + def test_dwg_upload_txt(self): + """Expect .pdf file to be refused upload + Need to login first. + """ + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + + self.assertTrue(u.is_active, "User '" + u.username + "' is INACTIVE") + c.login(username=u.username, password="secretword") + + with open("core/fixtures/test_upload_file.pdf", "r") as testf: + response = self.client.post( + "/dwgupload/uploads", data={"name": "test_upload_file.txt", "uploadfiles": testf} + ) + content = response.content.decode() + self.assertEqual(response.status_code, HTTPStatus.OK) + t = re.search("Files refused:", content) + self.assertIsNotNone(t, 'Logged in but failed to see "Files refused:"') + + def test_dwg_upload_drawing(self): + """Expect no-suffix file to upload + Note that this skips the git commit process. That would need a new test. + Need to login first. + """ + c = self.client + from django.contrib.auth.models import User + + u = User.objects.get(username="expotest") + + self.assertTrue(u.is_active, "User '" + u.username + "' is INACTIVE") + c.login(username=u.username, password="secretword") + + with open("core/fixtures/test_upload_nosuffix", "r") as testf: + response = self.client.post( + "/dwguploadnogit/uploads", data={"name": "test_upload_nosuffix", "uploadfiles": testf} + ) + content = response.content.decode() + # with open('_test_response.html', 'w') as f: + # f.write(content) + self.assertEqual(response.status_code, HTTPStatus.OK) + for ph in [ + r"test_upload_nosuffix", + r"You cannot create folders here", + r"Creating a folder is done by a nerd", + ]: + phmatch = re.search(ph, content) + self.assertIsNotNone( + phmatch, "Expect no-suffix file to upload OK. Failed to find expected text: '" + ph + "'" + ) + + # Does not use the filename Django actually uses, assumes it is unchanged. Bug: accumulates one file with random name added each time it is run. + remove_file = pathlib.Path(settings.DRAWINGS_DATA) / "uploads" / "test_upload_nosuffix" + remove_file.unlink() + + +class ComplexLoginTests(TestCase): + """These test the login and capabilities of logged-in users, they do not use fixtures""" + + def setUp(self): + """setUp runs once for each test in this class""" + from django.contrib.auth.models import User + + u = User() + u.pk = 9000 + u.user_id = 8000 + u.username, u.password = "expotest", "secretword" + u.email = "philip.sargent+ET@gmail.com" + u.first_name, u.last_name = "ExpoTest", "Caver" + 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) + self.user = u + + 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): # need to fix this in real system + # c = self.client + # # Need to login first. Tests that we are redirected to login page if not logged in + # response = c.get('noinfo/cave-number-index') + # 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("/accounts/login/") # defined by auth system + content = response.content.decode() + t = re.search(r"You are now logged in", content) + self.assertIsNotNone(t, "Logged in as '" + u.username + "' but failed to get 'Now you can' 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() # This next test always means user is still authenticated after logout. Surely not? + # self.assertFalse(u.is_authenticated, 'User \'' + u.username + '\' is STILL AUTHENTICATED after logout') + + def test_admin_login(self): + c = self.client + u = self.user + + 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"Troggle database administration", content) + self.assertIsNotNone(t, "Logged in as '" + u.username + "' but failed to get the Troggle Admin page") + + def test_noinfo_login(self): + + c = self.client # inherited from TestCase + u = self.user + + 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("/stats") # a page with the Troggle menus + content = response.content.decode() + t = re.search(r"User\:expotest", content) + self.assertIsNotNone(t, "Logged in as '" + u.username + "' but failed to get 'User:expotest' heading") + + response = c.get("/noinfo/cave-number-index") + content = response.content.decode() + t = re.search(r"2001-07 Hoffnungschacht", content) + self.assertIsNotNone(t, "Logged in as '" + u.username + "' but failed to get /noinfo/ content") + + def test_user_force(self): + + c = self.client + u = self.user + + 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("/stats") # a page with the Troggle menus + content = response.content.decode() + t = re.search(r"Log out", content) + self.assertIsNotNone(t, "Forced logged in as '" + u.username + "' but failed to get Log out heading") + + response = c.get("/accounts/login/") + content = response.content.decode() + t = re.search(r"You are now logged in", content) + self.assertIsNotNone(t, "Forced logged in as '" + u.username + "' but failed to get /accounts/profile/ content") |