Add update-vendor.sh symlink support, unify install.sh vendor flow

- update-vendor.sh now creates webui/vendor symlinks (htm.js)
- install.sh calls update-vendor.sh after package install
- Add vendor/.empty and webui/vendor/.empty as directory placeholders in git
This commit is contained in:
2026-07-01 00:44:08 +00:00
parent 575cf06a4b
commit 8c13ad55ce
32 changed files with 1371 additions and 445 deletions
+166
View File
@@ -477,6 +477,172 @@ class TestDaemonConfigPending:
result = daemonfirewall.config_pending_handler(None, None)
assert result["needs_apply"] is True
@patch("lib.state.state")
def test_no_state_mutation(self, mock_st):
pending = {
"needs_apply": True,
"pending": [{"zone": "public", "type": "services"}],
}
mock_st.get.return_value = {**_mock_state(), "pending": pending}
original_keys = set(pending.keys())
result = daemonfirewall.config_pending_handler(None, None)
assert "pending_summary" in result
assert set(pending.keys()) == original_keys, (
"config_pending_handler must not mutate state store pending dict"
)
@patch("lib.state.state")
def test_detail_text_interfaces(self, mock_st):
mock_st.get.return_value = {
**_mock_state(),
"pending": {
"needs_apply": True,
"pending": [
{
"zone": "internal",
"type": "interfaces",
"config": ["eth1", "eth2"],
"live": ["eth1"],
}
],
},
}
result = daemonfirewall.config_pending_handler(None, None)
assert len(result["pending_summary"]) == 1
assert "Zone internal: interfaces changed" in result["pending_summary"][0]
assert "eth2" in result["pending_summary"][0]
@patch("lib.state.state")
def test_detail_text_services(self, mock_st):
mock_st.get.return_value = {
**_mock_state(),
"pending": {
"needs_apply": True,
"pending": [
{
"zone": "dmz",
"type": "services",
"config": ["ssh", "dns"],
"live": ["ssh"],
}
],
},
}
result = daemonfirewall.config_pending_handler(None, None)
assert len(result["pending_summary"]) == 1
assert "Zone dmz: services changed" in result["pending_summary"][0]
@patch("lib.state.state")
def test_detail_text_rich_rules(self, mock_st):
mock_st.get.return_value = {
**_mock_state(),
"pending": {
"needs_apply": True,
"pending": [
{
"zone": "public",
"type": "rich_rules",
"config_count": 3,
"live_count": 1,
}
],
},
}
result = daemonfirewall.config_pending_handler(None, None)
assert len(result["pending_summary"]) == 1
assert "Zone public: rich rules differ" in result["pending_summary"][0]
assert "config: 3" in result["pending_summary"][0]
assert "live: 1" in result["pending_summary"][0]
@patch("lib.state.state")
def test_detail_text_masquerade(self, mock_st):
mock_st.get.return_value = {
**_mock_state(),
"pending": {
"needs_apply": True,
"pending": [
{
"zone": "wan",
"type": "masquerade",
"config": True,
"live": False,
}
],
},
}
result = daemonfirewall.config_pending_handler(None, None)
assert len(result["pending_summary"]) == 1
assert "Zone wan: masquerade changed" in result["pending_summary"][0]
assert "config: True" in result["pending_summary"][0]
@patch("lib.state.state")
def test_detail_text_target(self, mock_st):
mock_st.get.return_value = {
**_mock_state(),
"pending": {
"needs_apply": True,
"pending": [
{
"zone": "trusted",
"type": "target",
"config": "ACCEPT",
"live": "default",
}
],
},
}
result = daemonfirewall.config_pending_handler(None, None)
assert len(result["pending_summary"]) == 1
assert "Zone trusted: target changed" in result["pending_summary"][0]
assert "config: ACCEPT" in result["pending_summary"][0]
@patch("lib.state.state")
def test_detail_text_unknown_type(self, mock_st):
mock_st.get.return_value = {
**_mock_state(),
"pending": {
"needs_apply": True,
"pending": [{"zone": "public", "type": "foobarLayout"}],
},
}
result = daemonfirewall.config_pending_handler(None, None)
assert len(result["pending_summary"]) == 1
assert "Zone public: foobarLayout changed" in result["pending_summary"][0]
@patch("lib.state.state")
def test_detail_text_mixed_types(self, mock_st):
mock_st.get.return_value = {
**_mock_state(),
"pending": {
"needs_apply": True,
"pending": [
{
"zone": "internal",
"type": "interfaces",
"config": ["eth1"],
"live": [],
},
{
"zone": "dmz",
"type": "services",
"config": ["ssh", "dns"],
"live": ["ssh"],
},
{
"zone": "public",
"type": "rich_rules",
"config_count": 2,
"live_count": 1,
},
],
},
}
result = daemonfirewall.config_pending_handler(None, None)
assert len(result["pending_summary"]) == 3
assert "Zone internal: interfaces changed" in result["pending_summary"][0]
assert "Zone dmz: services changed" in result["pending_summary"][1]
assert "Zone public: rich rules differ" in result["pending_summary"][2]
# ---------------------------------------------------------------------------
# Zone validation in add_rich_rule, remove_rich_rule, remove_forward_port