Automate post-import refresh and validation workflow
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from post_import_refresh import AutomationConfig, StepResult, build_steps, write_status
|
||||
|
||||
|
||||
class PostImportRefreshPlanTest(unittest.TestCase):
|
||||
def test_dry_run_is_the_default_and_never_enables_index_writes(self):
|
||||
config = AutomationConfig()
|
||||
steps = build_steps(config)
|
||||
commands = "\n".join(command for step in steps for command in step.commands)
|
||||
|
||||
self.assertFalse(config.apply)
|
||||
self.assertIn("validate semantic index dry-run", [step.name for step in steps])
|
||||
self.assertIn("semantic_index/refresh.sh", commands)
|
||||
self.assertNotIn("semantic_index/refresh.sh --apply", commands)
|
||||
self.assertNotIn("--force-rebuild", commands)
|
||||
self.assertNotIn("systemctl enable --now semantic-index-refresh.timer", commands)
|
||||
|
||||
def test_plugin_reapply_happens_before_migrations_and_helpdesk_reset(self):
|
||||
names = [step.name for step in build_steps(AutomationConfig())]
|
||||
|
||||
self.assertLess(names.index("reapply tracked plugins"), names.index("run plugin migrations"))
|
||||
self.assertLess(names.index("run plugin migrations"), names.index("reset Helpdesk mail settings"))
|
||||
|
||||
def test_expected_plugins_are_reapplied_to_remote_redmine_tree(self):
|
||||
steps = build_steps(AutomationConfig())
|
||||
plugin_step = next(step for step in steps if step.name == "reapply tracked plugins")
|
||||
commands = "\n".join(plugin_step.commands)
|
||||
|
||||
self.assertIn("plugins/redmine_event_outbox", commands)
|
||||
self.assertIn("plugins/redmine_contacts", commands)
|
||||
self.assertIn("plugins/redmine_contacts_helpdesk", commands)
|
||||
self.assertNotIn("plugins/redmine_event_outbox/", commands)
|
||||
self.assertIn("reddev@192.168.50.170:/usr/share/redmine/plugins/", commands)
|
||||
|
||||
def test_apply_mode_runs_mutating_validation_sequence(self):
|
||||
steps = build_steps(AutomationConfig(apply=True))
|
||||
commands = "\n".join(command for step in steps for command in step.commands)
|
||||
|
||||
self.assertIn("bundle exec rake redmine:plugins:migrate", commands)
|
||||
self.assertIn("./reset_helpdesk_mail_settings.py", commands)
|
||||
self.assertIn("touch tmp/restart.txt", commands)
|
||||
self.assertIn("./validate_test_instance.py", commands)
|
||||
|
||||
def test_remote_write_steps_use_sudo_by_default(self):
|
||||
commands = "\n".join(command for step in build_steps(AutomationConfig()) for command in step.commands)
|
||||
|
||||
self.assertIn("--rsync-path 'sudo rsync'", commands)
|
||||
self.assertIn("sudo mkdir -p", commands)
|
||||
self.assertIn("sudo chmod -R g+rwX", commands)
|
||||
|
||||
def test_local_mode_emits_local_commands_without_ssh(self):
|
||||
config = AutomationConfig(local=True)
|
||||
commands = "\n".join(command for step in build_steps(config) for command in step.commands)
|
||||
|
||||
self.assertNotIn("ssh -i", commands)
|
||||
self.assertNotIn("rsync-path", commands)
|
||||
self.assertIn("reset_helpdesk_mail_settings.py --local", commands)
|
||||
self.assertIn("validate_test_instance.py --local", commands)
|
||||
self.assertNotIn("--composer-bin", commands)
|
||||
self.assertIn("redmine_outbox_worker.py --local --status", commands)
|
||||
self.assertIn("/opt/lanscratch/redmine-post-import/repo/plugins/redmine_event_outbox", commands)
|
||||
self.assertIn("/usr/share/redmine/plugins/", commands)
|
||||
self.assertIn("cd /usr/share/redmine && RAILS_ENV=production bundle exec rake redmine:plugins:migrate", commands)
|
||||
|
||||
def test_local_semantic_check_is_non_blocking_without_staged_venv(self):
|
||||
config = AutomationConfig(local=True)
|
||||
semantic_step = next(step for step in build_steps(config) if step.name == "validate semantic index dry-run")
|
||||
command = semantic_step.commands[0]
|
||||
|
||||
self.assertIn("test -x /opt/lanscratch/redmine-post-import/repo/.venv/bin/python", command)
|
||||
self.assertIn("semantic index runtime missing; skipping dry-run", command)
|
||||
self.assertIn("else", command)
|
||||
|
||||
def test_status_paths_default_to_lanscratch(self):
|
||||
config = AutomationConfig()
|
||||
|
||||
self.assertEqual(Path("/opt/lanscratch/redmine-post-import/status"), config.status_dir)
|
||||
|
||||
def test_write_status_updates_latest_and_success_only_on_success(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
config = AutomationConfig(status_dir=Path(tmp))
|
||||
failed = write_status(
|
||||
config,
|
||||
run_id="20260428T010000Z",
|
||||
status="failed",
|
||||
results=[StepResult("preflight", "test -d missing", 1)],
|
||||
failed_step="preflight",
|
||||
)
|
||||
|
||||
self.assertTrue((Path(tmp) / "latest.json").exists())
|
||||
self.assertTrue((Path(tmp) / "runs" / "20260428T010000Z.json").exists())
|
||||
self.assertFalse((Path(tmp) / "latest-success.json").exists())
|
||||
self.assertEqual("failed", failed["status"])
|
||||
|
||||
successful = write_status(
|
||||
config,
|
||||
run_id="20260428T010100Z",
|
||||
status="success",
|
||||
results=[StepResult("preflight", "test -d plugins", 0)],
|
||||
)
|
||||
|
||||
latest_success = json.loads((Path(tmp) / "latest-success.json").read_text())
|
||||
self.assertEqual(successful["run_id"], latest_success["run_id"])
|
||||
self.assertEqual("success", latest_success["status"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user