文件
chengduzhongchuliang/ops/test_auto_deploy.py

107 行
4.7 KiB
Python

import importlib.util
import json
from pathlib import Path
import subprocess
import tempfile
import unittest
from unittest.mock import patch
spec = importlib.util.spec_from_file_location('auto_deploy', Path(__file__).with_name('auto_deploy.py'))
deploy = importlib.util.module_from_spec(spec)
spec.loader.exec_module(deploy)
class DeployTest(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.base = Path(self.temp.name)
self.repo = self.base / 'source'
self.repo.mkdir()
self.git('init')
self.git('symbolic-ref', 'HEAD', 'refs/heads/master')
self.config = {'state': str(self.base / 'state'), 'backups': str(self.base / 'backups'),
'host': 'localhost', 'php': 'php', 'health': []}
self.target = self.base / 'site'
self.target.mkdir()
self.settings = {'name': 'backend', 'url': str(self.repo), 'target': str(self.target)}
def git(self, *args):
return subprocess.check_output(['git', '-C', str(self.repo)] + list(args), stderr=subprocess.STDOUT)
def write(self, name, value):
p = self.repo / name
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(value)
def commit(self):
self.git('add', '-A')
self.git('-c', 'user.name=Deploy Test', '-c', 'user.email=deploy@example.invalid', 'commit', '-m', 'fixture')
def test_publish_and_repeat_without_republishing(self):
self.write('public/app.txt', 'first')
self.commit()
self.assertTrue(deploy.publish(self.config, self.settings))
self.assertEqual((self.target / 'public/app.txt').read_text(), 'first')
self.assertFalse(deploy.publish(self.config, self.settings))
def test_preserves_production_config_uploads_and_screen(self):
for name in ['application/database.php', 'application/config.php', 'application/extra/site.php',
'public/uploads/photo.jpg', 'public/screen/index.html']:
self.write(name, 'repository version')
p = self.target / name
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text('production version')
self.write('public/app.txt', 'deploy me')
self.commit()
deploy.publish(self.config, self.settings)
self.assertEqual((self.target / 'application/database.php').read_text(), 'production version')
self.assertEqual((self.target / 'public/uploads/photo.jpg').read_text(), 'production version')
self.assertEqual((self.target / 'public/screen/index.html').read_text(), 'production version')
def test_health_failure_restores_files_and_does_not_advance_revision(self):
self.write('public/app.txt', 'first')
self.commit()
deploy.publish(self.config, self.settings)
state = (Path(self.config['state']) / 'backend.json').read_text()
self.write('public/app.txt', 'broken')
self.write('public/new.txt', 'new')
self.commit()
self.config['health'] = ['http://localhost/health']
with patch.object(deploy, 'healthcheck', side_effect=RuntimeError('unhealthy')):
with self.assertRaises(RuntimeError):
deploy.publish(self.config, self.settings)
self.assertEqual((self.target / 'public/app.txt').read_text(), 'first')
self.assertFalse((self.target / 'public/new.txt').exists())
self.assertEqual((Path(self.config['state']) / 'backend.json').read_text(), state)
def test_deletion_only_removes_previously_deployed_files(self):
self.write('public/app.txt', 'first')
self.write('public/old.txt', 'old')
self.commit()
deploy.publish(self.config, self.settings)
(self.target / 'public/unmanaged.txt').write_text('retain')
(self.repo / 'public/old.txt').unlink()
self.commit()
deploy.publish(self.config, self.settings)
self.assertFalse((self.target / 'public/old.txt').exists())
self.assertTrue((self.target / 'public/unmanaged.txt').exists())
def test_frontend_deploys_source_instead_of_stale_dist(self):
self.settings['name'] = 'frontend'
self.write('index.html', 'new screen')
self.write('dist/index.html', 'stale build')
self.write('js/app.js', 'export const value = 1;')
self.commit()
deploy.publish(self.config, self.settings)
self.assertEqual((self.target / 'index.html').read_text(), 'new screen')
self.assertFalse((self.target / 'dist').exists())
def test_rejects_paths_outside_the_site(self):
with self.assertRaises(RuntimeError):
deploy.destination(self.target, '../outside')
if __name__ == '__main__':
unittest.main()