Skip to content

Commit 48e5ba9

Browse files
committed
added tests for all methods that are not group/scene related
1 parent 3cb2cec commit 48e5ba9

4 files changed

Lines changed: 149 additions & 30 deletions

File tree

build/tradfri-client.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class TradfriClient extends events_1.EventEmitter {
250250
// check response code
251251
if (response.code.toString() !== "2.05") {
252252
if (!this.handleNonSuccessfulResponse(response, `observeDevice(${instanceId})`))
253-
return;
253+
return false;
254254
}
255255
const result = parsePayload(response);
256256
logger_1.log(`observeDevice > ` + JSON.stringify(result), "debug");
@@ -366,7 +366,7 @@ class TradfriClient extends events_1.EventEmitter {
366366
// check response code
367367
if (response.code.toString() !== "2.05") {
368368
if (!this.handleNonSuccessfulResponse(response, `observeGroup(${instanceId})`))
369-
return;
369+
return false;
370370
}
371371
const result = parsePayload(response);
372372
// parse group info
@@ -446,7 +446,7 @@ class TradfriClient extends events_1.EventEmitter {
446446
// check response code
447447
if (response.code.toString() !== "2.05") {
448448
if (!this.handleNonSuccessfulResponse(response, `observeScene(${groupId}, ${instanceId})`))
449-
return;
449+
return false;
450450
}
451451
const result = parsePayload(response);
452452
// parse scene info
@@ -468,7 +468,7 @@ class TradfriClient extends events_1.EventEmitter {
468468
// check response code
469469
const code = resp.code.toString();
470470
const payload = parsePayload(resp) || "";
471-
if (code === "4.04" && !ignore404) {
471+
if (code === "4.04" && ignore404) {
472472
// not found
473473
// An observed resource has been deleted - all good
474474
// The observer will be removed soon
@@ -593,9 +593,9 @@ exports.TradfriClient = TradfriClient;
593593
function normalizeResourcePath(path) {
594594
path = path || "";
595595
while (path.startsWith("/"))
596-
path = path.substring(1);
596+
path = path.slice(1);
597597
while (path.endsWith("/"))
598-
path = path.substring(0, -1);
598+
path = path.slice(0, -1);
599599
return path;
600600
}
601601
function parsePayload(response) {

src/tradfri-client.test.ts

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import { TradfriClient } from "./tradfri-client";
1111
import { ContentFormats } from "node-coap-client/build/ContentFormats";
1212
import { MessageCode, MessageCodes } from "node-coap-client/build/Message";
1313
import * as sinonChai from "sinon-chai";
14-
import { createEmptyAccessoryResponse, createNetworkMock, createResponse, createRGBBulb, createErrorResponse } from "../test/mocks";
15-
import { Accessory, Light, TradfriError, TradfriErrorCodes } from "./";
14+
import { createEmptyAccessoryResponse, createErrorResponse, createNetworkMock, createResponse, createRGBBulb } from "../test/mocks";
15+
import { Accessory, AccessoryTypes, Light, TradfriError, TradfriErrorCodes } from "./";
1616
import { createDeferredPromise, DeferredPromise } from "./lib/defer-promise";
1717
import { padStart } from "./lib/strings";
1818

@@ -25,7 +25,7 @@ function assertPayload(actual: any, expected: {}) {
2525
expect(JSON.parse(actual.toString())).to.deep.equal(expected);
2626
}
2727

28-
describe("tradfri-client => surrounding functionality => ", () => {
28+
describe("tradfri-client => infrastructure => ", () => {
2929

3030
// Setup the mock
3131
const {
@@ -125,6 +125,33 @@ describe("tradfri-client => surrounding functionality => ", () => {
125125

126126
});
127127

128+
describe("ping =>", () => {
129+
it("should call coap.ping for the coap host", () => {
130+
tradfri.ping();
131+
fakeCoap.ping.should.have.been.called;
132+
});
133+
134+
it("should pass the correct arguments", () => {
135+
tradfri.ping();
136+
fakeCoap.ping.should.have.been.calledWithExactly("coaps://localhost:5684/", undefined);
137+
fakeCoap.ping.resetHistory();
138+
139+
tradfri.ping(500);
140+
fakeCoap.ping.should.have.been.calledWithExactly("coaps://localhost:5684/", 500);
141+
fakeCoap.ping.resetHistory();
142+
});
143+
144+
it("should pass through the returned promise", async () => {
145+
fakeCoap.ping.returns(Promise.resolve(true));
146+
await tradfri.ping().should.become(true);
147+
148+
fakeCoap.ping.returns(Promise.resolve(false));
149+
await tradfri.ping().should.become(false);
150+
151+
fakeCoap.ping.resetBehavior();
152+
});
153+
});
154+
128155
});
129156

130157
describe("tradfri-client => observing resources => ", () => {
@@ -158,9 +185,17 @@ describe("tradfri-client => observing resources => ", () => {
158185
fakeCoap.observe.should.not.have.been.called;
159186
});
160187

161-
it("calling it again with an absolute path should also not call coap.observe", async () => {
188+
it("calling it again with similar paths pointing to the same resource should also not call coap.observe", async () => {
162189
const cb = spy();
163-
await tradfri.observeResource("coaps://localhost:5684/15001", cb);
190+
await Promise.all(
191+
[
192+
"coaps://localhost:5684/15001",
193+
"coaps://localhost:5684/15001/",
194+
"/15001",
195+
"/15001/",
196+
"15001/",
197+
].map(path => tradfri.observeResource(path, cb)),
198+
);
164199
fakeCoap.observe.should.not.have.been.called;
165200
});
166201

@@ -313,7 +348,6 @@ describe("tradfri-client => observing devices => ", () => {
313348
});
314349
}
315350

316-
317351
});
318352

319353
describe("stopObservingDevices => ", () => {
@@ -327,6 +361,29 @@ describe("tradfri-client => observing devices => ", () => {
327361
});
328362
});
329363

364+
describe("observeDevices (with errors) => ", () => {
365+
it("should be rejected when one of the device callbacks receives an invalid response", async () => {
366+
367+
// the error spy has to be used our chai fails our test
368+
const errorSpy = spy();
369+
tradfri.on("error", errorSpy);
370+
371+
const devicesPromise = tradfri.observeDevices();
372+
const devices = [65536];
373+
await callbacks.observeDevices(createResponse(devices));
374+
375+
// we intercepted the device_callback, so we need to manually call it
376+
// now for the following tests to work
377+
await callbacks.observeDevice[65536](createErrorResponse(MessageCodes.clientError.forbidden));
378+
379+
errorSpy.should.have.been.called;
380+
tradfri.removeAllListeners();
381+
382+
// now the deferred promise should have been rejected
383+
await devicesPromise.should.be.rejectedWith("could not be observed");
384+
});
385+
});
386+
330387
});
331388

332389
describe("tradfri-client => updating resources => ", () => {
@@ -370,7 +427,7 @@ describe("tradfri-client => updating resources => ", () => {
370427
light = lightAccessory.lightList[0];
371428
}
372429

373-
describe("updateResource => ", () => {
430+
describe("updateDevice => ", () => {
374431

375432
beforeEach(resetDeviceInfrastructure);
376433

@@ -394,6 +451,20 @@ describe("tradfri-client => updating resources => ", () => {
394451
}],
395452
});
396453
});
454+
455+
it("calling it with a non-observed device should throw", () => {
456+
const nonExisting = lightAccessory.clone();
457+
nonExisting.instanceId = 12345;
458+
expect(() => tradfri.updateDevice(nonExisting)).to.throw("is not known");
459+
});
460+
});
461+
462+
describe("operateLight => ", () => {
463+
it("should throw when called with a non-light accessory", () => {
464+
const notALight = new Accessory();
465+
notALight.type = AccessoryTypes.remote;
466+
expect(() => tradfri.operateLight(notALight, {})).to.throw("must be a lightbulb");
467+
});
397468
});
398469

399470
});
@@ -453,5 +524,43 @@ describe("tradfri-client => custom requests => ", () => {
453524
actualResponse.code.should.equal(response.code.toString());
454525
actualResponse.payload.should.deep.equal(responsePayload);
455526
});
527+
528+
it("responses with content-format 0 or without one should be parsed as a string", async () => {
529+
const expected = "HALLO";
530+
const stringResponse = createResponse(expected, undefined, ContentFormats.text_plain);
531+
532+
fakeCoap.request.returns(Promise.resolve(stringResponse));
533+
await tradfri.request(null, null).should.be.fulfilled.then(
534+
resp => expect(resp.payload).to.be.a("string").and.equal(expected),
535+
);
536+
537+
stringResponse.format = null;
538+
fakeCoap.request.returns(Promise.resolve(stringResponse));
539+
await tradfri.request(null, null).should.be.fulfilled.then(
540+
resp => expect(resp.payload).to.be.a("string").and.equal(expected),
541+
);
542+
543+
fakeCoap.request.resetBehavior();
544+
});
545+
546+
it("responses with any other content format should pass the raw Buffer through", async () => {
547+
for (const contentFormat of [
548+
ContentFormats.application_octetStream,
549+
ContentFormats.application_exi,
550+
ContentFormats.application_linkFormat,
551+
ContentFormats.application_xml,
552+
]) {
553+
const expected = Buffer.from("unknown");
554+
const jsonResponse = createResponse(expected, undefined, ContentFormats.application_octetStream);
555+
556+
fakeCoap.request.returns(Promise.resolve(jsonResponse));
557+
await tradfri.request(null, null).should.be.fulfilled.then(
558+
resp => expect(resp.payload).to.be.an.instanceof(Buffer)
559+
.and.deep.equal(expected),
560+
);
561+
562+
fakeCoap.request.resetBehavior();
563+
}
564+
});
456565
});
457566
});

src/tradfri-client.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
332332
// check response code
333333
if (response.code.toString() !== "2.05") {
334334
if (!this.handleNonSuccessfulResponse(
335-
response, `observeDevice(${instanceId})`
336-
)) return;
335+
response, `observeDevice(${instanceId})`,
336+
)) return false;
337337
}
338338

339339
const result = parsePayload(response);
@@ -376,7 +376,7 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
376376
response, `observeGroups()`, false,
377377
)) return;
378378
}
379-
379+
380380
const newGroups: number[] = parsePayload(response);
381381

