"""Changelog exports and remote publication boundaries.""" import copy import importlib.util import json from pathlib import Path import tempfile import unittest from unittest.mock import patch ROOT = Path(__file__).resolve().parents[1] def module(name, file): spec = importlib.util.spec_from_file_location(name, ROOT/'tools'/file) result = importlib.util.module_from_spec(spec) spec.loader.exec_module(result) return result export = module('export', 'export-changelog.py') sync = module('sync', 'sync-forgejo-changelog.py') class ChangelogTests(unittest.TestCase): def setUp(self): self.source, self.languages = export.load_source(ROOT/'changelog.json') self.tag = 'android-2.7-preview.14' def test_all_outputs_reuse_source_entries(self): with tempfile.TemporaryDirectory() as d: root = Path(d) files = export.generate(ROOT/'changelog.json', root, root/'site.json', root/'releases') data = json.loads(files[root/'site.json']) self.assertEqual(data['languages'], self.languages) for lang, name in [('fr', 'CHANGELOG.md'), ('en', 'CHANGELOG.en.md')]: for entry in self.source['entries']: self.assertIn(entry['text'][lang], files[root/name]) def test_range_and_summary(self): documents = export.release_documents(self.source, self.tag) self.assertTrue(documents['release-body.md'].startswith(self.source['releases'][self.tag]['summary'])) for lang in export.LANGUAGES: text = documents[f'CHANGELOG.{lang}.md'] self.assertIn('## 2.7-preview.14', text) self.assertNotIn('## 2.7-156.0.1 ', text) future = copy.deepcopy(self.source['entries'][0]) future['id'] = 'future' self.source['entries'].insert(0, future) self.assertEqual(documents, export.release_documents(self.source, self.tag)) def test_scope_and_fixed_ids(self): self.assertTrue(any(g['platforms'] == ['pc', 'models'] for entry in self.languages['fr'] for g in entry['groups'])) self.assertEqual(len(self.source['entries']), len({e['id'] for e in self.source['entries']})) def test_invalid_source_rejected_before_output(self): with tempfile.TemporaryDirectory() as d: p = Path(d)/'source.json' del self.source['entries'][0]['text']['en'] p.write_text(json.dumps(self.source)) with self.assertRaisesRegex(ValueError, 'Missing translation'): export.generate(p, Path(d), Path(d)/'site.json', Path(d)/'releases') self.assertFalse((Path(d)/'site.json').exists()) def test_flat_entry_keeps_explicit_platforms_in_both_languages(self): for language in export.LANGUAGES: for source, rendered in zip(self.source['entries'], self.languages[language]): self.assertEqual(rendered['platforms'], source['metadata']['platforms']) self.assertEqual(rendered['models'], source['metadata']['models']) self.assertEqual(rendered['status'], source['metadata']['status']) self.assertEqual(rendered['groups'][0]['platforms'], rendered['platforms']) def test_badges_are_images_with_escaped_attributes(self): rendered = export.inline('![Fix](https://img.shields.io/badge/Fix-52677D?style=flat-square) **Camera**') self.assertIn('Camera', rendered) self.assertNotIn('