iobroker.roborock
Version:
160 lines (140 loc) • 6.24 kB
text/typescript
import { beforeEach, describe, expect, it } from "vitest";
import { Feature } from "../features/features.enum";
import { V1VacuumFeatures } from "../features/vacuum/v1VacuumFeatures";
import { MockAdapter } from "./MockAdapter";
import { MockRobot } from "./MockRobot";
class TestVacuum extends V1VacuumFeatures {
protected getDynamicFeatures(): Set<Feature> {
return new Set();
}
public async detectAndApplyRuntimeFeatures(): Promise<boolean> {
return false;
}
public setMockMapManagerComponents(processMapStub: any): void {
(this as any).mapService.mapManager.processMap = processMapStub;
(this as any).mapService.mapManager.mapParser = {
parsedata: async () => ({ image: { pixels: {} }, path: {}, charger: [25600, 25600], robot: [25600, 25600], rooms: {} })
};
(this as any).mapService.mapManager.mapCreator = {
canvasMap: async () => ["base64_uncropped", "base64_full"]
};
}
}
describe("Map Processing", () => {
let mockAdapter: MockAdapter;
let mockRobot: MockRobot;
let vacuumFeatures: TestVacuum;
let depsMock: any;
beforeEach(async () => {
mockAdapter = new MockAdapter();
mockRobot = new MockRobot();
depsMock = {
adapter: mockAdapter,
log: mockAdapter.log,
ensureState: async (id: string, common: any) => {
await mockAdapter.setObjectNotExistsAsync(id, { type: "state", common });
},
ensureFolder: async (id: string) => {
await mockAdapter.setObjectNotExistsAsync(id, { type: "folder", common: { name: id } });
},
getStatesAsync: async () => ({}), // Return empty to avoid crash in copyRecordStates
getObjectAsync: async () => null,
config: { staticFeatures: [], enable_map_creation: true },
http_api: {
getFwFeaturesResult: () => mockRobot.features,
storeFwFeaturesResult: () => {},
getRobotModel: () => mockRobot.model,
getMatchedRoomIDs: () => []
},
requestsHandler: {
sendRequest: async (duid: string, method: string, params: any[]) => {
if (duid !== mockRobot.duid) return [];
if (method === "get_map_v1") return Buffer.from("dummy_map_data");
return mockRobot.handleRequest(method, params);
},
command: async () => {},
mapParser: {
parsedata: async (buf: Buffer) => {
// Mock parser logic: return dummy JSON if buffer is valid
if (buf && buf.length > 0) return {
image: { pixels: {} },
path: {},
charger: [25600, 25600],
robot: [25600, 25600],
rooms: {
1: { pixelCount: 100 },
2: { pixelCount: 200 }
}
};
return null;
}
},
mapCreator: {
canvasMap: async () => {
return ["base64_uncropped", "base64_full", "base64_truncated"];
}
}
}
};
mockAdapter.requestsHandler = depsMock.requestsHandler;
mockAdapter.http_api = depsMock.http_api;
// Attach helpers that MapManager expects on the adapter
(mockAdapter as any).ensureState = depsMock.ensureState;
(mockAdapter as any).ensureFolder = depsMock.ensureFolder;
(mockAdapter as any).setStateChangedAsync = mockAdapter.setStateAsync;
// Mock MapManager on adapter (used by V1MapService)
(mockAdapter as any).mapManager = {
mapParser: depsMock.requestsHandler.mapParser,
mapCreator: depsMock.requestsHandler.mapCreator,
processMap: async () => ({}) // Dummy
};
vacuumFeatures = new TestVacuum(depsMock, mockRobot.duid, mockRobot.model, { staticFeatures: [] });
const processMapStub = async (rawData: Buffer) => {
if (!rawData) return null;
// Return different base64 depending on input to differentiate tests?
// Or just return keys expected by test
// Maps expectation: "base64_full" for updateMap, "base64_truncated" for cleanRecord
if (rawData.toString() === "dummy_map_data") return { mapBase64: "base64_full", mapData: {} };
if (rawData.toString() === "record_map_data") return { mapBase64Clean: "base64_truncated", mapBase64: "base64_full", mapData: {} }; // cleanRecord expects truncated?
// cleanRecord test expects "mapBase64Truncated" state to equal "base64_truncated".
// But MapManager processing returns mapBase64 and mapBase64Clean.
// V1VacuumFeatures maps mapBase64Clean to mapBase64Truncated state key.
return { mapBase64: "base64_full", mapData: {} };
};
vacuumFeatures.setMockMapManagerComponents(processMapStub);
await vacuumFeatures.initialize();
});
it("should process updateMap correctly", async () => {
// Mock get_map_v1 to return a dummy buffer via depsMock
const originalSendRequest = depsMock.requestsHandler.sendRequest;
depsMock.requestsHandler.sendRequest = async (duid: string, method: string, params: any[]) => {
if (method === "get_map_v1") return Buffer.from("dummy_map_data");
return originalSendRequest(duid, method, params);
};
await vacuumFeatures.updateMap();
// Verify states were set
expect(mockAdapter.states[`Devices.${mockRobot.duid}.map.mapBase64`]).to.equal("base64_full");
expect(mockAdapter.states[`Devices.${mockRobot.duid}.map.mapData`]).to.exist;
});
it("should handle cleaning record maps", async () => {
// Mock get_clean_record_map via depsMock
const originalSendRequest = depsMock.requestsHandler.sendRequest;
depsMock.requestsHandler.sendRequest = async (duid: string, method: string, params: any[]) => {
if (method === "get_clean_record_map") return Buffer.from("record_map_data");
return originalSendRequest(duid, method, params);
};
// We access getCleaningRecordMap privately? It's private.
// But updateCleanSummary calls it.
// Let's verify via updateCleanSummary, ensuring it processes records.
// MockRobot has records.
await vacuumFeatures.updateCleanSummary();
// Check if map data exists for a record
// Check if map data exists for a record
// Note: Clean record maps are only fetched if enable_map_creation is true (set in mock config above)
// And if get_clean_record returns a valid map pointer? No, it requests by start_time.
// Logic in updateCleanSummary:
// loops records, if enable_map_creation, calls getCleaningRecordMap(startTime)
const mapStatePath = `Devices.${mockRobot.duid}.cleaningInfo.records.0.map.mapBase64`;
expect(mockAdapter.states[mapStatePath]).to.equal("base64_full");
});
});