@@ -110,3 +110,321 @@ describe('fromRpcTypedData', () => {
110110 expect ( result [ 1 ] . toString ( ) ) . to . equal ( '9007199254740992' ) ;
111111 } ) ;
112112} ) ;
113+
114+ describe ( 'fromRpcTypedData - modelBindingData path' , ( ) => {
115+ // Use SinonSandbox for automatic cleanup of stubs
116+ let sandbox : sinon . SinonSandbox ;
117+
118+ // Store original ResourceFactoryResolver.getInstance to restore after tests
119+ let originalGetInstance : typeof ResourceFactoryResolver . getInstance ;
120+
121+ beforeEach ( ( ) => {
122+ sandbox = sinon . createSandbox ( ) ;
123+ // Store original method
124+ originalGetInstance = ResourceFactoryResolver . getInstance . bind ( ResourceFactoryResolver ) ;
125+ } ) ;
126+
127+ afterEach ( ( ) => {
128+ // Restore all stubs and original methods
129+ sandbox . restore ( ) ;
130+ ResourceFactoryResolver . getInstance = originalGetInstance ;
131+ } ) ;
132+
133+ it ( 'should successfully create a client when modelBindingData is valid' , ( ) => {
134+ // Arrange
135+ const mockClient = {
136+ name : 'testClient' ,
137+ download : ( ) => Promise . resolve ( { readableStreamBody : Buffer . from ( 'test' ) } ) ,
138+ } ;
139+
140+ // Create mock ResourceFactoryResolver
141+ const mockResolver = {
142+ createClient : sinon . stub ( ) . returns ( mockClient ) ,
143+ } ;
144+
145+ // Replace ResourceFactoryResolver.getInstance with our mock
146+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
147+
148+ // Create test data
149+ const modelBindingData = {
150+ content : Buffer . from ( 'test-content' ) ,
151+ source : 'blob' ,
152+ contentType : 'application/octet-stream' ,
153+ } ;
154+
155+ const data : RpcTypedData = {
156+ modelBindingData : modelBindingData ,
157+ } ;
158+
159+ // Act
160+ const result = fromRpcTypedData ( data ) ;
161+
162+ // Assert
163+ sinon . assert . calledWith ( mockResolver . createClient , 'blob' , modelBindingData ) ;
164+ expect ( result ) . to . equal ( mockClient ) ;
165+ } ) ;
166+
167+ it ( 'should handle modelBindingData with undefined source' , ( ) => {
168+ // Arrange
169+ const mockClient = { name : 'testClient' } ;
170+
171+ const mockResolver = {
172+ createClient : sinon . stub ( ) . returns ( mockClient ) ,
173+ } ;
174+
175+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
176+
177+ const modelBindingData = {
178+ content : Buffer . from ( 'test-content' ) ,
179+ // No source specified
180+ contentType : 'application/octet-stream' ,
181+ } ;
182+
183+ const data : RpcTypedData = {
184+ modelBindingData : modelBindingData ,
185+ } ;
186+
187+ // Act
188+ const result = fromRpcTypedData ( data ) ;
189+
190+ // Assert
191+ expect ( mockResolver . createClient . calledWith ( undefined , modelBindingData ) ) . to . be . true ;
192+ expect ( result ) . to . equal ( mockClient ) ;
193+ } ) ;
194+
195+ it ( 'should throw enhanced error when ResourceFactoryResolver.createClient throws' , ( ) => {
196+ // Arrange
197+ const originalError = new Error ( 'Factory not registered' ) ;
198+
199+ const mockResolver = {
200+ createClient : sinon . stub ( ) . throws ( originalError ) ,
201+ } ;
202+
203+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
204+
205+ const modelBindingData = {
206+ content : Buffer . from ( 'test-content' ) ,
207+ source : 'blob' ,
208+ contentType : 'application/octet-stream' ,
209+ } ;
210+
211+ const data : RpcTypedData = {
212+ modelBindingData : modelBindingData ,
213+ } ;
214+
215+ // Act & Assert
216+ expect ( ( ) => fromRpcTypedData ( data ) ) . to . throw (
217+ 'Unable to create client. Please register the extensions library with your function app. ' +
218+ 'Error: Factory not registered'
219+ ) ;
220+ } ) ;
221+
222+ it ( 'should throw enhanced error when ResourceFactoryResolver.getInstance throws' , ( ) => {
223+ // Arrange
224+ const originalError = new Error ( 'Resolver not initialized' ) ;
225+
226+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . throws ( originalError ) ;
227+
228+ const modelBindingData = {
229+ content : Buffer . from ( 'test-content' ) ,
230+ source : 'blob' ,
231+ contentType : 'application/octet-stream' ,
232+ } ;
233+
234+ const data : RpcTypedData = {
235+ modelBindingData : modelBindingData ,
236+ } ;
237+
238+ // Act & Assert
239+ expect ( ( ) => fromRpcTypedData ( data ) ) . to . throw (
240+ 'Unable to create client. Please register the extensions library with your function app. ' +
241+ 'Error: Resolver not initialized'
242+ ) ;
243+ } ) ;
244+
245+ it ( 'should handle non-Error exceptions by converting to string' , ( ) => {
246+ // Arrange
247+ const mockResolver = {
248+ createClient : sinon . stub ( ) . throws ( 'String exception' ) , // Non-Error exception
249+ } ;
250+
251+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
252+
253+ const modelBindingData = {
254+ content : Buffer . from ( 'test-content' ) ,
255+ source : 'blob' ,
256+ contentType : 'application/octet-stream' ,
257+ } ;
258+
259+ const data : RpcTypedData = {
260+ modelBindingData : modelBindingData ,
261+ } ;
262+
263+ // Act & Assert
264+ expect ( ( ) => fromRpcTypedData ( data ) ) . to . throw (
265+ 'Unable to create client. Please register the extensions library with your function app. ' +
266+ 'Error: Sinon-provided String exception'
267+ ) ;
268+ } ) ;
269+ } ) ;
270+ describe ( 'fromRpcTypedData - collectionModelBindingData path' , ( ) => {
271+ let sandbox : sinon . SinonSandbox ;
272+ let originalGetInstance : typeof ResourceFactoryResolver . getInstance ;
273+
274+ beforeEach ( ( ) => {
275+ sandbox = sinon . createSandbox ( ) ;
276+ originalGetInstance = ResourceFactoryResolver . getInstance . bind ( ResourceFactoryResolver ) ;
277+ } ) ;
278+
279+ afterEach ( ( ) => {
280+ sandbox . restore ( ) ;
281+ ResourceFactoryResolver . getInstance = originalGetInstance ;
282+ } ) ;
283+
284+ it ( 'should successfully create a client when collectionModelBindingData is valid' , ( ) => {
285+ const mockClient = { name : 'testCollectionClient' } ;
286+ const mockResolver = {
287+ createClient : sinon . stub ( ) . returns ( mockClient ) ,
288+ } ;
289+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
290+
291+ const collectionModelBindingData = {
292+ modelBindingData : [
293+ {
294+ content : Buffer . from ( 'test-content-1' ) ,
295+ source : 'blob' ,
296+ contentType : 'application/octet-stream' ,
297+ } ,
298+ {
299+ content : Buffer . from ( 'test-content-2' ) ,
300+ source : 'blob' ,
301+ contentType : 'application/octet-stream' ,
302+ } ,
303+ ] ,
304+ } ;
305+
306+ const data : RpcTypedData = {
307+ collectionModelBindingData,
308+ } ;
309+
310+ const result = fromRpcTypedData ( data ) ;
311+
312+ sinon . assert . calledWith ( mockResolver . createClient , 'blob' , collectionModelBindingData . modelBindingData ) ;
313+ expect ( result ) . to . equal ( mockClient ) ;
314+ } ) ;
315+
316+ it ( 'should handle collectionModelBindingData with undefined source' , ( ) => {
317+ const mockClient = { name : 'testCollectionClient' } ;
318+ const mockResolver = {
319+ createClient : sinon . stub ( ) . returns ( mockClient ) ,
320+ } ;
321+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
322+
323+ const collectionModelBindingData = {
324+ modelBindingData : [
325+ {
326+ content : Buffer . from ( 'test-content-1' ) ,
327+ // source is undefined
328+ contentType : 'application/octet-stream' ,
329+ } ,
330+ ] ,
331+ } ;
332+
333+ const data : RpcTypedData = {
334+ collectionModelBindingData,
335+ } ;
336+
337+ const result = fromRpcTypedData ( data ) ;
338+
339+ expect ( mockResolver . createClient . calledWith ( undefined , collectionModelBindingData . modelBindingData ) ) . to . be . true ;
340+ expect ( result ) . to . equal ( mockClient ) ;
341+ } ) ;
342+
343+ it ( 'should throw enhanced error when ResourceFactoryResolver.createClient throws for collectionModelBindingData' , ( ) => {
344+ const originalError = new Error ( 'Collection factory not registered' ) ;
345+ const mockResolver = {
346+ createClient : sinon . stub ( ) . throws ( originalError ) ,
347+ } ;
348+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
349+
350+ const collectionModelBindingData = {
351+ modelBindingData : [
352+ {
353+ content : Buffer . from ( 'test-content-1' ) ,
354+ source : 'blob' ,
355+ contentType : 'application/octet-stream' ,
356+ } ,
357+ ] ,
358+ } ;
359+
360+ const data : RpcTypedData = {
361+ collectionModelBindingData,
362+ } ;
363+
364+ expect ( ( ) => fromRpcTypedData ( data ) ) . to . throw (
365+ 'Unable to create client. Please register the extensions library with your function app. ' +
366+ 'Error: Collection factory not registered'
367+ ) ;
368+ } ) ;
369+
370+ it ( 'should throw enhanced error when ResourceFactoryResolver.getInstance throws for collectionModelBindingData' , ( ) => {
371+ const originalError = new Error ( 'Collection resolver not initialized' ) ;
372+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . throws ( originalError ) ;
373+
374+ const collectionModelBindingData = {
375+ modelBindingData : [
376+ {
377+ content : Buffer . from ( 'test-content-1' ) ,
378+ source : 'blob' ,
379+ contentType : 'application/octet-stream' ,
380+ } ,
381+ ] ,
382+ } ;
383+
384+ const data : RpcTypedData = {
385+ collectionModelBindingData,
386+ } ;
387+
388+ expect ( ( ) => fromRpcTypedData ( data ) ) . to . throw (
389+ 'Unable to create client. Please register the extensions library with your function app. ' +
390+ 'Error: Collection resolver not initialized'
391+ ) ;
392+ } ) ;
393+
394+ it ( 'should handle non-Error exceptions by converting to string for collectionModelBindingData' , ( ) => {
395+ const mockResolver = {
396+ createClient : sinon . stub ( ) . throws ( 'String exception for collection' ) , // Non-Error exception
397+ } ;
398+ ResourceFactoryResolver . getInstance = sinon . stub ( ) . returns ( mockResolver ) ;
399+
400+ const collectionModelBindingData = {
401+ modelBindingData : [
402+ {
403+ content : Buffer . from ( 'test-content-1' ) ,
404+ source : 'blob' ,
405+ contentType : 'application/octet-stream' ,
406+ } ,
407+ ] ,
408+ } ;
409+
410+ const data : RpcTypedData = {
411+ collectionModelBindingData,
412+ } ;
413+
414+ expect ( ( ) => fromRpcTypedData ( data ) ) . to . throw (
415+ 'Unable to create client. Please register the extensions library with your function app. ' +
416+ 'Error: Sinon-provided String exception for collection'
417+ ) ;
418+ } ) ;
419+ } ) ;
420+
421+ describe ( 'fromRpcTypedData - fallback/undefined cases' , ( ) => {
422+ it ( 'should return undefined for unknown data shape' , ( ) => {
423+ const data : RpcTypedData = { foo : 'bar' } as any ;
424+ expect ( fromRpcTypedData ( data ) ) . to . be . undefined ;
425+ } ) ;
426+
427+ it ( 'should return undefined for empty object' , ( ) => {
428+ expect ( fromRpcTypedData ( { } as RpcTypedData ) ) . to . be . undefined ;
429+ } ) ;
430+ } ) ;
0 commit comments