"use strict"; // StateBuilder: the properties of one message, checked by the descriptors of the registry. const { test } = require("node:test"); const assert = require("node:assert/strict"); const { BrightnessController, Capability, EndpointHealth, PowerController, RangeController, SchemaError, StateBuilder, TemperatureSensor, messages, } = require("alex2node"); const AT = "2026-09-28T10:00:00.000Z"; const now = () => new Date(AT); test("set: the property of a described interface, its time from the clock of the builder", () => { const state = new StateBuilder({ now }).set(PowerController, "powerState", "ON").set(BrightnessController, "brightness", 40); assert.deepEqual(state.context, [ { namespace: "Alexa.PowerController", name: "powerState", value: "ON", timeOfSample: AT, uncertaintyInMilliseconds: 0 }, { namespace: "Alexa.BrightnessController", name: "brightness", value: 40, timeOfSample: AT, uncertaintyInMilliseconds: 0 }, ]); assert.deepEqual(state.change, []); }); test("set: a property has the time and the uncertainty it is given", () => { const changedAt = "2026-09-28T09:12:00.000Z"; const [property] = new StateBuilder({ now }) .set(PowerController, "powerState", "OFF", { timeOfSample: changedAt, uncertaintyInMilliseconds: 6000 }).context; assert.equal(property.timeOfSample, changedAt); assert.equal(property.uncertaintyInMilliseconds, 6000); }); test("set: a temperature is reported in the scale it is given in", () => { const [property] = new StateBuilder({ now }).set(TemperatureSensor, "temperature", { value: 70, scale: "FAHRENHEIT" }).context; assert.deepEqual(property.value, { value: 70, scale: "FAHRENHEIT" }); }); test("set: a value Alexa would not take is refused, the error names the interface and the property", () => { const state = new StateBuilder({ now }); assert.throws(() => state.set(PowerController, "powerState", "on"), (err) => { assert.ok(err instanceof SchemaError); assert.match(err.message, /^Alexa\.PowerController: powerState: expected .*ON.*got "on"$/); return true; }); assert.throws(() => state.set(PowerController, "power", "ON"), /Alexa\.PowerController: power: not a property of the interface, which has powerState/); assert.deepEqual(state.context, []); }); test("set: a generic controller needs its instance, given with the property or by the capability", () => { const state = new StateBuilder({ now }); assert.throws(() => state.set(RangeController, "rangeValue", 3), /Alexa\.RangeController: rangeValue: the interface has instances/); const lift = new Capability(RangeController, { instance: "Blind.Lift", options: {} }); state.set(RangeController, "rangeValue", 3, { instance: "Blind.Tilt" }).set(lift, "rangeValue", 40); assert.deepEqual(state.context, [ { namespace: "Alexa.RangeController", instance: "Blind.Tilt", name: "rangeValue", value: 3, timeOfSample: AT, uncertaintyInMilliseconds: 0 }, { namespace: "Alexa.RangeController", instance: "Blind.Lift", name: "rangeValue", value: 40, timeOfSample: AT, uncertaintyInMilliseconds: 0 }, ]); }); test("setRaw: a property of an interface the library does not describe, nothing checked", () => { const state = new StateBuilder({ now }).setRaw("Alexa.Vendor.Thing", "level", { anything: true }, { instance: "Thing.One" }); assert.deepEqual(state.context, [ { namespace: "Alexa.Vendor.Thing", instance: "Thing.One", name: "level", value: { anything: true }, timeOfSample: AT, uncertaintyInMilliseconds: 0 }, ]); }); test("health: the connectivity of Alexa.EndpointHealth, with the reason when there is one", () => { const state = new StateBuilder({ now }).health("OK").health("UNREACHABLE", "WIFI_AP_NOT_FOUND"); assert.deepEqual(state.context.map((property) => [property.namespace, property.name, property.value]), [ [EndpointHealth.namespace, "connectivity", { value: "OK" }], [EndpointHealth.namespace, "connectivity", { value: "UNREACHABLE", reason: "WIFI_AP_NOT_FOUND" }], ]); assert.throws(() => new StateBuilder().health("ASLEEP"), SchemaError); }); test("changed and unchanged: the list the properties that follow go to; add() names one", () => { const state = new StateBuilder({ now, target: "change" }) .set(PowerController, "powerState", "ON") .unchanged() .set(BrightnessController, "brightness", 40) .changed() .health("OK") .add(messages.property("Alexa.Vendor.Thing", "level", 1, { timeOfSample: AT }), "context"); assert.deepEqual(state.change.map((property) => property.name), ["powerState", "connectivity"]); assert.deepEqual(state.context.map((property) => property.name), ["brightness", "level"]); }); test("context and change are copies: the message built from them does not change with the builder", () => { const state = new StateBuilder({ now }).set(PowerController, "powerState", "ON"); const report = messages.stateReport({ endpointId: "lamp-1", correlationToken: "ct", messageId: "m", context: state.context }); state.set(BrightnessController, "brightness", 40); assert.equal(report.context.properties.length, 1); });