mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 10:22:08 +00:00
Merge remote-tracking branch 'origin/develop' into HEAD
# Conflicts: # .github/scripts/run_offline_qualified.py # lang/sk.json
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
"""PBS CLI language and no-effect boundaries using real functions, isolated files."""
|
||||
import contextlib
|
||||
import copy
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
def function(path, name):
|
||||
text = (ROOT/path).read_text()
|
||||
start = text.index(name+'() {')
|
||||
return text[start:text.index('\n}\n', start)+3]
|
||||
|
||||
|
||||
def repository(components, suite='trixie'):
|
||||
return dict(Types=['deb'], URIs=['https://credential.invalid/private'], Suites=[suite],
|
||||
Components=components.split(), Enabled=True)
|
||||
|
||||
|
||||
class RepositoryCliTests(unittest.TestCase):
|
||||
def run_policy(self, case, language='sk', mode='plan', malformed=None, product='pbs'):
|
||||
spec = importlib.util.spec_from_file_location('policy_fixture', ROOT/'scripts/global/repository_policy.py')
|
||||
module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module)
|
||||
state = dict(files=[dict(path='/etc/apt/sources.list.d/pbs.sources', repositories=[repository('pbs-enterprise')]),
|
||||
dict(path='/etc/apt/sources.list.d/debian.sources', repositories=[repository('main')])],
|
||||
errors=[], digest='fixture', **{'standard-repos':[{'handle':'no-subscription'}]})
|
||||
status='notfound'; calls=[]
|
||||
if case=='inventory': state['errors']=['PRIVATE RAW DIAGNOSTIC']
|
||||
if case=='status': status='unknown'
|
||||
if case=='suite': state['files'][0]['repositories'][0]['Suites']=['bookworm']
|
||||
if case=='active': status='active'; state['files']=state['files'][1:]
|
||||
if case=='debian': state['files']=state['files'][:1]
|
||||
if case=='mixed': state['files'][0]['repositories'][0]['Components'].append('main')
|
||||
if case=='handle': state['standard-repos']=[]
|
||||
if case=='preserve': state['files'][0]['repositories'][0]['Components']=['pbs-no-subscription']
|
||||
original=copy.deepcopy(state)
|
||||
def run(args):
|
||||
calls.append(args)
|
||||
if args[0]=='proxmox-backup-manager': return json.dumps(dict(status=status, serverid='PRIVATE SERVER ID'))
|
||||
if 'create' in args:
|
||||
state['files'][0]['repositories'][0]['Enabled']=False
|
||||
if case=='partial': state['errors']=['PRIVATE RAW DIAGNOSTIC']
|
||||
return ''
|
||||
if 'set' in args:
|
||||
state['files'].append(dict(path='/etc/apt/sources.list.d/public.sources',repositories=[repository('pbs-no-subscription')]))
|
||||
return ''
|
||||
return json.dumps(state)
|
||||
with tempfile.TemporaryDirectory(prefix='pbs-policy-i18n-') as folder:
|
||||
module.BASE=Path(folder)
|
||||
(module.BASE/'lang').mkdir()
|
||||
(module.BASE/'config.json').write_text(json.dumps({'language':language}))
|
||||
catalog=(ROOT/'lang/sk.json').read_text() if malformed is None else malformed
|
||||
(module.BASE/'lang/sk.json').write_text(catalog)
|
||||
stdout,stderr=io.StringIO(),io.StringIO()
|
||||
suite='sid' if case=='unsupported' else 'trixie'
|
||||
with patch.object(module,'run',side_effect=run), patch.object(module.sys,'argv',['repository_policy.py',mode,suite,product]), contextlib.redirect_stdout(stdout),contextlib.redirect_stderr(stderr):
|
||||
result=module.main()
|
||||
return result,stdout.getvalue(),stderr.getvalue(),calls,state,original
|
||||
|
||||
def test_pbs_errors_are_localized_without_changing_decisions_or_exposing_raw_data(self):
|
||||
expectations={'inventory':'Zoznam repozitárov APT', 'status':'Stav predplatného',
|
||||
'suite':'iné vydanie', 'active':'aktívne predplatné', 'debian':'základný repozitár Debianu',
|
||||
'mixed':'spoločný záznam APT', 'handle':'no-subscription', 'unsupported':'Nepodporované vydanie',
|
||||
'partial':'stav repozitárov je neznámy'}
|
||||
for case,expected in expectations.items():
|
||||
with self.subTest(case=case):
|
||||
result,out,error,calls,state,original=self.run_policy(case,mode='apply' if case=='partial' else 'plan')
|
||||
self.assertEqual(result,1); self.assertEqual(out,''); self.assertIn(expected,error)
|
||||
self.assertNotIn('PRIVATE',error); self.assertNotIn('credential.invalid',error)
|
||||
writes=[c for c in calls if 'create' in c or 'set' in c]
|
||||
if case=='partial':
|
||||
self.assertEqual(len(writes),1)
|
||||
self.assertFalse(state['files'][0]['repositories'][0]['Enabled'])
|
||||
self.assertIn('Automatický návrat',error)
|
||||
else:
|
||||
self.assertEqual(writes,[]); self.assertEqual(state,original)
|
||||
if case=='unsupported': self.assertEqual(calls,[])
|
||||
|
||||
def test_english_error_and_stdout_tokens_remain_compatible(self):
|
||||
en=self.run_policy('unsupported',language='en')
|
||||
self.assertEqual(en[2],'Unsupported Proxmox suite; no repository changed.\n')
|
||||
pve=self.run_policy('unsupported',product='pve')
|
||||
self.assertEqual(pve[2],en[2])
|
||||
for mode,expected in [('plan','offer\n'),('apply','changed\n')]:
|
||||
result,out,error,*_=self.run_policy('offer',mode=mode)
|
||||
self.assertEqual((result,out,error),(0,expected,''))
|
||||
result,out,error,calls,*_=self.run_policy('preserve',mode='apply')
|
||||
self.assertEqual((result,out,error),(0,'preserve\n',''))
|
||||
self.assertFalse(any(c[0]=='proxmox-backup-manager' or 'set' in c or 'create' in c for c in calls))
|
||||
|
||||
def test_bad_or_missing_translation_falls_back_to_original_english(self):
|
||||
for catalog in ('{}','not json','[]',json.dumps({'Unsupported Proxmox suite; no repository changed.':42}),
|
||||
json.dumps({'Unsupported Proxmox suite; no repository changed.':'{unknown}'})):
|
||||
with self.subTest(catalog=catalog):
|
||||
result,out,error,calls,*_=self.run_policy('unsupported',malformed=catalog)
|
||||
self.assertEqual(result,1);self.assertEqual(out,'');self.assertEqual(calls,[])
|
||||
self.assertEqual(error,'Unsupported Proxmox suite; no repository changed.\n')
|
||||
|
||||
def test_translation_cannot_drop_actual_repository_details(self):
|
||||
spec=importlib.util.spec_from_file_location('policy_format_fixture',ROOT/'scripts/global/repository_policy.py')
|
||||
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
||||
with tempfile.TemporaryDirectory(prefix='pbs-policy-format-') as folder:
|
||||
module.BASE=Path(folder);(module.BASE/'lang').mkdir()
|
||||
(module.BASE/'config.json').write_text('{"language":"sk"}')
|
||||
error=module.PolicyError('Inspect {ui}; no repository changed.',ui='Administration > Repositories')
|
||||
for invalid in ('Skontrolujte repozitáre.','{ui.__class__}','{other}','{ui:.0}','{ui!r}'):
|
||||
(module.BASE/'lang/sk.json').write_text(json.dumps({error.template:invalid}))
|
||||
self.assertEqual(module.cli_error(error,'pbs'),str(error))
|
||||
for invalid in ('not json','[]','{"language":"../../private"}'):
|
||||
(module.BASE/'config.json').write_text(invalid)
|
||||
self.assertEqual(module.cli_error(error,'pbs'),str(error))
|
||||
|
||||
|
||||
UPDATE_HARNESS=r'''
|
||||
BASE_DIR="$FIXTURE"
|
||||
LANG_DIR="$REPO/lang"
|
||||
SCRIPT_TITLE='fixture'
|
||||
TAB='' BGN='' CL='' GN='' BL=''
|
||||
mkdir -p "$FIXTURE/log" "$FIXTURE/tmp" "$FIXTURE/boot" "$FIXTURE/run"
|
||||
: > "$FIXTURE/calls"
|
||||
record() { printf '%s\n' "$*" >> "$FIXTURE/calls"; }
|
||||
pmx_journal_context() { :; }
|
||||
pmx_is_pbs() { [[ "$CASE" != pve ]]; }
|
||||
pmx_platform_field() { [[ "$1" != supported ]] && printf '%s' "$SUITE" || printf true; }
|
||||
ensure_pbs_repositories() { record repository-plan; return 0; }
|
||||
dpkg-query() { [[ "$SUITE" == bookworm ]] && printf 3.4.0 || printf 4.0.1; }
|
||||
apt-cache() { record apt-cache "$@"; printf ' Candidate: 4.0.2\n'; }
|
||||
df() { printf 'Filesystem 1K-blocks Used Available Use%% Mounted\nfixture 10000 1000 %s 10%% /\n' "$([[ "$CASE" == disk ]] && echo 512000 || echo 4096000)"; }
|
||||
apt-get() { record apt-get "$@"; [[ "$CASE" != refresh ]]; }
|
||||
apt() {
|
||||
record apt "$@"
|
||||
if [[ "$*" == 'list --upgradable' ]]; then
|
||||
printf 'Listing...\n'
|
||||
[[ "$CASE" == current ]] || printf 'fixture/trixie 1.2 amd64 [upgradable from: 1.1]\n'
|
||||
return 0
|
||||
fi
|
||||
[[ "$CASE" != failure ]]
|
||||
}
|
||||
fixture_env() { "$@"; }
|
||||
APT_ENV='fixture_env'
|
||||
pmx_format_upgradable() { cat; }
|
||||
pmx_record_execution() { record journal "$1"; }
|
||||
pmx_dpkg_changes_since() { :; }
|
||||
msg_error() { printf '%s\n' "$*"; }
|
||||
msg_ok() { printf '%s\n' "$*"; }
|
||||
msg_info2() { printf '%s\n' "$*"; }
|
||||
msg_warn() { printf '%s\n' "$*"; }
|
||||
msg_title() { :; }
|
||||
clear() { :; }
|
||||
show_proxmenux_logo() { :; }
|
||||
whiptail() { printf '%s\n' "$*" >> "$FIXTURE/dialog"; [[ "$CASE" != cancel ]]; }
|
||||
uname() { printf old-kernel; }
|
||||
touch "$FIXTURE/boot/vmlinuz-new-kernel"
|
||||
'''
|
||||
|
||||
|
||||
class UpdateCliTests(unittest.TestCase):
|
||||
def run_update(self,case,language='sk',suite='trixie'):
|
||||
with tempfile.TemporaryDirectory(prefix='pbs-update-i18n-') as folder:
|
||||
body=function('scripts/global/update-pbs.sh','update_pbs')
|
||||
for old,new in [('/var/log/',folder+'/log/'),('/tmp/proxmenux_screen_capture_',folder+'/tmp/proxmenux_screen_capture_'),('/var/run/',folder+'/run/'),('/boot/',folder+'/boot/')]: body=body.replace(old,new)
|
||||
script=UPDATE_HARNESS+function('scripts/utils.sh','translate')+body+'\nupdate_pbs\n'
|
||||
result=subprocess.run(['bash','-c',script],capture_output=True,text=True,timeout=15,
|
||||
env={**os.environ,'REPO':str(ROOT),'FIXTURE':folder,'CASE':case,'LANGUAGE':language,'SUITE':suite})
|
||||
files={str(p.relative_to(folder)):p.read_text() for p in Path(folder).rglob('*') if p.is_file()}
|
||||
return result,files
|
||||
|
||||
def test_pve_guard_cannot_reach_repository_or_apt_calls(self):
|
||||
result,files=self.run_update('pve')
|
||||
self.assertEqual(result.returncode,1,result.stderr)
|
||||
self.assertEqual(files['calls'],'')
|
||||
self.assertFalse(any(name.startswith('log/') for name in files))
|
||||
|
||||
def test_disk_and_upgrade_errors_keep_their_actual_parameters(self):
|
||||
result,files=self.run_update('disk')
|
||||
self.assertEqual(result.returncode,1,result.stderr)
|
||||
self.assertIn('Nedostatok miesta na disku. Dostupné:',result.stdout)
|
||||
self.assertIn('500MB',result.stdout)
|
||||
self.assertNotIn('apt',files['calls'])
|
||||
result,files=self.run_update('failure')
|
||||
self.assertEqual(result.returncode,1,result.stderr)
|
||||
self.assertIn('Aktualizácia systému zlyhala. Skontrolujte denník:',result.stdout)
|
||||
self.assertIn('/log/proxmox-update-',result.stdout)
|
||||
self.assertNotIn('autoremove',files['calls'])
|
||||
|
||||
def test_update_status_cancel_refresh_error_and_reboot_notice(self):
|
||||
for suite in ('bookworm','trixie'):
|
||||
for case in ('current','cancel','refresh','success'):
|
||||
with self.subTest(suite=suite,case=case):
|
||||
result,files=self.run_update(case,suite=suite)
|
||||
self.assertEqual(result.returncode,1 if case=='refresh' else 0,result.stderr)
|
||||
if case!='success': self.assertNotIn('full-upgrade',files['calls'])
|
||||
if case=='success': self.assertIn('reštartujte server',result.stdout)
|
||||
if case in ('current','cancel'): self.assertIn('Aktuálna verzia PBS',files['dialog'])
|
||||
|
||||
|
||||
class DialogTests(unittest.TestCase):
|
||||
def test_automated_confirmation_and_banner_cancel_do_not_run_actions(self):
|
||||
cases=[('scripts/menus/menu_post_install.sh','confirm_automated_script',
|
||||
['repozitár no-subscription','upozornenie na predplatné','pamäť','jadro','sieť']),
|
||||
('scripts/post_install/auto_post_install.sh','remove_subscription_banner',
|
||||
['Proxmox Backup Server – odstránenie upozornenia na predplatné'])]
|
||||
for path,name,expected in cases:
|
||||
with tempfile.TemporaryDirectory(prefix='pbs-dialog-') as folder:
|
||||
script=function('scripts/utils.sh','translate')+function(path,name)+r'''
|
||||
PMX_ON_PBS=true
|
||||
LOCAL_SCRIPTS='/fixture/scripts'
|
||||
clear() { :; }; sleep() { :; }; stop_spinner() { :; }
|
||||
msg_warn() { printf '%s\n' "$*"; }
|
||||
bash() { printf forbidden >> "$CAPTURE/actions"; }
|
||||
register_tool() { printf forbidden >> "$CAPTURE/actions"; }
|
||||
dialog() { printf '%s\n' "$*" >> "$CAPTURE/dialog"; return 1; }
|
||||
whiptail() { dialog "$@"; }
|
||||
'''+name+'\n'
|
||||
result=subprocess.run(['bash','-c',script],capture_output=True,text=True,timeout=15,
|
||||
env={**os.environ,'LANGUAGE':'sk','LANG_DIR':str(ROOT/'lang'),'CAPTURE':folder})
|
||||
self.assertEqual(result.returncode,1 if name=='remove_subscription_banner' else 0,result.stderr)
|
||||
dialog=(Path(folder)/'dialog').read_text()
|
||||
for text in expected:self.assertIn(text,dialog)
|
||||
self.assertFalse((Path(folder)/'actions').exists())
|
||||
if name=='remove_subscription_banner':self.assertIn('bolo zrušené používateľom',result.stdout)
|
||||
|
||||
def test_community_labels_preserve_dispatch_names_and_translate_display(self):
|
||||
script=function('scripts/utils.sh','translate')+function('scripts/menus/menu_post_install.sh','post_install_label')+'\npost_install_label "$1"\n'
|
||||
for text,expected in [('Proxmox Backup Server Post Install','nastavenie po inštalácii'),
|
||||
('Proxmox Backup Server Microcode','mikrokód procesora')]:
|
||||
result=subprocess.run(['bash','-c',script,'bash',text],capture_output=True,text=True,timeout=15,
|
||||
env={**os.environ,'LANGUAGE':'sk','LANG_DIR':str(ROOT/'lang')})
|
||||
self.assertEqual(result.returncode,0,result.stderr);self.assertIn(expected,result.stdout)
|
||||
|
||||
def test_failed_apt_hook_check_removes_only_fixture_hook_with_localized_warning(self):
|
||||
with tempfile.TemporaryDirectory(prefix='pbs-hook-') as folder:
|
||||
script=function('scripts/utils.sh','translate')+function('scripts/global/remove-banner-pve-v3.sh','create_apt_hook')+r'''
|
||||
apt-config() { return 1; }
|
||||
msg_warn() { printf '%s\n' "$*"; }
|
||||
create_apt_hook
|
||||
'''
|
||||
result=subprocess.run(['bash','-c',script],capture_output=True,text=True,timeout=15,
|
||||
env={**os.environ,'LANGUAGE':'sk','LANG_DIR':str(ROOT/'lang'),'APT_HOOK':folder+'/hook'})
|
||||
self.assertEqual(result.returncode,0,result.stderr)
|
||||
self.assertIn('Nesprávna syntax hooku APT',result.stdout)
|
||||
self.assertFalse((Path(folder)/'hook').exists())
|
||||
@@ -0,0 +1,168 @@
|
||||
"""Render the real ZRAM CLI paths in isolated files with host effects stubbed."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
DETAILS = {
|
||||
"version": ("Cannot determine installed systemd-zram-generator version", "Nepodarilo sa zistiť verziu nainštalovaného balíka systemd-zram-generator"),
|
||||
"mismatch": ("Existing ZRAM profile is active but does not match its configuration", "Existujúci profil ZRAM je aktívny, ale nezodpovedá svojej konfigurácii"),
|
||||
"stop": ("Could not stop existing dev-zram0.swap", "Nepodarilo sa zastaviť existujúcu jednotku dev-zram0.swap"),
|
||||
"directory": ("Could not create ZRAM generator directory", "Nepodarilo sa vytvoriť adresár pre konfiguráciu generátora ZRAM"),
|
||||
"config": ("Could not write ZRAM generator configuration", "Nepodarilo sa zapísať konfiguráciu generátora ZRAM"),
|
||||
"marker": ("Could not write ZRAM package ownership state", "Nepodarilo sa zapísať údaj o tom, či balík ZRAM nainštaloval ProxMenux"),
|
||||
"start": ("Could not activate dev-zram0.swap", "Nepodarilo sa aktivovať jednotku dev-zram0.swap"),
|
||||
"verify": ("Active ZRAM algorithm, capacity or swap sources did not match the profile", "Algoritmus kompresie, veľkosť alebo zdroje swapu aktívnej ZRAM nezodpovedajú profilu"),
|
||||
}
|
||||
REFUSAL = "Another ZRAM generator configuration exists; refusing to overwrite it:"
|
||||
REFUSAL_SK = "Existuje iná konfigurácia generátora ZRAM; neprepíše sa:"
|
||||
|
||||
|
||||
def function(path, name):
|
||||
text = path.read_text()
|
||||
start = text.index(name + "() {")
|
||||
return text[start:text.index("\n}\n", start) + 3]
|
||||
|
||||
|
||||
HARNESS = r'''
|
||||
set -u
|
||||
BASE_DIR="$FIXTURE/state"
|
||||
LANG_DIR="$REPO/lang"
|
||||
mkdir -p "$BASE_DIR" "$FIXTURE/etc/systemd/zram-generator.conf.d" "$FIXTURE/proc"
|
||||
CONFIG="$FIXTURE/etc/systemd/zram-generator.conf.d/90-proxmenux.conf"
|
||||
MARKER="$BASE_DIR/zram-swap.state"
|
||||
printf 'Filename\tType\tSize\tUsed\tPriority\n' > "$FIXTURE/proc/swaps"
|
||||
printf '/swapfile none swap sw 0 0\n' > "$FIXTURE/etc/fstab"
|
||||
: > "$FIXTURE/calls"
|
||||
record() { printf '%s\n' "$*" >> "$FIXTURE/calls"; }
|
||||
pmx_journal_context() { :; }
|
||||
msg_error() { printf '%s\n' "$*"; }
|
||||
msg_success() { printf '%s\n' "$*"; }
|
||||
register_tool() { record register "$@"; }
|
||||
pmx_install_pkg() { record package-install; return 1; }
|
||||
apt-get() { record apt "$@"; return 1; }
|
||||
dpkg-query() {
|
||||
record dpkg-query
|
||||
case "$*" in
|
||||
*'${Status}'*) printf 'install ok installed\n';;
|
||||
*'${Version}'*) [[ "$CASE" == version ]] || printf '1.2.0-1\n';;
|
||||
esac
|
||||
}
|
||||
dpkg() { return 0; }
|
||||
install() { record directory; [[ "$CASE" != directory ]]; }
|
||||
pmx_write_file() {
|
||||
record write "$1"
|
||||
if [[ "$CASE" == config && "$1" == "$CONFIG" ]] ||
|
||||
[[ "$CASE" == marker && "$1" == "$MARKER" ]]; then
|
||||
cat >/dev/null
|
||||
return 1
|
||||
fi
|
||||
cat > "$1"
|
||||
}
|
||||
pmx_remove_file() { record remove "$1"; rm -f -- "$1"; }
|
||||
pmx_record_uninstall() { record record-uninstall; }
|
||||
swapoff() { record swapoff; return 0; }
|
||||
systemctl() {
|
||||
record systemctl "$@"
|
||||
[[ "$CASE" != start || "$1" != start ]] &&
|
||||
[[ "$CASE" != stop || "$1" != stop ]]
|
||||
}
|
||||
source "$REPO/scripts/post_install/zram_profile_helpers.sh"
|
||||
zram_profile_verify_active() { [[ "$CASE" != verify && "$CASE" != mismatch ]]; }
|
||||
if [[ "$CASE" == mismatch || "$CASE" == stop ]]; then
|
||||
printf '# ProxMenux ZRAM profile\n[zram0]\nzram-size = ram / 10\n' > "$CONFIG"
|
||||
[[ "$CASE" != mismatch ]] || zram_render_generator_config true > "$CONFIG"
|
||||
printf 'installed_by_proxmenux=false\n' > "$MARKER"
|
||||
printf '/dev/zram0\tpartition\t3355443\t8\t32767\n' >> "$FIXTURE/proc/swaps"
|
||||
fi
|
||||
if [[ "$CASE" == foreign ]]; then
|
||||
printf '[zram1]\nzram-size = ram / 5\n' > "$FIXTURE/etc/systemd/zram-generator.conf"
|
||||
fi
|
||||
if [[ "$CASE" == unreadable ]]; then
|
||||
zram_foreign_generator_config() { return 2; }
|
||||
fi
|
||||
'''
|
||||
|
||||
|
||||
class ZramLocalizationTests(unittest.TestCase):
|
||||
def test_missing_optional_translation_uses_original_english(self):
|
||||
with tempfile.TemporaryDirectory(prefix="zram-fallback-") as folder:
|
||||
(Path(folder) / 'xx.json').write_text('{}')
|
||||
script = function(ROOT / "scripts/utils.sh", "translate") + '\ntranslate "$1"\n'
|
||||
result = subprocess.run(["bash", "-c", script, "bash", DETAILS['version'][0]],
|
||||
text=True, capture_output=True, timeout=15,
|
||||
env={**os.environ, 'LANGUAGE': 'xx', 'LANG_DIR': folder})
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout.strip(), DETAILS['version'][0])
|
||||
|
||||
def run_case(self, case, language="sk"):
|
||||
with tempfile.TemporaryDirectory(prefix="zram-i18n-") as folder:
|
||||
directory = Path(folder)
|
||||
installer = function(ROOT / "scripts/post_install/customizable_post_install.sh", "configure_zram_swap")
|
||||
installer = installer.replace("/etc/systemd/zram-generator.conf.d/90-proxmenux.conf", folder + "/etc/systemd/zram-generator.conf.d/90-proxmenux.conf")
|
||||
installer = installer.replace("/proc/swaps", folder + "/proc/swaps").replace("/etc/fstab", folder + "/etc/fstab")
|
||||
installer = installer.replace('zram_foreign_generator_config "$config"', 'zram_foreign_generator_config "$config" "$FIXTURE"')
|
||||
installer = installer.replace('zram_list_foreign_systemd_zram_units)', 'zram_list_foreign_systemd_zram_units "$FIXTURE")')
|
||||
script = HARNESS + function(ROOT / "scripts/utils.sh", "translate") + installer + "\nconfigure_zram_swap\n"
|
||||
result = subprocess.run(["bash", "-c", script], text=True, capture_output=True,
|
||||
env={**os.environ, "REPO": str(ROOT), "FIXTURE": folder, "CASE": case, "LANGUAGE": language}, timeout=15)
|
||||
calls = (directory / "calls").read_text().splitlines()
|
||||
files = {str(p.relative_to(directory)): p.read_text() for p in directory.rglob("*") if p.is_file()}
|
||||
return result, calls, files
|
||||
|
||||
def test_all_activation_details_are_localized_and_english_is_preserved(self):
|
||||
for case, (english, slovak) in DETAILS.items():
|
||||
for language, expected in (("sk", slovak), ("en", english)):
|
||||
with self.subTest(case=case, language=language):
|
||||
result, calls, files = self.run_case(case, language)
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
self.assertIn(expected, result.stdout)
|
||||
self.assertEqual(files["etc/fstab"], "/swapfile none swap sw 0 0\n")
|
||||
if case in ("version", "mismatch"):
|
||||
self.assertTrue(all(call == "dpkg-query" for call in calls), calls)
|
||||
elif case == "stop":
|
||||
self.assertEqual(files["state/zram-swap.state"], "installed_by_proxmenux=false\n")
|
||||
self.assertIn("# ProxMenux ZRAM profile", files["etc/systemd/zram-generator.conf.d/90-proxmenux.conf"])
|
||||
else:
|
||||
self.assertNotIn("state/zram-swap.state", files)
|
||||
self.assertNotIn("etc/systemd/zram-generator.conf.d/90-proxmenux.conf", files)
|
||||
self.assertFalse(any(call.startswith(("apt ", "package-install", "register ")) for call in calls), calls)
|
||||
|
||||
def test_foreign_generator_is_refused_in_both_languages_without_any_effect(self):
|
||||
for language, expected in (("sk", REFUSAL_SK), ("en", REFUSAL)):
|
||||
result, calls, files = self.run_case("foreign", language)
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
self.assertIn(expected, result.stdout)
|
||||
self.assertIn("/etc/systemd/zram-generator.conf", result.stdout)
|
||||
self.assertEqual(calls, [])
|
||||
self.assertEqual(files["etc/systemd/zram-generator.conf"], "[zram1]\nzram-size = ram / 5\n")
|
||||
self.assertNotIn("state/zram-swap.state", files)
|
||||
self.assertNotIn("etc/systemd/zram-generator.conf.d/90-proxmenux.conf", files)
|
||||
self.assertEqual(files["etc/fstab"], "/swapfile none swap sw 0 0\n")
|
||||
self.assertEqual(files["proc/swaps"], "Filename\tType\tSize\tUsed\tPriority\n")
|
||||
|
||||
def test_failed_inspection_is_not_mistaken_for_no_conflict(self):
|
||||
result, calls, files = self.run_case("unreadable")
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
self.assertIn("Nie je možné skontrolovať existujúcu konfiguráciu generátora ZRAM", result.stdout)
|
||||
self.assertEqual(calls, [])
|
||||
self.assertNotIn("state/zram-swap.state", files)
|
||||
self.assertNotIn("etc/systemd/zram-generator.conf.d/90-proxmenux.conf", files)
|
||||
|
||||
def test_uninstall_label_and_cancel_do_not_call_uninstall(self):
|
||||
for language, label in (("sk", "Profil swapu ZRAM"), ("en", "ZRAM swap profile")):
|
||||
with tempfile.TemporaryDirectory(prefix="zram-menu-") as folder:
|
||||
tools = Path(folder) / "tools.json"
|
||||
tools.write_text('{"zram_swap":true}')
|
||||
script = function(ROOT / "scripts/utils.sh", "translate")
|
||||
script += function(ROOT / "scripts/post_install/uninstall-tools.sh", "show_uninstall_menu")
|
||||
script += '\nensure_tools_json() { :; }; migrate_installed_tools() { :; }\nuninstall_zram_swap() { printf forbidden; return 1; }\ndialog() { printf "%s\\n" "$@" > "$MENU_CAPTURE"; return 1; }\nshow_uninstall_menu\n'
|
||||
result = subprocess.run(["bash", "-c", script], text=True, capture_output=True,
|
||||
env={**os.environ, "LANGUAGE": language, "LANG_DIR": str(ROOT / "lang"), "TOOLS_JSON": str(tools), "MENU_CAPTURE": folder + "/dialog.txt"}, timeout=15)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn(label, (Path(folder) / "dialog.txt").read_text())
|
||||
self.assertNotIn("forbidden", result.stdout + result.stderr)
|
||||
self.assertEqual(json.loads(tools.read_text()), {"zram_swap": True})
|
||||
Reference in New Issue
Block a user