feat(system-hub): extend diagnostics + tests
This commit is contained in:
parent
eeaa0d2f37
commit
22e0bc9272
@ -176,7 +176,8 @@
|
||||
"Bash(pkill -f \"local-build.sh build secubox-app-crowdsec\")",
|
||||
"Bash(go version:*)",
|
||||
"Bash(timeout 600 ./secubox-tools/local-build.sh:*)",
|
||||
"Bash(timeout 300 ./secubox-tools/local-build.sh:*)"
|
||||
"Bash(timeout 300 ./secubox-tools/local-build.sh:*)",
|
||||
"Bash(paste:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,6 +142,19 @@ var callUploadDiagnostics = rpc.declare({
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callListDiagnosticProfiles = rpc.declare({
|
||||
object: 'luci.system-hub',
|
||||
method: 'list_diagnostic_profiles',
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callGetDiagnosticProfile = rpc.declare({
|
||||
object: 'luci.system-hub',
|
||||
method: 'get_diagnostic_profile',
|
||||
params: ['name'],
|
||||
expect: {}
|
||||
});
|
||||
|
||||
var callRemoteStatus = rpc.declare({
|
||||
object: 'luci.system-hub',
|
||||
method: 'remote_status',
|
||||
@ -206,16 +219,24 @@ return baseclass.extend({
|
||||
getSettings: callGetSettings,
|
||||
saveSettings: callSaveSettings,
|
||||
|
||||
collectDiagnostics: function(includeLogs, includeConfig, includeNetwork, anonymize) {
|
||||
collectDiagnostics: function(includeLogs, includeConfig, includeNetwork, anonymize, profile) {
|
||||
return callCollectDiagnostics({
|
||||
include_logs: includeLogs ? 1 : 0,
|
||||
include_config: includeConfig ? 1 : 0,
|
||||
include_network: includeNetwork ? 1 : 0,
|
||||
anonymize: anonymize ? 1 : 0
|
||||
anonymize: anonymize ? 1 : 0,
|
||||
profile: profile || 'manual'
|
||||
});
|
||||
},
|
||||
|
||||
listDiagnostics: callListDiagnostics,
|
||||
|
||||
listDiagnosticProfiles: callListDiagnosticProfiles,
|
||||
|
||||
getDiagnosticProfile: function(name) {
|
||||
return callGetDiagnosticProfile({ name: name });
|
||||
},
|
||||
|
||||
downloadDiagnostic: function(name) {
|
||||
return callDownloadDiagnostic({ name: name });
|
||||
},
|
||||
|
||||
@ -15,11 +15,16 @@ Theme.init({ language: shLang });
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return API.listDiagnostics();
|
||||
return Promise.all([
|
||||
API.listDiagnostics(),
|
||||
API.listDiagnosticProfiles()
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
this.currentArchives = (data && data.archives) || [];
|
||||
this.currentArchives = (data && data[0] && data[0].archives) || [];
|
||||
this.profiles = (data && data[1] && data[1].profiles) || [];
|
||||
this.selectedProfile = null;
|
||||
var archives = this.currentArchives;
|
||||
|
||||
var view = E('div', { 'class': 'system-hub-dashboard' }, [
|
||||
@ -27,7 +32,28 @@ return view.extend({
|
||||
ThemeAssets.stylesheet('common.css'),
|
||||
ThemeAssets.stylesheet('dashboard.css'),
|
||||
HubNav.renderTabs('diagnostics'),
|
||||
|
||||
|
||||
// Profile Selector
|
||||
E('div', { 'class': 'sh-card' }, [
|
||||
E('div', { 'class': 'sh-card-header' }, [
|
||||
E('div', { 'class': 'sh-card-title' }, [
|
||||
E('span', { 'class': 'sh-card-title-icon' }, '📋'),
|
||||
'Profils de Diagnostic'
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'sh-card-body' }, [
|
||||
E('div', {
|
||||
'class': 'profile-grid',
|
||||
'style': 'display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1rem;'
|
||||
}, this.renderProfileButtons()),
|
||||
E('div', {
|
||||
'id': 'profile-description',
|
||||
'class': 'sh-info-box',
|
||||
'style': 'display: none; padding: 0.75rem; background: rgba(66, 153, 225, 0.1); border-left: 3px solid #4299e1; border-radius: 4px;'
|
||||
})
|
||||
])
|
||||
]),
|
||||
|
||||
// Collect Diagnostics
|
||||
E('div', { 'class': 'sh-card' }, [
|
||||
E('div', { 'class': 'sh-card-header' }, [
|
||||
@ -55,17 +81,14 @@ return view.extend({
|
||||
// Quick Tests
|
||||
E('div', { 'class': 'sh-card' }, [
|
||||
E('div', { 'class': 'sh-card-header' }, [
|
||||
E('div', { 'class': 'sh-card-title' }, [ E('span', { 'class': 'sh-card-title-icon' }, '🧪'), 'Tests Rapides' ])
|
||||
E('div', { 'class': 'sh-card-title' }, [ E('span', { 'class': 'sh-card-title-icon' }, '🧪'), 'Tests Rapides' ]),
|
||||
E('div', { 'id': 'test-profile-info', 'style': 'font-size: 12px; color: #888; display: none;' })
|
||||
]),
|
||||
E('div', { 'class': 'sh-card-body' }, [
|
||||
E('div', { 'style': 'display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;' }, [
|
||||
this.renderTestButton('🌐', 'Test Connectivité', 'Ping WAN & DNS', 'connectivity'),
|
||||
this.renderTestButton('📡', 'Test DNS', 'Résolution de noms', 'dns'),
|
||||
this.renderTestButton('⚡', 'Test Latence', 'Ping vers Google', 'latency'),
|
||||
this.renderTestButton('💾', 'Test Disque', 'Lecture/Écriture', 'disk'),
|
||||
this.renderTestButton('🔒', 'Test Firewall', 'Règles actives', 'firewall'),
|
||||
this.renderTestButton('📶', 'Test WiFi', 'Signal et clients', 'wifi')
|
||||
])
|
||||
E('div', {
|
||||
'id': 'quick-tests-grid',
|
||||
'style': 'display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;'
|
||||
}, this.renderTestButtons(null))
|
||||
])
|
||||
]),
|
||||
|
||||
@ -111,19 +134,6 @@ return view.extend({
|
||||
]);
|
||||
},
|
||||
|
||||
renderTestButton: function(icon, label, desc, type) {
|
||||
var self = this;
|
||||
return E('button', {
|
||||
'class': 'sh-btn',
|
||||
'style': 'flex-direction: column; height: auto; padding: 16px;',
|
||||
'click': function() { self.runTest(type); }
|
||||
}, [
|
||||
E('span', { 'style': 'font-size: 24px; margin-bottom: 8px;' }, icon),
|
||||
E('span', { 'style': 'font-weight: 600;' }, label),
|
||||
E('span', { 'style': 'font-size: 10px; color: #707080;' }, desc)
|
||||
]);
|
||||
},
|
||||
|
||||
renderArchiveList: function(archives) {
|
||||
if (!archives.length) {
|
||||
return [
|
||||
@ -135,7 +145,7 @@ return view.extend({
|
||||
|
||||
renderArchiveItem: function(archive) {
|
||||
var name = archive.name || '';
|
||||
var size = api.formatBytes(archive.size || 0);
|
||||
var size = API.formatBytes(archive.size || 0);
|
||||
var date = archive.created_at || '';
|
||||
return E('div', { 'style': 'display: flex; align-items: center; justify-content: space-between; padding: 12px; background: #1a1a24; border-radius: 8px; margin-bottom: 10px;' }, [
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 12px;' }, [
|
||||
@ -171,10 +181,10 @@ return view.extend({
|
||||
E('div', { 'class': 'spinning' })
|
||||
]);
|
||||
|
||||
API.collectDiagnostics(includeLogs, includeConfig, includeNetwork, anonymize).then(L.bind(function(result) {
|
||||
API.collectDiagnostics(includeLogs, includeConfig, includeNetwork, anonymize, this.selectedProfile || 'manual').then(L.bind(function(result) {
|
||||
ui.hideModal();
|
||||
if (result.success) {
|
||||
ui.addNotification(null, E('p', {}, '✅ Archive créée: ' + result.file + ' (' + api.formatBytes(result.size) + ')'), 'success');
|
||||
ui.addNotification(null, E('p', {}, '✅ Archive créée: ' + result.file + ' (' + API.formatBytes(result.size) + ')'), 'success');
|
||||
this.refreshArchives();
|
||||
} else {
|
||||
ui.addNotification(null, E('p', {}, '❌ Erreur lors de la collecte'), 'error');
|
||||
@ -283,6 +293,135 @@ return view.extend({
|
||||
}, this));
|
||||
},
|
||||
|
||||
// Mapping des tests avec leurs métadonnées
|
||||
testDefinitions: {
|
||||
'connectivity': { icon: '🌐', label: 'Test Connectivité', desc: 'Ping WAN & DNS' },
|
||||
'dns': { icon: '📡', label: 'Test DNS', desc: 'Résolution de noms' },
|
||||
'latency': { icon: '⚡', label: 'Test Latence', desc: 'Ping vers Google' },
|
||||
'disk': { icon: '💾', label: 'Test Disque', desc: 'Lecture/Écriture' },
|
||||
'firewall': { icon: '🔒', label: 'Test Firewall', desc: 'Règles actives' },
|
||||
'wifi': { icon: '📶', label: 'Test WiFi', desc: 'Signal et clients' }
|
||||
},
|
||||
|
||||
renderTestButtons: function(profileTests) {
|
||||
var self = this;
|
||||
var testsToShow = [];
|
||||
|
||||
if (profileTests) {
|
||||
// Filtrer selon les tests du profil
|
||||
testsToShow = profileTests.split(',').map(function(t) { return t.trim(); });
|
||||
} else {
|
||||
// Afficher tous les tests par défaut
|
||||
testsToShow = ['connectivity', 'dns', 'latency', 'disk', 'firewall', 'wifi'];
|
||||
}
|
||||
|
||||
return testsToShow.map(function(testType) {
|
||||
var testDef = self.testDefinitions[testType];
|
||||
if (!testDef) return null;
|
||||
|
||||
return E('button', {
|
||||
'class': 'sh-btn',
|
||||
'style': 'flex-direction: column; height: auto; padding: 16px;',
|
||||
'click': function() { self.runTest(testType); }
|
||||
}, [
|
||||
E('span', { 'style': 'font-size: 24px; margin-bottom: 8px;' }, testDef.icon),
|
||||
E('span', { 'style': 'font-weight: 600;' }, testDef.label),
|
||||
E('span', { 'style': 'font-size: 10px; color: #707080;' }, testDef.desc)
|
||||
]);
|
||||
}).filter(function(btn) { return btn !== null; });
|
||||
},
|
||||
|
||||
updateQuickTests: function(profile) {
|
||||
var grid = document.getElementById('quick-tests-grid');
|
||||
var profileInfo = document.getElementById('test-profile-info');
|
||||
|
||||
if (!grid) return;
|
||||
|
||||
// Vider la grille
|
||||
grid.innerHTML = '';
|
||||
|
||||
// Rendre les nouveaux boutons
|
||||
var buttons = this.renderTestButtons(profile ? profile.tests : null);
|
||||
buttons.forEach(function(btn) {
|
||||
grid.appendChild(btn);
|
||||
});
|
||||
|
||||
// Mettre à jour l'info du profil
|
||||
if (profileInfo) {
|
||||
if (profile && profile.tests) {
|
||||
var testCount = profile.tests.split(',').length;
|
||||
profileInfo.textContent = '📋 ' + testCount + ' test(s) recommandé(s) pour ce profil';
|
||||
profileInfo.style.display = 'block';
|
||||
} else {
|
||||
profileInfo.style.display = 'none';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderProfileButtons: function() {
|
||||
var self = this;
|
||||
return (this.profiles || []).map(function(profile) {
|
||||
return E('button', {
|
||||
'class': 'sh-btn sh-btn-secondary profile-btn',
|
||||
'data-profile': profile.name,
|
||||
'style': 'display: flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem;',
|
||||
'click': L.bind(self.selectProfile, self, profile.name)
|
||||
}, [
|
||||
E('span', { 'style': 'font-size: 1.5rem;' }, profile.icon || '📋'),
|
||||
E('span', {}, profile.label || profile.name)
|
||||
]);
|
||||
});
|
||||
},
|
||||
|
||||
selectProfile: function(profileName) {
|
||||
var self = this;
|
||||
|
||||
// Highlight selected button
|
||||
document.querySelectorAll('.profile-btn').forEach(function(btn) {
|
||||
if (btn.dataset.profile === profileName) {
|
||||
btn.classList.add('active');
|
||||
btn.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
|
||||
btn.style.color = 'white';
|
||||
} else {
|
||||
btn.classList.remove('active');
|
||||
btn.style.background = '';
|
||||
btn.style.color = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Load profile config and update toggles
|
||||
API.getDiagnosticProfile(profileName).then(function(profile) {
|
||||
self.selectedProfile = profileName;
|
||||
|
||||
// Update toggles to match profile
|
||||
self.setToggle('diag_logs', profile.include_logs);
|
||||
self.setToggle('diag_config', profile.include_config);
|
||||
self.setToggle('diag_network', profile.include_network);
|
||||
self.setToggle('diag_anonymize', profile.anonymize);
|
||||
|
||||
// Show description
|
||||
var descBox = document.getElementById('profile-description');
|
||||
if (descBox) {
|
||||
descBox.style.display = 'block';
|
||||
descBox.textContent = '📝 ' + (profile.description || '');
|
||||
}
|
||||
|
||||
// Update quick tests to show only relevant tests
|
||||
self.updateQuickTests(profile);
|
||||
});
|
||||
},
|
||||
|
||||
setToggle: function(id, value) {
|
||||
var toggle = document.getElementById(id);
|
||||
if (toggle) {
|
||||
if (value == 1 || value === true) {
|
||||
toggle.classList.add('active');
|
||||
} else {
|
||||
toggle.classList.remove('active');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
|
||||
@ -604,16 +604,54 @@ collect_diagnostics() {
|
||||
read input
|
||||
[ -n "$input" ] && json_load "$input"
|
||||
|
||||
local include_logs include_config include_network anonymize
|
||||
local include_logs include_config include_network anonymize profile
|
||||
json_get_var profile profile 2>/dev/null
|
||||
json_get_var include_logs include_logs 2>/dev/null
|
||||
json_get_var include_config include_config 2>/dev/null
|
||||
json_get_var include_network include_network 2>/dev/null
|
||||
json_get_var anonymize anonymize 2>/dev/null
|
||||
|
||||
# If profile specified, load its config and override flags
|
||||
if [ -n "$profile" ] && [ "$profile" != "manual" ]; then
|
||||
case "$profile" in
|
||||
network-issues)
|
||||
include_logs=1
|
||||
include_config=1
|
||||
include_network=1
|
||||
anonymize=0
|
||||
;;
|
||||
performance-problems)
|
||||
include_logs=1
|
||||
include_config=0
|
||||
include_network=0
|
||||
anonymize=0
|
||||
;;
|
||||
security-audit)
|
||||
include_logs=1
|
||||
include_config=1
|
||||
include_network=1
|
||||
anonymize=1
|
||||
;;
|
||||
wifi-problems)
|
||||
include_logs=1
|
||||
include_config=1
|
||||
include_network=1
|
||||
anonymize=0
|
||||
;;
|
||||
full-diagnostic)
|
||||
include_logs=1
|
||||
include_config=1
|
||||
include_network=1
|
||||
anonymize=1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
[ -z "$include_logs" ] && include_logs=1
|
||||
[ -z "$include_config" ] && include_config=1
|
||||
[ -z "$include_network" ] && include_network=1
|
||||
[ -z "$anonymize" ] && anonymize=0
|
||||
[ -z "$profile" ] && profile="manual"
|
||||
|
||||
local timestamp="$(date +%Y%m%d-%H%M%S)"
|
||||
local workdir="$DIAG_DIR/work-$timestamp"
|
||||
@ -622,6 +660,9 @@ collect_diagnostics() {
|
||||
# System info
|
||||
{
|
||||
echo "=== System Information ==="
|
||||
echo "Profile: $profile"
|
||||
echo "Generated: $(date)"
|
||||
echo
|
||||
uname -a
|
||||
echo
|
||||
echo "--- CPU ---"
|
||||
@ -681,7 +722,7 @@ collect_diagnostics() {
|
||||
} >"$workdir/network.txt"
|
||||
fi
|
||||
|
||||
local archive_name="diagnostics-$(hostname)-$timestamp.tar.gz"
|
||||
local archive_name="diagnostics-$(hostname)-${profile}-$timestamp.tar.gz"
|
||||
local archive_path="$DIAG_DIR/$archive_name"
|
||||
|
||||
tar -czf "$archive_path" -C "$workdir" . >/dev/null 2>&1 || {
|
||||
@ -825,6 +866,169 @@ run_diagnostic_test() {
|
||||
json_dump
|
||||
}
|
||||
|
||||
# List all available diagnostic profiles
|
||||
list_diagnostic_profiles() {
|
||||
json_init
|
||||
json_add_array "profiles"
|
||||
|
||||
# Profile 1: Network Issues
|
||||
json_add_object ""
|
||||
json_add_string "name" "network-issues"
|
||||
json_add_string "label" "Problèmes Réseau"
|
||||
json_add_string "icon" "🌐"
|
||||
json_add_string "description" "Diagnostique les pannes de routage, DNS, perte de paquets et blocages firewall"
|
||||
json_add_string "tests" "connectivity,dns,latency,firewall"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 0
|
||||
json_close_object
|
||||
|
||||
# Profile 2: Performance Problems
|
||||
json_add_object ""
|
||||
json_add_string "name" "performance-problems"
|
||||
json_add_string "label" "Problèmes Performance"
|
||||
json_add_string "icon" "⚡"
|
||||
json_add_string "description" "Identifie les goulots d'étranglement CPU/mémoire, problèmes d'E/S disque"
|
||||
json_add_string "tests" "disk,latency"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 0
|
||||
json_add_int "include_network" 0
|
||||
json_add_int "anonymize" 0
|
||||
json_close_object
|
||||
|
||||
# Profile 3: Security Audit
|
||||
json_add_object ""
|
||||
json_add_string "name" "security-audit"
|
||||
json_add_string "label" "Audit Sécurité"
|
||||
json_add_string "icon" "🔐"
|
||||
json_add_string "description" "Revue des règles firewall, logs d'authentification, exposition réseau"
|
||||
json_add_string "tests" "firewall"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 1
|
||||
json_close_object
|
||||
|
||||
# Profile 4: WiFi Problems
|
||||
json_add_object ""
|
||||
json_add_string "name" "wifi-problems"
|
||||
json_add_string "label" "Problèmes WiFi"
|
||||
json_add_string "icon" "📶"
|
||||
json_add_string "description" "Analyse la force du signal, interférences canaux, associations clients"
|
||||
json_add_string "tests" "wifi,connectivity,latency"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 0
|
||||
json_close_object
|
||||
|
||||
# Profile 5: Full Diagnostic
|
||||
json_add_object ""
|
||||
json_add_string "name" "full-diagnostic"
|
||||
json_add_string "label" "Diagnostic Complet"
|
||||
json_add_string "icon" "📋"
|
||||
json_add_string "description" "Diagnostic complet pour escalade support - collecte tout"
|
||||
json_add_string "tests" "connectivity,dns,latency,disk,firewall,wifi"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 1
|
||||
json_close_object
|
||||
|
||||
# TODO: Add UCI custom profiles here
|
||||
# config_load system-hub
|
||||
# list_custom_profiles() { ... }
|
||||
|
||||
json_close_array
|
||||
json_dump
|
||||
}
|
||||
|
||||
# Get specific diagnostic profile
|
||||
get_diagnostic_profile() {
|
||||
read input
|
||||
[ -n "$input" ] && json_load "$input"
|
||||
|
||||
local profile_name
|
||||
json_get_var profile_name name
|
||||
|
||||
# Hardcoded profiles
|
||||
case "$profile_name" in
|
||||
network-issues)
|
||||
json_init
|
||||
json_add_string "name" "network-issues"
|
||||
json_add_string "label" "Problèmes Réseau"
|
||||
json_add_string "icon" "🌐"
|
||||
json_add_string "description" "Diagnostique les pannes de routage, DNS, perte de paquets et blocages firewall"
|
||||
json_add_string "tests" "connectivity,dns,latency,firewall"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 0
|
||||
json_dump
|
||||
;;
|
||||
performance-problems)
|
||||
json_init
|
||||
json_add_string "name" "performance-problems"
|
||||
json_add_string "label" "Problèmes Performance"
|
||||
json_add_string "icon" "⚡"
|
||||
json_add_string "description" "Identifie les goulots d'étranglement CPU/mémoire, problèmes d'E/S disque"
|
||||
json_add_string "tests" "disk,latency"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 0
|
||||
json_add_int "include_network" 0
|
||||
json_add_int "anonymize" 0
|
||||
json_dump
|
||||
;;
|
||||
security-audit)
|
||||
json_init
|
||||
json_add_string "name" "security-audit"
|
||||
json_add_string "label" "Audit Sécurité"
|
||||
json_add_string "icon" "🔐"
|
||||
json_add_string "description" "Revue des règles firewall, logs d'authentification, exposition réseau"
|
||||
json_add_string "tests" "firewall"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 1
|
||||
json_dump
|
||||
;;
|
||||
wifi-problems)
|
||||
json_init
|
||||
json_add_string "name" "wifi-problems"
|
||||
json_add_string "label" "Problèmes WiFi"
|
||||
json_add_string "icon" "📶"
|
||||
json_add_string "description" "Analyse la force du signal, interférences canaux, associations clients"
|
||||
json_add_string "tests" "wifi,connectivity,latency"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 0
|
||||
json_dump
|
||||
;;
|
||||
full-diagnostic)
|
||||
json_init
|
||||
json_add_string "name" "full-diagnostic"
|
||||
json_add_string "label" "Diagnostic Complet"
|
||||
json_add_string "icon" "📋"
|
||||
json_add_string "description" "Diagnostic complet pour escalade support - collecte tout"
|
||||
json_add_string "tests" "connectivity,dns,latency,disk,firewall,wifi"
|
||||
json_add_int "include_logs" 1
|
||||
json_add_int "include_config" 1
|
||||
json_add_int "include_network" 1
|
||||
json_add_int "anonymize" 1
|
||||
json_dump
|
||||
;;
|
||||
*)
|
||||
# TODO: Check UCI for custom profile
|
||||
json_init
|
||||
json_add_boolean "success" 0
|
||||
json_add_string "error" "Profile not found: $profile_name"
|
||||
json_dump
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
remote_status() {
|
||||
local section="system-hub.remote"
|
||||
local enabled=$(uci -q get $section.rustdesk_enabled || echo 0)
|
||||
@ -1278,9 +1482,12 @@ case "$1" in
|
||||
"include_logs": 1,
|
||||
"include_config": 1,
|
||||
"include_network": 1,
|
||||
"anonymize": 1
|
||||
"anonymize": 1,
|
||||
"profile": "string"
|
||||
},
|
||||
"list_diagnostics": {},
|
||||
"list_diagnostic_profiles": {},
|
||||
"get_diagnostic_profile": { "name": "string" },
|
||||
"download_diagnostic": { "name": "string" },
|
||||
"delete_diagnostic": { "name": "string" },
|
||||
"run_diagnostic_test": { "test": "string" },
|
||||
@ -1320,6 +1527,8 @@ EOF
|
||||
save_settings) save_settings ;;
|
||||
collect_diagnostics) collect_diagnostics ;;
|
||||
list_diagnostics) list_diagnostics ;;
|
||||
list_diagnostic_profiles) list_diagnostic_profiles ;;
|
||||
get_diagnostic_profile) get_diagnostic_profile ;;
|
||||
download_diagnostic) download_diagnostic ;;
|
||||
delete_diagnostic) delete_diagnostic ;;
|
||||
run_diagnostic_test) run_diagnostic_test ;;
|
||||
|
||||
@ -14,6 +14,8 @@
|
||||
"get_storage",
|
||||
"get_settings",
|
||||
"list_diagnostics",
|
||||
"list_diagnostic_profiles",
|
||||
"get_diagnostic_profile",
|
||||
"download_diagnostic",
|
||||
"run_diagnostic_test",
|
||||
"remote_status",
|
||||
|
||||
341
luci-app-system-hub/tests/MANUAL_TESTS.md
Normal file
341
luci-app-system-hub/tests/MANUAL_TESTS.md
Normal file
@ -0,0 +1,341 @@
|
||||
# Manual Test Procedure - Diagnostic Profiles
|
||||
|
||||
## Test Environment Setup
|
||||
|
||||
- **Router**: OpenWrt with luci-app-system-hub installed
|
||||
- **Access**: LuCI web interface
|
||||
- **Browser**: Chrome, Firefox, or Edge (latest version)
|
||||
- **Prerequisites**: Admin access to the router
|
||||
|
||||
---
|
||||
|
||||
## Test Case 1: Profile Selector Display
|
||||
|
||||
**Objective**: Verify that the profile selector card displays correctly with all 5 profiles
|
||||
|
||||
**Steps**:
|
||||
1. Login to LuCI web interface
|
||||
2. Navigate to **System → System Hub → Diagnostics**
|
||||
3. Observe the "Profils de Diagnostic" card at the top
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Profile selector card is visible
|
||||
- ✅ 5 profile buttons are displayed in a grid layout
|
||||
- ✅ Each button shows an icon and label:
|
||||
- 🌐 Problèmes Réseau
|
||||
- ⚡ Problèmes Performance
|
||||
- 🔐 Audit Sécurité
|
||||
- 📶 Problèmes WiFi
|
||||
- 📋 Diagnostic Complet
|
||||
- ✅ Buttons are clickable and styled properly
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 2: Network Issues Profile
|
||||
|
||||
**Objective**: Test the network-issues profile workflow
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Click the "🌐 Problèmes Réseau" button
|
||||
3. Observe the toggle switches below
|
||||
4. Observe the description box
|
||||
5. Click "📦 Générer Archive"
|
||||
6. Wait for completion notification
|
||||
7. Check the "Archives Récentes" section
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Profile button is highlighted (gradient background)
|
||||
- ✅ Description appears: "Diagnostique les pannes de routage..."
|
||||
- ✅ Toggles are set to:
|
||||
- Logs: ON
|
||||
- Configuration: ON
|
||||
- Network: ON
|
||||
- Anonymize: OFF
|
||||
- ✅ Success notification appears: "✅ Archive créée: ..."
|
||||
- ✅ Archive filename contains "network-issues"
|
||||
- ✅ Archive appears in recent archives list
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 3: Performance Problems Profile
|
||||
|
||||
**Objective**: Test the performance-problems profile with different toggles
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Click the "⚡ Problèmes Performance" button
|
||||
3. Observe toggle switches
|
||||
4. Verify that Config toggle is OFF
|
||||
5. Click "📦 Générer Archive"
|
||||
6. Verify archive creation
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Profile button is highlighted
|
||||
- ✅ Description appears about CPU/memory bottlenecks
|
||||
- ✅ Toggles are set to:
|
||||
- Logs: ON
|
||||
- Configuration: OFF (different from network profile!)
|
||||
- Network: OFF
|
||||
- Anonymize: OFF
|
||||
- ✅ Archive filename contains "performance-problems"
|
||||
- ✅ Archive is smaller than full diagnostic (no config/network)
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 4: Security Audit Profile with Anonymization
|
||||
|
||||
**Objective**: Verify anonymization works with security-audit profile
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Click the "🔐 Audit Sécurité" button
|
||||
3. Observe that Anonymize toggle is ON
|
||||
4. Click "📦 Générer Archive"
|
||||
5. Download the archive
|
||||
6. Extract and inspect config files
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Profile button is highlighted
|
||||
- ✅ Anonymize toggle is ON
|
||||
- ✅ Archive filename contains "security-audit"
|
||||
- ✅ Config files in archive have sensitive data removed (passwords, keys, IPs, MACs)
|
||||
- ✅ Logs still contain useful diagnostic information
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 5: WiFi Problems Profile
|
||||
|
||||
**Objective**: Test WiFi-specific profile
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Click the "📶 Problèmes WiFi" button
|
||||
3. Observe toggles
|
||||
4. Click "📦 Générer Archive"
|
||||
5. Verify archive creation
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Profile button is highlighted
|
||||
- ✅ Description mentions signal strength and interference
|
||||
- ✅ All toggles ON except anonymize
|
||||
- ✅ Archive filename contains "wifi-problems"
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 6: Full Diagnostic Profile
|
||||
|
||||
**Objective**: Test the complete diagnostic profile
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Click the "📋 Diagnostic Complet" button
|
||||
3. Observe toggles
|
||||
4. Click "📦 Générer Archive"
|
||||
5. Compare archive size with other profiles
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Profile button is highlighted
|
||||
- ✅ Description mentions "support escalation"
|
||||
- ✅ All toggles ON including anonymize
|
||||
- ✅ Archive filename contains "full-diagnostic"
|
||||
- ✅ Archive is largest (contains everything)
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 7: Profile Override
|
||||
|
||||
**Objective**: Verify user can manually adjust toggles after selecting profile
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Click the "🌐 Problèmes Réseau" profile
|
||||
3. Manually toggle OFF the "Configuration" switch
|
||||
4. Click "📦 Générer Archive"
|
||||
5. Verify archive creation
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Manual toggle change is respected
|
||||
- ✅ Archive is created without config files
|
||||
- ✅ Filename still contains "network-issues"
|
||||
- ✅ User override works correctly
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 8: Profile Switching
|
||||
|
||||
**Objective**: Test switching between different profiles
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Click "🌐 Problèmes Réseau" profile
|
||||
3. Observe toggles state
|
||||
4. Click "⚡ Problèmes Performance" profile
|
||||
5. Observe toggles state changes
|
||||
6. Switch to "🔐 Audit Sécurité"
|
||||
7. Observe toggles state changes
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Only one profile button is highlighted at a time
|
||||
- ✅ Toggles update correctly for each profile
|
||||
- ✅ Description updates for each profile
|
||||
- ✅ No visual glitches or errors
|
||||
- ✅ Profile highlighting transitions smoothly
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 9: Archive Download and Deletion
|
||||
|
||||
**Objective**: Test downloading and deleting profile-based archives
|
||||
|
||||
**Steps**:
|
||||
1. Create archive with any profile
|
||||
2. Click download button for the archive
|
||||
3. Verify file downloads to browser
|
||||
4. Click delete button
|
||||
5. Confirm deletion
|
||||
6. Verify archive removed from list
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Download initiates correctly
|
||||
- ✅ Filename matches what was shown
|
||||
- ✅ File can be extracted (valid .tar.gz)
|
||||
- ✅ Deletion confirmation dialog appears
|
||||
- ✅ Archive is removed from list
|
||||
- ✅ Archive file is deleted from router
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Test Case 10: Multiple Profiles in Sequence
|
||||
|
||||
**Objective**: Create archives with multiple different profiles in sequence
|
||||
|
||||
**Steps**:
|
||||
1. Click "🌐 Problèmes Réseau" → Generate Archive
|
||||
2. Wait for completion
|
||||
3. Click "⚡ Problèmes Performance" → Generate Archive
|
||||
4. Wait for completion
|
||||
5. Click "🔐 Audit Sécurité" → Generate Archive
|
||||
6. Check archives list
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ All 3 archives created successfully
|
||||
- ✅ Each archive has correct profile name in filename
|
||||
- ✅ Archives have different sizes (based on included data)
|
||||
- ✅ All archives appear in recent archives list
|
||||
- ✅ No errors or conflicts
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Regression Tests
|
||||
|
||||
### RT-1: Existing Diagnostics Still Work
|
||||
|
||||
**Objective**: Ensure non-profile diagnostics still function
|
||||
|
||||
**Steps**:
|
||||
1. Don't select any profile
|
||||
2. Manually set toggles (Logs: ON, Config: ON, Network: OFF, Anonymize: OFF)
|
||||
3. Click "📦 Générer Archive"
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Archive is created successfully
|
||||
- ✅ Filename contains "manual" instead of profile name
|
||||
- ✅ Only logs and config are included (no network info)
|
||||
- ✅ Backward compatibility maintained
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
### RT-2: Quick Tests Unchanged
|
||||
|
||||
**Objective**: Verify quick test buttons still work
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to **System → System Hub → Diagnostics**
|
||||
2. Scroll to "Tests Rapides" section
|
||||
3. Click each test button:
|
||||
- Connectivity
|
||||
- DNS
|
||||
- Latency
|
||||
- Disk
|
||||
- Firewall
|
||||
- WiFi
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ All test buttons are visible
|
||||
- ✅ Each test executes and shows results
|
||||
- ✅ Tests are independent of profile selection
|
||||
- ✅ No errors or regressions
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
### RT-3: Upload to Support
|
||||
|
||||
**Objective**: Verify upload functionality still works with profiles
|
||||
|
||||
**Steps**:
|
||||
1. Create archive with any profile
|
||||
2. Click "☁️ Envoyer au Support"
|
||||
3. Select the profile-based archive
|
||||
4. Confirm upload
|
||||
|
||||
**Expected Results**:
|
||||
- ✅ Upload dialog appears
|
||||
- ✅ Profile-based archives are listed
|
||||
- ✅ Upload process works (or shows appropriate error if no endpoint configured)
|
||||
- ✅ No regressions in upload functionality
|
||||
|
||||
**Result**: ☐ PASS ☐ FAIL
|
||||
**Notes**: ___________________________________________
|
||||
|
||||
---
|
||||
|
||||
## Sign-off
|
||||
|
||||
**Tester Name**: _______________________________
|
||||
**Date**: _______________________________
|
||||
**Build Version**: _______________________________
|
||||
**Browser**: _______________________________
|
||||
**Overall Result**: ☐ ALL PASS ☐ SOME FAILURES
|
||||
|
||||
**Notes/Issues**:
|
||||
___________________________________________
|
||||
___________________________________________
|
||||
___________________________________________
|
||||
|
||||
**Failures to Address**:
|
||||
___________________________________________
|
||||
___________________________________________
|
||||
___________________________________________
|
||||
397
luci-app-system-hub/tests/README.md
Normal file
397
luci-app-system-hub/tests/README.md
Normal file
@ -0,0 +1,397 @@
|
||||
# System Hub Testing Guide
|
||||
|
||||
Comprehensive testing suite for the diagnostic profiles feature in luci-app-system-hub.
|
||||
|
||||
## Test Structure
|
||||
|
||||
```
|
||||
tests/
|
||||
├── README.md # This file
|
||||
├── test-profiles.sh # Unit tests (shell-based)
|
||||
├── MANUAL_TESTS.md # Manual testing checklist
|
||||
├── integration/
|
||||
│ └── test-diagnostic-workflow.sh # End-to-end workflow test
|
||||
└── frontend/
|
||||
└── test-diagnostics-ui.js # Browser console tests
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Run All Tests
|
||||
|
||||
```bash
|
||||
# On the router (via SSH)
|
||||
cd /usr/share/system-hub/tests # or wherever tests are installed
|
||||
./test-profiles.sh
|
||||
./integration/test-diagnostic-workflow.sh
|
||||
```
|
||||
|
||||
### Run Individual Test Suites
|
||||
|
||||
See detailed instructions below for each test type.
|
||||
|
||||
---
|
||||
|
||||
## 1. Unit Tests
|
||||
|
||||
**File**: `test-profiles.sh`
|
||||
**Type**: Shell script
|
||||
**Execution**: On the router via SSH
|
||||
**Duration**: ~30 seconds
|
||||
|
||||
### What It Tests
|
||||
|
||||
- Profile listing (list_diagnostic_profiles RPC)
|
||||
- Profile retrieval (get_diagnostic_profile RPC)
|
||||
- Profile-based diagnostic collection
|
||||
- Archive filename formatting
|
||||
- Profile flag overrides
|
||||
- All 5 profiles individually
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
```bash
|
||||
# SSH to the router
|
||||
ssh root@192.168.8.205
|
||||
|
||||
# Navigate to tests directory
|
||||
cd /usr/share/system-hub/tests # adjust path if needed
|
||||
|
||||
# Run unit tests
|
||||
./test-profiles.sh
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
|
||||
```
|
||||
========================================
|
||||
System Hub Diagnostic Profiles Unit Tests
|
||||
========================================
|
||||
Starting test run at Mon Dec 30 16:00:00 2025
|
||||
|
||||
TEST 1: list_diagnostic_profiles returns valid JSON
|
||||
==================================================
|
||||
✅ PASS: list_diagnostic_profiles returns valid JSON
|
||||
|
||||
TEST 2: At least 5 profiles available
|
||||
======================================
|
||||
✅ PASS: At least 5 profiles returned (found 5)
|
||||
|
||||
...
|
||||
|
||||
========================================
|
||||
Test Results Summary
|
||||
========================================
|
||||
Passed: 15
|
||||
Failed: 0
|
||||
Total: 15
|
||||
|
||||
✅ All tests passed!
|
||||
```
|
||||
|
||||
### Exit Codes
|
||||
|
||||
- `0`: All tests passed
|
||||
- `1`: Some tests failed
|
||||
|
||||
---
|
||||
|
||||
## 2. Integration Tests
|
||||
|
||||
**File**: `integration/test-diagnostic-workflow.sh`
|
||||
**Type**: Shell script
|
||||
**Execution**: On the router via SSH
|
||||
**Duration**: ~60 seconds
|
||||
|
||||
### What It Tests
|
||||
|
||||
Complete end-to-end workflow:
|
||||
1. List available profiles
|
||||
2. Select a specific profile
|
||||
3. Collect diagnostics with profile
|
||||
4. Verify archive creation
|
||||
5. Verify profile name in filename
|
||||
6. List diagnostics
|
||||
7. Download archive (RPC)
|
||||
8. Delete archive
|
||||
9. Verify deletion
|
||||
|
||||
### Running Integration Tests
|
||||
|
||||
```bash
|
||||
# SSH to the router
|
||||
ssh root@192.168.8.205
|
||||
|
||||
# Navigate to integration tests
|
||||
cd /usr/share/system-hub/tests/integration
|
||||
|
||||
# Run workflow test
|
||||
./test-diagnostic-workflow.sh
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
|
||||
```
|
||||
========================================
|
||||
Diagnostic Workflow Integration Test
|
||||
========================================
|
||||
Testing complete workflow with performance-problems profile
|
||||
|
||||
STEP 1: Listing available profiles
|
||||
---------------------------------------
|
||||
✅ SUCCESS: Profiles listed
|
||||
- network-issues
|
||||
- performance-problems
|
||||
- security-audit
|
||||
- wifi-problems
|
||||
- full-diagnostic
|
||||
|
||||
STEP 2: Selecting performance-problems profile
|
||||
---------------------------------------
|
||||
✅ SUCCESS: Profile retrieved
|
||||
Name: performance-problems
|
||||
Label: Problèmes Performance
|
||||
Tests: disk,latency
|
||||
|
||||
...
|
||||
|
||||
========================================
|
||||
✅ ALL WORKFLOW STEPS PASSED
|
||||
========================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Frontend Validation Tests
|
||||
|
||||
**File**: `frontend/test-diagnostics-ui.js`
|
||||
**Type**: JavaScript (browser console)
|
||||
**Execution**: In browser developer tools
|
||||
**Duration**: ~5 seconds
|
||||
|
||||
### What It Tests
|
||||
|
||||
- Profile grid rendering
|
||||
- Profile button attributes
|
||||
- Profile selection highlighting
|
||||
- Toggle switch updates
|
||||
- Description box display
|
||||
- All 5 profiles present in UI
|
||||
|
||||
### Running Frontend Tests
|
||||
|
||||
1. **Open LuCI** in your browser
|
||||
2. **Navigate** to System → System Hub → Diagnostics
|
||||
3. **Open Developer Tools** (F12)
|
||||
4. **Switch to Console tab**
|
||||
5. **Copy and paste** the entire contents of `test-diagnostics-ui.js`
|
||||
6. **Run** the tests:
|
||||
```javascript
|
||||
runAllTests()
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
|
||||
```
|
||||
========================================
|
||||
Diagnostic Profiles Frontend Tests
|
||||
========================================
|
||||
Starting tests at 4:00:00 PM
|
||||
|
||||
📋 TEST 1: Profile grid renders
|
||||
================================
|
||||
✅ PASS: Profile grid element exists
|
||||
✅ PASS: At least 5 profile buttons rendered (found 5, expected at least 5)
|
||||
|
||||
📋 TEST 2: Profile buttons have correct attributes
|
||||
===================================================
|
||||
✅ PASS: Profile button has data-profile attribute
|
||||
✅ PASS: Profile button contains icon span
|
||||
|
||||
...
|
||||
|
||||
========================================
|
||||
Test Results Summary
|
||||
========================================
|
||||
Passed: 12
|
||||
Failed: 0
|
||||
Total: 12
|
||||
|
||||
✅ All tests passed!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Manual Tests
|
||||
|
||||
**File**: `MANUAL_TESTS.md`
|
||||
**Type**: Documented checklist
|
||||
**Execution**: Manual testing in LuCI
|
||||
**Duration**: ~30-45 minutes
|
||||
|
||||
### What It Tests
|
||||
|
||||
- Comprehensive user workflows
|
||||
- Visual appearance and styling
|
||||
- Error handling
|
||||
- Edge cases
|
||||
- Regression testing
|
||||
|
||||
### Running Manual Tests
|
||||
|
||||
1. **Open** `MANUAL_TESTS.md`
|
||||
2. **Follow** each test case step-by-step
|
||||
3. **Record** results in the document
|
||||
4. **Sign off** at the end
|
||||
|
||||
### Test Cases Included
|
||||
|
||||
- Test Case 1: Profile Selector Display
|
||||
- Test Case 2: Network Issues Profile
|
||||
- Test Case 3: Performance Problems Profile
|
||||
- Test Case 4: Security Audit Profile with Anonymization
|
||||
- Test Case 5: WiFi Problems Profile
|
||||
- Test Case 6: Full Diagnostic Profile
|
||||
- Test Case 7: Profile Override
|
||||
- Test Case 8: Profile Switching
|
||||
- Test Case 9: Archive Download and Deletion
|
||||
- Test Case 10: Multiple Profiles in Sequence
|
||||
- Regression Tests (3 additional tests)
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
Use this checklist to ensure complete test coverage:
|
||||
|
||||
- [ ] Unit tests run successfully
|
||||
- [ ] Integration tests run successfully
|
||||
- [ ] Frontend tests run successfully
|
||||
- [ ] Manual test cases completed
|
||||
- [ ] All test cases passed
|
||||
- [ ] Edge cases tested
|
||||
- [ ] Regression tests passed
|
||||
- [ ] Documentation reviewed
|
||||
|
||||
---
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
### Automated Testing
|
||||
|
||||
To run tests automatically on each deployment:
|
||||
|
||||
```bash
|
||||
# Example CI script
|
||||
#!/bin/bash
|
||||
|
||||
echo "Running System Hub Profile Tests..."
|
||||
|
||||
# Deploy to test router
|
||||
./deploy.sh test-router
|
||||
|
||||
# Run tests via SSH
|
||||
ssh root@test-router "cd /usr/share/system-hub/tests && ./test-profiles.sh"
|
||||
TEST_RESULT=$?
|
||||
|
||||
ssh root@test-router "cd /usr/share/system-hub/tests/integration && ./test-diagnostic-workflow.sh"
|
||||
INTEGRATION_RESULT=$?
|
||||
|
||||
# Check results
|
||||
if [ $TEST_RESULT -eq 0 ] && [ $INTEGRATION_RESULT -eq 0 ]; then
|
||||
echo "✅ All automated tests passed"
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Tests failed"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Unit Tests Failing
|
||||
|
||||
**Problem**: `ubus call` commands fail
|
||||
|
||||
**Solutions**:
|
||||
- Verify rpcd is running: `/etc/init.d/rpcd status`
|
||||
- Restart rpcd: `/etc/init.d/rpcd restart`
|
||||
- Check ACL permissions are deployed
|
||||
- Verify user has luci-app-system-hub ACL
|
||||
|
||||
### Integration Tests Failing
|
||||
|
||||
**Problem**: Archive creation fails
|
||||
|
||||
**Solutions**:
|
||||
- Check `/tmp/system-hub/diagnostics/` directory exists
|
||||
- Verify disk space: `df -h`
|
||||
- Check system logs: `logread | grep system-hub`
|
||||
|
||||
### Frontend Tests Failing
|
||||
|
||||
**Problem**: Profile buttons not found
|
||||
|
||||
**Solutions**:
|
||||
- Clear browser cache (Ctrl+Shift+R)
|
||||
- Verify diagnostics.js is deployed
|
||||
- Check browser console for JavaScript errors
|
||||
- Ensure LuCI cache is cleared: `rm -rf /tmp/luci-*`
|
||||
|
||||
### Manual Tests Failing
|
||||
|
||||
**Problem**: UI doesn't match expected results
|
||||
|
||||
**Solutions**:
|
||||
- Logout and login again (ACL refresh)
|
||||
- Clear browser cache completely
|
||||
- Verify all files deployed correctly
|
||||
- Check browser compatibility (use latest Chrome/Firefox)
|
||||
|
||||
---
|
||||
|
||||
## Test Maintenance
|
||||
|
||||
### Adding New Tests
|
||||
|
||||
1. **Unit Tests**: Add new test functions to `test-profiles.sh`
|
||||
2. **Integration Tests**: Create new script in `integration/`
|
||||
3. **Frontend Tests**: Add new test functions to `test-diagnostics-ui.js`
|
||||
4. **Manual Tests**: Add new test case to `MANUAL_TESTS.md`
|
||||
|
||||
### Updating Tests
|
||||
|
||||
When adding new profiles or features:
|
||||
1. Update test expectations (profile count, names, etc.)
|
||||
2. Add tests for new functionality
|
||||
3. Run full test suite to ensure no regressions
|
||||
4. Update this README if test procedures change
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
When contributing tests:
|
||||
- Follow existing patterns and conventions
|
||||
- Add comments explaining what is being tested
|
||||
- Include both positive and negative test cases
|
||||
- Update documentation
|
||||
- Test on actual hardware before submitting
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For questions about testing:
|
||||
- Check test output for specific error messages
|
||||
- Review router logs: `logread`
|
||||
- Check RPC backend: `ubus call luci.system-hub list`
|
||||
- Verify deployment: `opkg list-installed | grep system-hub`
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
Same license as luci-app-system-hub (MIT or GPL-2.0).
|
||||
270
luci-app-system-hub/tests/frontend/test-diagnostics-ui.js
Normal file
270
luci-app-system-hub/tests/frontend/test-diagnostics-ui.js
Normal file
@ -0,0 +1,270 @@
|
||||
/**
|
||||
* Frontend Validation Tests for Diagnostic Profiles
|
||||
*
|
||||
* USAGE:
|
||||
* 1. Navigate to System Hub → Diagnostics in LuCI
|
||||
* 2. Open browser console (F12)
|
||||
* 3. Copy and paste this entire file
|
||||
* 4. Run: runAllTests()
|
||||
*/
|
||||
|
||||
// Test counter
|
||||
var testResults = {
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
tests: []
|
||||
};
|
||||
|
||||
// Helper functions
|
||||
function assert(condition, testName) {
|
||||
if (condition) {
|
||||
console.log('%c✅ PASS: ' + testName, 'color: green');
|
||||
testResults.passed++;
|
||||
testResults.tests.push({ name: testName, status: 'PASS' });
|
||||
return true;
|
||||
} else {
|
||||
console.error('❌ FAIL: ' + testName);
|
||||
testResults.failed++;
|
||||
testResults.tests.push({ name: testName, status: 'FAIL' });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function assertExists(element, testName) {
|
||||
return assert(element !== null && element !== undefined, testName);
|
||||
}
|
||||
|
||||
function assertCount(actual, expected, testName) {
|
||||
if (actual >= expected) {
|
||||
return assert(true, testName + ' (found ' + actual + ', expected at least ' + expected + ')');
|
||||
} else {
|
||||
console.error('Expected at least ' + expected + ' but found ' + actual);
|
||||
return assert(false, testName);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 1: Profile grid renders
|
||||
function testProfileGridRenders() {
|
||||
console.log('\n📋 TEST 1: Profile grid renders');
|
||||
console.log('================================');
|
||||
|
||||
var grid = document.querySelector('.profile-grid');
|
||||
assertExists(grid, 'Profile grid element exists');
|
||||
|
||||
var buttons = grid ? grid.querySelectorAll('button.profile-btn') : [];
|
||||
assertCount(buttons.length, 5, 'At least 5 profile buttons rendered');
|
||||
}
|
||||
|
||||
// Test 2: Profile buttons have correct attributes
|
||||
function testProfileButtonAttributes() {
|
||||
console.log('\n📋 TEST 2: Profile buttons have correct attributes');
|
||||
console.log('===================================================');
|
||||
|
||||
var buttons = document.querySelectorAll('.profile-btn');
|
||||
|
||||
if (buttons.length === 0) {
|
||||
assert(false, 'No profile buttons found');
|
||||
return;
|
||||
}
|
||||
|
||||
var firstButton = buttons[0];
|
||||
assertExists(firstButton.dataset.profile, 'Profile button has data-profile attribute');
|
||||
|
||||
var hasIcon = firstButton.querySelector('span') !== null;
|
||||
assert(hasIcon, 'Profile button contains icon span');
|
||||
}
|
||||
|
||||
// Test 3: Profile selection highlights button
|
||||
function testProfileSelection() {
|
||||
console.log('\n📋 TEST 3: Profile selection highlights button');
|
||||
console.log('================================================');
|
||||
|
||||
var buttons = document.querySelectorAll('.profile-btn');
|
||||
|
||||
if (buttons.length === 0) {
|
||||
assert(false, 'No profile buttons found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Click first profile button
|
||||
var firstButton = buttons[0];
|
||||
var profileName = firstButton.dataset.profile;
|
||||
console.log('Clicking profile:', profileName);
|
||||
|
||||
firstButton.click();
|
||||
|
||||
// Wait a bit for the async operation
|
||||
setTimeout(function() {
|
||||
var isActive = firstButton.classList.contains('active') ||
|
||||
firstButton.style.background.includes('gradient');
|
||||
|
||||
assert(isActive, 'Clicked profile button is highlighted');
|
||||
|
||||
// Check other buttons are not active
|
||||
var otherButtonsInactive = true;
|
||||
for (var i = 1; i < buttons.length; i++) {
|
||||
if (buttons[i].classList.contains('active')) {
|
||||
otherButtonsInactive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(otherButtonsInactive, 'Other profile buttons are not highlighted');
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Test 4: Profile description appears
|
||||
function testProfileDescriptionAppears() {
|
||||
console.log('\n📋 TEST 4: Profile description appears');
|
||||
console.log('=======================================');
|
||||
|
||||
var descBox = document.getElementById('profile-description');
|
||||
assertExists(descBox, 'Profile description box exists');
|
||||
|
||||
// Click a profile button to trigger description
|
||||
var buttons = document.querySelectorAll('.profile-btn');
|
||||
if (buttons.length > 0) {
|
||||
buttons[0].click();
|
||||
|
||||
setTimeout(function() {
|
||||
var isVisible = descBox && descBox.style.display !== 'none';
|
||||
assert(isVisible, 'Description box is visible after profile selection');
|
||||
|
||||
var hasContent = descBox && descBox.textContent.length > 0;
|
||||
assert(hasContent, 'Description box contains text');
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 5: Toggle switches update when profile selected
|
||||
function testToggleSwitchesUpdate() {
|
||||
console.log('\n📋 TEST 5: Toggle switches update with profile');
|
||||
console.log('================================================');
|
||||
|
||||
var logsToggle = document.getElementById('diag_logs');
|
||||
var configToggle = document.getElementById('diag_config');
|
||||
var networkToggle = document.getElementById('diag_network');
|
||||
var anonymizeToggle = document.getElementById('diag_anonymize');
|
||||
|
||||
assertExists(logsToggle, 'Logs toggle exists');
|
||||
assertExists(configToggle, 'Config toggle exists');
|
||||
assertExists(networkToggle, 'Network toggle exists');
|
||||
assertExists(anonymizeToggle, 'Anonymize toggle exists');
|
||||
|
||||
// Click network-issues profile (should have all logs/config/network enabled)
|
||||
var buttons = document.querySelectorAll('.profile-btn');
|
||||
var networkBtn = null;
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
if (buttons[i].dataset.profile === 'network-issues') {
|
||||
networkBtn = buttons[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (networkBtn) {
|
||||
networkBtn.click();
|
||||
|
||||
setTimeout(function() {
|
||||
var logsActive = logsToggle && logsToggle.classList.contains('active');
|
||||
var configActive = configToggle && configToggle.classList.contains('active');
|
||||
var networkActive = networkToggle && networkToggle.classList.contains('active');
|
||||
|
||||
assert(logsActive, 'Logs toggle activated by network-issues profile');
|
||||
assert(configActive, 'Config toggle activated by network-issues profile');
|
||||
assert(networkActive, 'Network toggle activated by network-issues profile');
|
||||
}, 500);
|
||||
} else {
|
||||
assert(false, 'Could not find network-issues profile button');
|
||||
}
|
||||
}
|
||||
|
||||
// Test 6: Diagnostic collection includes profile (mock test)
|
||||
function testDiagnosticCollectionProfile() {
|
||||
console.log('\n📋 TEST 6: Diagnostic collection includes profile');
|
||||
console.log('==================================================');
|
||||
|
||||
// This is a simulated test - we check that the API call would include profile
|
||||
// We can't actually trigger collection without mocking
|
||||
|
||||
console.log('Note: This would require mocking API.collectDiagnostics()');
|
||||
console.log('Manual test: Click "Generate Archive" and verify archive filename includes profile name');
|
||||
|
||||
assert(true, 'Profile parameter mechanism in place (manual verification required)');
|
||||
}
|
||||
|
||||
// Test 7: All 5 profiles are present
|
||||
function testAllProfilesPresent() {
|
||||
console.log('\n📋 TEST 7: All 5 expected profiles are present');
|
||||
console.log('================================================');
|
||||
|
||||
var expectedProfiles = [
|
||||
'network-issues',
|
||||
'performance-problems',
|
||||
'security-audit',
|
||||
'wifi-problems',
|
||||
'full-diagnostic'
|
||||
];
|
||||
|
||||
var buttons = document.querySelectorAll('.profile-btn');
|
||||
var foundProfiles = [];
|
||||
|
||||
for (var i = 0; i < buttons.length; i++) {
|
||||
foundProfiles.push(buttons[i].dataset.profile);
|
||||
}
|
||||
|
||||
expectedProfiles.forEach(function(profile) {
|
||||
var found = foundProfiles.indexOf(profile) !== -1;
|
||||
assert(found, 'Profile "' + profile + '" is present');
|
||||
});
|
||||
}
|
||||
|
||||
// Run all tests
|
||||
function runAllTests() {
|
||||
console.clear();
|
||||
console.log('========================================');
|
||||
console.log('Diagnostic Profiles Frontend Tests');
|
||||
console.log('========================================');
|
||||
console.log('Starting tests at ' + new Date().toLocaleTimeString());
|
||||
console.log('');
|
||||
|
||||
testResults = { passed: 0, failed: 0, tests: [] };
|
||||
|
||||
// Run tests
|
||||
testProfileGridRenders();
|
||||
testProfileButtonAttributes();
|
||||
testAllProfilesPresent();
|
||||
|
||||
// Tests with delays
|
||||
setTimeout(function() {
|
||||
testProfileSelection();
|
||||
testProfileDescriptionAppears();
|
||||
testToggleSwitchesUpdate();
|
||||
testDiagnosticCollectionProfile();
|
||||
|
||||
// Summary after all tests complete
|
||||
setTimeout(function() {
|
||||
console.log('\n========================================');
|
||||
console.log('Test Results Summary');
|
||||
console.log('========================================');
|
||||
console.log('%cPassed: ' + testResults.passed, 'color: green; font-weight: bold');
|
||||
console.log('%cFailed: ' + testResults.failed, 'color: red; font-weight: bold');
|
||||
console.log('Total: ' + (testResults.passed + testResults.failed));
|
||||
console.log('');
|
||||
|
||||
if (testResults.failed === 0) {
|
||||
console.log('%c✅ All tests passed!', 'color: green; font-weight: bold; font-size: 16px');
|
||||
} else {
|
||||
console.log('%c❌ Some tests failed', 'color: red; font-weight: bold; font-size: 16px');
|
||||
console.log('');
|
||||
console.log('Failed tests:');
|
||||
testResults.tests.forEach(function(test) {
|
||||
if (test.status === 'FAIL') {
|
||||
console.log(' - ' + test.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 2000);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
console.log('✅ Frontend test suite loaded');
|
||||
console.log('Run tests with: runAllTests()');
|
||||
203
luci-app-system-hub/tests/integration/test-diagnostic-workflow.sh
Executable file
203
luci-app-system-hub/tests/integration/test-diagnostic-workflow.sh
Executable file
@ -0,0 +1,203 @@
|
||||
#!/bin/sh
|
||||
# Integration Test: Complete diagnostic workflow with profiles
|
||||
# Tests end-to-end user workflow from profile selection to archive download
|
||||
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo "========================================"
|
||||
echo "Diagnostic Workflow Integration Test"
|
||||
echo "========================================"
|
||||
echo "Testing complete workflow with performance-problems profile"
|
||||
echo ""
|
||||
|
||||
# Step 1: List available profiles
|
||||
echo -e "${YELLOW}STEP 1:${NC} Listing available profiles"
|
||||
echo "---------------------------------------"
|
||||
|
||||
profiles=$(ubus call luci.system-hub list_diagnostic_profiles 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Could not list profiles"
|
||||
echo "$profiles"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Profiles listed"
|
||||
echo "$profiles" | jsonfilter -e '@.profiles[*].name' | sed 's/^/ - /'
|
||||
echo ""
|
||||
|
||||
# Step 2: Select performance-problems profile
|
||||
echo -e "${YELLOW}STEP 2:${NC} Selecting performance-problems profile"
|
||||
echo "---------------------------------------"
|
||||
|
||||
profile=$(ubus call luci.system-hub get_diagnostic_profile '{"name":"performance-problems"}' 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Could not get profile"
|
||||
echo "$profile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Profile retrieved"
|
||||
echo " Name: $(echo "$profile" | jsonfilter -e '@.name')"
|
||||
echo " Label: $(echo "$profile" | jsonfilter -e '@.label')"
|
||||
echo " Tests: $(echo "$profile" | jsonfilter -e '@.tests')"
|
||||
echo ""
|
||||
|
||||
# Step 3: Collect diagnostics using profile
|
||||
echo -e "${YELLOW}STEP 3:${NC} Collecting diagnostics with profile"
|
||||
echo "---------------------------------------"
|
||||
|
||||
archive=$(ubus call luci.system-hub collect_diagnostics '{"profile":"performance-problems"}' 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Could not collect diagnostics"
|
||||
echo "$archive"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success=$(echo "$archive" | jsonfilter -e '@.success')
|
||||
if [ "$success" != "true" ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Collection reported failure"
|
||||
echo "$archive"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
filename=$(echo "$archive" | jsonfilter -e '@.file')
|
||||
size=$(echo "$archive" | jsonfilter -e '@.size')
|
||||
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Archive created"
|
||||
echo " Filename: $filename"
|
||||
echo " Size: $size bytes"
|
||||
echo ""
|
||||
|
||||
# Step 4: Verify archive exists
|
||||
echo -e "${YELLOW}STEP 4:${NC} Verifying archive exists on filesystem"
|
||||
echo "---------------------------------------"
|
||||
|
||||
archive_path="/tmp/system-hub/diagnostics/$filename"
|
||||
if [ ! -f "$archive_path" ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Archive file not found at $archive_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Archive file exists"
|
||||
ls -lh "$archive_path" | awk '{print " Size: " $5 " Modified: " $6 " " $7 " " $8}'
|
||||
echo ""
|
||||
|
||||
# Step 5: Verify archive contains profile name
|
||||
echo -e "${YELLOW}STEP 5:${NC} Verifying profile name in filename"
|
||||
echo "---------------------------------------"
|
||||
|
||||
if echo "$filename" | grep -q "performance-problems"; then
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Filename contains profile name"
|
||||
echo " $filename"
|
||||
else
|
||||
echo -e "${RED}❌ FAILED${NC}: Profile name not in filename"
|
||||
echo " Expected: performance-problems"
|
||||
echo " Found: $filename"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 6: List diagnostics to verify it appears
|
||||
echo -e "${YELLOW}STEP 6:${NC} Listing diagnostics archives"
|
||||
echo "---------------------------------------"
|
||||
|
||||
diagnostics=$(ubus call luci.system-hub list_diagnostics 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Could not list diagnostics"
|
||||
echo "$diagnostics"
|
||||
rm -f "$archive_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if echo "$diagnostics" | grep -q "$filename"; then
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Archive appears in diagnostics list"
|
||||
echo "$diagnostics" | jsonfilter -e '@.archives[*].name' | grep "$filename" | sed 's/^/ - /'
|
||||
else
|
||||
echo -e "${RED}❌ FAILED${NC}: Archive not in diagnostics list"
|
||||
rm -f "$archive_path"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Step 7: Download archive (simulate)
|
||||
echo -e "${YELLOW}STEP 7:${NC} Downloading archive (verifying RPC)"
|
||||
echo "---------------------------------------"
|
||||
|
||||
download=$(ubus call luci.system-hub download_diagnostic "{\"name\":\"$filename\"}" 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Could not download archive"
|
||||
echo "$download"
|
||||
rm -f "$archive_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
download_success=$(echo "$download" | jsonfilter -e '@.success')
|
||||
if [ "$download_success" != "true" ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Download reported failure"
|
||||
rm -f "$archive_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Download RPC successful"
|
||||
echo " (Archive data would be base64 encoded for browser download)"
|
||||
echo ""
|
||||
|
||||
# Step 8: Delete archive
|
||||
echo -e "${YELLOW}STEP 8:${NC} Deleting archive"
|
||||
echo "---------------------------------------"
|
||||
|
||||
delete=$(ubus call luci.system-hub delete_diagnostic "{\"name\":\"$filename\"}" 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Could not delete archive"
|
||||
echo "$delete"
|
||||
rm -f "$archive_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
delete_success=$(echo "$delete" | jsonfilter -e '@.success')
|
||||
if [ "$delete_success" != "true" ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Delete reported failure"
|
||||
rm -f "$archive_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Archive deleted"
|
||||
echo ""
|
||||
|
||||
# Step 9: Verify deletion
|
||||
echo -e "${YELLOW}STEP 9:${NC} Verifying archive was deleted"
|
||||
echo "---------------------------------------"
|
||||
|
||||
if [ -f "$archive_path" ]; then
|
||||
echo -e "${RED}❌ FAILED${NC}: Archive file still exists after deletion"
|
||||
rm -f "$archive_path"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ SUCCESS${NC}: Archive file removed from filesystem"
|
||||
echo ""
|
||||
|
||||
# Final summary
|
||||
echo "========================================"
|
||||
echo -e "${GREEN}✅ ALL WORKFLOW STEPS PASSED${NC}"
|
||||
echo "========================================"
|
||||
echo "Successfully tested complete diagnostic workflow:"
|
||||
echo " 1. List profiles"
|
||||
echo " 2. Get specific profile"
|
||||
echo " 3. Collect diagnostics with profile"
|
||||
echo " 4. Verify archive creation"
|
||||
echo " 5. Verify profile name in filename"
|
||||
echo " 6. List diagnostics"
|
||||
echo " 7. Download archive (RPC)"
|
||||
echo " 8. Delete archive"
|
||||
echo " 9. Verify deletion"
|
||||
echo ""
|
||||
|
||||
exit 0
|
||||
273
luci-app-system-hub/tests/test-profiles.sh
Executable file
273
luci-app-system-hub/tests/test-profiles.sh
Executable file
@ -0,0 +1,273 @@
|
||||
#!/bin/sh
|
||||
# System Hub Diagnostic Profiles - Unit Tests
|
||||
# Tests RPCD backend functions in isolation
|
||||
|
||||
. /lib/functions.sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
# Test counters
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
TEST_NAME=""
|
||||
|
||||
# Color codes for output
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Assert functions
|
||||
assert_equals() {
|
||||
local expected="$1"
|
||||
local actual="$2"
|
||||
local test_name="$3"
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo -e "${GREEN}✅ PASS${NC}: $test_name"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ FAIL${NC}: $test_name"
|
||||
echo " Expected: $expected"
|
||||
echo " Actual: $actual"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local haystack="$1"
|
||||
local needle="$2"
|
||||
local test_name="$3"
|
||||
|
||||
if echo "$haystack" | grep -q "$needle"; then
|
||||
echo -e "${GREEN}✅ PASS${NC}: $test_name"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ FAIL${NC}: $test_name"
|
||||
echo " Expected to find: $needle"
|
||||
echo " In: $haystack"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_json_valid() {
|
||||
local json="$1"
|
||||
local test_name="$2"
|
||||
|
||||
if echo "$json" | jsonfilter -e '@' >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ PASS${NC}: $test_name"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ FAIL${NC}: $test_name"
|
||||
echo " Invalid JSON: $json"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 1: Profile listing returns valid JSON
|
||||
test_list_profiles_json() {
|
||||
echo ""
|
||||
echo "TEST 1: list_diagnostic_profiles returns valid JSON"
|
||||
echo "=================================================="
|
||||
|
||||
local result=$(ubus call luci.system-hub list_diagnostic_profiles 2>&1)
|
||||
|
||||
assert_json_valid "$result" "list_diagnostic_profiles returns valid JSON"
|
||||
}
|
||||
|
||||
# Test 2: At least 5 profiles returned
|
||||
test_list_profiles_count() {
|
||||
echo ""
|
||||
echo "TEST 2: At least 5 profiles available"
|
||||
echo "======================================"
|
||||
|
||||
local result=$(ubus call luci.system-hub list_diagnostic_profiles 2>&1)
|
||||
local count=$(echo "$result" | jsonfilter -e '@.profiles[*]' 2>/dev/null | wc -l)
|
||||
|
||||
if [ "$count" -ge 5 ]; then
|
||||
assert_equals "5" "$count" "At least 5 profiles returned (found $count)"
|
||||
else
|
||||
assert_equals "5" "$count" "At least 5 profiles returned"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 3: Profile names are correct
|
||||
test_profile_names() {
|
||||
echo ""
|
||||
echo "TEST 3: Profile names are correct"
|
||||
echo "=================================="
|
||||
|
||||
local result=$(ubus call luci.system-hub list_diagnostic_profiles 2>&1)
|
||||
|
||||
assert_contains "$result" "network-issues" "Contains network-issues profile"
|
||||
assert_contains "$result" "performance-problems" "Contains performance-problems profile"
|
||||
assert_contains "$result" "security-audit" "Contains security-audit profile"
|
||||
assert_contains "$result" "wifi-problems" "Contains wifi-problems profile"
|
||||
assert_contains "$result" "full-diagnostic" "Contains full-diagnostic profile"
|
||||
}
|
||||
|
||||
# Test 4: Get specific profile - network-issues
|
||||
test_get_profile_network() {
|
||||
echo ""
|
||||
echo "TEST 4: Get network-issues profile"
|
||||
echo "==================================="
|
||||
|
||||
local result=$(ubus call luci.system-hub get_diagnostic_profile '{"name":"network-issues"}' 2>&1)
|
||||
|
||||
assert_json_valid "$result" "get_diagnostic_profile returns valid JSON"
|
||||
|
||||
local name=$(echo "$result" | jsonfilter -e '@.name' 2>/dev/null)
|
||||
assert_equals "network-issues" "$name" "Profile name is network-issues"
|
||||
|
||||
local tests=$(echo "$result" | jsonfilter -e '@.tests' 2>/dev/null)
|
||||
assert_contains "$tests" "connectivity" "Profile contains connectivity test"
|
||||
assert_contains "$tests" "dns" "Profile contains dns test"
|
||||
|
||||
local include_logs=$(echo "$result" | jsonfilter -e '@.include_logs' 2>/dev/null)
|
||||
assert_equals "1" "$include_logs" "Profile includes logs"
|
||||
}
|
||||
|
||||
# Test 5: Get specific profile - performance-problems
|
||||
test_get_profile_performance() {
|
||||
echo ""
|
||||
echo "TEST 5: Get performance-problems profile"
|
||||
echo "========================================="
|
||||
|
||||
local result=$(ubus call luci.system-hub get_diagnostic_profile '{"name":"performance-problems"}' 2>&1)
|
||||
|
||||
local name=$(echo "$result" | jsonfilter -e '@.name' 2>/dev/null)
|
||||
assert_equals "performance-problems" "$name" "Profile name is performance-problems"
|
||||
|
||||
local include_config=$(echo "$result" | jsonfilter -e '@.include_config' 2>/dev/null)
|
||||
assert_equals "0" "$include_config" "Performance profile does not include config"
|
||||
}
|
||||
|
||||
# Test 6: Get invalid profile
|
||||
test_get_profile_invalid() {
|
||||
echo ""
|
||||
echo "TEST 6: Get invalid profile returns error"
|
||||
echo "=========================================="
|
||||
|
||||
local result=$(ubus call luci.system-hub get_diagnostic_profile '{"name":"invalid-profile"}' 2>&1)
|
||||
|
||||
assert_json_valid "$result" "Invalid profile returns valid JSON error"
|
||||
assert_contains "$result" "error" "Response contains error field"
|
||||
}
|
||||
|
||||
# Test 7: Collect diagnostics with profile
|
||||
test_collect_with_profile() {
|
||||
echo ""
|
||||
echo "TEST 7: Collect diagnostics with profile"
|
||||
echo "========================================="
|
||||
|
||||
local result=$(ubus call luci.system-hub collect_diagnostics '{"profile":"network-issues"}' 2>&1)
|
||||
|
||||
assert_json_valid "$result" "collect_diagnostics with profile returns valid JSON"
|
||||
|
||||
local success=$(echo "$result" | jsonfilter -e '@.success' 2>/dev/null)
|
||||
assert_equals "true" "$success" "Diagnostic collection succeeds"
|
||||
|
||||
local filename=$(echo "$result" | jsonfilter -e '@.file' 2>/dev/null)
|
||||
assert_contains "$filename" "network-issues" "Archive filename contains profile name"
|
||||
|
||||
# Cleanup
|
||||
if [ -n "$filename" ]; then
|
||||
rm -f "/tmp/system-hub/diagnostics/$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 8: Profile filename format
|
||||
test_profile_filename_format() {
|
||||
echo ""
|
||||
echo "TEST 8: Profile filename format is correct"
|
||||
echo "==========================================="
|
||||
|
||||
local result=$(ubus call luci.system-hub collect_diagnostics '{"profile":"security-audit"}' 2>&1)
|
||||
local filename=$(echo "$result" | jsonfilter -e '@.file' 2>/dev/null)
|
||||
|
||||
# Format should be: diagnostics-{hostname}-{profile}-{timestamp}.tar.gz
|
||||
assert_contains "$filename" "security-audit" "Filename contains profile name"
|
||||
assert_contains "$filename" ".tar.gz" "Filename has .tar.gz extension"
|
||||
assert_contains "$filename" "diagnostics-" "Filename starts with diagnostics-"
|
||||
|
||||
# Cleanup
|
||||
if [ -n "$filename" ]; then
|
||||
rm -f "/tmp/system-hub/diagnostics/$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 9: Profile overrides flags
|
||||
test_profile_overrides_flags() {
|
||||
echo ""
|
||||
echo "TEST 9: Profile overrides individual flags"
|
||||
echo "==========================================="
|
||||
|
||||
# Performance profile has include_config=0
|
||||
local result=$(ubus call luci.system-hub collect_diagnostics '{"profile":"performance-problems","include_config":1}' 2>&1)
|
||||
|
||||
local success=$(echo "$result" | jsonfilter -e '@.success' 2>/dev/null)
|
||||
assert_equals "true" "$success" "Collection with profile override succeeds"
|
||||
|
||||
local filename=$(echo "$result" | jsonfilter -e '@.file' 2>/dev/null)
|
||||
assert_contains "$filename" "performance-problems" "Filename contains profile name"
|
||||
|
||||
# Cleanup
|
||||
if [ -n "$filename" ]; then
|
||||
rm -f "/tmp/system-hub/diagnostics/$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test 10: All profiles can be retrieved
|
||||
test_all_profiles_retrievable() {
|
||||
echo ""
|
||||
echo "TEST 10: All 5 profiles can be retrieved individually"
|
||||
echo "======================================================"
|
||||
|
||||
local profiles="network-issues performance-problems security-audit wifi-problems full-diagnostic"
|
||||
|
||||
for profile in $profiles; do
|
||||
local result=$(ubus call luci.system-hub get_diagnostic_profile "{\"name\":\"$profile\"}" 2>&1)
|
||||
local name=$(echo "$result" | jsonfilter -e '@.name' 2>/dev/null)
|
||||
assert_equals "$profile" "$name" "Profile $profile can be retrieved"
|
||||
done
|
||||
}
|
||||
|
||||
# Run all tests
|
||||
echo "========================================"
|
||||
echo "System Hub Diagnostic Profiles Unit Tests"
|
||||
echo "========================================"
|
||||
echo "Starting test run at $(date)"
|
||||
echo ""
|
||||
|
||||
test_list_profiles_json
|
||||
test_list_profiles_count
|
||||
test_profile_names
|
||||
test_get_profile_network
|
||||
test_get_profile_performance
|
||||
test_get_profile_invalid
|
||||
test_collect_with_profile
|
||||
test_profile_filename_format
|
||||
test_profile_overrides_flags
|
||||
test_all_profiles_retrievable
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Test Results Summary"
|
||||
echo "========================================"
|
||||
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
|
||||
echo -e "${RED}Failed: $TESTS_FAILED${NC}"
|
||||
echo "Total: $((TESTS_PASSED + TESTS_FAILED))"
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ All tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}❌ Some tests failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user