| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- """Retention regression tests only create and remove synthetic temporary releases."""
- import json
- import os
- from pathlib import Path
- import tempfile
- import unittest
- from unittest.mock import patch
- from step4_web.release_retention import prune_releases
- class RetentionTests(unittest.TestCase):
- def setUp(self):
- self.tmp=tempfile.TemporaryDirectory()
- self.root=Path(self.tmp.name).resolve()
- self.base=self.root/'.runtime/releases'
- self.base.mkdir(parents=True)
- self.manifest=self.root/'current_release.json'
- def tearDown(self): self.tmp.cleanup()
- def run_dir(self, i):
- path=self.base/f'{i:032x}'; path.mkdir()
- (path/'production').mkdir(); (path/'production/data.txt').write_text('synthetic')
- os.utime(path,(1700000000+i,1700000000+i))
- return path
- def publish(self, path, schema=None):
- self.manifest.write_text(json.dumps({'data_version':path.name,
- 'schema_file':str((schema or path)/'meta_graph_schema.json'),
- 'display_schema_file':str((schema or path)/'meta_graph_schema_display.json'),
- 'production_dir':str(path/'production'), 'assessment_file':str(path/'production/analysis.json')}))
- def test_tenth_kept_eleventh_removes_oldest_and_backlog_trimmed(self):
- runs=[self.run_dir(i) for i in range(10)]
- self.publish(runs[-1])
- self.assertEqual(prune_releases(self.root,self.manifest)['deleted'],[])
- runs.append(self.run_dir(10)); self.publish(runs[-1])
- result=prune_releases(self.root,self.manifest)
- self.assertEqual(result['deleted'],[runs[0].name]); self.assertFalse(runs[0].exists())
- self.assertEqual(result['remaining'],10)
- for i in range(11,15):self.run_dir(i)
- result=prune_releases(self.root,self.manifest)
- self.assertEqual(result['deleted'],[runs[i].name for i in range(1,5)])
- self.assertTrue(runs[-1].exists())
- def test_current_old_schema_and_running_failure_are_protected(self):
- runs=[self.run_dir(i) for i in range(12)]
- self.publish(runs[1],schema=runs[0])
- result=prune_releases(self.root,self.manifest,keep_paths=(runs[11],))
- self.assertEqual(result['deleted'],[runs[2].name,runs[3].name])
- self.assertTrue(all(runs[i].exists() for i in (0,1,11)))
- def test_timestamp_name_beats_modified_time_and_unknown_dirs_untouched(self):
- old=self.base/('20200101_080000_'+'a'*32); old.mkdir()
- for i in range(10):self.run_dir(i)
- unknown=self.base/'manual-backup'; unknown.mkdir()
- result=prune_releases(self.root,self.manifest)
- self.assertEqual(result['deleted'],[old.name]); self.assertTrue(unknown.exists())
- def test_invalid_manifest_fails_closed(self):
- for i in range(11):self.run_dir(i)
- self.manifest.write_text('{')
- with self.assertRaises(json.JSONDecodeError):prune_releases(self.root,self.manifest)
- self.assertEqual(len(list(self.base.iterdir())),11)
- def test_locked_directory_warns_without_failing_update(self):
- for i in range(11):self.run_dir(i)
- with patch('step4_web.release_retention.shutil.rmtree',side_effect=PermissionError):
- result=prune_releases(self.root,self.manifest)
- self.assertEqual(result['remaining'],11); self.assertTrue(result['warnings'])
- def test_nested_link_is_not_followed_or_deleted(self):
- runs=[self.run_dir(i) for i in range(11)]
- sentinel=runs[0]/'production/data.txt'
- with patch('step4_web.release_retention._is_link',side_effect=lambda p:p==sentinel):
- result=prune_releases(self.root,self.manifest)
- self.assertTrue(sentinel.exists())
- self.assertEqual(result['deleted'],[runs[1].name])
- self.assertTrue(result['warnings'])
- if __name__=='__main__':unittest.main()
|