mirror of
https://github.com/home-assistant/frontend.git
synced 2026-05-31 13:17:50 -05:00
* first rough draft of Z-Wave credential mangement * separate user and credentials, error handling, dialog tweaks * align with upstream API changes, improve error handling * align more with Matter, use lock entity for services * remove get_credential_status service * address review feedback, clarify user types * user_index -> user_id, fix some pending states * address review feedback * clean up unused code, strongly type credential types * Clear -> Delete, drop icons * Simplify flow to 1 PIN/Password credential per user * cleanup, comments, etc. * address review feedback * do not show existing credential data * fix lint errors after branch update * ignore non-enterable credential types when editing user
304 lines
8.1 KiB
TypeScript
304 lines
8.1 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
import {
|
|
getZwaveCredentialCapabilities,
|
|
getZwaveUsers,
|
|
setZwaveUser,
|
|
deleteZwaveUser,
|
|
deleteZwaveAllUsers,
|
|
setZwaveCredential,
|
|
deleteZwaveCredential,
|
|
} from "../../src/data/zwave_js-credentials";
|
|
import type { HomeAssistant } from "../../src/types";
|
|
|
|
const ENTITY_ID = "lock.zwave_front_door";
|
|
|
|
const mockHass = (response?: unknown) =>
|
|
({
|
|
callService: vi
|
|
.fn()
|
|
.mockResolvedValue(
|
|
response !== undefined ? { response } : { response: undefined }
|
|
),
|
|
}) as unknown as HomeAssistant;
|
|
|
|
describe("zwave_js-credentials", () => {
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
describe("getZwaveCredentialCapabilities", () => {
|
|
it("calls the correct service with entity_id target and returnResponse", async () => {
|
|
const capabilities = {
|
|
supports_user_management: true,
|
|
max_users: 20,
|
|
supported_user_types: ["general", "programming", "remote_only"],
|
|
max_user_name_length: 20,
|
|
supported_credential_rules: ["single", "dual", "triple"],
|
|
supported_credential_types: {
|
|
pin_code: {
|
|
num_slots: 10,
|
|
min_length: 4,
|
|
max_length: 10,
|
|
supports_learn: false,
|
|
},
|
|
rfid_code: {
|
|
num_slots: 5,
|
|
min_length: 1,
|
|
max_length: 32,
|
|
supports_learn: true,
|
|
},
|
|
},
|
|
};
|
|
const hass = mockHass({ [ENTITY_ID]: capabilities });
|
|
|
|
const result = await getZwaveCredentialCapabilities(hass, ENTITY_ID);
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"get_credential_capabilities",
|
|
{},
|
|
{ entity_id: ENTITY_ID },
|
|
false,
|
|
true
|
|
);
|
|
expect(result).toEqual(capabilities);
|
|
});
|
|
|
|
it("propagates errors from callService", async () => {
|
|
const hass = {
|
|
callService: vi
|
|
.fn()
|
|
.mockRejectedValue(new Error("Service unavailable")),
|
|
} as unknown as HomeAssistant;
|
|
|
|
await expect(
|
|
getZwaveCredentialCapabilities(hass, ENTITY_ID)
|
|
).rejects.toThrow("Service unavailable");
|
|
});
|
|
});
|
|
|
|
describe("getZwaveUsers", () => {
|
|
it("calls the correct service with entity_id target and returnResponse", async () => {
|
|
const usersResponse = {
|
|
max_users: 20,
|
|
users: [
|
|
{
|
|
user_id: 1,
|
|
user_name: "Alice",
|
|
active: true,
|
|
user_type: "general",
|
|
credential_rule: "single",
|
|
credentials: [
|
|
{ type: "pin_code", slot: 1 },
|
|
{ type: "finger_biometric", slot: 1 },
|
|
],
|
|
},
|
|
],
|
|
};
|
|
const hass = mockHass({ [ENTITY_ID]: usersResponse });
|
|
|
|
const result = await getZwaveUsers(hass, ENTITY_ID);
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"get_users",
|
|
{},
|
|
{ entity_id: ENTITY_ID },
|
|
false,
|
|
true
|
|
);
|
|
expect(result).toEqual(usersResponse);
|
|
expect(result.users).toHaveLength(1);
|
|
expect(result.users[0].user_name).toBe("Alice");
|
|
expect(result.users[0].credentials).toHaveLength(2);
|
|
});
|
|
|
|
it("propagates errors from callService", async () => {
|
|
const hass = {
|
|
callService: vi.fn().mockRejectedValue(new Error("Not supported")),
|
|
} as unknown as HomeAssistant;
|
|
|
|
await expect(getZwaveUsers(hass, ENTITY_ID)).rejects.toThrow(
|
|
"Not supported"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("setZwaveUser", () => {
|
|
const setUserResponse = (inner: unknown) =>
|
|
({
|
|
callService: vi
|
|
.fn()
|
|
.mockResolvedValue({ response: { [ENTITY_ID]: inner } }),
|
|
}) as unknown as HomeAssistant;
|
|
|
|
it("unwraps the entity_id-keyed response and returns user_id", async () => {
|
|
const hass = setUserResponse({ user_id: 5 });
|
|
|
|
const result = await setZwaveUser(hass, ENTITY_ID, {
|
|
user_id: 5,
|
|
user_name: "Bob",
|
|
user_type: "general",
|
|
});
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"set_user",
|
|
{ user_id: 5, user_name: "Bob", user_type: "general" },
|
|
{ entity_id: ENTITY_ID },
|
|
false,
|
|
true
|
|
);
|
|
expect(result.user_id).toBe(5);
|
|
});
|
|
|
|
it("returns the allocated user_id when auto-finding", async () => {
|
|
const hass = setUserResponse({ user_id: 1 });
|
|
|
|
const result = await setZwaveUser(hass, ENTITY_ID, {
|
|
user_name: "Carol",
|
|
});
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"set_user",
|
|
{ user_name: "Carol" },
|
|
{ entity_id: ENTITY_ID },
|
|
false,
|
|
true
|
|
);
|
|
expect(result.user_id).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("deleteZwaveUser", () => {
|
|
it("calls the correct service with user_id and entity_id target", async () => {
|
|
const hass = mockHass();
|
|
|
|
await deleteZwaveUser(hass, ENTITY_ID, 2);
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"delete_user",
|
|
{ user_id: 2 },
|
|
{ entity_id: ENTITY_ID },
|
|
false
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("deleteZwaveAllUsers", () => {
|
|
it("calls delete_all_users with entity_id target and no params", async () => {
|
|
const hass = mockHass();
|
|
|
|
await deleteZwaveAllUsers(hass, ENTITY_ID);
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"delete_all_users",
|
|
{},
|
|
{ entity_id: ENTITY_ID },
|
|
false
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("setZwaveCredential", () => {
|
|
const setCredResponse = (inner: unknown) =>
|
|
({
|
|
callService: vi
|
|
.fn()
|
|
.mockResolvedValue({ response: { [ENTITY_ID]: inner } }),
|
|
}) as unknown as HomeAssistant;
|
|
|
|
it("unwraps the entity_id-keyed response", async () => {
|
|
const credentialResult = {
|
|
credential_slot: 1,
|
|
user_id: 3,
|
|
};
|
|
const hass = setCredResponse(credentialResult);
|
|
|
|
const result = await setZwaveCredential(hass, ENTITY_ID, {
|
|
user_id: 3,
|
|
credential_type: "pin_code",
|
|
credential_data: "1234",
|
|
});
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"set_credential",
|
|
{
|
|
user_id: 3,
|
|
credential_type: "pin_code",
|
|
credential_data: "1234",
|
|
},
|
|
{ entity_id: ENTITY_ID },
|
|
false,
|
|
true
|
|
);
|
|
expect(result).toEqual(credentialResult);
|
|
expect(result.credential_slot).toBe(1);
|
|
expect(result.user_id).toBe(3);
|
|
});
|
|
|
|
it("forwards explicit credential_slot when provided", async () => {
|
|
const hass = setCredResponse({ credential_slot: 5, user_id: 3 });
|
|
|
|
await setZwaveCredential(hass, ENTITY_ID, {
|
|
user_id: 3,
|
|
credential_type: "pin_code",
|
|
credential_data: "1234",
|
|
credential_slot: 5,
|
|
});
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"set_credential",
|
|
{
|
|
user_id: 3,
|
|
credential_type: "pin_code",
|
|
credential_data: "1234",
|
|
credential_slot: 5,
|
|
},
|
|
{ entity_id: ENTITY_ID },
|
|
false,
|
|
true
|
|
);
|
|
});
|
|
|
|
it("propagates errors from callService", async () => {
|
|
const hass = {
|
|
callService: vi.fn().mockRejectedValue(new Error("Slot full")),
|
|
} as unknown as HomeAssistant;
|
|
|
|
await expect(
|
|
setZwaveCredential(hass, ENTITY_ID, {
|
|
user_id: 1,
|
|
credential_type: "pin_code",
|
|
credential_data: "1234",
|
|
})
|
|
).rejects.toThrow("Slot full");
|
|
});
|
|
});
|
|
|
|
describe("deleteZwaveCredential", () => {
|
|
it("calls the correct service with params and entity_id target", async () => {
|
|
const hass = mockHass();
|
|
|
|
await deleteZwaveCredential(hass, ENTITY_ID, {
|
|
user_id: 2,
|
|
credential_type: "pin_code",
|
|
credential_slot: 1,
|
|
});
|
|
|
|
expect(hass.callService).toHaveBeenCalledWith(
|
|
"zwave_js",
|
|
"delete_credential",
|
|
{ user_id: 2, credential_type: "pin_code", credential_slot: 1 },
|
|
{ entity_id: ENTITY_ID },
|
|
false
|
|
);
|
|
});
|
|
});
|
|
});
|