|
4 | 4 | * SPDX-License-Identifier: Apache-2.0 |
5 | 5 | */ |
6 | 6 |
|
| 7 | +import { BasicInformationClient } from "#behaviors/basic-information"; |
| 8 | +import { OperationalCredentialsClient } from "#behaviors/operational-credentials"; |
7 | 9 | import { Endpoint } from "#endpoint/Endpoint.js"; |
| 10 | +import { EndpointBehaviorNotPresentError } from "#endpoint/errors.js"; |
8 | 11 | import type { EndpointType } from "#endpoint/type/EndpointType.js"; |
9 | 12 | import { MockSite } from "../node/mock-site.js"; |
10 | 13 | import { subscribedPeer } from "../node/node-helpers.js"; |
@@ -63,4 +66,115 @@ describe("Endpoint.get on a client endpoint", () => { |
63 | 66 | const result = await asEndpoint(peer).get({}); |
64 | 67 | expect(result).to.deep.equal({}); |
65 | 68 | }); |
| 69 | + |
| 70 | + // The `fabricFilter` option threads through to the protocol Read on the client branch of |
| 71 | + // `Endpoint.#performRead`. The server branch ignores it (the request is built with |
| 72 | + // `fabricFilter: false` unconditionally). This test covers the option's plumbing on the |
| 73 | + // client path: passing `fabricFilter: false` must not throw and must still return a usable |
| 74 | + // slice. |
| 75 | + it("accepts the fabricFilter option on the client read path", async () => { |
| 76 | + await using site = new MockSite(); |
| 77 | + const { controller } = await site.addCommissionedPair(); |
| 78 | + const peer = await subscribedPeer(controller, "peer1"); |
| 79 | + |
| 80 | + const result = (await asEndpoint(peer).get( |
| 81 | + { basicInformation: ["vendorName"] }, |
| 82 | + { fabricFilter: false }, |
| 83 | + )) as Record<string, Record<string, unknown>>; |
| 84 | + expect(result.basicInformation).to.have.property("vendorName"); |
| 85 | + }); |
| 86 | +}); |
| 87 | + |
| 88 | +describe("Endpoint.getStateOf on a client endpoint", () => { |
| 89 | + before(() => { |
| 90 | + MockTime.init(); |
| 91 | + }); |
| 92 | + |
| 93 | + it("returns full behavior state when called with type only (Behavior.Type overload)", async () => { |
| 94 | + await using site = new MockSite(); |
| 95 | + const { controller } = await site.addCommissionedPair(); |
| 96 | + const peer = await subscribedPeer(controller, "peer1"); |
| 97 | + |
| 98 | + const result = (await asEndpoint(peer).getStateOf(BasicInformationClient)) as unknown as Record< |
| 99 | + string, |
| 100 | + unknown |
| 101 | + >; |
| 102 | + const cachedBi = (peer as unknown as { state: { basicInformation: Record<string, unknown> } }).state |
| 103 | + .basicInformation; |
| 104 | + expect(result.vendorName).to.equal(cachedBi.vendorName); |
| 105 | + expect(result.productName).to.equal(cachedBi.productName); |
| 106 | + }); |
| 107 | + |
| 108 | + it("returns selected attributes when called with a key list (Behavior.Type overload)", async () => { |
| 109 | + await using site = new MockSite(); |
| 110 | + const { controller } = await site.addCommissionedPair(); |
| 111 | + const peer = await subscribedPeer(controller, "peer1"); |
| 112 | + |
| 113 | + const result = (await asEndpoint(peer).getStateOf(BasicInformationClient, [ |
| 114 | + "vendorName", |
| 115 | + "productName", |
| 116 | + ])) as unknown as Record<string, unknown>; |
| 117 | + expect(Object.keys(result).sort()).to.deep.equal(["productName", "vendorName"]); |
| 118 | + const cachedBi = (peer as unknown as { state: { basicInformation: Record<string, unknown> } }).state |
| 119 | + .basicInformation; |
| 120 | + expect(result.vendorName).to.equal(cachedBi.vendorName); |
| 121 | + expect(result.productName).to.equal(cachedBi.productName); |
| 122 | + }); |
| 123 | + |
| 124 | + it("supports the string-id overload", async () => { |
| 125 | + await using site = new MockSite(); |
| 126 | + const { controller } = await site.addCommissionedPair(); |
| 127 | + const peer = await subscribedPeer(controller, "peer1"); |
| 128 | + |
| 129 | + const result = (await asEndpoint(peer).getStateOf("basicInformation", ["vendorName"])) as Record< |
| 130 | + string, |
| 131 | + unknown |
| 132 | + >; |
| 133 | + expect(Object.keys(result)).to.deep.equal(["vendorName"]); |
| 134 | + const cachedBi = (peer as unknown as { state: { basicInformation: Record<string, unknown> } }).state |
| 135 | + .basicInformation; |
| 136 | + expect(result.vendorName).to.equal(cachedBi.vendorName); |
| 137 | + }); |
| 138 | + |
| 139 | + it("returns {} when called with an empty attribute list", async () => { |
| 140 | + await using site = new MockSite(); |
| 141 | + const { controller } = await site.addCommissionedPair(); |
| 142 | + const peer = await subscribedPeer(controller, "peer1"); |
| 143 | + |
| 144 | + const result = await asEndpoint(peer).getStateOf("basicInformation", []); |
| 145 | + expect(result).to.deep.equal({}); |
| 146 | + }); |
| 147 | + |
| 148 | + it("throws EndpointBehaviorNotPresentError for an unknown behavior id", async () => { |
| 149 | + await using site = new MockSite(); |
| 150 | + const { controller } = await site.addCommissionedPair(); |
| 151 | + const peer = await subscribedPeer(controller, "peer1"); |
| 152 | + |
| 153 | + let threw = false; |
| 154 | + try { |
| 155 | + await asEndpoint(peer).getStateOf("unknownBehaviorXyz"); |
| 156 | + } catch (e) { |
| 157 | + threw = true; |
| 158 | + expect(e).to.be.instanceof(EndpointBehaviorNotPresentError); |
| 159 | + } |
| 160 | + expect(threw).to.be.true; |
| 161 | + }); |
| 162 | + |
| 163 | + // ClientNode behaviors are added dynamically by ClientStructure after commissioning, so they |
| 164 | + // are not part of `ClientNode.RootEndpoint`'s static behavior map. The Behavior.Type overload of |
| 165 | + // `getStateOf()` must therefore accept any `Behavior.Type` (not only `BehaviorOf<T>`) so callers |
| 166 | + // can pass cluster behaviors like `OperationalCredentialsClient` directly without an `asEndpoint` |
| 167 | + // widening cast. |
| 168 | + it("accepts a dynamically-added behavior class without a widening cast (typed call)", async () => { |
| 169 | + await using site = new MockSite(); |
| 170 | + const { controller } = await site.addCommissionedPair(); |
| 171 | + const peer = await subscribedPeer(controller, "peer1"); |
| 172 | + |
| 173 | + const result = (await peer.getStateOf(OperationalCredentialsClient, ["fabrics"])) as unknown as Record< |
| 174 | + string, |
| 175 | + unknown |
| 176 | + >; |
| 177 | + expect(Object.keys(result)).to.deep.equal(["fabrics"]); |
| 178 | + expect(Array.isArray(result.fabrics)).to.be.true; |
| 179 | + }); |
66 | 180 | }); |
0 commit comments