165 lines
5.1 KiB
TypeScript
165 lines
5.1 KiB
TypeScript
import {
|
|
decodeBinary,
|
|
routeMqttData,
|
|
asTaggedResource,
|
|
handleCreate,
|
|
handleUpdate,
|
|
handleCreateOrUpdate,
|
|
} from "../auto_sync";
|
|
import { SpecialStatus, TaggedSequence } from "farmbot";
|
|
import { Actions } from "../../constants";
|
|
import { fakeState } from "../../__test_support__/fake_state";
|
|
import { GetState } from "../../redux/interfaces";
|
|
import { SyncPayload, UpdateMqttData, Reason } from "../interfaces";
|
|
import { storeUUID } from "../data_consistency";
|
|
import { unpackUUID } from "../../util";
|
|
|
|
function toBinary(input: object): Buffer {
|
|
return Buffer.from(JSON.stringify(input), "utf8");
|
|
}
|
|
|
|
const fakePayload: SyncPayload = {
|
|
args: { label: "label1" },
|
|
body: { foo: "bar" }
|
|
};
|
|
|
|
const payload = (): UpdateMqttData<TaggedSequence> => ({
|
|
status: "UPDATE",
|
|
kind: "Sequence",
|
|
id: 5,
|
|
body: {} as TaggedSequence["body"],
|
|
sessionId: "wow"
|
|
});
|
|
|
|
describe("handleCreateOrUpdate", () => {
|
|
it("creates new records if it doesn't have one locally", () => {
|
|
const myPayload = payload();
|
|
const dispatch = jest.fn();
|
|
const getState = jest.fn(fakeState);
|
|
const result = handleCreateOrUpdate(dispatch, getState, myPayload);
|
|
expect(result).toBe(undefined);
|
|
expect(dispatch).toHaveBeenCalled();
|
|
expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
type: Actions.INIT_RESOURCE
|
|
}));
|
|
});
|
|
|
|
it("ignores local echo", () => {
|
|
jest.resetAllMocks();
|
|
const myPayload = payload();
|
|
const dispatch = jest.fn();
|
|
const getState = jest.fn(fakeState) as GetState;
|
|
const fakeUuid = "x";
|
|
myPayload.sessionId = fakeUuid;
|
|
storeUUID(fakeUuid);
|
|
|
|
const result = handleCreateOrUpdate(dispatch, getState, myPayload);
|
|
expect(result).toBe(true);
|
|
expect(dispatch).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("updates existing records when found locally", () => {
|
|
const myPayload = payload();
|
|
const dispatch = jest.fn();
|
|
const getState = jest.fn(fakeState) as GetState;
|
|
const { index } = getState().resources;
|
|
|
|
const fakeId =
|
|
unpackUUID(Object.keys(index.byKind.Sequence)[0]).remoteId || -1;
|
|
myPayload.id = fakeId;
|
|
myPayload.kind = "Sequence";
|
|
handleCreateOrUpdate(dispatch, getState, myPayload);
|
|
expect(dispatch).toHaveBeenCalled();
|
|
expect(dispatch).toHaveBeenCalledWith(expect.objectContaining({
|
|
type: Actions.OVERWRITE_RESOURCE
|
|
}));
|
|
});
|
|
});
|
|
|
|
describe("handleUpdate", () => {
|
|
it("creates Redux actions when data updates", () => {
|
|
const uuid = "THIS IS IT";
|
|
const wow = handleUpdate(payload(), uuid);
|
|
expect(wow.type).toEqual(Actions.OVERWRITE_RESOURCE);
|
|
expect(wow.payload.uuid).toBe(uuid);
|
|
});
|
|
});
|
|
|
|
describe("handleCreate", () => {
|
|
it("creates appropriate Redux actions", () => {
|
|
const wow = handleCreate(payload());
|
|
expect(wow.type).toEqual(Actions.INIT_RESOURCE);
|
|
});
|
|
});
|
|
|
|
describe("asTaggedResource", () => {
|
|
it("turns MQTT data into FE data", () => {
|
|
const p = payload();
|
|
const result = asTaggedResource(p);
|
|
expect(result.body).toEqual(p.body);
|
|
expect(result.kind).toEqual(p.kind);
|
|
expect(result.specialStatus).toEqual(SpecialStatus.SAVED);
|
|
expect(result.uuid).toContain(p.kind);
|
|
});
|
|
});
|
|
|
|
describe("decodeBinary()", () => {
|
|
it("transforms binary back to JSON", () => {
|
|
const results = decodeBinary(toBinary(fakePayload));
|
|
|
|
expect(results.args).toBeInstanceOf(Object);
|
|
expect(results.args.label).toEqual("label1");
|
|
expect(results.body).toBeInstanceOf(Object);
|
|
});
|
|
});
|
|
|
|
describe("routeMqttData", () => {
|
|
it("tosses out irrelevant data", () => {
|
|
const other = "something/else";
|
|
expect(routeMqttData(other, toBinary({})).status).toEqual("SKIP");
|
|
const status_v8 = "bot/device_0/status_v8/configuration/auto_sync";
|
|
expect(routeMqttData(status_v8, toBinary({})).status).toEqual("SKIP");
|
|
const sync = "bot/device_0/sync/Resource/0";
|
|
expect(routeMqttData(sync, toBinary({})).status).not.toEqual("SKIP");
|
|
});
|
|
|
|
it("tosses out data missing an ID", () => {
|
|
const results = routeMqttData("bot/device_9/sync", toBinary({}));
|
|
expect(results.status).toEqual("ERR");
|
|
results.status === "ERR" && expect(results.reason).toEqual(Reason.BAD_CHAN);
|
|
});
|
|
|
|
it("tosses out resources that the FE does not care about", () => {
|
|
const results =
|
|
routeMqttData("bot/device_9/sync/DeviceSerialNumber/1", toBinary({}));
|
|
expect(results.status).toEqual("SKIP");
|
|
});
|
|
|
|
it("handles well formed deletion data", () => {
|
|
const results = routeMqttData("bot/device_9/sync/Sequence/1", toBinary({}));
|
|
expect(results.status).toEqual("DELETE");
|
|
if (results.status !== "DELETE") { fail(); }
|
|
expect(results.id).toEqual(1);
|
|
expect(results.kind).toEqual("Sequence");
|
|
});
|
|
|
|
it("handles well formed update data", () => {
|
|
const fake1 = {
|
|
args: {
|
|
label: "hey"
|
|
},
|
|
body: {
|
|
foo: "bar"
|
|
}
|
|
};
|
|
const payl = toBinary(fake1);
|
|
const results = routeMqttData("bot/device_9/sync/Sequence/1", payl);
|
|
expect(results.status).toEqual("UPDATE");
|
|
if (results.status !== "UPDATE") { fail(); }
|
|
|
|
expect(results.id).toEqual(1);
|
|
expect(results.kind).toEqual("Sequence");
|
|
expect(results.body).toEqual(fake1.body);
|
|
});
|
|
});
|