From 4d398bbf577b6758378a6955b85507639bf66dd1 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 16 Jun 2026 16:20:28 +0200 Subject: [PATCH] test: skip webui test when not available in build Add conditional YANG feature 'web-ui' to infix-services.yang, set when the webui is built. This feature is what the test now looks for to determine if it should be skipped or not. Also, clean up remnant of old test, which was entirely replaced by Mattias' login/csrf test. Signed-off-by: Joachim Wiberg --- package/confd/confd.mk | 7 ++ src/confd/yang/confd.inc | 2 +- src/confd/yang/confd/infix-services.yang | 12 +++ ...20.yang => infix-services@2026-06-17.yang} | 0 src/confd/yang/web.inc | 3 + test/case/services/webui/login_csrf/test.py | 6 ++ test/case/webui/basic/test.py | 86 ------------------- 7 files changed, 29 insertions(+), 87 deletions(-) rename src/confd/yang/confd/{infix-services@2026-03-20.yang => infix-services@2026-06-17.yang} (100%) create mode 100644 src/confd/yang/web.inc delete mode 100644 test/case/webui/basic/test.py diff --git a/package/confd/confd.mk b/package/confd/confd.mk index ccb50d2b..428e85b9 100644 --- a/package/confd/confd.mk +++ b/package/confd/confd.mk @@ -91,6 +91,12 @@ define CONFD_INSTALL_YANG_MODULES_GPS $(BR2_EXTERNAL_INFIX_PATH)/utils/srload $(@D)/yang/gps.inc endef endif +ifeq ($(BR2_PACKAGE_WEBUI),y) +define CONFD_INSTALL_YANG_MODULES_WEBUI + $(COMMON_SYSREPO_ENV) \ + $(BR2_EXTERNAL_INFIX_PATH)/utils/srload $(@D)/yang/web.inc +endef +endif # PER_PACKAGE_DIR # Since the last package in the dependency chain that runs sysrepoctl is confd, we need to @@ -121,6 +127,7 @@ CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_CONTAINERS CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_WIFI CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_GPS +CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_YANG_MODULES_WEBUI CONFD_POST_INSTALL_TARGET_HOOKS += CONFD_INSTALL_IN_ROMFS CONFD_TARGET_FINALIZE_HOOKS += CONFD_CLEANUP diff --git a/src/confd/yang/confd.inc b/src/confd/yang/confd.inc index c4b23343..44c29d4f 100644 --- a/src/confd/yang/confd.inc +++ b/src/confd/yang/confd.inc @@ -43,7 +43,7 @@ MODULES=( "infix-firewall-icmp-types@2025-04-26.yang" "infix-meta@2025-12-10.yang" "infix-system@2026-03-09.yang" - "infix-services@2026-03-20.yang" + "infix-services@2026-06-17.yang" "ieee802-ethernet-interface@2025-09-10.yang" "ieee802-ethernet-phy-type@2025-09-10.yang" "infix-ethernet-interface@2026-05-21.yang" diff --git a/src/confd/yang/confd/infix-services.yang b/src/confd/yang/confd/infix-services.yang index 7d64f848..c23651ec 100644 --- a/src/confd/yang/confd/infix-services.yang +++ b/src/confd/yang/confd/infix-services.yang @@ -31,6 +31,11 @@ module infix-services { contact "kernelkit@googlegroups.com"; description "Infix services, generic."; + revision 2026-06-17 { + description "Add web-ui feature, advertised when the web management + interface (webui) is built into the image."; + reference "internal"; + } revision 2026-03-20 { description "Add hostname leaf to mdns container for avahi host-name override. Add neighbors container to mdns for mDNS-SD neighbor table. @@ -71,6 +76,13 @@ module infix-services { reference "internal"; } + feature web-ui { + description "The web management interface (webui) is an optional build-time + feature in Infix. Advertised when it is built; it gates no + data nodes — the /web service tree (including restconf) is + present regardless, so the feature is a pure capability flag."; + } + /* * Data nodes */ diff --git a/src/confd/yang/confd/infix-services@2026-03-20.yang b/src/confd/yang/confd/infix-services@2026-06-17.yang similarity index 100% rename from src/confd/yang/confd/infix-services@2026-03-20.yang rename to src/confd/yang/confd/infix-services@2026-06-17.yang diff --git a/src/confd/yang/web.inc b/src/confd/yang/web.inc new file mode 100644 index 00000000..f9eb6130 --- /dev/null +++ b/src/confd/yang/web.inc @@ -0,0 +1,3 @@ +MODULES=( + "infix-services -e web-ui" +) diff --git a/test/case/services/webui/login_csrf/test.py b/test/case/services/webui/login_csrf/test.py index 27c843b3..910e6749 100755 --- a/test/case/services/webui/login_csrf/test.py +++ b/test/case/services/webui/login_csrf/test.py @@ -102,6 +102,12 @@ with infamy.Test() as test: host = target.location.host password = target.location.password + # The web UI is an optional build-time feature (BR2_PACKAGE_WEBUI); + # minimal builds omit it. infix-services advertises the "web-ui" + # feature only when it's built — skip rather than fail otherwise. + if not target.has_feature("infix-services", "web-ui"): + test.skip() + with test.step("Wait for the web UI to come up"): def webui_ready(): try: diff --git a/test/case/webui/basic/test.py b/test/case/webui/basic/test.py deleted file mode 100644 index c8f02687..00000000 --- a/test/case/webui/basic/test.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -""" -WebUI login and logout - -Smoke test that the WebUI is reachable, accepts admin credentials, -serves the dashboard while authenticated, and clears the session on -logout. Pure HTTP — no browser framework — so we only catch backend -and plumbing regressions (nginx down, Go service crashed, TLS or -RESTCONF auth broken, CSRF or session cookie logic broken, wrong -defaults). JS, htmx, and visual issues are deliberately out of -scope. -""" -import re -import time -import infamy -import requests - - -with infamy.Test() as test: - with test.step("Set up topology and wait for WebUI to come up"): - env = infamy.Env() - target = env.attach("target", "mgmt") - password = env.get_password("target") - - # Bracket the host so IPv6 link-local mgmt addresses - # (e.g. fe80::...%eth0) form a valid URL. - base = f"https://[{target.location.host}]" - - sess = requests.Session() - sess.verify = False # device ships a self-signed cert - - # env.attach() returns once NETCONF/RESTCONF is reachable, but - # nginx + the Go webui process may still be coming up. Poll - # /login briefly to ride out the early-boot window where - # default.conf's error_page would otherwise hand us /50x.html. - for _ in range(15): - try: - r = sess.get(f"{base}/login", timeout=10) - if r.status_code == 200 and 'name="csrf"' in r.text: - break - except requests.RequestException: - pass - time.sleep(1) - else: - test.fail("WebUI did not come up within 15s") - m = re.search(r'name="csrf"\s+value="([^"]+)"', r.text) - if not m: - test.fail("CSRF token field not found on /login") - login_csrf = m.group(1) - - with test.step("Authenticate as admin and load the dashboard"): - r = sess.post( - f"{base}/login", - data={"username": "admin", "password": password, "csrf": login_csrf}, - allow_redirects=False, - timeout=10, - ) - if r.status_code != 303: - test.fail(f"POST /login: expected 303, got {r.status_code}") - if "session" not in sess.cookies: - test.fail("session cookie not set after successful login") - - r = sess.get(f"{base}/", timeout=10) - if r.status_code != 200: - test.fail(f"GET /: expected 200, got {r.status_code}") - # base.html ships an htmx-config meta tag on every authenticated page. - if 'name="htmx-config"' not in r.text: - test.fail("htmx-config meta tag missing from /") - m = re.search(r'name="csrf-token"\s+content="([^"]+)"', r.text) - if not m: - test.fail("csrf-token meta tag not found on dashboard") - meta_csrf = m.group(1) - - with test.step("Logout clears the session cookie"): - r = sess.post( - f"{base}/logout", - data={"csrf": meta_csrf}, - allow_redirects=False, - timeout=10, - ) - if r.status_code != 303: - test.fail(f"POST /logout: expected 303, got {r.status_code}") - if "session" in sess.cookies: - test.fail("session cookie still present after logout") - - test.succeed()