#!/usr/bin/env python3 import os import shutil import tempfile import unittest import argparse from main import process_directory, replace_content, main as cli_main class TestBulkStringReplacer(unittest.TestCase): def setUp(self): # create isolated temp dir self.base = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.base) def create_file(self, relpath, content=''): full = os.path.join(self.base, relpath) os.makedirs(os.path.dirname(full), exist_ok=True) with open(full, 'w', encoding='utf-8') as f: f.write(content) return full def test_replace_content(self): f = self.create_file('foo.txt', 'hello OLD world') replace_content(f, 'OLD', 'NEW', preview=False, verbose=False) with open(f, 'r', encoding='utf-8') as fp: self.assertIn('hello NEW world', fp.read()) def test_rename_file(self): f = self.create_file('OLDfile.txt', '') process_directory( base_path=self.base, old_string='OLD', new_string='NEW', recursive=False, rename_folders=False, rename_files=True, replace_in_content=False, preview=False, verbose=False, include_hidden=True, rename_paths=False ) # file moved self.assertTrue(os.path.exists(os.path.join(self.base, 'NEWfile.txt'))) self.assertFalse(os.path.exists(f)) def test_rename_folder(self): os.makedirs(os.path.join(self.base, 'OLDfolder', 'x')) process_directory( base_path=self.base, old_string='OLD', new_string='NEW', recursive=False, rename_folders=True, rename_files=False, replace_in_content=False, preview=False, verbose=False, include_hidden=True, rename_paths=False ) self.assertTrue(os.path.isdir(os.path.join(self.base, 'NEWfolder'))) self.assertFalse(os.path.isdir(os.path.join(self.base, 'OLDfolder'))) def test_full_path_move(self): # prepare vars/configuration.yml cfg = 'vars/configuration.yml' full = self.create_file(cfg, 'DATA') # run with -P process_directory( base_path=self.base, old_string='vars/configuration.yml', new_string='config/main.yml', recursive=True, rename_folders=False, rename_files=False, replace_in_content=False, preview=False, verbose=False, include_hidden=True, rename_paths=True ) # original gone, new exists self.assertFalse(os.path.exists(full)) self.assertTrue(os.path.exists(os.path.join(self.base, 'config', 'main.yml'))) def test_path_and_folders_conflict(self): # simulate CLI error when combining --path and --folders parser = argparse.ArgumentParser() # replicate only the conflict check parser.add_argument('-P', dest='rename_paths', action='store_true') parser.add_argument('-F', dest='folders', action='store_true') args = parser.parse_args(['-P', '-F']) # manual conflict with self.assertRaises(SystemExit) as cm: # mimic the parser.error behavior if args.rename_paths and args.folders: parser.error("Cannot use --path and --folders together.") self.assertNotEqual(cm.exception.code, 0) def test_preview_does_nothing(self): # create file and folder matching f = self.create_file('OLD.txt', 'OLD') os.makedirs(os.path.join(self.base, 'OLDdir')) process_directory( base_path=self.base, old_string='OLD', new_string='NEW', recursive=True, rename_folders=True, rename_files=True, replace_in_content=True, preview=True, verbose=False, include_hidden=True, rename_paths=True ) # nothing changed self.assertTrue(os.path.exists(f)) self.assertTrue(os.path.isdir(os.path.join(self.base, 'OLDdir'))) with open(f, 'r', encoding='utf-8') as fp: self.assertIn('OLD', fp.read()) def test_module_path_replacement_in_python_files(self): # Prepare a nested Python module under the old path content = 'from old/path import func' src = self.create_file('old/path/module.py', content) # Run with -P: should move the file and replace path separators in .py files process_directory( base_path=self.base, old_string='old/path', new_string='old.path', recursive=True, rename_folders=False, rename_files=False, replace_in_content=False, preview=False, verbose=False, include_hidden=True, rename_paths=True ) # The file should have been moved new_path = os.path.join(self.base, 'old.path', 'module.py') self.assertTrue(os.path.exists(new_path)) # Its content should have been updated with open(new_path, 'r', encoding='utf-8') as fp: self.assertIn('from old.path import func', fp.read()) def test_non_python_files_are_not_content_updated(self): # Prepare a non-Python file under the old path content = 'some reference to old/path in text' txt = self.create_file('old/path/readme.txt', content) # Run with -P: .txt files get moved but their content stays the same process_directory( base_path=self.base, old_string='old/path', new_string='old.path', recursive=True, rename_folders=False, rename_files=False, replace_in_content=False, preview=False, verbose=False, include_hidden=True, rename_paths=True ) # The .txt should be moved but its content unchanged new_txt = os.path.join(self.base, 'old.path', 'readme.txt') self.assertTrue(os.path.exists(new_txt)) with open(new_txt, 'r', encoding='utf-8') as fp: self.assertIn('old/path', fp.read()) if __name__ == '__main__': unittest.main()