Compare commits
No commits in common. "77d4a2e95530a14774ba03d5b5fb7b00c3096ec5" and "adc40fc16aa97557de7cb65b04be682f74e744cd" have entirely different histories.
77d4a2e955
...
adc40fc16a
8 changed files with 110 additions and 242 deletions
|
@ -296,18 +296,7 @@ class HostsManager:
|
||||||
try:
|
try:
|
||||||
# Add the new entry at the end
|
# Add the new entry at the end
|
||||||
hosts_file.entries.append(entry)
|
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:
|
except Exception as e:
|
||||||
return False, f"Error adding entry: {e}"
|
return False, f"Error adding entry: {e}"
|
||||||
|
@ -342,19 +331,15 @@ class HostsManager:
|
||||||
|
|
||||||
# Remove the entry
|
# Remove the entry
|
||||||
deleted_entry = hosts_file.entries.pop(index)
|
deleted_entry = hosts_file.entries.pop(index)
|
||||||
canonical_hostname = (
|
canonical_hostname = deleted_entry.hostnames[0] if deleted_entry.hostnames else deleted_entry.ip_address
|
||||||
deleted_entry.hostnames[0]
|
|
||||||
if deleted_entry.hostnames
|
|
||||||
else deleted_entry.ip_address
|
|
||||||
)
|
|
||||||
|
|
||||||
# Save the file immediately
|
# Save the file immediately
|
||||||
save_success, save_message = self.save_hosts_file(hosts_file)
|
save_success, save_message = self.save_hosts_file(hosts_file)
|
||||||
if not save_success:
|
if not save_success:
|
||||||
# If save fails, restore the entry
|
# If save fails, restore the entry
|
||||||
hosts_file.entries.insert(index, deleted_entry)
|
hosts_file.entries.insert(index, deleted_entry)
|
||||||
return False, f"Failed to save after deletion: {save_message}"
|
return False, f"Failed to save after deletion: {save_message}"
|
||||||
|
|
||||||
return True, f"Entry deleted: {canonical_hostname}"
|
return True, f"Entry deleted: {canonical_hostname}"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
@ -7,7 +7,7 @@ all the handlers and provides the primary user interface.
|
||||||
|
|
||||||
from textual.app import App, ComposeResult
|
from textual.app import App, ComposeResult
|
||||||
from textual.containers import Horizontal, Vertical
|
from textual.containers import Horizontal, Vertical
|
||||||
from textual.widgets import Header, Static, DataTable, Input, Checkbox
|
from textual.widgets import Header, Static, DataTable, Input, Checkbox, Label
|
||||||
from textual.reactive import reactive
|
from textual.reactive import reactive
|
||||||
|
|
||||||
from ..core.parser import HostsParser
|
from ..core.parser import HostsParser
|
||||||
|
@ -91,80 +91,24 @@ class HostsManagerApp(App):
|
||||||
# Right pane - entry details or edit form
|
# Right pane - entry details or edit form
|
||||||
with Vertical(classes="common-pane right-pane") as right_pane:
|
with Vertical(classes="common-pane right-pane") as right_pane:
|
||||||
right_pane.border_title = "Entry Details"
|
right_pane.border_title = "Entry Details"
|
||||||
|
yield DataTable(
|
||||||
# Details display form (disabled inputs)
|
id="entry-details-table",
|
||||||
with Vertical(id="entry-details-display", classes="entry-form"):
|
show_header=False,
|
||||||
with Vertical(
|
show_cursor=False,
|
||||||
classes="default-section section-no-top-margin"
|
disabled=True,
|
||||||
) as ip_address:
|
)
|
||||||
ip_address.border_title = "IP Address"
|
|
||||||
yield Input(
|
|
||||||
placeholder="No entry selected",
|
|
||||||
id="details-ip-input",
|
|
||||||
disabled=True,
|
|
||||||
classes="default-input",
|
|
||||||
)
|
|
||||||
|
|
||||||
with Vertical(classes="default-section") as hostnames:
|
|
||||||
hostnames.border_title = "Hostnames (comma-separated)"
|
|
||||||
yield Input(
|
|
||||||
placeholder="No entry selected",
|
|
||||||
id="details-hostname-input",
|
|
||||||
disabled=True,
|
|
||||||
classes="default-input",
|
|
||||||
)
|
|
||||||
|
|
||||||
with Vertical(classes="default-section") as comment:
|
|
||||||
comment.border_title = "Comment:"
|
|
||||||
yield Input(
|
|
||||||
placeholder="No entry selected",
|
|
||||||
id="details-comment-input",
|
|
||||||
disabled=True,
|
|
||||||
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",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Edit form (initially hidden)
|
# Edit form (initially hidden)
|
||||||
with Vertical(id="entry-edit-form", classes="entry-form hidden"):
|
with Vertical(id="entry-edit-form", classes="hidden"):
|
||||||
with Vertical(
|
yield Label("IP Address:")
|
||||||
classes="default-section section-no-top-margin"
|
yield Input(placeholder="Enter IP address", id="ip-input")
|
||||||
) as ip_address:
|
yield Label("Hostnames (comma-separated):")
|
||||||
ip_address.border_title = "IP Address"
|
yield Input(placeholder="Enter hostnames", id="hostname-input")
|
||||||
yield Input(
|
yield Label("Comment:")
|
||||||
placeholder="Enter IP address",
|
yield Input(
|
||||||
id="ip-input",
|
placeholder="Enter comment (optional)", id="comment-input"
|
||||||
classes="default-input",
|
)
|
||||||
)
|
yield Checkbox("Active", id="active-checkbox")
|
||||||
|
|
||||||
with Vertical(classes="default-section") as hostnames:
|
|
||||||
hostnames.border_title = "Hostnames (comma-separated)"
|
|
||||||
yield Input(
|
|
||||||
placeholder="Enter hostnames",
|
|
||||||
id="hostname-input",
|
|
||||||
classes="default-input",
|
|
||||||
)
|
|
||||||
|
|
||||||
with Vertical(classes="default-section") as comment:
|
|
||||||
comment.border_title = "Comment:"
|
|
||||||
yield Input(
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Status bar for error/temporary messages (overlay, doesn't affect layout)
|
# Status bar for error/temporary messages (overlay, doesn't affect layout)
|
||||||
yield Static("", id="status-bar", classes="status-bar hidden")
|
yield Static("", id="status-bar", classes="status-bar hidden")
|
||||||
|
@ -206,7 +150,7 @@ class HostsManagerApp(App):
|
||||||
# Skip tuple-style bindings and only process Binding objects
|
# Skip tuple-style bindings and only process Binding objects
|
||||||
if not hasattr(binding, "show"):
|
if not hasattr(binding, "show"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Only show bindings marked with show=True
|
# Only show bindings marked with show=True
|
||||||
if binding.show:
|
if binding.show:
|
||||||
# Get the display key
|
# Get the display key
|
||||||
|
|
|
@ -169,7 +169,7 @@ class CustomFooter(Widget):
|
||||||
def add_left_item_old(self, item: str) -> None:
|
def add_left_item_old(self, item: str) -> None:
|
||||||
"""Backward compatibility method."""
|
"""Backward compatibility method."""
|
||||||
self.add_left_item_legacy(item)
|
self.add_left_item_legacy(item)
|
||||||
|
|
||||||
def add_right_item_old(self, item: str) -> None:
|
def add_right_item_old(self, item: str) -> None:
|
||||||
"""Backward compatibility method."""
|
"""Backward compatibility method."""
|
||||||
self.add_right_item_legacy(item)
|
self.add_right_item_legacy(item)
|
||||||
|
|
|
@ -5,7 +5,7 @@ This module handles the display and updating of entry details
|
||||||
and edit forms in the right pane.
|
and edit forms in the right pane.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from textual.widgets import Input, Checkbox
|
from textual.widgets import Input, Checkbox, DataTable
|
||||||
|
|
||||||
|
|
||||||
class DetailsHandler:
|
class DetailsHandler:
|
||||||
|
@ -23,41 +23,30 @@ class DetailsHandler:
|
||||||
self.update_details_display()
|
self.update_details_display()
|
||||||
|
|
||||||
def update_details_display(self) -> None:
|
def update_details_display(self) -> None:
|
||||||
"""Update the details display using disabled Input widgets."""
|
"""Update the details display using a DataTable with labeled rows."""
|
||||||
details_display = self.app.query_one("#entry-details-display")
|
details_table = self.app.query_one("#entry-details-table", DataTable)
|
||||||
edit_form = self.app.query_one("#entry-edit-form")
|
edit_form = self.app.query_one("#entry-edit-form")
|
||||||
|
|
||||||
# Show details display, hide edit form
|
# Show details table, hide edit form
|
||||||
details_display.remove_class("hidden")
|
details_table.remove_class("hidden")
|
||||||
edit_form.add_class("hidden")
|
edit_form.add_class("hidden")
|
||||||
|
|
||||||
# Get the input widgets
|
# Clear existing data
|
||||||
ip_input = self.app.query_one("#details-ip-input", Input)
|
details_table.clear()
|
||||||
hostname_input = self.app.query_one("#details-hostname-input", Input)
|
|
||||||
comment_input = self.app.query_one("#details-comment-input", Input)
|
|
||||||
active_checkbox = self.app.query_one("#details-active-checkbox", Checkbox)
|
|
||||||
|
|
||||||
if not self.app.hosts_file.entries:
|
if not self.app.hosts_file.entries:
|
||||||
# Show empty message
|
# Show empty message in a single row
|
||||||
ip_input.value = ""
|
if not details_table.columns:
|
||||||
ip_input.placeholder = "No entries loaded"
|
details_table.add_column("Field", key="field")
|
||||||
hostname_input.value = ""
|
details_table.add_row("No entries loaded")
|
||||||
hostname_input.placeholder = "No entries loaded"
|
|
||||||
comment_input.value = ""
|
|
||||||
comment_input.placeholder = "No entries loaded"
|
|
||||||
active_checkbox.value = False
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get visible entries to check if we need to adjust selection
|
# Get visible entries to check if we need to adjust selection
|
||||||
visible_entries = self.app.table_handler.get_visible_entries()
|
visible_entries = self.app.table_handler.get_visible_entries()
|
||||||
if not visible_entries:
|
if not visible_entries:
|
||||||
ip_input.value = ""
|
if not details_table.columns:
|
||||||
ip_input.placeholder = "No visible entries"
|
details_table.add_column("Field", key="field")
|
||||||
hostname_input.value = ""
|
details_table.add_row("No visible entries")
|
||||||
hostname_input.placeholder = "No visible entries"
|
|
||||||
comment_input.value = ""
|
|
||||||
comment_input.placeholder = "No visible entries"
|
|
||||||
active_checkbox.value = False
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# If default entries are hidden and selected_entry_index points to a hidden entry,
|
# If default entries are hidden and selected_entry_index points to a hidden entry,
|
||||||
|
@ -84,28 +73,36 @@ class DetailsHandler:
|
||||||
|
|
||||||
entry = self.app.hosts_file.entries[self.app.selected_entry_index]
|
entry = self.app.hosts_file.entries[self.app.selected_entry_index]
|
||||||
|
|
||||||
# Update the input widgets with entry data
|
# Add columns for labeled rows (Field, Value) - only if not already present
|
||||||
ip_input.value = entry.ip_address
|
if not details_table.columns:
|
||||||
ip_input.placeholder = ""
|
details_table.add_column("Field", key="field")
|
||||||
hostname_input.value = ", ".join(entry.hostnames)
|
details_table.add_column("Value", key="value")
|
||||||
hostname_input.placeholder = ""
|
|
||||||
comment_input.value = entry.comment or ""
|
|
||||||
comment_input.placeholder = "No comment"
|
|
||||||
active_checkbox.value = entry.is_active
|
|
||||||
|
|
||||||
# For default entries, show warning in placeholder text
|
# Add rows in the same order as edit form
|
||||||
|
details_table.add_row("IP Address", entry.ip_address, key="ip")
|
||||||
|
details_table.add_row("Hostnames", ", ".join(entry.hostnames), key="hostnames")
|
||||||
|
details_table.add_row("Comment", entry.comment or "", key="comment")
|
||||||
|
details_table.add_row(
|
||||||
|
"Active", "Yes" if entry.is_active else "No", key="active"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add DNS name if present (not in edit form but good to show)
|
||||||
|
if entry.dns_name:
|
||||||
|
details_table.add_row("DNS Name", entry.dns_name, key="dns")
|
||||||
|
|
||||||
|
# Add notice for default system entries
|
||||||
if entry.is_default_entry():
|
if entry.is_default_entry():
|
||||||
ip_input.placeholder = "⚠️ SYSTEM DEFAULT ENTRY - Cannot be modified"
|
details_table.add_row("", "", key="spacer")
|
||||||
hostname_input.placeholder = "⚠️ SYSTEM DEFAULT ENTRY - Cannot be modified"
|
details_table.add_row("⚠️ WARNING", "SYSTEM DEFAULT ENTRY", key="warning")
|
||||||
comment_input.placeholder = "⚠️ SYSTEM DEFAULT ENTRY - Cannot be modified"
|
details_table.add_row("Note", "This entry cannot be modified", key="note")
|
||||||
|
|
||||||
def update_edit_form(self) -> None:
|
def update_edit_form(self) -> None:
|
||||||
"""Update the edit form with current entry values."""
|
"""Update the edit form with current entry values."""
|
||||||
details_display = self.app.query_one("#entry-details-display")
|
details_table = self.app.query_one("#entry-details-table", DataTable)
|
||||||
edit_form = self.app.query_one("#entry-edit-form")
|
edit_form = self.app.query_one("#entry-edit-form")
|
||||||
|
|
||||||
# Hide details display, show edit form
|
# Hide details table, show edit form
|
||||||
details_display.add_class("hidden")
|
details_table.add_class("hidden")
|
||||||
edit_form.remove_class("hidden")
|
edit_form.remove_class("hidden")
|
||||||
|
|
||||||
if not self.app.hosts_file.entries or self.app.selected_entry_index >= len(
|
if not self.app.hosts_file.entries or self.app.selected_entry_index >= len(
|
||||||
|
|
|
@ -9,7 +9,7 @@ from textual.binding import Binding
|
||||||
|
|
||||||
# Key bindings for the hosts manager application
|
# Key bindings for the hosts manager application
|
||||||
HOSTS_MANAGER_BINDINGS = [
|
HOSTS_MANAGER_BINDINGS = [
|
||||||
Binding("n", "add_entry", "New entry", show=True, id="left:new_entry"),
|
Binding("a", "add_entry", "Add new entry", show=True, id="left:add_entry"),
|
||||||
Binding("d", "delete_entry", "Delete entry", show=True, id="left:delete_entry"),
|
Binding("d", "delete_entry", "Delete entry", show=True, id="left:delete_entry"),
|
||||||
Binding("e", "edit_entry", "Edit entry", show=True, id="left:edit_entry"),
|
Binding("e", "edit_entry", "Edit entry", show=True, id="left:edit_entry"),
|
||||||
Binding(
|
Binding(
|
||||||
|
|
|
@ -41,10 +41,6 @@ COMMON_CSS = """
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-no-top-margin {
|
|
||||||
margin-top: 0 !important;
|
|
||||||
}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# CSS styles for the hosts manager application
|
# CSS styles for the hosts manager application
|
||||||
|
@ -130,11 +126,39 @@ HOSTS_MANAGER_CSS = (
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-form {
|
#entry-edit-form {
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 1;
|
padding: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#entry-edit-form Label {
|
||||||
|
margin-bottom: 1;
|
||||||
|
color: $accent;
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry-edit-form Input {
|
||||||
|
margin-bottom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry-edit-form Checkbox {
|
||||||
|
margin-bottom: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Entry details table styling */
|
||||||
|
#entry-details-table {
|
||||||
|
background: $background;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry-details-table .datatable--even-row {
|
||||||
|
background: $background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entry-details-table .datatable--odd-row {
|
||||||
|
background: $surface;
|
||||||
|
}
|
||||||
|
|
||||||
Header {
|
Header {
|
||||||
height: 1;
|
height: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,27 +156,16 @@ class TestHostsManagerApp:
|
||||||
):
|
):
|
||||||
app = HostsManagerApp()
|
app = HostsManagerApp()
|
||||||
|
|
||||||
# Mock the query_one method to return disabled input widgets
|
# Mock the query_one method to return DataTable mock
|
||||||
mock_details_display = Mock()
|
mock_details_table = Mock()
|
||||||
|
mock_details_table.columns = [] # Mock empty columns list
|
||||||
mock_edit_form = Mock()
|
mock_edit_form = Mock()
|
||||||
mock_ip_input = Mock()
|
|
||||||
mock_hostname_input = Mock()
|
|
||||||
mock_comment_input = Mock()
|
|
||||||
mock_active_checkbox = Mock()
|
|
||||||
|
|
||||||
def mock_query_one(selector, widget_type=None):
|
def mock_query_one(selector, widget_type=None):
|
||||||
if selector == "#entry-details-display":
|
if selector == "#entry-details-table":
|
||||||
return mock_details_display
|
return mock_details_table
|
||||||
elif selector == "#entry-edit-form":
|
elif selector == "#entry-edit-form":
|
||||||
return mock_edit_form
|
return mock_edit_form
|
||||||
elif selector == "#details-ip-input":
|
|
||||||
return mock_ip_input
|
|
||||||
elif selector == "#details-hostname-input":
|
|
||||||
return mock_hostname_input
|
|
||||||
elif selector == "#details-comment-input":
|
|
||||||
return mock_comment_input
|
|
||||||
elif selector == "#details-active-checkbox":
|
|
||||||
return mock_active_checkbox
|
|
||||||
return Mock()
|
return Mock()
|
||||||
|
|
||||||
app.query_one = mock_query_one
|
app.query_one = mock_query_one
|
||||||
|
@ -193,13 +182,12 @@ class TestHostsManagerApp:
|
||||||
|
|
||||||
app.update_entry_details()
|
app.update_entry_details()
|
||||||
|
|
||||||
# Verify input widgets were updated with entry data
|
# Verify DataTable operations were called
|
||||||
mock_details_display.remove_class.assert_called_with("hidden")
|
mock_details_table.remove_class.assert_called_with("hidden")
|
||||||
mock_edit_form.add_class.assert_called_with("hidden")
|
mock_edit_form.add_class.assert_called_with("hidden")
|
||||||
assert mock_ip_input.value == "127.0.0.1"
|
mock_details_table.clear.assert_called_once()
|
||||||
assert mock_hostname_input.value == "localhost, local"
|
mock_details_table.add_column.assert_called()
|
||||||
assert mock_comment_input.value == "Test comment"
|
mock_details_table.add_row.assert_called()
|
||||||
assert mock_active_checkbox.value
|
|
||||||
|
|
||||||
def test_update_entry_details_no_entries(self):
|
def test_update_entry_details_no_entries(self):
|
||||||
"""Test updating entry details with no entries."""
|
"""Test updating entry details with no entries."""
|
||||||
|
@ -212,27 +200,16 @@ class TestHostsManagerApp:
|
||||||
):
|
):
|
||||||
app = HostsManagerApp()
|
app = HostsManagerApp()
|
||||||
|
|
||||||
# Mock the query_one method to return disabled input widgets
|
# Mock the query_one method to return DataTable mock
|
||||||
mock_details_display = Mock()
|
mock_details_table = Mock()
|
||||||
|
mock_details_table.columns = [] # Mock empty columns list
|
||||||
mock_edit_form = Mock()
|
mock_edit_form = Mock()
|
||||||
mock_ip_input = Mock()
|
|
||||||
mock_hostname_input = Mock()
|
|
||||||
mock_comment_input = Mock()
|
|
||||||
mock_active_checkbox = Mock()
|
|
||||||
|
|
||||||
def mock_query_one(selector, widget_type=None):
|
def mock_query_one(selector, widget_type=None):
|
||||||
if selector == "#entry-details-display":
|
if selector == "#entry-details-table":
|
||||||
return mock_details_display
|
return mock_details_table
|
||||||
elif selector == "#entry-edit-form":
|
elif selector == "#entry-edit-form":
|
||||||
return mock_edit_form
|
return mock_edit_form
|
||||||
elif selector == "#details-ip-input":
|
|
||||||
return mock_ip_input
|
|
||||||
elif selector == "#details-hostname-input":
|
|
||||||
return mock_hostname_input
|
|
||||||
elif selector == "#details-comment-input":
|
|
||||||
return mock_comment_input
|
|
||||||
elif selector == "#details-active-checkbox":
|
|
||||||
return mock_active_checkbox
|
|
||||||
return Mock()
|
return Mock()
|
||||||
|
|
||||||
app.query_one = mock_query_one
|
app.query_one = mock_query_one
|
||||||
|
@ -240,16 +217,12 @@ class TestHostsManagerApp:
|
||||||
|
|
||||||
app.update_entry_details()
|
app.update_entry_details()
|
||||||
|
|
||||||
# Verify widgets show empty state placeholders
|
# Verify DataTable operations were called for empty state
|
||||||
mock_details_display.remove_class.assert_called_with("hidden")
|
mock_details_table.remove_class.assert_called_with("hidden")
|
||||||
mock_edit_form.add_class.assert_called_with("hidden")
|
mock_edit_form.add_class.assert_called_with("hidden")
|
||||||
assert mock_ip_input.value == ""
|
mock_details_table.clear.assert_called_once()
|
||||||
assert mock_ip_input.placeholder == "No entries loaded"
|
mock_details_table.add_column.assert_called_with("Field", key="field")
|
||||||
assert mock_hostname_input.value == ""
|
mock_details_table.add_row.assert_called_with("No entries loaded")
|
||||||
assert mock_hostname_input.placeholder == "No entries loaded"
|
|
||||||
assert mock_comment_input.value == ""
|
|
||||||
assert mock_comment_input.placeholder == "No entries loaded"
|
|
||||||
assert not mock_active_checkbox.value
|
|
||||||
|
|
||||||
def test_update_status_default(self):
|
def test_update_status_default(self):
|
||||||
"""Test status bar update with default information."""
|
"""Test status bar update with default information."""
|
||||||
|
|
|
@ -634,58 +634,3 @@ class TestHostsManager:
|
||||||
finally:
|
finally:
|
||||||
# Clean up
|
# Clean up
|
||||||
Path(temp_path).unlink()
|
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