1
+ Describe " Connection Module Tests" {
2
+ BeforeAll {
3
+ # Import required modules
4
+ Import-Module " $PSScriptRoot /../../../src/common/Connection.psm1" - Force
5
+
6
+ # Mock external dependencies for cross-platform testing
7
+ Mock Connect-ExchangeOnline { } - ModuleName Connection
8
+ Mock Disconnect-ExchangeOnline { } - ModuleName Connection
9
+ Mock Connect-IPPSSession { } - ModuleName Connection
10
+ Mock Connect-MgGraph { } - ModuleName Connection
11
+ Mock Disconnect-MgGraph { } - ModuleName Connection
12
+ Mock Get-ConnectionInformation {
13
+ [PSCustomObject ]@ {
14
+ UserPrincipalName = ' [email protected] '
15
+ ConnectionId = ' test-connection-id'
16
+ }
17
+ } - ModuleName Connection
18
+ Mock Get-MgContext {
19
+ [PSCustomObject ]@ {
20
+
21
+ Scopes = @ (' User.Read' , ' Mail.Read' )
22
+ }
23
+ } - ModuleName Connection
24
+ Mock Get-UserConfirmation { $true } - ModuleName Connection
25
+ }
26
+
27
+ Context " Module Import" {
28
+ It " Should import Connection module successfully" {
29
+ Get-Module - Name Connection* | Should -Not - BeNullOrEmpty
30
+ }
31
+
32
+ It " Should export expected functions" {
33
+ $ExportedFunctions = (Get-Module - Name Connection* ).ExportedFunctions.Keys
34
+ $ExportedFunctions | Should - Contain ' Connect-Service'
35
+ }
36
+ }
37
+
38
+ Context " Connect-Service Parameter Validation" {
39
+ It " Should require Services parameter" {
40
+ { Connect-Service } | Should - Throw
41
+ }
42
+
43
+ It " Should validate Services parameter values" {
44
+ { Connect-Service - Services ' InvalidService' } | Should - Throw
45
+ }
46
+
47
+ It " Should accept valid service names" {
48
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
49
+
50
+ { Connect-Service - Services ' ExchangeOnline' - DontConfirm } | Should -Not - Throw
51
+ { Connect-Service - Services ' SecurityComplience' - DontConfirm } | Should -Not - Throw
52
+ { Connect-Service - Services ' Graph' - DontConfirm } | Should -Not - Throw
53
+ }
54
+
55
+ It " Should accept multiple services" {
56
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
57
+ Mock Get-MgContext { $null } - ModuleName Connection
58
+
59
+ { Connect-Service - Services @ (' ExchangeOnline' , ' Graph' ) - DontConfirm } | Should -Not - Throw
60
+ }
61
+
62
+ It " Should accept optional Scopes parameter" {
63
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
64
+ Mock Get-MgContext { $null } - ModuleName Connection
65
+
66
+ { Connect-Service - Services ' Graph' - Scopes @ (' User.Read' ) - DontConfirm } | Should -Not - Throw
67
+ }
68
+
69
+ It " Should accept optional AccessToken parameter" {
70
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
71
+ Mock Get-MgContext { $null } - ModuleName Connection
72
+ $SecureToken = ConvertTo-SecureString ' token123' - AsPlainText - Force
73
+
74
+ { Connect-Service - Services ' Graph' - AccessToken $SecureToken - DontConfirm } | Should -Not - Throw
75
+ }
76
+ }
77
+
78
+ Context " ExchangeOnline Service Tests" {
79
+ BeforeEach {
80
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
81
+ }
82
+
83
+ It " Should handle ExchangeOnline connection" {
84
+ Mock Connect-ExchangeOnline { } - ModuleName Connection
85
+ Mock Get-ConnectionInformation {
86
+ [PSCustomObject ]@ {
87
+ UserPrincipalName = ' [email protected] '
88
+ ConnectionId = ' exchange-connection'
89
+ }
90
+ } - ModuleName Connection
91
+
92
+ { Connect-Service - Services ' ExchangeOnline' - DontConfirm } | Should -Not - Throw
93
+
94
+ Assert-MockCalled Connect-ExchangeOnline - Times 1 - ModuleName Connection
95
+ }
96
+
97
+ It " Should handle existing ExchangeOnline connection" {
98
+ Mock Get-ConnectionInformation {
99
+ [PSCustomObject ]@ {
100
+ UserPrincipalName = ' [email protected] '
101
+ ConnectionId = ' existing-connection'
102
+ }
103
+ } - ModuleName Connection
104
+ Mock Get-UserConfirmation { $true } - ModuleName Connection
105
+
106
+ { Connect-Service - Services ' ExchangeOnline' } | Should -Not - Throw
107
+
108
+ # Should not call Connect-ExchangeOnline if already connected and user confirms
109
+ Assert-MockCalled Get-UserConfirmation - Times 1 - ModuleName Connection
110
+ }
111
+
112
+ It " Should disconnect and reconnect if user declines" {
113
+ Mock Get-ConnectionInformation {
114
+ [PSCustomObject ]@ {
115
+ UserPrincipalName = ' [email protected] '
116
+ ConnectionId = ' existing-connection'
117
+ }
118
+ } - ModuleName Connection
119
+ Mock Get-UserConfirmation { $false } - ModuleName Connection
120
+ Mock Disconnect-ExchangeOnline { } - ModuleName Connection
121
+ Mock Connect-ExchangeOnline { } - ModuleName Connection
122
+
123
+ { Connect-Service - Services ' ExchangeOnline' } | Should -Not - Throw
124
+
125
+ Assert-MockCalled Disconnect-ExchangeOnline - Times 1 - ModuleName Connection
126
+ Assert-MockCalled Connect-ExchangeOnline - Times 1 - ModuleName Connection
127
+ }
128
+ }
129
+
130
+ Context " SecurityComplience Service Tests" {
131
+ BeforeEach {
132
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
133
+ }
134
+
135
+ It " Should handle SecurityComplience connection" {
136
+ Mock Connect-IPPSSession { } - ModuleName Connection
137
+ Mock Get-ConnectionInformation {
138
+ [PSCustomObject ]@ {
139
+ UserPrincipalName = ' [email protected] '
140
+ ConnectionId = ' ipps-connection'
141
+ }
142
+ } - ModuleName Connection
143
+
144
+ { Connect-Service - Services ' SecurityComplience' - DontConfirm } | Should -Not - Throw
145
+
146
+ Assert-MockCalled Connect-IPPSSession - Times 1 - ModuleName Connection
147
+ }
148
+
149
+ It " Should handle SecurityComplience disconnection" {
150
+ Mock Get-ConnectionInformation {
151
+ [PSCustomObject ]@ {
152
+ UserPrincipalName = ' [email protected] '
153
+ ConnectionId = ' ipps-connection'
154
+ }
155
+ } - ModuleName Connection
156
+ Mock Disconnect-ExchangeOnline { } - ModuleName Connection
157
+
158
+ # The disconnect for SecurityComplience uses Disconnect-ExchangeOnline
159
+ { Connect-Service - Services ' SecurityComplience' - CheckOnly } | Should -Not - Throw
160
+ }
161
+ }
162
+
163
+ Context " Graph Service Tests" {
164
+ BeforeEach {
165
+ Mock Get-MgContext { $null } - ModuleName Connection
166
+ }
167
+
168
+ It " Should handle Graph connection without scopes" {
169
+ Mock Connect-MgGraph { } - ModuleName Connection
170
+ Mock Get-MgContext {
171
+ [PSCustomObject ]@ {
172
+
173
+ Scopes = @ ()
174
+ }
175
+ } - ModuleName Connection
176
+
177
+ { Connect-Service - Services ' Graph' - DontConfirm } | Should -Not - Throw
178
+
179
+ Assert-MockCalled Connect-MgGraph - Times 1 - ModuleName Connection
180
+ }
181
+
182
+ It " Should handle Graph connection with scopes" {
183
+ Mock Connect-MgGraph { } - ModuleName Connection
184
+ Mock Get-MgContext {
185
+ [PSCustomObject ]@ {
186
+
187
+ Scopes = @ (' User.Read' , ' Mail.Read' )
188
+ }
189
+ } - ModuleName Connection
190
+
191
+ $Scopes = @ (' User.Read' , ' Mail.Read' )
192
+ { Connect-Service - Services ' Graph' - Scopes $Scopes - DontConfirm } | Should -Not - Throw
193
+
194
+ Assert-MockCalled Connect-MgGraph - Times 1 - ModuleName Connection
195
+ }
196
+
197
+ It " Should handle Graph connection with access token" {
198
+ Mock Connect-MgGraph { } - ModuleName Connection
199
+ Mock Get-MgContext {
200
+ [PSCustomObject ]@ {
201
+
202
+ Scopes = @ (' User.Read' )
203
+ }
204
+ } - ModuleName Connection
205
+
206
+ $SecureToken = ConvertTo-SecureString ' token123' - AsPlainText - Force
207
+ { Connect-Service - Services ' Graph' - AccessToken $SecureToken - DontConfirm } | Should -Not - Throw
208
+
209
+ Assert-MockCalled Connect-MgGraph - Times 1 - ModuleName Connection - ParameterFilter { $AccessToken -ne $null }
210
+ }
211
+
212
+ It " Should handle insufficient scopes in Graph connection" {
213
+ Mock Connect-MgGraph { } - ModuleName Connection
214
+ Mock Get-MgContext {
215
+ [PSCustomObject ]@ {
216
+
217
+ Scopes = @ (' User.Read' ) # Missing Mail.Read
218
+ }
219
+ } - ModuleName Connection
220
+ Mock Disconnect-MgGraph { } - ModuleName Connection
221
+
222
+ $RequiredScopes = @ (' User.Read' , ' Mail.Read' )
223
+ { Connect-Service - Services ' Graph' - Scopes $RequiredScopes - DontConfirm } | Should -Not - Throw
224
+
225
+ # Should disconnect due to insufficient scopes
226
+ Assert-MockCalled Disconnect-MgGraph - Times 1 - ModuleName Connection
227
+ }
228
+
229
+ It " Should handle Graph disconnection" {
230
+ Mock Disconnect-MgGraph { } - ModuleName Connection
231
+
232
+ { Disconnect-MgGraph } | Should -Not - Throw
233
+
234
+ Assert-MockCalled Disconnect-MgGraph - Times 1 - ModuleName Connection
235
+ }
236
+ }
237
+
238
+ Context " CheckOnly Parameter Tests" {
239
+ It " Should check connection status without connecting" {
240
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
241
+ Mock Invoke-FailedExit { throw " Not connected" } - ModuleName Connection
242
+
243
+ { Connect-Service - Services ' ExchangeOnline' - CheckOnly } | Should - Throw " Not connected"
244
+
245
+ # Should not attempt to connect
246
+ Assert-MockCalled Connect-ExchangeOnline - Times 0 - ModuleName Connection
247
+ }
248
+
249
+ It " Should pass check when already connected" {
250
+ Mock Get-ConnectionInformation {
251
+ [PSCustomObject ]@ {
252
+ UserPrincipalName = ' [email protected] '
253
+ ConnectionId = ' existing-connection'
254
+ }
255
+ } - ModuleName Connection
256
+
257
+ { Connect-Service - Services ' ExchangeOnline' - CheckOnly } | Should -Not - Throw
258
+ }
259
+ }
260
+
261
+ Context " DontConfirm Parameter Tests" {
262
+ It " Should skip confirmation when DontConfirm is used" {
263
+ Mock Get-ConnectionInformation {
264
+ [PSCustomObject ]@ {
265
+ UserPrincipalName = ' [email protected] '
266
+ ConnectionId = ' existing-connection'
267
+ }
268
+ } - ModuleName Connection
269
+
270
+ { Connect-Service - Services ' ExchangeOnline' - DontConfirm } | Should -Not - Throw
271
+
272
+ # Should not call Get-UserConfirmation
273
+ Assert-MockCalled Get-UserConfirmation - Times 0 - ModuleName Connection
274
+ }
275
+
276
+ It " Should prompt for confirmation by default" {
277
+ Mock Get-ConnectionInformation {
278
+ [PSCustomObject ]@ {
279
+ UserPrincipalName = ' [email protected] '
280
+ ConnectionId = ' existing-connection'
281
+ }
282
+ } - ModuleName Connection
283
+ Mock Get-UserConfirmation { $true } - ModuleName Connection
284
+
285
+ { Connect-Service - Services ' ExchangeOnline' } | Should -Not - Throw
286
+
287
+ Assert-MockCalled Get-UserConfirmation - Times 1 - ModuleName Connection
288
+ }
289
+ }
290
+
291
+ Context " Error Handling" {
292
+ It " Should handle connection failures" {
293
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
294
+ Mock Connect-ExchangeOnline { throw " Connection failed" } - ModuleName Connection
295
+ Mock Invoke-FailedExit { throw " Failed to connect" } - ModuleName Connection
296
+
297
+ { Connect-Service - Services ' ExchangeOnline' - DontConfirm } | Should - Throw
298
+
299
+ Assert-MockCalled Invoke-FailedExit - Times 1 - ModuleName Connection
300
+ }
301
+
302
+ It " Should handle disconnection failures" {
303
+ Mock Disconnect-ExchangeOnline { throw " Disconnect failed" } - ModuleName Connection
304
+ Mock Invoke-FailedExit { throw " Failed to disconnect" } - ModuleName Connection
305
+
306
+ { Disconnect-ExchangeOnline } | Should - Throw
307
+ }
308
+
309
+ It " Should handle Graph context retrieval failures" {
310
+ Mock Get-MgContext { throw " Graph context error" } - ModuleName Connection
311
+ Mock Connect-MgGraph { } - ModuleName Connection
312
+
313
+ { Connect-Service - Services ' Graph' - DontConfirm } | Should -Not - Throw
314
+
315
+ # Should attempt to connect when context retrieval fails
316
+ Assert-MockCalled Connect-MgGraph - Times 1 - ModuleName Connection
317
+ }
318
+ }
319
+
320
+ Context " Multiple Services Integration" {
321
+ It " Should handle connecting to multiple services" {
322
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
323
+ Mock Get-MgContext { $null } - ModuleName Connection
324
+ Mock Connect-ExchangeOnline { } - ModuleName Connection
325
+ Mock Connect-MgGraph { } - ModuleName Connection
326
+
327
+ { Connect-Service - Services @ (' ExchangeOnline' , ' Graph' ) - DontConfirm } | Should -Not - Throw
328
+
329
+ Assert-MockCalled Connect-ExchangeOnline - Times 1 - ModuleName Connection
330
+ Assert-MockCalled Connect-MgGraph - Times 1 - ModuleName Connection
331
+ }
332
+
333
+ It " Should handle mixed connection states" {
334
+ # ExchangeOnline already connected, Graph not connected
335
+ Mock Get-ConnectionInformation {
336
+ param ($ConnectionId )
337
+ if ($ConnectionId ) {
338
+ [PSCustomObject ]@ {
339
+ UserPrincipalName = ' [email protected] '
340
+ ConnectionId = $ConnectionId
341
+ }
342
+ } else {
343
+ [PSCustomObject ]@ {
344
+ UserPrincipalName = ' [email protected] '
345
+ ConnectionId = ' exchange-connection'
346
+ }
347
+ }
348
+ } - ModuleName Connection
349
+ Mock Get-MgContext { $null } - ModuleName Connection
350
+ Mock Connect-MgGraph { } - ModuleName Connection
351
+
352
+ { Connect-Service - Services @ (' ExchangeOnline' , ' Graph' ) - DontConfirm } | Should -Not - Throw
353
+
354
+ # Should only connect to Graph
355
+ Assert-MockCalled Connect-ExchangeOnline - Times 0 - ModuleName Connection
356
+ Assert-MockCalled Connect-MgGraph - Times 1 - ModuleName Connection
357
+ }
358
+ }
359
+
360
+ Context " Cross-Platform Considerations" {
361
+ It " Should handle module import failures gracefully" {
362
+ # Test that the function handles cases where Exchange/Graph modules aren't available
363
+ Mock Connect-ExchangeOnline { throw " Module not found" } - ModuleName Connection
364
+ Mock Invoke-FailedExit { throw " Connection failed" } - ModuleName Connection
365
+
366
+ { Connect-Service - Services ' ExchangeOnline' - DontConfirm } | Should - Throw
367
+ }
368
+
369
+ It " Should work with mock implementations" {
370
+ # Verify that our mocking strategy allows tests to run on any platform
371
+ Mock Get-ConnectionInformation { $null } - ModuleName Connection
372
+ Mock Connect-ExchangeOnline { } - ModuleName Connection
373
+
374
+ { Connect-Service - Services ' ExchangeOnline' - DontConfirm } | Should -Not - Throw
375
+ }
376
+ }
377
+ }
0 commit comments