mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-06-21 12:41:47 +00:00
178 lines
6.0 KiB
TypeScript
178 lines
6.0 KiB
TypeScript
// biome-ignore assist/source/organizeImports: import mocks first
|
|
import {afterAll, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
|
import {mockLogger} from "./mocks/logger";
|
|
|
|
import {initSdNotify} from "../lib/util/sd-notify";
|
|
|
|
const mockPlatform = vi.fn(() => "linux");
|
|
|
|
vi.mock("node:os", () => ({
|
|
platform: vi.fn(() => mockPlatform()),
|
|
}));
|
|
|
|
const mockUnixDgramSocket = {
|
|
send: vi.fn(),
|
|
};
|
|
const mockCreateSocket = vi.fn(() => {
|
|
if (mockPlatform() !== "win32") {
|
|
return mockUnixDgramSocket;
|
|
}
|
|
|
|
throw new Error("Unix datagrams not available on this platform");
|
|
});
|
|
|
|
vi.mock("unix-dgram", () => ({
|
|
createSocket: mockCreateSocket,
|
|
}));
|
|
|
|
const mocksClear = [
|
|
mockLogger.log,
|
|
mockLogger.debug,
|
|
mockLogger.info,
|
|
mockLogger.warning,
|
|
mockLogger.error,
|
|
mockUnixDgramSocket.send,
|
|
mockCreateSocket,
|
|
mockPlatform,
|
|
];
|
|
|
|
describe("sd-notify", () => {
|
|
const expectSocketNthSend = (nth: number, message: string): void => {
|
|
expect(mockUnixDgramSocket.send).toHaveBeenNthCalledWith(nth, Buffer.from(message), 0, expect.any(Number), "mocked", expect.any(Function));
|
|
};
|
|
|
|
beforeAll(() => {
|
|
vi.useFakeTimers();
|
|
});
|
|
|
|
afterAll(() => {
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
for (const mock of mocksClear) mock.mockClear();
|
|
delete process.env.NOTIFY_SOCKET;
|
|
delete process.env.WATCHDOG_USEC;
|
|
delete process.env.WSL_DISTRO_NAME;
|
|
});
|
|
|
|
it("No socket", async () => {
|
|
const res = await initSdNotify();
|
|
|
|
expect(mockCreateSocket).toHaveBeenCalledTimes(0);
|
|
expect(res).toBeUndefined();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(0);
|
|
});
|
|
|
|
it("Error on unsupported platform", async () => {
|
|
// also called by `mockCreateSocket`
|
|
mockPlatform.mockImplementationOnce(() => "win32").mockImplementationOnce(() => "win32");
|
|
|
|
process.env.NOTIFY_SOCKET = "mocked";
|
|
const res = await initSdNotify();
|
|
|
|
expect(mockCreateSocket).toHaveBeenCalledTimes(1);
|
|
expect(res).toBeUndefined();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(0);
|
|
expect(mockLogger.warning).toHaveBeenCalledWith("NOTIFY_SOCKET env is set: Unix datagrams not available on this platform");
|
|
});
|
|
|
|
it("Error on supported platform", async () => {
|
|
// NOTE: `import('unix-dgram')` can also fail in similar way when bindings are missing (not compiled)
|
|
mockCreateSocket.mockImplementationOnce(() => {
|
|
throw new Error("Error create socket");
|
|
});
|
|
|
|
process.env.NOTIFY_SOCKET = "mocked";
|
|
const res = await initSdNotify();
|
|
|
|
expect(mockCreateSocket).toHaveBeenCalledTimes(1);
|
|
expect(res).toBeUndefined();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(0);
|
|
expect(mockLogger.error).toHaveBeenCalledWith("Could not init sd_notify: Error create socket");
|
|
});
|
|
|
|
it("Socket only", async () => {
|
|
process.env.NOTIFY_SOCKET = "mocked";
|
|
const res = await initSdNotify();
|
|
|
|
expect(res).toStrictEqual({notifyStopping: expect.any(Function), stop: expect.any(Function)});
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(1);
|
|
expectSocketNthSend(1, "READY=1");
|
|
|
|
await vi.advanceTimersByTimeAsync(7500);
|
|
|
|
res!.notifyStopping();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(2);
|
|
expectSocketNthSend(2, "STOPPING=1");
|
|
|
|
res!.stop();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("Invalid watchdog timeout - socket only", async () => {
|
|
process.env.NOTIFY_SOCKET = "mocked";
|
|
process.env.WATCHDOG_USEC = "mocked";
|
|
const res = await initSdNotify();
|
|
|
|
expect(res).toStrictEqual({notifyStopping: expect.any(Function), stop: expect.any(Function)});
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(1);
|
|
expectSocketNthSend(1, "READY=1");
|
|
|
|
await vi.advanceTimersByTimeAsync(7500);
|
|
|
|
res!.notifyStopping();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(2);
|
|
expectSocketNthSend(2, "STOPPING=1");
|
|
|
|
res!.stop();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("Socket and watchdog", async () => {
|
|
process.env.NOTIFY_SOCKET = "mocked";
|
|
process.env.WATCHDOG_USEC = "10000000";
|
|
const res = await initSdNotify();
|
|
|
|
expect(res).toStrictEqual({notifyStopping: expect.any(Function), stop: expect.any(Function)});
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(1);
|
|
expectSocketNthSend(1, "READY=1");
|
|
|
|
await vi.advanceTimersByTimeAsync(7500);
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(2);
|
|
expectSocketNthSend(2, "WATCHDOG=1");
|
|
|
|
res!.notifyStopping();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(3);
|
|
expectSocketNthSend(3, "STOPPING=1");
|
|
|
|
await vi.advanceTimersByTimeAsync(6000);
|
|
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(4);
|
|
expectSocketNthSend(4, "WATCHDOG=1");
|
|
|
|
res!.stop();
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(4);
|
|
|
|
await vi.advanceTimersByTimeAsync(10000);
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(4);
|
|
});
|
|
|
|
it("Fails to send", async () => {
|
|
mockUnixDgramSocket.send.mockImplementationOnce(
|
|
(_buf: Buffer, _offset: number, _length: number, _path: string, callback?: (err?: Error) => void) => {
|
|
callback!(new Error("Failure"));
|
|
},
|
|
);
|
|
|
|
process.env.NOTIFY_SOCKET = "mocked";
|
|
const res = await initSdNotify();
|
|
|
|
expect(res).toStrictEqual({notifyStopping: expect.any(Function), stop: expect.any(Function)});
|
|
expect(mockUnixDgramSocket.send).toHaveBeenCalledTimes(1);
|
|
expectSocketNthSend(1, "READY=1");
|
|
|
|
expect(mockLogger.warning).toHaveBeenCalledWith(`Failed to send "READY=1" to systemd: Failure`);
|
|
});
|
|
});
|