@homebridge-plugins/homebridge-august 3.4.1 → 3.4.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +1 -2
- package/dist/Platform.HAP.d.ts +2 -1
- package/dist/Platform.HAP.d.ts.map +1 -1
- package/dist/Platform.HAP.js +34 -5
- package/dist/Platform.HAP.js.map +1 -1
- package/dist/devices/device.js +2 -2
- package/dist/devices/device.js.map +1 -1
- package/dist/devices/lock.d.ts.map +1 -1
- package/dist/devices/lock.js +23 -10
- package/dist/devices/lock.js.map +1 -1
- package/dist/homebridge-ui/public/index.html +2 -3
- package/dist/platform.matter.d.ts.map +1 -1
- package/dist/platform.matter.js +20 -1
- package/dist/platform.matter.js.map +1 -1
- package/package.json +7 -20
- package/dist/devices/device.test.d.ts +0 -2
- package/dist/devices/device.test.d.ts.map +0 -1
- package/dist/devices/device.test.js +0 -294
- package/dist/devices/device.test.js.map +0 -1
- package/test/connectivity-manager.test.ts +0 -592
- package/test/platform.matter.test.ts +0 -743
- package/test/platform.polling.test.ts +0 -313
- package/test/platform.test.ts +0 -642
- package/test/redact.test.ts +0 -86
- package/test/settings.test.ts +0 -148
package/test/platform.test.ts
DELETED
|
@@ -1,642 +0,0 @@
|
|
|
1
|
-
/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
|
|
2
|
-
*
|
|
3
|
-
* platform.test.ts: homebridge-august platform tests.
|
|
4
|
-
*/
|
|
5
|
-
import type { API, Logging, PlatformAccessory } from 'homebridge'
|
|
6
|
-
|
|
7
|
-
import type { AugustPlatformConfig } from '../src/settings.js'
|
|
8
|
-
|
|
9
|
-
import { readFileSync, writeFileSync } from 'node:fs'
|
|
10
|
-
|
|
11
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
12
|
-
|
|
13
|
-
import { AugustPlatform } from '../src/Platform.HAP.js'
|
|
14
|
-
|
|
15
|
-
// Mock the august-yale module
|
|
16
|
-
vi.mock('august-yale', () => {
|
|
17
|
-
// eslint-disable-next-line prefer-arrow-callback
|
|
18
|
-
const MockAugust = vi.fn().mockImplementation(function () {
|
|
19
|
-
return { end: vi.fn() }
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
// Type assertion to add static methods
|
|
23
|
-
const MockConstructor = MockAugust as any
|
|
24
|
-
MockConstructor.details = vi.fn()
|
|
25
|
-
MockConstructor.authorize = vi.fn()
|
|
26
|
-
MockConstructor.validate = vi.fn()
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
default: MockConstructor,
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
// Mock file system
|
|
34
|
-
vi.mock('node:fs', () => ({
|
|
35
|
-
readFileSync: vi.fn(),
|
|
36
|
-
writeFileSync: vi.fn(),
|
|
37
|
-
}))
|
|
38
|
-
|
|
39
|
-
describe('augustPlatform', () => {
|
|
40
|
-
let platform: AugustPlatform
|
|
41
|
-
let mockApi: API
|
|
42
|
-
let mockLog: Logging
|
|
43
|
-
let mockConfig: AugustPlatformConfig
|
|
44
|
-
|
|
45
|
-
beforeEach(() => {
|
|
46
|
-
// Reset all mocks
|
|
47
|
-
vi.clearAllMocks()
|
|
48
|
-
|
|
49
|
-
// Create mock API
|
|
50
|
-
mockApi = {
|
|
51
|
-
hap: {
|
|
52
|
-
uuid: {
|
|
53
|
-
generate: vi.fn(() => 'mock-uuid'),
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
user: {
|
|
57
|
-
configPath: vi.fn(() => '/mock/config.json'),
|
|
58
|
-
},
|
|
59
|
-
on: vi.fn(),
|
|
60
|
-
registerPlatformAccessories: vi.fn(),
|
|
61
|
-
updatePlatformAccessories: vi.fn(),
|
|
62
|
-
unregisterPlatformAccessories: vi.fn(),
|
|
63
|
-
publishExternalAccessories: vi.fn(),
|
|
64
|
-
} as unknown as API
|
|
65
|
-
|
|
66
|
-
// Create mock logger
|
|
67
|
-
mockLog = {
|
|
68
|
-
info: vi.fn(),
|
|
69
|
-
warn: vi.fn(),
|
|
70
|
-
error: vi.fn(),
|
|
71
|
-
debug: vi.fn(),
|
|
72
|
-
success: vi.fn(),
|
|
73
|
-
} as any
|
|
74
|
-
|
|
75
|
-
// Create mock config
|
|
76
|
-
mockConfig = {
|
|
77
|
-
platform: 'August',
|
|
78
|
-
name: 'Test August',
|
|
79
|
-
credentials: {
|
|
80
|
-
augustId: 'test@example.com',
|
|
81
|
-
password: 'testpassword',
|
|
82
|
-
countryCode: 'US',
|
|
83
|
-
installId: 'test-install-id',
|
|
84
|
-
isValidated: true,
|
|
85
|
-
},
|
|
86
|
-
options: {
|
|
87
|
-
logging: 'debug',
|
|
88
|
-
},
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Mock readFileSync for config file
|
|
92
|
-
vi.mocked(readFileSync).mockReturnValue(JSON.stringify({
|
|
93
|
-
platforms: [mockConfig],
|
|
94
|
-
}))
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('should normalize Canadian country code to US for API compatibility', async () => {
|
|
98
|
-
const config: AugustPlatformConfig = {
|
|
99
|
-
platform: 'August',
|
|
100
|
-
name: 'Test August',
|
|
101
|
-
credentials: {
|
|
102
|
-
installId: 'test-install-id',
|
|
103
|
-
augustId: 'test@example.com',
|
|
104
|
-
password: 'test-password',
|
|
105
|
-
countryCode: 'CA',
|
|
106
|
-
isValidated: true,
|
|
107
|
-
},
|
|
108
|
-
options: {
|
|
109
|
-
logging: 'debug',
|
|
110
|
-
},
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
114
|
-
|
|
115
|
-
// Access the private method for testing
|
|
116
|
-
const normalizeMethod = (platform as any).normalizeCredentialsForApi.bind(platform)
|
|
117
|
-
const normalizedCredentials = await normalizeMethod(config.credentials)
|
|
118
|
-
|
|
119
|
-
expect(normalizedCredentials.countryCode).toBe('US')
|
|
120
|
-
expect(normalizedCredentials.augustId).toBe('test@example.com')
|
|
121
|
-
expect(normalizedCredentials.password).toBe('test-password')
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('should normalize Mexican country code to US for API compatibility', async () => {
|
|
125
|
-
const config: AugustPlatformConfig = {
|
|
126
|
-
platform: 'August',
|
|
127
|
-
name: 'Test August',
|
|
128
|
-
credentials: {
|
|
129
|
-
installId: 'test-install-id',
|
|
130
|
-
augustId: 'test@example.com',
|
|
131
|
-
password: 'test-password',
|
|
132
|
-
countryCode: 'MX',
|
|
133
|
-
isValidated: true,
|
|
134
|
-
},
|
|
135
|
-
options: {
|
|
136
|
-
logging: 'debug',
|
|
137
|
-
},
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
141
|
-
|
|
142
|
-
// Access the private method for testing
|
|
143
|
-
const normalizeMethod = (platform as any).normalizeCredentialsForApi.bind(platform)
|
|
144
|
-
const normalizedCredentials = await normalizeMethod(config.credentials)
|
|
145
|
-
|
|
146
|
-
expect(normalizedCredentials.countryCode).toBe('US')
|
|
147
|
-
expect(normalizedCredentials.augustId).toBe('test@example.com')
|
|
148
|
-
expect(normalizedCredentials.password).toBe('test-password')
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('should respect disableCountryCodeNormalization option', async () => {
|
|
152
|
-
const config: AugustPlatformConfig = {
|
|
153
|
-
platform: 'August',
|
|
154
|
-
name: 'Test August',
|
|
155
|
-
credentials: {
|
|
156
|
-
installId: 'test-install-id',
|
|
157
|
-
augustId: 'test@example.com',
|
|
158
|
-
password: 'test-password',
|
|
159
|
-
countryCode: 'CA',
|
|
160
|
-
isValidated: true,
|
|
161
|
-
},
|
|
162
|
-
options: {
|
|
163
|
-
logging: 'debug',
|
|
164
|
-
disableCountryCodeNormalization: true,
|
|
165
|
-
},
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
169
|
-
|
|
170
|
-
// Access the private method for testing
|
|
171
|
-
const normalizeMethod = (platform as any).normalizeCredentialsForApi.bind(platform)
|
|
172
|
-
const normalizedCredentials = await normalizeMethod(config.credentials)
|
|
173
|
-
|
|
174
|
-
expect(normalizedCredentials.countryCode).toBe('CA') // Should remain unchanged
|
|
175
|
-
expect(normalizedCredentials.augustId).toBe('test@example.com')
|
|
176
|
-
expect(normalizedCredentials.password).toBe('test-password')
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('should handle case-insensitive country codes', async () => {
|
|
180
|
-
const config: AugustPlatformConfig = {
|
|
181
|
-
platform: 'August',
|
|
182
|
-
name: 'Test August',
|
|
183
|
-
credentials: {
|
|
184
|
-
installId: 'test-install-id',
|
|
185
|
-
augustId: 'test@example.com',
|
|
186
|
-
password: 'test-password',
|
|
187
|
-
countryCode: 'ca', // lowercase
|
|
188
|
-
isValidated: true,
|
|
189
|
-
},
|
|
190
|
-
options: {
|
|
191
|
-
logging: 'debug',
|
|
192
|
-
},
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
196
|
-
|
|
197
|
-
// Access the private method for testing
|
|
198
|
-
const normalizeMethod = (platform as any).normalizeCredentialsForApi.bind(platform)
|
|
199
|
-
const normalizedCredentials = await normalizeMethod(config.credentials)
|
|
200
|
-
|
|
201
|
-
expect(normalizedCredentials.countryCode).toBe('US')
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it('should not change US country code', async () => {
|
|
205
|
-
const config: AugustPlatformConfig = {
|
|
206
|
-
platform: 'August',
|
|
207
|
-
name: 'Test August',
|
|
208
|
-
credentials: {
|
|
209
|
-
installId: 'test-install-id',
|
|
210
|
-
augustId: 'test@example.com',
|
|
211
|
-
password: 'test-password',
|
|
212
|
-
countryCode: 'US',
|
|
213
|
-
isValidated: true,
|
|
214
|
-
},
|
|
215
|
-
options: {
|
|
216
|
-
logging: 'debug',
|
|
217
|
-
},
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
221
|
-
|
|
222
|
-
// Access the private method for testing
|
|
223
|
-
const normalizeMethod = (platform as any).normalizeCredentialsForApi.bind(platform)
|
|
224
|
-
const normalizedCredentials = await normalizeMethod(config.credentials)
|
|
225
|
-
|
|
226
|
-
expect(normalizedCredentials.countryCode).toBe('US')
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('should preserve other credential properties when normalizing', async () => {
|
|
230
|
-
const config: AugustPlatformConfig = {
|
|
231
|
-
platform: 'August',
|
|
232
|
-
name: 'Test August',
|
|
233
|
-
credentials: {
|
|
234
|
-
apiKey: 'test-api-key',
|
|
235
|
-
pnSubKey: 'test-pn-sub-key',
|
|
236
|
-
installId: 'test-install-id',
|
|
237
|
-
augustId: 'test@example.com',
|
|
238
|
-
password: 'test-password',
|
|
239
|
-
countryCode: 'CA',
|
|
240
|
-
isValidated: true,
|
|
241
|
-
validateCode: 'test-code',
|
|
242
|
-
},
|
|
243
|
-
options: {
|
|
244
|
-
logging: 'debug',
|
|
245
|
-
},
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
249
|
-
|
|
250
|
-
// Access the private method for testing
|
|
251
|
-
const normalizeMethod = (platform as any).normalizeCredentialsForApi.bind(platform)
|
|
252
|
-
const normalizedCredentials = await normalizeMethod(config.credentials)
|
|
253
|
-
|
|
254
|
-
expect(normalizedCredentials.apiKey).toBe('test-api-key')
|
|
255
|
-
expect(normalizedCredentials.pnSubKey).toBe('test-pn-sub-key')
|
|
256
|
-
expect(normalizedCredentials.installId).toBe('test-install-id')
|
|
257
|
-
expect(normalizedCredentials.augustId).toBe('test@example.com')
|
|
258
|
-
expect(normalizedCredentials.password).toBe('test-password')
|
|
259
|
-
expect(normalizedCredentials.countryCode).toBe('US') // Should be normalized
|
|
260
|
-
expect(normalizedCredentials.isValidated).toBe(true)
|
|
261
|
-
expect(normalizedCredentials.validateCode).toBe('test-code')
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
it('should provide public method to get normalized credentials', async () => {
|
|
265
|
-
const config: AugustPlatformConfig = {
|
|
266
|
-
platform: 'August',
|
|
267
|
-
name: 'Test August',
|
|
268
|
-
credentials: {
|
|
269
|
-
installId: 'test-install-id',
|
|
270
|
-
augustId: 'test@example.com',
|
|
271
|
-
password: 'test-password',
|
|
272
|
-
countryCode: 'CA',
|
|
273
|
-
isValidated: true,
|
|
274
|
-
},
|
|
275
|
-
options: {
|
|
276
|
-
logging: 'debug',
|
|
277
|
-
},
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
281
|
-
|
|
282
|
-
// Test the public method
|
|
283
|
-
const normalizedCredentials = await platform.getNormalizedCredentials()
|
|
284
|
-
|
|
285
|
-
expect(normalizedCredentials.countryCode).toBe('US')
|
|
286
|
-
expect(normalizedCredentials.augustId).toBe('test@example.com')
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
it('should handle null credentials gracefully', async () => {
|
|
290
|
-
const config: AugustPlatformConfig = {
|
|
291
|
-
platform: 'August',
|
|
292
|
-
name: 'Test August',
|
|
293
|
-
credentials: {
|
|
294
|
-
installId: 'test-install-id',
|
|
295
|
-
augustId: 'test@example.com',
|
|
296
|
-
password: 'test-password',
|
|
297
|
-
countryCode: 'CA',
|
|
298
|
-
isValidated: true,
|
|
299
|
-
},
|
|
300
|
-
options: {
|
|
301
|
-
logging: 'debug',
|
|
302
|
-
},
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
306
|
-
|
|
307
|
-
// Access the private method for testing
|
|
308
|
-
const normalizeMethod = (platform as any).normalizeCredentialsForApi.bind(platform)
|
|
309
|
-
|
|
310
|
-
await expect(normalizeMethod(null)).rejects.toThrow('Credentials cannot be null or undefined')
|
|
311
|
-
await expect(normalizeMethod(undefined)).rejects.toThrow('Credentials cannot be null or undefined')
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
it('should use cached credentials for performance', async () => {
|
|
315
|
-
const config: AugustPlatformConfig = {
|
|
316
|
-
platform: 'August',
|
|
317
|
-
name: 'Test August',
|
|
318
|
-
credentials: {
|
|
319
|
-
installId: 'test-install-id',
|
|
320
|
-
augustId: 'test@example.com',
|
|
321
|
-
password: 'test-password',
|
|
322
|
-
countryCode: 'CA',
|
|
323
|
-
isValidated: true,
|
|
324
|
-
},
|
|
325
|
-
options: {
|
|
326
|
-
logging: 'debug',
|
|
327
|
-
},
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
platform = new AugustPlatform(mockLog, config, mockApi)
|
|
331
|
-
|
|
332
|
-
// First call should normalize and cache
|
|
333
|
-
const firstCall = await platform.getNormalizedCredentials()
|
|
334
|
-
expect(firstCall.countryCode).toBe('US')
|
|
335
|
-
|
|
336
|
-
// Second call should use cache
|
|
337
|
-
const secondCall = await platform.getNormalizedCredentials()
|
|
338
|
-
expect(secondCall.countryCode).toBe('US')
|
|
339
|
-
expect(secondCall).toBe(firstCall) // Should be the same object reference
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
afterEach(() => {
|
|
343
|
-
vi.restoreAllMocks()
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
describe('discoverDevices', () => {
|
|
347
|
-
it('should handle 401 authentication errors and trigger re-authentication', async () => {
|
|
348
|
-
// Create platform instance
|
|
349
|
-
platform = new AugustPlatform(mockLog, mockConfig, mockApi)
|
|
350
|
-
|
|
351
|
-
// Mock August.details to throw 401 error
|
|
352
|
-
const August = await import('august-yale')
|
|
353
|
-
const detailsMock = vi.fn().mockRejectedValueOnce(new Error('GET failed with: 401'))
|
|
354
|
-
August.default.details = detailsMock
|
|
355
|
-
|
|
356
|
-
// Mock validated method to avoid actual re-authentication
|
|
357
|
-
const validateSpy = vi.spyOn(platform, 'validated').mockImplementation(async () => {
|
|
358
|
-
// Don't change the flag back to true, just simulate the method being called
|
|
359
|
-
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
// Call discoverDevices
|
|
363
|
-
await expect(platform.discoverDevices()).resolves.toBeUndefined()
|
|
364
|
-
|
|
365
|
-
// Verify that validation was reset and re-authentication was attempted
|
|
366
|
-
expect(platform.config.credentials!.isValidated).toBe(false)
|
|
367
|
-
expect(writeFileSync).toHaveBeenCalled()
|
|
368
|
-
expect(validateSpy).toHaveBeenCalled()
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
it('should handle "unauthorized" errors and trigger re-authentication', async () => {
|
|
372
|
-
// Create platform instance
|
|
373
|
-
platform = new AugustPlatform(mockLog, mockConfig, mockApi)
|
|
374
|
-
|
|
375
|
-
// Mock August.details to throw unauthorized error
|
|
376
|
-
const August = await import('august-yale')
|
|
377
|
-
const detailsMock = vi.fn().mockRejectedValueOnce(new Error('Request failed: Unauthorized'))
|
|
378
|
-
August.default.details = detailsMock
|
|
379
|
-
|
|
380
|
-
// Mock validated method
|
|
381
|
-
const validateSpy = vi.spyOn(platform, 'validated').mockImplementation(async () => {
|
|
382
|
-
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
// Call discoverDevices
|
|
386
|
-
await expect(platform.discoverDevices()).resolves.toBeUndefined()
|
|
387
|
-
|
|
388
|
-
// Verify re-authentication was triggered
|
|
389
|
-
expect(validateSpy).toHaveBeenCalled()
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
it('should re-throw non-authentication errors', async () => {
|
|
393
|
-
// Create platform instance
|
|
394
|
-
platform = new AugustPlatform(mockLog, mockConfig, mockApi)
|
|
395
|
-
|
|
396
|
-
// Mock August.details to throw non-401 error
|
|
397
|
-
const August = await import('august-yale')
|
|
398
|
-
const networkError = new Error('Network timeout')
|
|
399
|
-
const detailsMock = vi.fn().mockRejectedValueOnce(networkError)
|
|
400
|
-
August.default.details = detailsMock
|
|
401
|
-
|
|
402
|
-
// Call discoverDevices and expect it to throw
|
|
403
|
-
await expect(platform.discoverDevices()).rejects.toThrow('Failed to discover devices: Network timeout')
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
it('should process devices successfully when no errors occur', async () => {
|
|
407
|
-
// Create platform instance
|
|
408
|
-
platform = new AugustPlatform(mockLog, mockConfig, mockApi)
|
|
409
|
-
|
|
410
|
-
// Mock successful August.details response
|
|
411
|
-
const August = await import('august-yale')
|
|
412
|
-
const mockDevice = {
|
|
413
|
-
lockId: 'test-lock-id',
|
|
414
|
-
LockName: 'Test Lock',
|
|
415
|
-
SerialNumber: '123456',
|
|
416
|
-
} as any // Use any to avoid complex type requirements
|
|
417
|
-
|
|
418
|
-
const detailsMock = vi.fn().mockResolvedValueOnce([mockDevice])
|
|
419
|
-
August.default.details = detailsMock
|
|
420
|
-
|
|
421
|
-
// Mock the Lock method to avoid actual device processing
|
|
422
|
-
const lockSpy = vi.spyOn(platform as any, 'Lock').mockImplementation(async () => {})
|
|
423
|
-
|
|
424
|
-
// Call discoverDevices
|
|
425
|
-
await expect(platform.discoverDevices()).resolves.toBeUndefined()
|
|
426
|
-
|
|
427
|
-
// Verify successful processing
|
|
428
|
-
expect(detailsMock).toHaveBeenCalled()
|
|
429
|
-
// Due to the existing logic, single device arrays get wrapped: [devices] becomes [[device]]
|
|
430
|
-
// So Lock gets called with [device] instead of device
|
|
431
|
-
expect(lockSpy).toHaveBeenCalledWith([mockDevice])
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
it('should exclude locks matching excludeLockIds', async () => {
|
|
435
|
-
const configWithExcludes: AugustPlatformConfig = {
|
|
436
|
-
...mockConfig,
|
|
437
|
-
options: {
|
|
438
|
-
logging: 'debug',
|
|
439
|
-
excludeLockIds: ['excluded-lock-id'],
|
|
440
|
-
},
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
platform = new AugustPlatform(mockLog, configWithExcludes, mockApi)
|
|
444
|
-
|
|
445
|
-
const August = await import('august-yale')
|
|
446
|
-
const mockDeviceIncluded = { lockId: 'included-lock-id', LockName: 'Included Lock' } as any
|
|
447
|
-
const mockDeviceExcluded = { lockId: 'excluded-lock-id', LockName: 'Excluded Lock' } as any
|
|
448
|
-
August.default.details = vi.fn().mockResolvedValueOnce([mockDeviceIncluded, mockDeviceExcluded])
|
|
449
|
-
|
|
450
|
-
const lockSpy = vi.spyOn(platform as any, 'Lock').mockImplementation(async () => {})
|
|
451
|
-
|
|
452
|
-
await platform.discoverDevices()
|
|
453
|
-
|
|
454
|
-
// Only the non-excluded lock should be processed
|
|
455
|
-
expect(lockSpy).toHaveBeenCalledTimes(1)
|
|
456
|
-
expect(lockSpy).toHaveBeenCalledWith(expect.objectContaining({ lockId: 'included-lock-id' }))
|
|
457
|
-
})
|
|
458
|
-
|
|
459
|
-
it('should handle case-insensitive excludeLockIds matching', async () => {
|
|
460
|
-
const configWithExcludes: AugustPlatformConfig = {
|
|
461
|
-
...mockConfig,
|
|
462
|
-
options: {
|
|
463
|
-
logging: 'debug',
|
|
464
|
-
excludeLockIds: ['EXCLUDED-LOCK-ID'],
|
|
465
|
-
},
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
platform = new AugustPlatform(mockLog, configWithExcludes, mockApi)
|
|
469
|
-
|
|
470
|
-
const August = await import('august-yale')
|
|
471
|
-
const mockDeviceIncluded = { lockId: 'included-lock-id', LockName: 'Included Lock' } as any
|
|
472
|
-
const mockDeviceExcluded = { lockId: 'excluded-lock-id', LockName: 'Excluded Lock' } as any
|
|
473
|
-
August.default.details = vi.fn().mockResolvedValueOnce([mockDeviceIncluded, mockDeviceExcluded])
|
|
474
|
-
|
|
475
|
-
const lockSpy = vi.spyOn(platform as any, 'Lock').mockImplementation(async () => {})
|
|
476
|
-
|
|
477
|
-
await platform.discoverDevices()
|
|
478
|
-
|
|
479
|
-
expect(lockSpy).toHaveBeenCalledTimes(1)
|
|
480
|
-
expect(lockSpy).toHaveBeenCalledWith(expect.objectContaining({ lockId: 'included-lock-id' }))
|
|
481
|
-
})
|
|
482
|
-
|
|
483
|
-
it('should unregister cached accessories for excluded lock IDs', async () => {
|
|
484
|
-
const configWithExcludes: AugustPlatformConfig = {
|
|
485
|
-
...mockConfig,
|
|
486
|
-
options: {
|
|
487
|
-
logging: 'debug',
|
|
488
|
-
excludeLockIds: ['excluded-lock-id'],
|
|
489
|
-
},
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Make uuid.generate return predictable UUIDs based on input
|
|
493
|
-
vi.mocked(mockApi.hap.uuid.generate).mockImplementation((input: any) => `uuid-${input}`)
|
|
494
|
-
|
|
495
|
-
platform = new AugustPlatform(mockLog, configWithExcludes, mockApi)
|
|
496
|
-
|
|
497
|
-
// Simulate a cached accessory for the excluded lock
|
|
498
|
-
const mockAccessory = {
|
|
499
|
-
UUID: 'uuid-excluded-lock-id',
|
|
500
|
-
displayName: 'Excluded Lock',
|
|
501
|
-
} as unknown as PlatformAccessory
|
|
502
|
-
platform.accessories.push(mockAccessory)
|
|
503
|
-
|
|
504
|
-
const August = await import('august-yale')
|
|
505
|
-
const mockDevice = { lockId: 'included-lock-id', LockName: 'Included Lock' } as any
|
|
506
|
-
August.default.details = vi.fn().mockResolvedValueOnce([mockDevice])
|
|
507
|
-
|
|
508
|
-
vi.spyOn(platform as any, 'Lock').mockImplementation(async () => {})
|
|
509
|
-
|
|
510
|
-
await platform.discoverDevices()
|
|
511
|
-
|
|
512
|
-
// Verify the cached accessory was unregistered
|
|
513
|
-
expect(mockApi.unregisterPlatformAccessories).toHaveBeenCalledWith(
|
|
514
|
-
'homebridge-august',
|
|
515
|
-
'August',
|
|
516
|
-
[mockAccessory],
|
|
517
|
-
)
|
|
518
|
-
// Verify the accessory was removed from the accessories array
|
|
519
|
-
expect(platform.accessories.find(a => a.UUID === 'uuid-excluded-lock-id')).toBeUndefined()
|
|
520
|
-
})
|
|
521
|
-
|
|
522
|
-
it('should not filter when excludeLockIds is empty', async () => {
|
|
523
|
-
const configWithEmptyExcludes: AugustPlatformConfig = {
|
|
524
|
-
...mockConfig,
|
|
525
|
-
options: {
|
|
526
|
-
logging: 'debug',
|
|
527
|
-
excludeLockIds: [],
|
|
528
|
-
},
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
platform = new AugustPlatform(mockLog, configWithEmptyExcludes, mockApi)
|
|
532
|
-
|
|
533
|
-
const August = await import('august-yale')
|
|
534
|
-
const mockDeviceA = { lockId: 'lock-a', LockName: 'Lock A' } as any
|
|
535
|
-
const mockDeviceB = { lockId: 'lock-b', LockName: 'Lock B' } as any
|
|
536
|
-
August.default.details = vi.fn().mockResolvedValueOnce([mockDeviceA, mockDeviceB])
|
|
537
|
-
|
|
538
|
-
const lockSpy = vi.spyOn(platform as any, 'Lock').mockImplementation(async () => {})
|
|
539
|
-
|
|
540
|
-
await platform.discoverDevices()
|
|
541
|
-
|
|
542
|
-
expect(lockSpy).toHaveBeenCalledTimes(2)
|
|
543
|
-
})
|
|
544
|
-
|
|
545
|
-
it('should exclude locks when using per-device config', async () => {
|
|
546
|
-
const configWithBoth: AugustPlatformConfig = {
|
|
547
|
-
...mockConfig,
|
|
548
|
-
options: {
|
|
549
|
-
logging: 'debug',
|
|
550
|
-
excludeLockIds: ['excluded-lock-id'],
|
|
551
|
-
devices: [
|
|
552
|
-
{ lockId: 'included-lock-id', configLockName: 'My Lock', overrideHomeKitEnabled: false } as any,
|
|
553
|
-
],
|
|
554
|
-
},
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
platform = new AugustPlatform(mockLog, configWithBoth, mockApi)
|
|
558
|
-
|
|
559
|
-
const August = await import('august-yale')
|
|
560
|
-
const mockDeviceIncluded = { lockId: 'included-lock-id', LockName: 'Included Lock' } as any
|
|
561
|
-
const mockDeviceExcluded = { lockId: 'excluded-lock-id', LockName: 'Excluded Lock' } as any
|
|
562
|
-
August.default.details = vi.fn().mockResolvedValueOnce([mockDeviceIncluded, mockDeviceExcluded])
|
|
563
|
-
|
|
564
|
-
const lockSpy = vi.spyOn(platform as any, 'Lock').mockImplementation(async () => {})
|
|
565
|
-
|
|
566
|
-
await platform.discoverDevices()
|
|
567
|
-
|
|
568
|
-
// Only the included lock should reach the Lock method
|
|
569
|
-
expect(lockSpy).toHaveBeenCalledTimes(1)
|
|
570
|
-
})
|
|
571
|
-
})
|
|
572
|
-
|
|
573
|
-
describe('validated method', () => {
|
|
574
|
-
it('should handle undefined credentials in config file gracefully', async () => {
|
|
575
|
-
// Mock config file with undefined credentials
|
|
576
|
-
const mockConfigFile = {
|
|
577
|
-
platforms: [
|
|
578
|
-
{
|
|
579
|
-
platform: 'August',
|
|
580
|
-
name: 'Test August',
|
|
581
|
-
// credentials is undefined here
|
|
582
|
-
},
|
|
583
|
-
],
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockConfigFile))
|
|
587
|
-
|
|
588
|
-
platform = new AugustPlatform(mockLog, mockConfig, mockApi)
|
|
589
|
-
|
|
590
|
-
// This should not throw an error when trying to set installId
|
|
591
|
-
await expect(platform.validated()).resolves.toBeUndefined()
|
|
592
|
-
})
|
|
593
|
-
|
|
594
|
-
it('should handle null credentials in config file gracefully', async () => {
|
|
595
|
-
// Mock config file with null credentials
|
|
596
|
-
const mockConfigFile = {
|
|
597
|
-
platforms: [
|
|
598
|
-
{
|
|
599
|
-
platform: 'August',
|
|
600
|
-
name: 'Test August',
|
|
601
|
-
credentials: null,
|
|
602
|
-
},
|
|
603
|
-
],
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockConfigFile))
|
|
607
|
-
|
|
608
|
-
platform = new AugustPlatform(mockLog, mockConfig, mockApi)
|
|
609
|
-
|
|
610
|
-
// This should not throw an error when trying to set installId
|
|
611
|
-
await expect(platform.validated()).resolves.toBeUndefined()
|
|
612
|
-
})
|
|
613
|
-
|
|
614
|
-
it('should initialize credentials object and save installId when credentials are missing', async () => {
|
|
615
|
-
// Mock config file with undefined credentials
|
|
616
|
-
const mockConfigFile = {
|
|
617
|
-
platforms: [
|
|
618
|
-
{
|
|
619
|
-
platform: 'August',
|
|
620
|
-
name: 'Test August',
|
|
621
|
-
// credentials is undefined here
|
|
622
|
-
},
|
|
623
|
-
],
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
vi.mocked(readFileSync).mockReturnValue(JSON.stringify(mockConfigFile))
|
|
627
|
-
|
|
628
|
-
platform = new AugustPlatform(mockLog, mockConfig, mockApi)
|
|
629
|
-
|
|
630
|
-
await platform.validated()
|
|
631
|
-
|
|
632
|
-
// Verify that writeFileSync was called (meaning the config was updated)
|
|
633
|
-
expect(writeFileSync).toHaveBeenCalled()
|
|
634
|
-
|
|
635
|
-
// Verify the credentials object was created and populated
|
|
636
|
-
const writeCall = vi.mocked(writeFileSync).mock.calls[0]
|
|
637
|
-
const savedConfig = JSON.parse(writeCall[1] as string)
|
|
638
|
-
expect(savedConfig.platforms[0].credentials).toBeDefined()
|
|
639
|
-
expect(savedConfig.platforms[0].credentials.installId).toBeDefined()
|
|
640
|
-
})
|
|
641
|
-
})
|
|
642
|
-
})
|