Skip to content

Commit 1cf2f31

Browse files
committed
Convert Hue tests to use ConnectionScenario for simpler tests
1 parent 896179a commit 1cf2f31

11 files changed

Lines changed: 1807 additions & 1518 deletions

drivers/SmartThings/philips-hue/src/test/hue_test_helpers.lua

Lines changed: 1165 additions & 327 deletions
Large diffs are not rendered by default.

drivers/SmartThings/philips-hue/src/test/test_hue_bridge_sse.lua

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
--- Test for Hue bridge SSE connection lifecycle.
2+
--- Migrated to use connection_scenario 2.0.
3+
---
4+
--- This test uses a hybrid approach combining helpers with dynamic queueing:
5+
--- - Uses SSE event builder helpers (light_event()) for cleaner event construction
6+
--- - Uses dynamic queue_http_response() and queue_sse_event() for complex lifecycle timing
7+
--- - Cannot use pre-defined expectations because each test needs different response sequences
8+
--- - The double-refresh pattern (both .added and .init inject refresh) requires dynamic handling
9+
110
local test = require "integration_test"
211
local capabilities = require "st.capabilities"
312
local mock_devices_api = require "integration_test.mock_devices_api"
4-
local mock_lan_socket = require "integration_test.mock_lan_socket"
513
local hue_test_helpers = require "test.hue_test_helpers"
614

715
local LIGHT_RID = "22222222-2222-2222-2222-222222222222"
@@ -12,18 +20,16 @@ local NEW_DEVICE_RID = "66666666-6666-6666-6666-666666666666"
1220
local NEW_LIGHT_RID = "77777777-7777-7777-7777-777777777777"
1321
local NEW_LIGHT_NAME = "New Hue Light"
1422

15-
local mock_bridge, mock_light, get_bridge_server, test_init, get_sse_connection =
16-
hue_test_helpers.build_paired_bridge_and_light(
17-
LIGHT_RID,
18-
{
23+
local mock_bridge, mock_light, get_bridge_server, base_test_init, get_sse_connection =
24+
hue_test_helpers.HueDeviceBuilder.new()
25+
:with_bridge()
26+
:with_light(LIGHT_RID, {
1927
on = { on = true },
2028
dimming = { brightness = 80 },
2129
hue_device_id = HUE_DEVICE_ID,
22-
},
23-
"white-and-color-ambiance.yml",
24-
nil,
25-
{ enable_sse = true }
26-
)
30+
})
31+
:enable_sse()
32+
:start()
2733

2834
local function expect_switch_and_level_emit()
2935
test.socket.capability:__expect_send(
@@ -51,39 +57,24 @@ end
5157
-- full SSE connect sequence below -- independently, same as any other Hue test file.)
5258
test.set_test_init_function(function()
5359
expect_switch_and_level_emit() -- from .added's injected refresh
54-
test_init() -- registers .init's levelRange emit
60+
base_test_init() -- registers .init's levelRange emit
5561
expect_switch_and_level_emit() -- from .init's injected refresh
5662
end)
5763

58-
--- That injected refresh's own connect() and the SSE EventSource's own connect() both race for
59-
--- this address right after test_init returns, and *which one* claims the single "sse"
60-
--- reservation test_init made isn't reliably ordered (it can flip depending on unrelated timing,
61-
--- e.g. table/coroutine hashing) -- so this doesn't assume an order; it peeks at whichever
62-
--- connection actually claimed the "sse" label's first sent bytes (a non-consuming read, unlike
63-
--- `assert_http_request_received`) to tell the two apart, then returns them correctly identified.
64-
---
65-
--- @return integration_test.LanMockServer sse the real SSE stream
66-
--- @return integration_test.LanMockServer rest the bridge's persistent REST connection
67-
local function identify_sse_and_rest_connections()
68-
test.wait_for_events()
69-
local sse_entry = mock_lan_socket.tcp_registry.get_labeled(hue_test_helpers.BRIDGE_IP, 443, "sse")
70-
local sent_so_far = table.concat(sse_entry.sent_log)
71-
if sent_so_far:match("^GET /eventstream/clip/v2 ") then
72-
return get_sse_connection(), get_bridge_server()
73-
end
74-
return get_bridge_server(), get_sse_connection()
75-
end
76-
7764
--- Answers the REST calls `LightLifecycleHandlers.added`'s injected refresh makes (see above) --
7865
--- the same zigbee-connectivity-then-light-state sequence test_hue_light_refresh.lua exercises
7966
--- directly. All of the bridge's REST calls share one persistent connection (one PhilipsHueApi
8067
--- instance, one worker thread processing requests serially), so this unconditional first
8168
--- request has to be drained before anything else can get its response -- otherwise it blocks
8269
--- every later REST call (including the SSE onopen's own connectivity poll) behind it.
8370
---
84-
--- @param rest integration_test.LanMockServer the bridge's REST connection (see
85-
--- identify_sse_and_rest_connections)
86-
local function answer_initial_light_refresh(rest)
71+
--- @param rest integration_test.connection_scenario.Connection the bridge's REST connection
72+
--- @param light_on boolean|nil whether the light should be on (default: true)
73+
--- @param light_brightness number|nil the light brightness (default: 80)
74+
local function answer_initial_light_refresh(rest, light_on, light_brightness)
75+
light_on = light_on == nil and true or light_on
76+
light_brightness = light_brightness or 80
77+
8778
rest:queue_http_response(200, {}, {
8879
errors = {},
8980
data = { { services = { { rtype = "zigbee_connectivity", rid = ZIGBEE_RID } } } },
@@ -94,15 +85,15 @@ local function answer_initial_light_refresh(rest)
9485
})
9586
rest:queue_http_response(200, {}, {
9687
errors = {},
97-
data = { { id = LIGHT_RID, on = { on = true }, dimming = { brightness = 80 } } },
88+
data = { { id = LIGHT_RID, on = { on = light_on }, dimming = { brightness = light_brightness } } },
9889
})
9990
test.wait_for_events()
10091
rest:assert_http_request_received("GET", "/clip/v2/resource/device/" .. HUE_DEVICE_ID)
10192
rest:assert_http_request_received("GET", "/clip/v2/resource/zigbee_connectivity/" .. ZIGBEE_RID)
10293
rest:assert_http_request_received("GET", "/clip/v2/resource/light/" .. LIGHT_RID)
10394
end
10495

