|
| 1 | +// Copyright (c) Mondoo, Inc. |
| 2 | +// SPDX-License-Identifier: BUSL-1.1 |
| 3 | + |
| 4 | +//go:build windows |
| 5 | + |
| 6 | +package services |
| 7 | + |
| 8 | +import ( |
| 9 | + "testing" |
| 10 | + |
| 11 | + "github.com/stretchr/testify/assert" |
| 12 | + "github.com/stretchr/testify/require" |
| 13 | + "golang.org/x/sys/windows/svc" |
| 14 | +) |
| 15 | + |
| 16 | +func TestGetNativeWindowsServices(t *testing.T) { |
| 17 | + services, err := GetNativeWindowsServices() |
| 18 | + require.NoError(t, err) |
| 19 | + assert.NotEmpty(t, services, "expected at least one service") |
| 20 | + |
| 21 | + // Verify service structure is populated correctly |
| 22 | + for _, s := range services { |
| 23 | + assert.NotEmpty(t, s.Name, "service name should not be empty") |
| 24 | + assert.Equal(t, "windows", s.Type, "service type should be 'windows'") |
| 25 | + assert.True(t, s.Installed, "service should be marked as installed") |
| 26 | + // State should be one of the known states |
| 27 | + assert.Contains(t, []State{ |
| 28 | + ServiceStopped, |
| 29 | + ServiceStartPending, |
| 30 | + ServiceStopPending, |
| 31 | + ServiceRunning, |
| 32 | + ServiceContinuePending, |
| 33 | + ServicePausePending, |
| 34 | + ServicePaused, |
| 35 | + ServiceUnknown, |
| 36 | + }, s.State, "service state should be a known state") |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +func TestGetNativeWindowsServices_WellKnownServices(t *testing.T) { |
| 41 | + services, err := GetNativeWindowsServices() |
| 42 | + require.NoError(t, err) |
| 43 | + |
| 44 | + // These services should exist on all Windows systems |
| 45 | + wellKnownServices := []string{ |
| 46 | + "Winmgmt", // Windows Management Instrumentation |
| 47 | + "EventLog", // Windows Event Log |
| 48 | + "PlugPlay", // Plug and Play |
| 49 | + "RpcSs", // Remote Procedure Call (RPC) |
| 50 | + "Schedule", // Task Scheduler |
| 51 | + "SENS", // System Event Notification Service |
| 52 | + "Spooler", // Print Spooler (may be disabled but should exist) |
| 53 | + "W32Time", // Windows Time |
| 54 | + "Dhcp", // DHCP Client |
| 55 | + "Dnscache", // DNS Client |
| 56 | + "LanmanServer", // Server |
| 57 | + "LanmanWorkstation", // Workstation |
| 58 | + } |
| 59 | + |
| 60 | + serviceMap := make(map[string]*Service) |
| 61 | + for _, s := range services { |
| 62 | + serviceMap[s.Name] = s |
| 63 | + } |
| 64 | + |
| 65 | + foundCount := 0 |
| 66 | + for _, name := range wellKnownServices { |
| 67 | + if _, ok := serviceMap[name]; ok { |
| 68 | + foundCount++ |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + // At least half of the well-known services should be present |
| 73 | + // (some may be disabled/removed on minimal installations) |
| 74 | + assert.GreaterOrEqual(t, foundCount, len(wellKnownServices)/2, |
| 75 | + "expected at least %d well-known services, found %d", len(wellKnownServices)/2, foundCount) |
| 76 | +} |
| 77 | + |
| 78 | +func TestGetNativeWindowsServices_RunningServices(t *testing.T) { |
| 79 | + services, err := GetNativeWindowsServices() |
| 80 | + require.NoError(t, err) |
| 81 | + |
| 82 | + // At least some services should be running on any Windows system |
| 83 | + runningCount := 0 |
| 84 | + for _, s := range services { |
| 85 | + if s.Running { |
| 86 | + runningCount++ |
| 87 | + // Running services should have Running state |
| 88 | + assert.Equal(t, ServiceRunning, s.State, |
| 89 | + "service %s is marked running but state is %s", s.Name, s.State) |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + assert.Greater(t, runningCount, 0, "expected at least one running service") |
| 94 | +} |
| 95 | + |
| 96 | +func TestMapState(t *testing.T) { |
| 97 | + tests := []struct { |
| 98 | + input svc.State |
| 99 | + expected State |
| 100 | + }{ |
| 101 | + {svc.Stopped, ServiceStopped}, |
| 102 | + {svc.StartPending, ServiceStartPending}, |
| 103 | + {svc.StopPending, ServiceStopPending}, |
| 104 | + {svc.Running, ServiceRunning}, |
| 105 | + {svc.ContinuePending, ServiceContinuePending}, |
| 106 | + {svc.PausePending, ServicePausePending}, |
| 107 | + {svc.Paused, ServicePaused}, |
| 108 | + {svc.State(99), ServiceUnknown}, // Unknown state |
| 109 | + } |
| 110 | + |
| 111 | + for _, tc := range tests { |
| 112 | + t.Run(string(tc.expected), func(t *testing.T) { |
| 113 | + assert.Equal(t, tc.expected, mapState(tc.input)) |
| 114 | + }) |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +func TestIsEnabled(t *testing.T) { |
| 119 | + tests := []struct { |
| 120 | + name string |
| 121 | + startType uint32 |
| 122 | + expected bool |
| 123 | + }{ |
| 124 | + {"Boot", 0, true}, |
| 125 | + {"System", 1, true}, |
| 126 | + {"Automatic", 2, true}, |
| 127 | + {"Manual", 3, true}, |
| 128 | + {"Disabled", 4, false}, |
| 129 | + {"Unknown high value", 5, false}, |
| 130 | + {"Unknown higher value", 100, false}, |
| 131 | + } |
| 132 | + |
| 133 | + for _, tc := range tests { |
| 134 | + t.Run(tc.name, func(t *testing.T) { |
| 135 | + assert.Equal(t, tc.expected, isEnabled(tc.startType)) |
| 136 | + }) |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +func TestIsRunning(t *testing.T) { |
| 141 | + tests := []struct { |
| 142 | + state svc.State |
| 143 | + expected bool |
| 144 | + }{ |
| 145 | + {svc.Stopped, false}, |
| 146 | + {svc.StartPending, false}, |
| 147 | + {svc.StopPending, false}, |
| 148 | + {svc.Running, true}, |
| 149 | + {svc.ContinuePending, false}, |
| 150 | + {svc.PausePending, false}, |
| 151 | + {svc.Paused, false}, |
| 152 | + } |
| 153 | + |
| 154 | + for _, tc := range tests { |
| 155 | + t.Run(string(mapState(tc.state)), func(t *testing.T) { |
| 156 | + assert.Equal(t, tc.expected, isRunning(tc.state)) |
| 157 | + }) |
| 158 | + } |
| 159 | +} |
0 commit comments