diff --git a/core/TESTS/tests.py b/core/TESTS/tests.py
index 610149e..852fb08 100644
--- a/core/TESTS/tests.py
+++ b/core/TESTS/tests.py
@@ -12,10 +12,10 @@ etc. will test fine.
 
 But paths like this:
 /survey_scans/
+/caves/
 which rely on database resolution will fail unless a fixture has been set up for
 them.
 
-
 https://docs.djangoproject.com/en/3.0/topics/testing/tools/
 """
 import unittest
@@ -23,11 +23,6 @@ import re
 from django.test import TestCase, SimpleTestCase, Client
 
 class SimpleTest(SimpleTestCase):
-    def test_basic_addition(self):
-        """
-        Tests that 1 + 1 always equals 2.
-        """
-        self.assertEqual(1 + 1, 2)
     def test_test_setting(self):
         from django.conf import settings        
         self.assertEqual(settings.EMAIL_BACKEND, 'django.core.mail.backends.locmem.EmailBackend')
@@ -109,26 +104,105 @@ class PageTests(TestCase):
         # Every test needs a client.
         self.client = Client()
 
+    def test_expoweb_root(self):
+        response = self.client.get('')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'CUCC in Austria'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_expoweb_root(self):
+        response = self.client.get('')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'CUCC in Austria'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_expoweb_root_slash(self):
+        response = self.client.get('/')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'CUCC in Austria'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_expoweb_dir(self):
+        response = self.client.get('/handbook')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'Introduction to expo'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_expoweb_dirslash(self):
+        response = self.client.get('/handbook/')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'Introduction to expo'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+        
+    def test_expoweb_dir_no_index(self):
+        response = self.client.get('/handbook/troggle')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'Page not found handbook/troggle/index.html'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+  
+    def test_expoweb_htm(self):
+        response = self.client.get('/handbook/index.htm')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'Introduction to expo'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_expoweb_notfound(self):
+        response = self.client.get('/handbook/zyxxypqrqx.html')
+        content = response.content.decode()
+        self.assertEqual(response.status_code, 200)
+        ph = r'<h1>Page not found'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_expoweb_no_dir(self):
+        # slash where there should not be one
+        response = self.client.get('/handbook/zyxxypqrqx/')
+        self.assertEqual(response.status_code, 200)
+        content = response.content.decode()
+        ph = r"<h1>Directory not found"
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+
+    def test_cave_kataster_not_found(self):
+        # database not loaded, so no caves found
+        response = self.client.get('/cave/115')
+        self.assertEqual(response.status_code, 200)
+        content = response.content.decode()
+        ph = r"Cave not found in database"
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+
 
     def test_page_admin(self):
         # see the login page
         response = self.client.get('/admin/login/')
         content = response.content.decode()
         self.assertEqual(response.status_code, 200)
-        h1    = re.search(r'<h1 id="site-name">Troggle administration</h1>', content)
-
-    def test_page_admindocs(self):
-        response = self.client.get('/admin/login/models/')
-        content = response.content.decode()
-        self.assertEqual(response.status_code, 200)
-        h1    = re.search(r'<h1>Model documentation</h1>', content)
+        ph = r'<h1 id="site-name">Troggle administration</h1>'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
 
     def test_page_admindocs_exped(self):
         # Get redirected to login page
         response = self.client.get('/admin/doc/models/core.expedition/')
         content = response.content.decode()
         self.assertEqual(response.status_code, 302)
-        h1    = re.search(r'<td>logbookentry_set.all</td>', content)
 
     def test_page_expofiles_dir(self):
         # Flat file tests.
@@ -165,6 +239,26 @@ class PageTests(TestCase):
             phmatch    = re.search(ph, content)
             self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
 
+    def test_page_expofile_documents(self):
+        # this gets an empty page as the database has not been loaded
+        response = self.client.get('/expofiles/documents')
+        self.assertEqual(response.status_code, 200)
+        content = response.content.decode()
+        ph = r'notice_generale_cordes_courant'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_page_expofile_documents_slash(self):
+        # this gets an empty page as the database has not been loaded
+        response = self.client.get('/expofiles/documents/')
+        self.assertEqual(response.status_code, 200)
+        content = response.content.decode()
+        ph = r'notice_generale_cordes_courant'
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+
+
     def test_page_expofile_document_loeffler_pdf(self):
         # Flat file tests.
         response = self.client.get('/expofiles/documents/surveying/tunnel-loefflerCP35-only.pdf')
@@ -172,7 +266,7 @@ class PageTests(TestCase):
             self.assertEqual(response.status_code, 302)  
         if response.status_code != 302:
             self.assertEqual(response.status_code, 200)  
-        self.assertEqual(len(response.content), 2299270) # fails, but is working manually!
+            self.assertEqual(len(response.content), 2299270) 
 
     def test_page_expofile_document_rope_pdf(self):
         # Flat file tests.
@@ -181,7 +275,7 @@ class PageTests(TestCase):
             self.assertEqual(response.status_code, 302)  
         if response.status_code != 302:
             self.assertEqual(response.status_code, 200)  
-        self.assertEqual(len(response.content), 76197) # fails, but is working manually!
+            self.assertEqual(len(response.content), 76197) 
 
     def test_page_expofile_document_png(self):
         # Flat file tests.
@@ -190,7 +284,7 @@ class PageTests(TestCase):
             self.assertEqual(response.status_code, 302)  
         if response.status_code != 302:
             self.assertEqual(response.status_code, 200)  
-        self.assertEqual(len(response.content), 69921) # fails, but is working manually!
+            self.assertEqual(len(response.content), 69921) 
 
     def test_page_expofile_writeup(self):
         # Flat file tests.
@@ -199,7 +293,7 @@ class PageTests(TestCase):
             self.assertEqual(response.status_code, 302)  
         if response.status_code != 302:
             self.assertEqual(response.status_code, 200)  
-        self.assertEqual(len(response.content), 12915413) # fails, but is working manually!
+            self.assertEqual(len(response.content), 12915413) 
 
     def test_page_survey_scans_empty(self):
         # this gets an empty page as the database has not been loaded
@@ -215,7 +309,16 @@ class PageTests(TestCase):
         response = self.client.get('/tunneldataraw/')
         self.assertEqual(response.status_code, 200)
         content = response.content.decode()
-        ph = r'<h1>Page not found tunneldataraw/</h1>'
+        ph = r"<h1>Directory not found"
+        phmatch    = re.search(ph, content)
+        self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
+
+    def test_page_slash_empty(self):
+        # tslash where there should not be one
+        response = self.client.get('/expedition/1979/')
+        self.assertEqual(response.status_code, 200)
+        content = response.content.decode()
+        ph = r"<h1>Directory not found"
         phmatch    = re.search(ph, content)
         self.assertIsNotNone(phmatch, "Failed to find expected text: '" + ph +"'")
 
diff --git a/core/views_caves.py b/core/views_caves.py
index 946eba9..390d8f7 100644
--- a/core/views_caves.py
+++ b/core/views_caves.py
@@ -65,15 +65,16 @@ def getCave(cave_id):
     are duplicates'''
     try:
         cave = Cave.objects.get(kataster_number=cave_id)
+        return cave
     except Cave.MultipleObjectsReturned as ex:
         raise MultipleObjectsReturned("Duplicate kataster number")  from ex # propagate this up
 
-    except Cave.DoesNotExist:
+    except Cave.DoesNotExist as ex:
         Gcavelookup = GetCaveLookup() # dictionary makes strings to Cave objects
         if cave_id in Gcavelookup:
             return Gcavelookup[cave_id] 
-        else: 
-             raise ObjectDoesNotExist("No cave found with this identifier in any id field")
+        else:
+            raise ObjectDoesNotExist("No cave found with this identifier in any id field") from ex # propagate this up           
     except:
         raise ObjectDoesNotExist("No cave found with this identifier in any id field")
 
@@ -144,6 +145,8 @@ def cave(request, cave_id='', offical_name=''):
         return render(request, 'svxcaveseveral.html', {'settings': settings, "caves":caves }) # not the right template, needs a specific one
     except ObjectDoesNotExist:
         return render(request, 'svxcavesingle404.html', {'settings': settings, "cave":cave_id })
+    except:
+        return render(request, 'svxcavesingle404.html', {'settings': settings })
 
     if cave.non_public and settings.PUBLIC_SITE and not request.user.is_authenticated():
         return render(request, 'nonpublic.html', {'instance': cave, 'cavepage': True, 'cave_id': cave_id})
diff --git a/localsettings.py b/localsettings.py
index 15491ab..1333952 100644
--- a/localsettings.py
+++ b/localsettings.py
@@ -15,13 +15,11 @@ This file is included at the end of the main troggle/settings.py file so that
 it overwrites defaults in that file.
 """
 
-# link 'localsettings.py' to localsettingsWSL.py for use on a Windows 10 machine running WSL1
 print(" * importing troggle/localsettings.py")
 
 #-----------------------------------------------------------------
 #  THINK before you push this to a repo
 #  - have you checked that credentials.py is in .gitignore ?
-#  - have you run pre-push.sh to copy files and remove passwords?
 #  - we don't want to have to change the expo system password !
 #-----------------------------------------------------------------
 # default values, then get overwritten by real secrets
diff --git a/templates/pagenotfound.html b/templates/pagenotfound.html
index 9d54ae6..7d79c16 100644
--- a/templates/pagenotfound.html
+++ b/templates/pagenotfound.html
@@ -4,7 +4,7 @@
 <h1>Page not found {{ path }}</h1>
 <p>Probably a mistake. But you can use <a href="{%url "editexpopage" path %}">this link</a>
 <p>
- or 'Edit this page' in the menu on the left to create this page if you aare logged in.
+ or 'Edit this page' in the menu on the left to create this page if you are logged in.
  <p>If you can't see that option in the menu, then you are not logged in and you can't create anything.
 {% include "menu.html" %}
 {% endblock %}