Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,6 @@ local access_control_event = Notification.event.access_control

test.disable_startup_messages()


if table_utils.find_all_entries_by == nil then
function table_utils.find_all_entries_by(device, table_name, key, value)
local entries = table_utils.get_state(device, table_name) or {}
local matches = {}
for _, entry in ipairs(entries) do
if entry[key] == value then table.insert(matches, entry) end
end
return matches
end
end


local zwave_lock_endpoints = {
{
command_classes = {
Expand All @@ -52,6 +39,15 @@ end

local function install_state_mocks()
mock_latest_state = {}
-- tables.lua calls device.log.* unconditionally and get_state guards on
-- supports_capability; stub both for the pre-init seeding phase.
rawset(mock_device, "log", {
debug = function() end, info = function() end, warn = function() end, error = function() end,
})
rawset(mock_device, "supports_capability", function(_, capability)
local id = type(capability) == "table" and capability.ID or capability
return id == capabilities.lockUsers.ID or id == capabilities.lockCredentials.ID
end)
mock_device.get_latest_state = function(_, component_id, capability_id, attribute_name, default_value)
local value = mock_latest_state[mock_state_key(component_id, capability_id, attribute_name)]
if value == nil then
Expand Down Expand Up @@ -83,8 +79,8 @@ end
-- ── helpers ────────────────────────────────────────────────────────────────

local function test_init()
mock_device.persistent_store = mock_device.persistent_store or {}
mock_device.transient_store = mock_device.transient_store or {}
mock_device.persistent_store = {}
mock_device.transient_store = {}
install_state_mocks()
mock_device:set_field(constants.DRIVER_STATE.SLGA_MIGRATED, true, { persist = true })
test.mock_device.add_test_device(mock_device)
Expand All @@ -101,6 +97,15 @@ local function expect_set_pin_code(user_identifier, user_code)
)
end

local function expect_clear_pin_code(user_identifier)
test.socket.zwave:__expect_send(
UserCode:Set({
user_identifier = user_identifier,
user_id_status = UserCode.user_id_status.AVAILABLE,
}):build_test_tx(mock_device.id)
)
end

local function queue_user_code_added(user_identifier)
test.socket.zwave:__queue_receive({ mock_device.id, Notification:Report({
notification_type = Notification.notification_type.ACCESS_CONTROL,
Expand All @@ -109,32 +114,27 @@ local function queue_user_code_added(user_identifier)
}) })
end

local function seed_users(entries)
for _, entry in ipairs(entries) do
-- Seed through table_utils.add_entry: it calls set_field(..., {persist = true}),
-- which is copied to the real driver device when it is lazily created. Writing
-- only to mock_latest_state / persistent_store never reaches the driver.
local function seed_entries(table_name, capability_attr, key_name, entries)
for i, entry in ipairs(entries) do
local so_far = {}
for _, e in ipairs(entries) do
table.insert(so_far, e)
if e == entry then break end
end
local event = capabilities.lockUsers.users(so_far, { visibility = { displayed = false } })
mock_latest_state[mock_state_key("main", event.capability.ID, event.attribute.NAME)] = event.value.value
for j = 1, i do table.insert(so_far, entries[j]) end
test.socket.capability:__expect_send(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Since the expected event is queued before add_entry, a failed seed will show up as an unrelated missing-message error later. Consider asserting the return value first and queuing the expectation only on success. Otherwise the assert message is the clearer failure.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue here is that add_entry works synchronously, so unless the expectation is set prior to the event occurring, it will get marked as a failure

mock_device:generate_test_message("main", capability_attr(so_far, { visibility = { displayed = false } })))
assert(table_utils.add_entry(mock_device, table_name, entry) == constants.COMMAND_RESULT.SUCCESS,
"seed " .. table_name .. ": add_entry failed for " .. key_name .. "=" .. tostring(entry[key_name]))
end
mock_device.persistent_store.persistedUsers = entries
test.wait_for_events()
end

local function seed_users(entries)
seed_entries("users", capabilities.lockUsers.users, "userIndex", entries)
end

local function seed_credentials(entries)
for _, entry in ipairs(entries) do
local so_far = {}
for _, e in ipairs(entries) do
table.insert(so_far, e)
if e == entry then break end
end
local event = capabilities.lockCredentials.credentials(so_far, { visibility = { displayed = false } })
mock_latest_state[mock_state_key("main", event.capability.ID, event.attribute.NAME)] = event.value.value
end
mock_device.persistent_store.persistedCredentials = entries
test.wait_for_events()
seed_entries("credentials", capabilities.lockCredentials.credentials, "credentialIndex", entries)
end

-- ============================================================================
Expand Down Expand Up @@ -437,10 +437,14 @@ test.register_coroutine_test(
{ capability = capabilities.lockCredentials.ID, command = "deleteAllCredentials", args = {} },
})

test.timer.__create_and_queue_test_time_advance_timer(4, "oneshot")
-- Per-credential clears fire at 2 * (i - 1) s; final cleanup at 2 * #credentials + 4 s.
test.timer.__create_and_queue_test_time_advance_timer(0, "oneshot")
test.timer.__create_and_queue_test_time_advance_timer(6, "oneshot")
expect_clear_pin_code(1)
test.mock_time.advance_time(0)
test.wait_for_events()

test.mock_time.advance_time(4)
test.mock_time.advance_time(6)
Comment thread
hcarter-775 marked this conversation as resolved.
Resolved
test.socket.capability:__expect_send(
mock_device:generate_test_message("main",
capabilities.lockCredentials.credentials({}, { visibility = { displayed = false } }))
Expand Down Expand Up @@ -487,8 +491,6 @@ test.register_coroutine_test(
test.register_coroutine_test(
"deleteAllCredentials: returns busy when another operation is already in progress",
function()
seed_credentials({ { userIndex = 1, credentialIndex = 1, credentialType = "pin" } })

-- First deleteAllCredentials starts the z-wave flow (device is now busy)
test.socket.capability:__queue_receive({
mock_device.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,6 @@ local json = require "st.json"


test.disable_startup_messages()
if table_utils.find_all_entries_by == nil then
function table_utils.find_all_entries_by(device, table_name, key, value)
local entries = table_utils.get_state(device, table_name) or {}
local matches = {}
for _, entry in ipairs(entries) do
if entry[key] == value then table.insert(matches, entry) end
end
return matches
end
end


-- ── Shared device ──────────────────────────────────────────────────────────

Expand Down Expand Up @@ -70,8 +59,11 @@ local function install_state_mocks()
error = function() end,
})

-- get_state guards with device:supports_capability; always return true here.
rawset(mock_device, "supports_capability", function() return true end)
-- get_state guards with device:supports_capability; only the seeded capabilities are supported.
rawset(mock_device, "supports_capability", function(_, capability)
local id = type(capability) == "table" and capability.ID or capability
return id == capabilities.lockUsers.ID or id == capabilities.lockCredentials.ID
end)

-- get_state / get_max_entries use device:get_latest_state to read
-- capability attribute values from the state cache.
Expand Down
Loading