382382
log(`got all groups: ${JSON.stringify(newGroups)}`);
@@ -469,10 +469,10 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
469469
// check response code
470470
if (response.code.toString() !== "2.05") {
471471
if (!this.handleNonSuccessfulResponse(
472-
response, `observeGroup(${instanceId})`
473-
)) return;
472+
response, `observeGroup(${instanceId})`,
473+
)) return false;
474474
}
475-
475+
476476
const result = parsePayload(response);
477477
// parse group info
478478
const group = (new Group(this.ipsoOptions)).parse(result).createProxy();
@@ -511,7 +511,7 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
511511
response, `observeScenes(${groupId})`, false,
512512
)) return;
513513
}
514-
514+
515515
const groupInfo = this.groups[groupId];
516516
const newScenes: number[] = parsePayload(response);
517517

@@ -567,10 +567,10 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
567567
// check response code
568568
if (response.code.toString() !== "2.05") {
569569
if (!this.handleNonSuccessfulResponse(
570-
response, `observeScene(${groupId}, ${instanceId})`
571-
)) return;
570+
response, `observeScene(${groupId}, ${instanceId})`,
571+
)) return false;
572572
}
573-
573+
574574
const result = parsePayload(response);
575575
// parse scene info
576576
const scene = (new Scene(this.ipsoOptions)).parse(result).createProxy();
@@ -758,8 +758,8 @@ export class TradfriClient extends EventEmitter implements OperationProvider {
758758
/** Normalizes the path to a resource, so it can be used for storing the observer */
759759
function normalizeResourcePath(path: string): string {
760760
path = path || "";
761-
while (path.startsWith("/")) path = path.substring(1);
762-
while (path.endsWith("/")) path = path.substring(0, -1);
761+
while (path.startsWith("/")) path = path.slice(1);
762+
while (path.endsWith("/")) path = path.slice(0, -1);
763763
return path;
764764
}
765765

test/mocks.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,22 @@ import { ContentFormats } from "node-coap-client/build/ContentFormats";
1515
import { MessageCode, MessageCodes } from "node-coap-client/build/Message";
1616
import { Accessory, Light, TradfriClient } from "../src";
1717

18-
export function createResponse(json: string | any | null, code: MessageCode = MessageCodes.success.content): CoapResponse {
19-
if (typeof json !== "string" && typeof json !== "undefined") {
20-
json = JSON.stringify(json);
18+
export function createResponse(
19+
payload: Buffer | string | any | null,
20+
code: MessageCode = MessageCodes.success.content,
21+
contentFormat: ContentFormats = ContentFormats.application_json,
22+
): CoapResponse {
23+
if (!(payload instanceof Buffer)) {
24+
if (typeof payload !== "string" && typeof payload !== "undefined") {
25+
payload = JSON.stringify(payload);
26+
}
2127
}
2228
return {
2329
code,
24-
format: ContentFormats.application_json,
25-
payload: json != null ? Buffer.from(json, "utf8") : undefined,
30+
format: contentFormat,
31+
payload: payload != null ?
32+
payload instanceof Buffer ? payload :
33+
Buffer.from(payload, "utf8") : undefined,
2634
};
2735
}
2836
export function createErrorResponse(code: MessageCode = MessageCodes.clientError.notFound): CoapResponse {
@@ -52,6 +60,7 @@ export function createNetworkMock(
5260
reset: null as sinon.SinonStub,
5361
setSecurityParams: null as sinon.SinonStub,
5462
tryToConnect: null as sinon.SinonStub,
63+
ping: null as sinon.SinonStub,
5564
};
5665
const callbacks = {
5766
observeDevices: null as (response: CoapResponse) => Promise<void>,
@@ -83,6 +92,7 @@ export function createNetworkMock(
8392
fakeCoap.reset = stub(coap, "reset");
8493
fakeCoap.setSecurityParams = stub(coap, "setSecurityParams");
8594
fakeCoap.tryToConnect = stub(coap, "tryToConnect");
95+
fakeCoap.ping = stub(coap, "ping");
8696
}
8797

8898
function restoreStubs() {

0 commit comments

Comments
 (0)