import os import pathlib import tempfile from django.test import TestCase import settings from troggle.parsers import drawings from troggle.core.models.survex import DrawingFile from troggle.core.models.wallets import Wallet from troggle.core.models.survex import SingleScan from troggle.core.models.troggle import DataIssue class DrawingsPathlibTests(TestCase): def test_load_drawings_creates_expected_entries(self): with tempfile.TemporaryDirectory() as td: # create a small tree p = pathlib.Path(td) (p / 'one.pdf').write_text('pdf') (p / 'two.txt').write_text('txt') sub = p / 'dir' sub.mkdir() (sub / 'three.png').write_text('png') sub2 = p / 'dir2' sub2.mkdir() (sub2 / 'abc.th2').write_text('th2') (sub2 / 'abc.th').write_text('th') # point the module at our tempdir settings.DRAWINGS_DATA = td drawings.load_drawings_files() # all files should be present self.assertTrue(DrawingFile.objects.filter(dwgpath='one.pdf').exists()) self.assertTrue(DrawingFile.objects.filter(dwgpath='two.txt').exists()) self.assertTrue(DrawingFile.objects.filter(dwgpath='dir/three.png').exists()) self.assertTrue(DrawingFile.objects.filter(dwgpath='dir2/abc.th2').exists()) self.assertTrue(DrawingFile.objects.filter(dwgpath='dir2/abc.th').exists()) def test_hidden_and_backup_skipped(self): with tempfile.TemporaryDirectory() as td: p = pathlib.Path(td) (p / '.hidden').write_text('hid') (p / 'file~').write_text('bak') settings.DRAWINGS_DATA = td drawings.load_drawings_files() # Should not import hidden or backup files self.assertFalse(DrawingFile.objects.filter(dwgpath='.hidden').exists()) self.assertFalse(DrawingFile.objects.filter(dwgpath='file~').exists()) def test_no_extension_file(self): with tempfile.TemporaryDirectory() as td: p = pathlib.Path(td) (p / 'noext').write_text('data') settings.DRAWINGS_DATA = td drawings.load_drawings_files() self.assertTrue(DrawingFile.objects.filter(dwgpath='noext').exists()) def test_git_dir_skipped(self): with tempfile.TemporaryDirectory() as td: p = pathlib.Path(td) g = p / '.git' g.mkdir() (g / 'secret.txt').write_text('top secret') settings.DRAWINGS_DATA = td drawings.load_drawings_files() self.assertFalse(DrawingFile.objects.filter(dwgpath='.git/secret.txt').exists()) def test_bulk_create_chunks(self): # Create more than chunk size files to ensure bulk_create is called in multiple chunks count = 800 with tempfile.TemporaryDirectory() as td: p = pathlib.Path(td) for i in range(count): (p / f'file{i}.txt').write_text('x') settings.DRAWINGS_DATA = td drawings.load_drawings_files() self.assertEqual(DrawingFile.objects.count(), count) def test_parse_tunnel_links_wallet_and_scan(self): # Create a wallet and a singlescan, then ensure parse_tnl_file links them w = Wallet.objects.create(fpath='x', walletname='2025#20') ss = SingleScan.objects.create(ffile='x', name='notes.jpg', wallet=w) df = DrawingFile.objects.create(dwgpath='tst.th', dwgname='tst') drawings.parse_tnl_file(df, '2025#20/notes.jpg') self.assertIn(w, df.dwgwallets.all()) self.assertIn(ss, df.scans.all()) def test_findwalletimage_logs_missing_scan(self): # Wallet exists but no scan inside. Should create a DataIssue w = Wallet.objects.create(fpath='x', walletname='2026#01') df = DrawingFile.objects.create(dwgpath='tst2.th2', dwgname='tst2') drawings.findwalletimage(df, '2026#01/missing.jpg') di = DataIssue.objects.filter(parser='Therion', message__contains='not actually found') self.assertTrue(di.exists()) def test_drawing_reference_multiple_creates_dataissue(self): df1 = DrawingFile.objects.create(dwgpath='ref1', dwgname='shared') df2 = DrawingFile.objects.create(dwgpath='ref2', dwgname='shared') dfmain = DrawingFile.objects.create(dwgpath='main', dwgname='main') drawings.parse_tnl_file(dfmain, 'shared') di = DataIssue.objects.filter(parser='Tunnel', message__contains="files named 'shared'") self.assertTrue(di.exists()) def test_drawing_reference_single_no_dataissue(self): DrawingFile.objects.create(dwgpath='ref3', dwgname='unique') dfmain = DrawingFile.objects.create(dwgpath='main2', dwgname='main2') drawings.parse_tnl_file(dfmain, 'unique') di = DataIssue.objects.filter(parser='Tunnel', message__contains="files named 'unique'") self.assertFalse(di.exists()) def test_extension_helpers_and_constants(self): # Helpers should recognise supported/image suffixes (case-insensitive) self.assertTrue(drawings._is_supported_suffix('.png')) self.assertTrue(drawings._is_supported_suffix('.xml')) self.assertTrue(drawings._is_supported_suffix('.TH')) self.assertFalse(drawings._is_supported_suffix('')) self.assertFalse(drawings._is_supported_suffix('.exe')) self.assertTrue(drawings._is_image_suffix('.png')) self.assertTrue(drawings._is_image_suffix('.JPEG')) self.assertFalse(drawings._is_image_suffix('.xml')) self.assertFalse(drawings._is_image_suffix('')) # Constants should include expected values and be consistent self.assertIn('.png', drawings.IMAGE_EXTS) self.assertEqual(set(drawings.IMAGE_LIKE_EXTS), set(drawings.IMAGE_EXTS)) self.assertIn('.th', drawings.SUPPORTED_EXTENSIONS) self.assertIn('.png', drawings.SUPPORTED_EXTENSIONS) def test_fetch_drawingfiles_by_paths_chunks(self): # Create more items than typical SQLite parameter limit to ensure chunking count = 1200 rel_paths = [] objs = [] for i in range(count): rel = f'bigdir/file{i}.txt' rel_paths.append(rel) objs.append(DrawingFile(dwgpath=rel, dwgname=f'name{i}')) # Bulk create them efficiently DrawingFile.objects.bulk_create(objs) mapping = drawings.fetch_drawingfiles_by_paths(rel_paths, chunk_size=500) self.assertEqual(len(mapping), count) # Spot-check a few entries self.assertIn('bigdir/file0.txt', mapping) self.assertIn(f'bigdir/file{count-1}.txt', mapping)