fix(git): fix 17 missing async/await bugs + UX overhaul
Backend: - fix 17 missing 'await' in git route handlers causing silent no-ops (branches, diff, history, commit, push, pull, merge, promote, sync) - fix async coroutine passed to run_blocking in git_plugin.py Frontend: - add collapsible 'How it works' onboarding (GitHelpPanel) - add status legend with color-coded repository statuses - i18n: add 50+ missing keys, replace hardcoded strings - add Refresh button in modal header - add PROD deploy confirmation dialog (replaces browser prompt()) - add CommitHistory to workspace tab with timeline nodes - add post-commit success banner with next-step guidance - increase success toast duration to 8s - group local/remote branches in selector (optgroup) - format last_modified dates timezone-aware - change PROD badge from red to neutral indigo - extract shared resolveGitStatusToken to git-utils.ts - fix 'slug' label regression - remove dead init_repo_button key UI/UX audit fixes: - add descriptions to Create/Init buttons in init panel - add actionable CTA to server mismatch warning - improve checkbox text phrasing
This commit is contained in:
@@ -30,28 +30,28 @@ def svc(mock_cm):
|
||||
|
||||
|
||||
class TestEncryptionEdgeCases:
|
||||
def test_encrypt_encryption_none_fallback(self, svc):
|
||||
"""When _get_encryption returns None, password passes through."""
|
||||
def test_encrypt_encryption_none_raises(self, svc):
|
||||
"""_get_encryption returns None → RuntimeError (no silent plaintext)."""
|
||||
with patch.object(svc, '_get_encryption', return_value=None):
|
||||
result = svc._encrypt_password("my_pass")
|
||||
assert result == "my_pass"
|
||||
with pytest.raises(RuntimeError, match="ENCRYPTION_KEY not available"):
|
||||
svc._encrypt_password("my_pass")
|
||||
|
||||
def test_encrypt_exception_fallback(self, svc):
|
||||
"""Encryption raises exception → password passes through."""
|
||||
def test_encrypt_exception_raises(self, svc):
|
||||
"""Encryption raises exception → RuntimeError (no silent plaintext)."""
|
||||
enc = MagicMock()
|
||||
enc.encrypt.side_effect = RuntimeError("crypto fail")
|
||||
with patch.object(svc, '_get_encryption', return_value=enc):
|
||||
result = svc._encrypt_password("my_pass")
|
||||
assert result == "my_pass"
|
||||
with pytest.raises(RuntimeError, match="encryption failed"):
|
||||
svc._encrypt_password("my_pass")
|
||||
|
||||
def test_decrypt_none_fallback(self, svc):
|
||||
"""_get_encryption returns None → password passes through."""
|
||||
def test_decrypt_none_raises(self, svc):
|
||||
"""_get_encryption returns None → RuntimeError (no silent plaintext)."""
|
||||
with patch.object(svc, '_get_encryption', return_value=None):
|
||||
result = svc._decrypt_password("encrypted")
|
||||
assert result == "encrypted"
|
||||
with pytest.raises(RuntimeError, match="ENCRYPTION_KEY not available"):
|
||||
svc._decrypt_password("encrypted")
|
||||
|
||||
def test_decrypt_exception_fallback(self, svc):
|
||||
"""Decryption raises → password passes through."""
|
||||
"""Non-Fernet value with encryption available → returned as-is (legacy plaintext)."""
|
||||
enc = MagicMock()
|
||||
enc.decrypt.side_effect = RuntimeError("decrypt fail")
|
||||
with patch.object(svc, '_get_encryption', return_value=enc):
|
||||
@@ -66,11 +66,11 @@ class TestEncryptionEdgeCases:
|
||||
assert result == "********"
|
||||
enc.decrypt.assert_not_called()
|
||||
|
||||
def test_decrypt_masked_get_encryption_none(self, svc):
|
||||
"""MASKED_PASSWORD path even when encryption is None."""
|
||||
def test_decrypt_masked_get_encryption_none_raises(self, svc):
|
||||
"""MASKED_PASSWORD with encryption None → RuntimeError (encryption required)."""
|
||||
with patch.object(svc, '_get_encryption', return_value=None):
|
||||
result = svc._decrypt_password("********")
|
||||
assert result == "********"
|
||||
with pytest.raises(RuntimeError, match="ENCRYPTION_KEY not available"):
|
||||
svc._decrypt_password("********")
|
||||
|
||||
|
||||
class TestUnsupportedDialect:
|
||||
|
||||
Reference in New Issue
Block a user