Enhance entry addition process: implement immediate file save after adding an entry and handle save failure by removing the entry for improved reliability.
This commit is contained in:
parent
6171e0ca0b
commit
77d4a2e955
3 changed files with 84 additions and 9 deletions
|
@ -296,7 +296,18 @@ class HostsManager:
|
|||
try:
|
||||
# Add the new entry at the end
|
||||
hosts_file.entries.append(entry)
|
||||
return True, "Entry added successfully"
|
||||
|
||||
# Save the file immediately
|
||||
save_success, save_message = self.save_hosts_file(hosts_file)
|
||||
if not save_success:
|
||||
# If save fails, remove the entry that was just added
|
||||
hosts_file.entries.pop()
|
||||
return False, f"Failed to save after adding entry: {save_message}"
|
||||
|
||||
canonical_hostname = (
|
||||
entry.hostnames[0] if entry.hostnames else entry.ip_address
|
||||
)
|
||||
return True, f"Entry added: {canonical_hostname}"
|
||||
|
||||
except Exception as e:
|
||||
return False, f"Error adding entry: {e}"
|
||||
|
|
|
@ -7,7 +7,7 @@ all the handlers and provides the primary user interface.
|
|||
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.containers import Horizontal, Vertical
|
||||
from textual.widgets import Header, Static, DataTable, Input, Checkbox, Label
|
||||
from textual.widgets import Header, Static, DataTable, Input, Checkbox
|
||||
from textual.reactive import reactive
|
||||
|
||||
from ..core.parser import HostsParser
|
||||
|
@ -94,7 +94,9 @@ class HostsManagerApp(App):
|
|||
|
||||
# Details display form (disabled inputs)
|
||||
with Vertical(id="entry-details-display", classes="entry-form"):
|
||||
with Vertical(classes="default-section section-no-top-margin") as ip_address:
|
||||
with Vertical(
|
||||
classes="default-section section-no-top-margin"
|
||||
) as ip_address:
|
||||
ip_address.border_title = "IP Address"
|
||||
yield Input(
|
||||
placeholder="No entry selected",
|
||||
|
@ -118,18 +120,23 @@ class HostsManagerApp(App):
|
|||
placeholder="No entry selected",
|
||||
id="details-comment-input",
|
||||
disabled=True,
|
||||
classes="default-input",
|
||||
)
|
||||
classes="default-input",
|
||||
)
|
||||
|
||||
with Vertical(classes="default-section") as active:
|
||||
active.border_title = "Active"
|
||||
yield Checkbox(
|
||||
"Active", id="details-active-checkbox", disabled=True, classes="default-checkbox"
|
||||
"Active",
|
||||
id="details-active-checkbox",
|
||||
disabled=True,
|
||||
classes="default-checkbox",
|
||||
)
|
||||
|
||||
# Edit form (initially hidden)
|
||||
with Vertical(id="entry-edit-form", classes="entry-form hidden"):
|
||||
with Vertical(classes="default-section section-no-top-margin") as ip_address:
|
||||
with Vertical(
|
||||
classes="default-section section-no-top-margin"
|
||||
) as ip_address:
|
||||
ip_address.border_title = "IP Address"
|
||||
yield Input(
|
||||
placeholder="Enter IP address",
|
||||
|
@ -151,11 +158,13 @@ class HostsManagerApp(App):
|
|||
placeholder="Enter comment (optional)",
|
||||
id="comment-input",
|
||||
classes="default-input",
|
||||
)
|
||||
)
|
||||
|
||||
with Vertical(classes="default-section") as active:
|
||||
active.border_title = "Active"
|
||||
yield Checkbox("Active", id="active-checkbox", classes="default-checkbox")
|
||||
yield Checkbox(
|
||||
"Active", id="active-checkbox", classes="default-checkbox"
|
||||
)
|
||||
|
||||
# Status bar for error/temporary messages (overlay, doesn't affect layout)
|
||||
yield Static("", id="status-bar", classes="status-bar hidden")
|
||||
|
|
|
@ -634,3 +634,58 @@ class TestHostsManager:
|
|||
finally:
|
||||
# Clean up
|
||||
Path(temp_path).unlink()
|
||||
|
||||
def test_add_entry_success(self):
|
||||
"""Test successfully adding an entry with immediate save."""
|
||||
manager = HostsManager()
|
||||
manager.edit_mode = True
|
||||
|
||||
# Mock the save operation to succeed
|
||||
with patch.object(manager, "save_hosts_file") as mock_save:
|
||||
mock_save.return_value = (True, "File saved successfully")
|
||||
|
||||
hosts_file = HostsFile()
|
||||
test_entry = HostEntry(ip_address="192.168.1.100", hostnames=["testhost"])
|
||||
|
||||
success, message = manager.add_entry(hosts_file, test_entry)
|
||||
|
||||
assert success
|
||||
assert "Entry added: testhost" in message
|
||||
assert len(hosts_file.entries) == 1
|
||||
assert hosts_file.entries[0] == test_entry
|
||||
mock_save.assert_called_once_with(hosts_file)
|
||||
|
||||
def test_add_entry_save_failure(self):
|
||||
"""Test adding an entry when save fails."""
|
||||
manager = HostsManager()
|
||||
manager.edit_mode = True
|
||||
|
||||
# Mock the save operation to fail
|
||||
with patch.object(manager, "save_hosts_file") as mock_save:
|
||||
mock_save.return_value = (False, "Permission denied")
|
||||
|
||||
hosts_file = HostsFile()
|
||||
test_entry = HostEntry(ip_address="192.168.1.100", hostnames=["testhost"])
|
||||
|
||||
success, message = manager.add_entry(hosts_file, test_entry)
|
||||
|
||||
assert not success
|
||||
assert "Failed to save after adding entry" in message
|
||||
assert (
|
||||
len(hosts_file.entries) == 0
|
||||
) # Entry should be removed on save failure
|
||||
mock_save.assert_called_once_with(hosts_file)
|
||||
|
||||
def test_add_entry_not_in_edit_mode(self):
|
||||
"""Test adding an entry when not in edit mode."""
|
||||
manager = HostsManager()
|
||||
# edit_mode defaults to False
|
||||
|
||||
hosts_file = HostsFile()
|
||||
test_entry = HostEntry(ip_address="192.168.1.100", hostnames=["testhost"])
|
||||
|
||||
success, message = manager.add_entry(hosts_file, test_entry)
|
||||
|
||||
assert not success
|
||||
assert "Not in edit mode" in message
|
||||
assert len(hosts_file.entries) == 0
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue