Add 6 new test cases covering previously untested branches:
- Empty host returns error
- Whitespace-only host returns error
- Unknown node type keeps host without port
- Docker node type keeps host without port
- IPv4 address gets default port
- IPv4 with custom port preserved
Coverage improved from 92.6% to 96.3%.
Add 13 test cases for convertPBSSnapshots covering:
- Empty/nil input handling
- Basic snapshot field mapping
- Files as string array
- Files as object array with filename field
- Invalid file objects ignored gracefully
- Verification status as string ("ok" vs other)
- Verification status as object with state field
- Verification object without state field
- Multiple snapshots with unique IDs
- Empty namespace handling in ID generation
- Container (ct) backup type
Coverage: convertPBSSnapshots 0% -> 100%
- Test nil input returns nil
- Test empty slice returns nil
- Test single task with all fields populated
- Test task with error state (failed tasks)
- Test multiple tasks conversion
- Test nil time pointers preserved as nil
- Test zero time values converted to nil (defensive handling)
- Test all optional time fields (UpdatedAt, StartedAt, CompletedAt)
Improves monitoring package coverage for Docker Swarm task handling.
- sanitizeGuestAddressStrings: IPv6 loopback with CIDR (::1/128) edge case
(96.2% -> 100%)
- parseContainerConfigNetworks: parts without equals, mac/ips/ip6addr/ip6prefix
key variants, whitespace-only values, all-empty results (97.2% -> 100%)
- mergeNVMeTempsIntoDisks: NVMe disk with no legacy temps (continue branch),
more disks than temps (break branch) (97% -> 100%)
Go map iteration order is non-deterministic, causing flaky test failures.
This fix ensures "Composite" sensor is always preferred over "Sensor 1"
by checking them in separate loops rather than relying on iteration order.
- recordGuestSnapshot: nil handling, field setting, map initialization
- GetDiagnosticSnapshots: nil/empty handling, sorting verification
Both functions now at 100% coverage.
clearNodeOfflineAlert (0%→100%):
- No alert and no count is no-op
- Resets offline count when node comes online
- Clears existing alert and adds to resolved
clearPBSOfflineAlert (0%→100%):
- No alert and no count is no-op
- Resets offline confirmation count
- Clears existing alert and adds to resolved
clearPMGOfflineAlert (0%→100%):
- No alert and no count is no-op
- Resets offline confirmation count
- Clears existing alert and adds to resolved
Coverage: 57.4% → 58.5%
SetMetricHooks (0%→100%):
- Sets all four hooks correctly
- Nil hooks are safely handled
- Properly saves/restores state to avoid test pollution
NotifyExistingAlert (0%→100%):
- Non-existent alert is no-op
- Existing alert dispatches async notification
GetResolvedAlert (0%→100%):
- Returns nil for non-existent alert
- Returns nil for nil resolved entry
- Returns nil when Alert is nil
- Returns cloned alert with resolved time
GetAlertHistory/GetAlertHistorySince (0%→100%):
- Returns history from history manager
- Respects limit parameter
- Zero time returns all history
- Filters by time correctly
ClearAlertHistory (0%→100%):
- Clears all history entries
Coverage: 56.9% → 57.4%
HandleHostRemoved (0%→100%):
- Empty host ID is no-op
- Clears host offline alert and confirmations
- Clears host metric alerts (CPU, memory)
- Clears host disk alerts
- Clears all alert types together
ReevaluateGuestAlert (0%→100%):
- No active alerts is no-op
- Clears alert when threshold disabled (nil)
- Clears alert when trigger is zero
- Clears alert when value below clear threshold
- Clears alert when value below trigger threshold
- Keeps alert when value above both thresholds
- Processes all metric types (7 types)
- Clears pending alert when threshold disabled
- Uses clear equals trigger when clear is zero
- Ignores alerts for different guests
NormalizeMetricTimeThresholds (0%→100%):
- Updated existing test to call public wrapper instead of internal function
Coverage: 54.6% → 55.6%
- TestClearActiveAlertsEmptyMaps: tests early return when both maps empty
- sanitizeAlertKey: test for input with only slashes/backslashes -> root
Coverage improvement:
- sanitizeAlertKey: 96.4% → 100%
- alerts package: 52.8%
- calls callback synchronously with alert ID
- calls callback asynchronously
- noop when callback is nil
- recovers from panic in sync and async callbacks
- safeCallResolvedCallback 90%→100%
- calls callback with alert and level
- noop when callback is nil
- recovers from panic in callback
- clones alert to prevent concurrent modification
- safeCallEscalateCallback 0%→100%
- clears alerts and state tracking not in seen set
- skips alerts from other hosts
- handles empty seen set (clears all)
- cleanupDockerContainerAlerts 75%→100%
- clears alerts not in seen set (stale disk alerts)
- empty host ID is noop
- skips nil alerts without panic
- skips non-matching prefix (non-disk alerts)
- cleanupHostDiskAlerts 76.9%→100%
- clears all disk alerts for specified host
- empty hostID is noop
- skips nil alerts without panic
- noop when no matching alerts
- clearHostDiskAlerts 72.7%→100%
- with/without host ID (prefix selection)
- derives ID from service name with sanitization
- special chars replaced with dashes
- preserves alphanumeric, underscore, hyphen
- trims leading/trailing dashes/underscores
- truncates long IDs to 32 chars
- fallback to 'service' when empty
- dockerServiceResourceID 23.8%→100%
- returns name when present
- returns trimmed name
- returns truncated ID when name empty (first 12 chars)
- returns full short ID when < 12 chars
- returns 'service' when both empty/whitespace
- prefers name over ID
- dockerServiceDisplayName 33.3%→100%
- nil thresholds get defaults (CPU 95, Memory 92, Disk 95)
- low thresholds raised to minimum relaxed values
- high thresholds unchanged
- clear adjusted when too close to trigger
- original config unchanged (no mutation)
- alerts package coverage improvement: applyRelaxedGuestThresholds 75%→93.8%
Cover all branches: disabled rate limit (zero/negative MaxAlertsHour),
under limit, at limit blocking, separate limits per alertID, old entry
cleanup, and mixed old/recent entries. Coverage improved from 71.4% to 100%.
Cover both branches: single alert template (isSingle=true with 1 alert)
and grouped alert template (isSingle=false or multiple alerts). Tests
verify subject format, HTML structure, and text body generation.
Coverage improved from 66.7% to 100%.
Cover all branches: nil/empty TimeThresholds, direct match, canonical key
matching (vm→guest, container→guest), "all" fallback, specific match
precedence, and global threshold fallback. Coverage improved from 85.7% to 100%.
Cover all branches: user without realm gets @pam appended, user with
realm unchanged, token auth skips user modification, empty user handling,
and fingerprint/verifySSL preservation. Coverage improved from 66.7% to 100%.
Cover all branches: disabled state, invalid timezone fallback, day not
enabled, invalid start/end time parsing, overnight window spanning
midnight, and normal daytime window. Coverage improved from 61.5% to 96.2%.
Cover all branches of flapping detection: disabled state, below/at/above
threshold, already-flapping state (no suppression update), and window
expiry pruning. Coverage improved from 61.9% to 95.2%.
- Test different instance and node (full format)
- Test same instance and node (short format)
- Test empty instance defaults to node
- Test whitespace trimming
- Improves BuildGuestKey coverage from 71.4% to 100%
Add test case for malformed IPv6 URL that triggers url.Parse error.
Improves coverage from 90% to 95%. The remaining 5% is a template
Execute error path that's effectively unreachable with current types.
When tasks have identical NextRun and Priority, the Less function
falls back to comparing InstanceName alphabetically. Add test to
cover this edge case branch, improving Less coverage to 100%.
Tests for alert filter evaluation logic:
- evaluateVMCondition: metric/text/raw conditions for VMs
- evaluateContainerCondition: same for containers
- evaluateFilterStack: AND/OR logic with multiple conditions
- evaluateFilterCondition: type dispatch
74 test cases covering:
- All metric types (cpu, memory, disk, diskread, diskwrite, network)
- All comparison operators (>, <, >=, <=, =, ==)
- Text search fields (name, node, vmid) with case insensitivity
- Raw text search across multiple fields
- Value type conversions (int to float64)
- Edge cases (zero, empty, invalid types)
Coverage improved from 46.8% to 48.9%.
Tests for LXC container config parsing functions:
- sanitizeRootFSDevice: rootfs device extraction
- collectIPsFromInterface: recursive IP extraction from various types
- parseContainerRawIPs: JSON IP parsing
- parseContainerConfigNetworks: network interface parsing with MAC
normalization, CIDR stripping, sorting
- parseContainerMountMetadata: mount point metadata parsing
- extractContainerRootDeviceFromConfig: root device extraction
- mergeContainerNetworkInterface: interface merging by name/MAC
- convertContainerDiskInfo: Proxmox disk info conversion
- ensureContainerRootDiskEntry: root disk entry creation
91+ test cases covering edge cases, invalid data, and complex scenarios.
Coverage improved from 40.6% to 42.2%.
Tests for all 9 functions handling Docker host identification:
- uniqueNonEmptyStrings: dedupe with order preservation
- sanitizeDockerHostSuffix: string cleaning for IDs
- tokenHintFromRecord: redacted token hints
- dockerHostIDExists: ID existence check
- dockerHostSuffixCandidates: candidate suffix generation
- fallbackDockerHostID: hash-based ID generation
- findMatchingDockerHost: host matching by various criteria
- generateDockerHostIdentifier: unique ID generation with suffixes
- resolveDockerHostIdentifier: main resolver orchestration
86 test cases covering edge cases (nil, empty, whitespace), Unicode
handling, and complex matching scenarios.
Tests cover nil/empty inputs, basic field copying, time fields
(CreatedAt/UpdatedAt with nil and zero value handling), update status
conversion, endpoint ports, and labels cloning.
Add comprehensive error handling tests for two pure functions:
renderWebhookURL (8 new test cases):
- Empty/whitespace URL template validation
- Invalid template syntax (unclosed braces, undefined functions)
- Template producing empty URL
- Missing scheme or host in rendered URL
UpdateAllowedPrivateCIDRs (expanded from 8 to 29 cases):
- Invalid IP addresses (garbage, out of range, malformed)
- Invalid CIDR notation (prefix too large, negative, non-numeric)
- Malformed strings (double slash, invalid IP with valid prefix)
- Success cases for valid IPv4/IPv6 CIDRs and bare IPs
- Use public API AcknowledgeDockerHostCommand instead of private function
- Use strings.Contains instead of custom helper functions
- Add test case for empty hostID (skips validation, succeeds)
The error message referenced "Settings -> Docker -> Removed hosts" but
that UI path no longer exists. The correct path is now
"Settings -> Agents -> Removed Docker Hosts".
Related to #778
Add comprehensive tests for EnhancedEmailManager covering:
- Rate limiting (exceeds limit, resets after minute, concurrency, negative values)
- Provider username defaults (SendGrid, SparkPost, Resend, Postmark)
- TLS routing (TLS, StartTLS, plain connections)
- Retry logic (retries on failure, rate limit prevents retry)
- Connection error handling for sendTLS, sendStartTLS, sendPlain
This is the first test file for email_enhanced.go which previously had
0% coverage on sendTLS, sendStartTLS, and TestConnection functions.
Move guest threshold resolution and filter evaluation logic from
alerts.go to filter_evaluation.go for better code organization.
Extracted functions:
- evaluateFilterCondition
- evaluateVMCondition
- evaluateContainerCondition
- evaluateFilterStack
- getGuestThresholds
This reduces alerts.go by ~370 lines and isolates the rule evaluation
engine that determines which thresholds apply to guests.
Cover API validation functions that were previously untested:
- validateIPAddress: IPv4/IPv6 validation with edge cases
- validatePort: Port range validation (1-65535)
- defaultPortForNodeType: Proxmox node type to default port mapping
This function was superseded by HandleTestNode which is wired to the
router at /api/config/nodes/{id}/test. The old function was not
registered in the router and served no purpose.
Removes 121 lines of unused code.
The host-agent's isCPUChip function was missing rp1_adc (Raspberry Pi RP1 ADC),
which is already detected in the monitoring package. This sync ensures consistent
CPU temperature chip detection across both code paths.
Tests for annotateResolvedMetadata, resolveAppriseNotificationType,
normalizeQueueType, and resolvedTimeFromAlerts (44 test cases).
Coverage 38.0% → 39.0%.
Add 56 test cases covering:
- isTransientError: 13 cases for context errors, retryable MonitorErrors,
and standard error types (nil, Canceled, DeadlineExceeded, Timeout, etc.)
- shouldTryPortlessFallback: 14 cases for connection error patterns that
trigger port fallback (refused, reset, no such host, timeout variants)
- shouldAttemptFallback: 13 cases for timeout/deadline/canceled patterns
used in storage polling fallback
- classifyDLQReason: 8 cases for dead letter queue reason classification
(max_retry_attempts vs permanent_failure)
- Edge cases: 3 cases for overlapping error patterns and partial matches
First test file for these error classification utilities used in retry
and fallback decision logic.
52 test cases covering:
- firstValueForKeys: 18 cases for multi-key lookup with priority ordering
- hasAnyKey: 15 cases for key existence checking
- discoveryConfigMap: 19 cases for nested config extraction with camelCase/snake_case support
Tests verify edge cases including nil maps, empty key slices, type assertion failures,
and priority ordering for field name variants used in configuration parsing.
Add 97 test cases for four utility functions in monitor.go:
- stringValue (30 cases): Type-to-string conversion for various Go types
including string, int types, float types, json.Number, fmt.Stringer
- anyToInt64 (34 cases): Interface-to-int64 conversion with overflow
handling, string parsing, and json.Number support
- parseInterfaceStat (15 cases): Interface stat map parsing for network
interface statistics
- extractGuestOSInfo (18 cases): QEMU guest agent OS info extraction
with fallback logic for various Linux distros, Windows, FreeBSD
Coverage for these functions: 0% → 100%
Package coverage: 37.5% → 38.5%
Added comprehensive tests for 6 previously untested pure utility functions:
- sortContent: sorts comma-separated storage content values
- formatSeconds: converts total seconds to HH:MM:SS format
- dedupeStringsPreserveOrder: deduplicates strings while preserving order
- sanitizeGuestAddressStrings: validates and filters IP addresses
- copyFloatPointer: creates independent copy of float64 pointer
- clampInterval: clamps time.Duration to specified bounds
Tests cover edge cases including empty inputs, boundary conditions,
special values (DHCP, loopback addresses), and various separators.
Coverage increased from 36.8% to 37.5%.
Coverage for sessionHash, CreateSession, ValidateSession,
ValidateAndExtendSession, DeleteSession, persistence, and cleanup.
21 test cases covering session lifecycle, expiration handling,
and disk persistence. Coverage 25.0% → 25.3%.
Tests for detectProxy, isConnectionSecure, getCookieSettings, and
generateSessionToken functions. Covers proxy detection for various
headers including X-Forwarded-For, CF-Ray, Forwarded (RFC 7239),
and secure connection detection via TLS and proxy headers.
32 test cases covering makeNodeSnapshotKey and makeGuestSnapshotKey
utility functions used for diagnostic memory snapshot storage. Tests
cover simple inputs, edge cases (empty strings, negative VMIDs),
special characters, and key uniqueness verification. Also includes
struct field tests for NodeMemoryRaw and VMMemoryRaw.
First test file for diagnostic_snapshots.go. Coverage 36.7% → 36.8%.
The backup status indicator feature was incomplete - it added the UI
component but never populated VM/Container LastBackup from actual
backup data. Now SyncGuestBackupTimes() is called after storage
backups and PBS backups are polled, matching each guest's VMID to
its most recent backup timestamp.
Fixes#786
24 test cases covering transaction lifecycle: staging, commit, rollback, and cleanup.
Tests include atomic commit behavior, backup/restore on failure, directory creation,
permission handling, and idempotent cleanup.
First test file for import_transaction.go. Coverage 42.4% → 42.7%.
22 test cases covering CRUD operations (Get, GetAll, Set, Delete,
ReplaceAll), persistence (Load, save with atomic write, directory
creation), legacy ID migration (GetWithLegacyMigration for clustered
and standalone formats), round-trip verification, and concurrency.
First test file for guest_metadata.go, complementing the similar
docker_metadata_test.go added in previous run.
When multiple cluster nodes register sensor-proxy, each registration
was overwriting the previous node's control token on the shared
PVEInstance. This caused "Proxy token not recognized" errors on all
but the last-registered node.
Changes:
- Add TemperatureProxyControlToken field to ClusterEndpoint struct
- Store control tokens per-endpoint for cluster registrations
- Check both instance-level and endpoint-level tokens when validating
Related to #738
Test coverage for internal/config/oidc.go functions:
- normaliseList: 8 cases for deduplication, trimming, empty handling
- parseDelimited: 8 cases for comma/space separation
- DefaultRedirectURL: 7 cases for URL construction
- OIDCConfig.Clone: 2 cases for deep copy behavior
- OIDCConfig.ApplyDefaults: 9 cases for defaults and normalization
- OIDCConfig.Validate: 9 cases for validation rules
- OIDCConfig.MergeFromEnv: 5 cases for environment variable merging
- NewOIDCConfig: constructor verification
Total: 56 new test cases (583 lines).
Tests for unified_agent.go:
- normalizeUnifiedAgentArch: 51 test cases covering all supported
architectures (linux/darwin/windows), case insensitivity,
whitespace handling, and invalid inputs
Tests for updates.go:
- getClientIP: 18 test cases covering X-Forwarded-For priority,
X-Real-IP fallback, RemoteAddr extraction, IPv4/IPv6 support,
nil header safety, and case insensitivity