105-
--- Drives one full SSE connect: identifies which connection is which (see above), drains the
96+
--- Drives one full SSE connect: gets the connections, drains the
10697
--- unconditional initial light refresh, then the EventSource handshake, then the
10798
--- connectivity-status poll `onopen` makes before it settles (which also finishes flushing the
10899
--- light's own `init` lifecycle -- its levelRange emit shares scheduler turns with all of this,
@@ -111,10 +102,13 @@ end
111102
--- bridge/light/EventSource per test (see above), so there's no persistent connection to share
112103
--- across tests the way there might be within a single production run.
113104
---
114-
--- @return integration_test.LanMockServer sse
115-
--- @return integration_test.LanMockServer rest
105+
--- @return integration_test.connection_scenario.Connection sse
106+
--- @return integration_test.connection_scenario.Connection rest
116107
local function connect_sse()
117-
local sse, rest = identify_sse_and_rest_connections()
108+
test.wait_for_events()
109+
local sse = get_sse_connection()
110+
local rest = get_bridge_server()
111+
118112
answer_initial_light_refresh(rest) -- LightLifecycleHandlers.added's injected refresh
119113
answer_initial_light_refresh(rest) -- LightLifecycleHandlers.init's injected refresh
120114

@@ -169,11 +163,9 @@ test.register_coroutine_test(
169163
function()
170164
local sse = connect_sse()
171165

166+
-- Use helper to build the SSE event
172167
sse:queue_sse_event({
173-
{
174-
type = "update",
175-
data = { { type = "light", id = LIGHT_RID, on = { on = false }, dimming = { brightness = 42 } } },
176-
},
168+
hue_test_helpers.light_event(LIGHT_RID, false, 42)
177169
})
178170
test.socket.capability:__expect_send(
179171
mock_light:generate_test_message("main", capabilities.switch.switch.off())

drivers/SmartThings/philips-hue/src/test/test_hue_button_lifecycle.lua

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
--- Test for button device lifecycle (added/init/removed).
2+
--- Migrated to use connection_scenario 2.0.
3+
---
4+
--- Note: This test doesn't make HTTP requests during lifecycle operations,
5+
--- so no ConnectionScenario setup is needed. It primarily validates that
6+
--- lifecycle handlers complete without errors.
7+
18
local test = require "integration_test"
29
local capabilities = require "st.capabilities"
310
local hue_test_helpers = require "test.hue_test_helpers"
@@ -8,39 +15,17 @@ local BUTTON_DEVICE_ID = "aaaaaaaa-bbbb-cccc-dddd-222222222222"
815
local POWER_RID = "aaaaaaaa-bbbb-cccc-dddd-333333333333"
916

1017
-- Single button device fixture WITHOUT SSE (lifecycle only)
11-
local mock_bridge, mock_button, get_bridge_server, base_test_init, get_sse_connection =
12-
hue_test_helpers.build_paired_bridge_and_child(
13-
BUTTON_RID,
14-
{
15-
id = BUTTON_RID,
16-
hue_provided_name = "Hue Button",
17-
hue_device_id = BUTTON_DEVICE_ID,
18-
num_buttons = 1,
19-
button1 = {
20-
event_values = { "short_release", "long_press", "long_release" }
21-
},
22-
button1_id = BUTTON_RID,
23-
power_state = { battery_level = 85 },
24-
power_id = POWER_RID,
25-
},
26-
"single-button.yml",
27-
"button",
28-
function(mock_device)
29-
-- Register expectation for supportedButtonValues emit during init
30-
test.socket.capability:__expect_send(
31-
mock_device:generate_test_message("main",
32-
capabilities.button.supportedButtonValues(
33-
{ "pushed", "held" },
34-
{ visibility = { displayed = false } }
35-
)
36-
)
37-
)
38-
end,
39-
nil, -- No device template overrides
40-
{ enable_sse = false } -- No SSE for lifecycle-only test
41-
)
18+
local mock_bridge, mock_button, get_bridge_server, test_init =
19+
hue_test_helpers.HueDeviceBuilder.new()
20+
:with_bridge()
21+
:with_button(BUTTON_RID, {
22+
battery = 85,
23+
device_id = BUTTON_DEVICE_ID,
24+
power_rid = POWER_RID,
25+
})
26+
:start()
4227

43-
test.set_test_init_function(base_test_init)
28+
test.set_test_init_function(test_init)
4429

4530
test.register_coroutine_test(
4631
"Button device lifecycle completes successfully",

0 commit comments

Comments
 (0)