mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-29 18:16:43 +00:00
OCI manager Apps - App tab: containers installed from an OCI image are identified from their installation record; the application and image versions are shown and an update is detected by image digest; repository link; Refresh data. - Updates tab for OCI containers: Update and Recreate run the same flow as the OCI menu in the Monitor terminal; the pre-update backup can be kept in a backup storage; scheduled image updates with an optional minimum age. - Logs tab: console output of the application, kept on the host (lxc.console.logfile + logrotate) and followed live. - The Proxmox console opens a shell (cmode: shell) when the image has one. - A damaged image download is fetched again before failing. - Multi-container applications open at their LAN address; volume mount points on block storage report their usage. Monitor - Proxmox notifications are delivered to a loopback-only HTTP listener when HTTPS is enabled, so they no longer fail certificate verification. - Log persistence counts recurring patterns only; an ended burst is not reported as persistent and its warning clears on its own (#386). - Proxmox notification config backups are deduplicated and capped at three. - The update icon on the Apps page opens the container on its Updates tab. - Version 1.2.6.2-beta and its release notes in every Monitor language. Docs - OCI manager Apps and Audit & Report rebuilt as per-page message files, with a new page for OCI containers in the Monitor. - Seven pages fixed where rich-text tags were missing from t.rich. Translations - Spanish fixes across the OCI engine, the Monitor and the TUI menus. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
53 lines
2.4 KiB
Python
53 lines
2.4 KiB
Python
import json
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'remote'))
|
|
|
|
from oci_description import render
|
|
from oci_instances import identity
|
|
|
|
|
|
CATALOG = Path(__file__).resolve().parents[1] / 'catalog' / 'apps'
|
|
INSTANCE = '978b58f3-d8b1-41a6-b7aa-a7ecac76b57f'
|
|
|
|
|
|
class DescriptionTests(unittest.TestCase):
|
|
def test_adguard_notes_keep_identity_and_both_access_links(self):
|
|
template = json.loads((CATALOG / 'adguard-home.json').read_text())
|
|
notes = render(template, 'sha256:abc', INSTANCE, '192.168.0.42')
|
|
self.assertEqual(identity(('description: ' + notes.replace('\n', '\\n') + '\n').encode()), INSTANCE)
|
|
self.assertIn('/docs/oci-manager', notes)
|
|
self.assertIn('hub.docker.com/r/adguard/adguardhome', notes)
|
|
self.assertIn('http://192.168.0.42:3000/', notes)
|
|
self.assertIn('http://192.168.0.42:80/', notes)
|
|
self.assertNotIn('Digest:', notes)
|
|
self.assertIn('img.shields.io/badge/%F0%9F%93%9A_Docs-blue', notes)
|
|
self.assertIn('img.shields.io/badge/%F0%9F%92%BB_Code-green', notes)
|
|
self.assertIn('img.shields.io/badge/%E2%98%95_Ko--fi-red', notes)
|
|
self.assertIn('Image: <code>adguard/adguardhome:latest</code> ', notes)
|
|
self.assertIn('>Image</a> · ', notes)
|
|
self.assertIn('>App</a>', notes)
|
|
self.assertIn('href="http://192.168.0.42:3000/" target="_blank" rel="noopener noreferrer">Setup (first run)</a>', notes)
|
|
self.assertIn('>http://192.168.0.42:3000/</a>', notes)
|
|
self.assertIn('href="http://192.168.0.42:80/" target="_blank" rel="noopener noreferrer">Web UI (after setup)</a>', notes)
|
|
self.assertIn('>http://192.168.0.42:80/</a>', notes)
|
|
|
|
def test_missing_ip_does_not_publish_placeholder_links(self):
|
|
template = json.loads((CATALOG / 'adguard-home.json').read_text())
|
|
notes = render(template, '', INSTANCE)
|
|
self.assertNotIn('http://:3000', notes)
|
|
self.assertIn('proxmenux-instance=' + INSTANCE, notes)
|
|
|
|
def test_untrusted_title_is_escaped(self):
|
|
template = {'id': 'example', 'catalog_ui': {'title': {'en_US': '<script>x</script>'}},
|
|
'container_contract': {'image': {'reference': 'example:latest'}}}
|
|
notes = render(template, '', INSTANCE)
|
|
self.assertNotIn('<script>', notes)
|
|
self.assertIn('<script>', notes)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|