@adobe/acc-js-sdk 1.1.6 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +11 -0
- package/README.md +36 -2
- package/package-lock.json +5 -4
- package/package.json +1 -1
- package/src/application.js +56 -13
- package/src/cache.js +19 -0
- package/src/cacheRefresher.js +53 -13
- package/src/campaign.js +29 -28
- package/src/client.js +342 -104
- package/src/transport.js +11 -10
- package/test/application.test.js +71 -0
- package/test/caches.test.js +6 -0
- package/test/client.test.js +272 -31
- package/test/mock.js +160 -46
- package/test/observability.test.js +267 -0
package/test/mock.js
CHANGED
|
@@ -12,37 +12,37 @@ governing permissions and limitations under the License.
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
/**********************************************************************************
|
|
15
|
-
*
|
|
15
|
+
*
|
|
16
16
|
* Mock functions for unit tests
|
|
17
|
-
*
|
|
17
|
+
*
|
|
18
18
|
*********************************************************************************/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
19
|
+
const sdk = require('../src/index.js');
|
|
20
|
+
const crypto = require("crypto");
|
|
21
|
+
|
|
22
|
+
const makeKey = () => {
|
|
23
|
+
const a = [];
|
|
24
|
+
for (let i=0; i<32; i++) {
|
|
25
|
+
a.push(Math.floor(crypto.randomInt(0, 256)));
|
|
26
|
+
}
|
|
27
|
+
const buffer = Buffer.from(a);
|
|
28
|
+
const s = buffer.toString('base64');
|
|
29
|
+
return s;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function makeAnonymousClient(options) {
|
|
33
|
+
const connectionParameters = sdk.ConnectionParameters.ofAnonymousUser("http://acc-sdk:8080", options);
|
|
34
|
+
const client = await sdk.init(connectionParameters);
|
|
35
|
+
if (!options || !options.transport) // allow tests to explicitely set the transport
|
|
36
|
+
client._transport = jest.fn();
|
|
37
|
+
return client;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
async function makeClient(options) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
const connectionParameters = sdk.ConnectionParameters.ofUserAndPassword("http://acc-sdk:8080", "admin", "admin", options);
|
|
42
|
+
const client = await sdk.init(connectionParameters);
|
|
43
|
+
if (!options || !options.transport) // allow tests to explicitely set the transport
|
|
44
|
+
client._transport = jest.fn();
|
|
45
|
+
return client;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -51,16 +51,16 @@ async function makeClient(options) {
|
|
|
51
51
|
* @returns an array of logged messages
|
|
52
52
|
*/
|
|
53
53
|
async function withMockConsole(fn) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
const logs = [];
|
|
55
|
+
jest.spyOn(console, 'log').mockImplementation((message) => {
|
|
56
|
+
logs.push(message);
|
|
57
|
+
});
|
|
58
|
+
try {
|
|
59
|
+
await fn();
|
|
60
|
+
return logs;
|
|
61
|
+
} finally {
|
|
62
|
+
console.log.mockRestore();
|
|
63
|
+
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const R_TEST = Promise.resolve(`<redir status='OK' date='2021-08-27 08:02:07.963-07' build='9236' sha1='cc45440' instance='xxx_mkt_prod1' sourceIP='193.104.215.11' host='xxxol.campaign.adobe.com' localHost='xxxol-mkt-prod1-1'/>`);
|
|
@@ -91,7 +91,7 @@ const LOGON_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
|
91
91
|
</SOAP-ENV:Body>
|
|
92
92
|
</SOAP-ENV:Envelope>`);
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
const BEARER_LOGON_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
95
95
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:session' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
96
96
|
<SOAP-ENV:Body>
|
|
97
97
|
<BearerTokenLogonResponse xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
@@ -302,7 +302,7 @@ const GET_XTK_QUERY_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
|
302
302
|
</SOAP-ENV:Envelope>`);
|
|
303
303
|
|
|
304
304
|
const GET_MID_EXT_ACCOUNT_RESPONSE = (encryptedPassword) => {
|
|
305
|
-
|
|
305
|
+
return Promise.resolve(`<?xml version='1.0'?>
|
|
306
306
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:queryDef' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
307
307
|
<SOAP-ENV:Body>
|
|
308
308
|
<ExecuteQueryResponse xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
@@ -315,7 +315,7 @@ const GET_MID_EXT_ACCOUNT_RESPONSE = (encryptedPassword) => {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
const GET_BAD_EXT_ACCOUNT_RESPONSE = (encryptedPassword) => {
|
|
318
|
-
|
|
318
|
+
return Promise.resolve(`<?xml version='1.0'?>
|
|
319
319
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:queryDef' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
320
320
|
<SOAP-ENV:Body>
|
|
321
321
|
<ExecuteQueryResponse xmlns='urn:xtk:queryDef' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
@@ -328,7 +328,7 @@ const GET_BAD_EXT_ACCOUNT_RESPONSE = (encryptedPassword) => {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
const GET_SECRET_KEY_OPTION_RESPONSE = (key) => {
|
|
331
|
-
|
|
331
|
+
return Promise.resolve(`<?xml version='1.0'?>
|
|
332
332
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:session' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
333
333
|
<SOAP-ENV:Body>
|
|
334
334
|
<GetOptionResponse xmlns='urn:xtk:session' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
@@ -354,7 +354,7 @@ const GET_LOGON_MID_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
|
354
354
|
</LogonResponse>
|
|
355
355
|
</SOAP-ENV:Body>
|
|
356
356
|
</SOAP-ENV:Envelope>`);
|
|
357
|
-
|
|
357
|
+
|
|
358
358
|
const GET_TSTCNX_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
359
359
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:xtk:session' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
360
360
|
<SOAP-ENV:Body>
|
|
@@ -432,7 +432,7 @@ const GET_XTK_ALL_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
|
432
432
|
</GetEntityIfMoreRecentResponse>
|
|
433
433
|
</SOAP-ENV:Body>
|
|
434
434
|
</SOAP-ENV:Envelope>`);
|
|
435
|
-
|
|
435
|
+
|
|
436
436
|
const GET_XTK_ALL_TYPES_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
437
437
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:wpp:default' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
438
438
|
<SOAP-ENV:Body>
|
|
@@ -473,7 +473,7 @@ const GET_USER_INFO_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
|
473
473
|
</pUserInfo>
|
|
474
474
|
</GetUserInfoResponse>
|
|
475
475
|
</SOAP-ENV:Body>
|
|
476
|
-
</SOAP-ENV:Envelope>`);
|
|
476
|
+
</SOAP-ENV:Envelope>`);
|
|
477
477
|
|
|
478
478
|
const GET_MISSING_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
479
479
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:wpp:default' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
@@ -483,8 +483,8 @@ const GET_MISSING_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
|
483
483
|
</pdomDoc>
|
|
484
484
|
</GetEntityIfMoreRecentResponse>
|
|
485
485
|
</SOAP-ENV:Body>
|
|
486
|
-
</SOAP-ENV:Envelope>`);
|
|
487
|
-
|
|
486
|
+
</SOAP-ENV:Envelope>`);
|
|
487
|
+
|
|
488
488
|
const GET_XTK_WORKFLOW_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
489
489
|
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:wpp:default' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
490
490
|
<SOAP-ENV:Body>
|
|
@@ -635,6 +635,114 @@ const GETMODIFIEDENTITIES_CLEAR_RESPONSE = Promise.resolve(`<?xml version='1.0'?
|
|
|
635
635
|
</GetModifiedEntitiesResponse>
|
|
636
636
|
</SOAP-ENV:Body>
|
|
637
637
|
</SOAP-ENV:Envelope>`);
|
|
638
|
+
const GET_XTK_COUNTER_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
639
|
+
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:wpp:default' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
640
|
+
<SOAP-ENV:Body>
|
|
641
|
+
<GetEntityIfMoreRecentResponse xmlns='urn:wpp:default' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
642
|
+
<pdomDoc xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
|
|
643
|
+
<schema name="counter" namespace="xtk" xtkschema="xtk:schema">
|
|
644
|
+
<element name="counter"></element>
|
|
645
|
+
<methods>
|
|
646
|
+
<method name="IncreaseValue" static="true">
|
|
647
|
+
<parameters>
|
|
648
|
+
<param name="name" type="string" label="Name" desc="Counter name"/>
|
|
649
|
+
<param name="value" type="long" inout="out" label="Value" desc="New value of counter"/>
|
|
650
|
+
</parameters>
|
|
651
|
+
</method>
|
|
652
|
+
</methods>
|
|
653
|
+
</schema>
|
|
654
|
+
</pdomDoc>
|
|
655
|
+
</GetEntityIfMoreRecentResponse>
|
|
656
|
+
</SOAP-ENV:Body>
|
|
657
|
+
</SOAP-ENV:Envelope>`);
|
|
658
|
+
|
|
659
|
+
const GET_FILERES_QUERY_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
660
|
+
<SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:wpp:default' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
661
|
+
<SOAP-ENV:Body>
|
|
662
|
+
<GetEntityIfMoreRecentResponse xmlns='urn:wpp:default' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
663
|
+
<pdomDoc xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'>
|
|
664
|
+
<schema name="fileRes" namespace="xtk" xtkschema="xtk:schema">
|
|
665
|
+
<element name="fileRes"></element>
|
|
666
|
+
<methods>
|
|
667
|
+
<method name="PublishIfNeeded">
|
|
668
|
+
</method>
|
|
669
|
+
<method name="GetURL">
|
|
670
|
+
<parameters>
|
|
671
|
+
<param name="url" type="string" inout="out"/>
|
|
672
|
+
</parameters>
|
|
673
|
+
</method>
|
|
674
|
+
</methods>
|
|
675
|
+
</schema>
|
|
676
|
+
</pdomDoc>
|
|
677
|
+
</GetEntityIfMoreRecentResponse>
|
|
678
|
+
</SOAP-ENV:Body>
|
|
679
|
+
</SOAP-ENV:Envelope>`);
|
|
680
|
+
|
|
681
|
+
const INCREASE_VALUE_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
682
|
+
<SOAP-ENV:Envelope
|
|
683
|
+
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
|
|
684
|
+
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
|
685
|
+
xmlns:ns='urn:xtk:counter'
|
|
686
|
+
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
687
|
+
<SOAP-ENV:Body>
|
|
688
|
+
<IncreaseValueResponse
|
|
689
|
+
xmlns='urn:xtk:counter' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
690
|
+
<plValue xsi:type='xsd:int'>1</plValue>
|
|
691
|
+
</IncreaseValueResponse>
|
|
692
|
+
</SOAP-ENV:Body>
|
|
693
|
+
</SOAP-ENV:Envelope>
|
|
694
|
+
|
|
695
|
+
`);
|
|
696
|
+
|
|
697
|
+
const FILE_RES_WRITE_RESPONSE = Promise.resolve(`<?xml version='1.0'?><SOAP-ENV:Envelope xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:ns='urn:wpp:default' xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'><SOAP-ENV:Body><GetEntityIfMoreRecentResponse xmlns='urn:wpp:default' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'><pdomDoc xsi:type='ns:Element' SOAP-ENV:encodingStyle='http://xml.apache.org/xml-soap/literalxml'><schema _cs="Sessions to the application server (xtk)" created="2022-02-03 12:55:02.621Z" createdBy-id="0" dependingSchemas="" desc="Sessions to the application server" entitySchema="xtk:schema" img="" implements="xtk:persist" label="Sessions to the application server" labelSingular="Session" lastModified="2022-07-11 02:29:30.026Z" library="true" mappingType="xmlFile" md5="9D716FE59E174E87C5BA8A834DD4B11B" modifiedBy-id="0" name="session" namespace="xtk" xtkschema="xtk:schema">
|
|
698
|
+
|
|
699
|
+
<interface async="true" label="Persistence" name="persist">
|
|
700
|
+
<method name="Write" static="true">
|
|
701
|
+
<help>Update an entity</help>
|
|
702
|
+
<parameters>
|
|
703
|
+
<param desc="Document of difference" name="doc" type="DOMDocument"/>
|
|
704
|
+
</parameters>
|
|
705
|
+
<example><para>A simple recipient creation:</para>
|
|
706
|
+
\t <programlisting>
|
|
707
|
+
xtk.session.Write(
|
|
708
|
+
{recipient: {xtkschema: "nms:recipient", firstName: "Raul", lastName: "Endymion"}})
|
|
709
|
+
\t</programlisting>
|
|
710
|
+
</example>
|
|
711
|
+
</method>
|
|
712
|
+
</interface>
|
|
713
|
+
|
|
714
|
+
</schema></pdomDoc></GetEntityIfMoreRecentResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
|
|
715
|
+
|
|
716
|
+
`);
|
|
717
|
+
|
|
718
|
+
const PUBLISH_IF_NEEDED_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
719
|
+
<SOAP-ENV:Envelope
|
|
720
|
+
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
|
|
721
|
+
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
|
722
|
+
xmlns:ns='urn:xtk:fileRes'
|
|
723
|
+
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
724
|
+
<SOAP-ENV:Body>
|
|
725
|
+
<PublishIfNeededResponse
|
|
726
|
+
xmlns='urn:xtk:fileRes' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
727
|
+
</PublishIfNeededResponse>
|
|
728
|
+
</SOAP-ENV:Body>
|
|
729
|
+
</SOAP-ENV:Envelope>`);
|
|
730
|
+
|
|
731
|
+
const GET_URL_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
732
|
+
<SOAP-ENV:Envelope
|
|
733
|
+
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
|
|
734
|
+
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
|
|
735
|
+
xmlns:ns='urn:xtk:fileRes'
|
|
736
|
+
xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/'>
|
|
737
|
+
<SOAP-ENV:Body>
|
|
738
|
+
<GetURLResponse
|
|
739
|
+
xmlns='urn:xtk:fileRes' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>
|
|
740
|
+
<pUrl xsi:type='xsd:string'>http://hello.com</pUrl>
|
|
741
|
+
</GetURLResponse>
|
|
742
|
+
</SOAP-ENV:Body>
|
|
743
|
+
</SOAP-ENV:Envelope>`);
|
|
744
|
+
|
|
745
|
+
|
|
638
746
|
|
|
639
747
|
|
|
640
748
|
const GETMODIFIEDENTITIES_SCHEMA_RESPONSE = Promise.resolve(`<?xml version='1.0'?>
|
|
@@ -721,5 +829,11 @@ exports.Mock = {
|
|
|
721
829
|
GETMODIFIEDENTITIES_CLEAR_RESPONSE: GETMODIFIEDENTITIES_CLEAR_RESPONSE,
|
|
722
830
|
GETMODIFIEDENTITIES_SCHEMA_RESPONSE: GETMODIFIEDENTITIES_SCHEMA_RESPONSE,
|
|
723
831
|
GETMODIFIEDENTITIES_UNDEFINED_RESPONSE: GETMODIFIEDENTITIES_UNDEFINED_RESPONSE,
|
|
724
|
-
GETMODIFIEDENTITIES_ERROR_RESPONSE: GETMODIFIEDENTITIES_ERROR_RESPONSE
|
|
832
|
+
GETMODIFIEDENTITIES_ERROR_RESPONSE: GETMODIFIEDENTITIES_ERROR_RESPONSE,
|
|
833
|
+
GET_XTK_COUNTER_RESPONSE: GET_XTK_COUNTER_RESPONSE,
|
|
834
|
+
GET_FILERES_QUERY_SCHEMA_RESPONSE: GET_FILERES_QUERY_SCHEMA_RESPONSE,
|
|
835
|
+
INCREASE_VALUE_RESPONSE: INCREASE_VALUE_RESPONSE,
|
|
836
|
+
FILE_RES_WRITE_RESPONSE: FILE_RES_WRITE_RESPONSE,
|
|
837
|
+
PUBLISH_IF_NEEDED_RESPONSE: PUBLISH_IF_NEEDED_RESPONSE,
|
|
838
|
+
GET_URL_RESPONSE: GET_URL_RESPONSE
|
|
725
839
|
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**********************************************************************************
|
|
15
|
+
*
|
|
16
|
+
* Unit tests for the ACC client observability hooks
|
|
17
|
+
*
|
|
18
|
+
*********************************************************************************/
|
|
19
|
+
const sdk = require('../src/index.js');
|
|
20
|
+
const Mock = require('./mock.js').Mock;
|
|
21
|
+
|
|
22
|
+
const makeObservableClient = async(options, callback) => {
|
|
23
|
+
// No TTL for option cache so that we can test that we send cache stats every 5 mins
|
|
24
|
+
const client = await Mock.makeClient({ ...options, optionCacheTTL: 999999000 });
|
|
25
|
+
return [client, new ObservabilityAssertion(client, callback)];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class ObservabilityAssertion {
|
|
29
|
+
constructor(client, callback) {
|
|
30
|
+
const that = this;
|
|
31
|
+
this._callback = callback;
|
|
32
|
+
client.registerObserver({
|
|
33
|
+
event: (event, parentEvent) => { return that.onEvent(event, parentEvent); },
|
|
34
|
+
});
|
|
35
|
+
this._eventsByName = {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
onEvent(event, parentEvent) {
|
|
39
|
+
if (!this._eventsByName[event.eventName]) this._eventsByName[event.eventName] = [];
|
|
40
|
+
this._eventsByName[event.eventName].push({ event: event, parentEvent: parentEvent });
|
|
41
|
+
if (this._callback) this._callback.call(this, event, parentEvent);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
hasObserved(eventName) {
|
|
45
|
+
if (!this._eventsByName[eventName]) return false;
|
|
46
|
+
return this._eventsByName[eventName].length > 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getFirstObserved(eventName) {
|
|
50
|
+
if (!this._eventsByName[eventName]) return;
|
|
51
|
+
return this._eventsByName[eventName][0];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getObserved(eventName) {
|
|
55
|
+
if (!this._eventsByName[eventName]) [];
|
|
56
|
+
return this._eventsByName[eventName].filter(o => o.event.eventName === eventName).map(o => o.event);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe('ACC Client Observability', function () {
|
|
61
|
+
|
|
62
|
+
it('Should observe logon and logoff', async function () {
|
|
63
|
+
const [client, assertion] = await makeObservableClient();
|
|
64
|
+
|
|
65
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
66
|
+
await client.NLWS.xtkSession.logon();
|
|
67
|
+
expect(assertion.hasObserved("SDK//logon")).toBe(true);
|
|
68
|
+
expect(assertion.hasObserved("SDK//logoff")).toBe(false);;
|
|
69
|
+
const logon = assertion.getFirstObserved("SDK//logon");
|
|
70
|
+
expect(logon.parentEvent).toBeUndefined();
|
|
71
|
+
expect(logon.event).toMatchObject({ eventId: 1, eventName: "SDK//logon" });
|
|
72
|
+
expect(logon.event.timestamp).toBeGreaterThan(0);
|
|
73
|
+
|
|
74
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
75
|
+
await client.NLWS.xtkSession.logoff();
|
|
76
|
+
expect(assertion.hasObserved("SDK//logoff")).toBe(true);
|
|
77
|
+
const logoff = assertion.getFirstObserved("SDK//logoff");
|
|
78
|
+
expect(logoff.event).toMatchObject({ eventName: "SDK//logoff" });
|
|
79
|
+
expect(logoff.event.eventId).toBeGreaterThan(1);
|
|
80
|
+
expect(logoff.event.timestamp).toBeGreaterThanOrEqual(logon.event.timestamp);
|
|
81
|
+
|
|
82
|
+
// there should not be an auto refresh event if auto-refresh mechanism
|
|
83
|
+
// is off
|
|
84
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//stop")).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('Should ignore exceptions throws from observer', async () => {
|
|
88
|
+
const [client, assertion] = await makeObservableClient({}, (event, parentEvent) => {
|
|
89
|
+
throw new Error("Simulated failure in observer");
|
|
90
|
+
});
|
|
91
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
92
|
+
// logon will send an observability event, but the error will be logged and ignored
|
|
93
|
+
await client.NLWS.xtkSession.logon();
|
|
94
|
+
expect(assertion.hasObserved("SDK//logon")).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('Should send internal stats every 5 minutes', async () => {
|
|
98
|
+
jest.useFakeTimers();
|
|
99
|
+
const [client, assertion] = await makeObservableClient();
|
|
100
|
+
|
|
101
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
102
|
+
await client.NLWS.xtkSession.logon();
|
|
103
|
+
expect(assertion.hasObserved("SDK//logon")).toBe(true);
|
|
104
|
+
|
|
105
|
+
// Calling get option should generate some cache hits
|
|
106
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
107
|
+
client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
|
|
108
|
+
var databaseId = await client.getOption("XtkDatabaseId");
|
|
109
|
+
expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
|
|
110
|
+
|
|
111
|
+
jest.advanceTimersByTime(310000);
|
|
112
|
+
client._trackEvent("TEST/dummy", undefined, {});
|
|
113
|
+
// Note: options cache is written twice: once in the SOAP call itself, and once by the getOption method
|
|
114
|
+
// entity cache is also written twice: once for xtk:session and once from xtk:persist
|
|
115
|
+
expect(assertion.getObserved("CACHE//stats")).toEqual(expect.arrayContaining(
|
|
116
|
+
[
|
|
117
|
+
expect.objectContaining({ payload: { name: 'entityCache', clears: 0, loads: 1, memoryHits: 0, reads: 1, removals: 0, saves: 2, storageHits: 0, writes: 2 } }),
|
|
118
|
+
expect.objectContaining({ payload: { name: 'optionCache', clears: 0, loads: 1, memoryHits: 0, reads: 1, removals: 0, saves: 2, storageHits: 0, writes: 2 } }),
|
|
119
|
+
]
|
|
120
|
+
));
|
|
121
|
+
|
|
122
|
+
// Calling get option again should make a cache hit
|
|
123
|
+
// Only work if options cache TTL is very high
|
|
124
|
+
databaseId = await client.getOption("XtkDatabaseId");
|
|
125
|
+
expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
|
|
126
|
+
jest.advanceTimersByTime(310000);
|
|
127
|
+
client._trackEvent("TEST/dummy", undefined, {});
|
|
128
|
+
expect(assertion.getObserved("CACHE//stats")).toEqual(expect.arrayContaining(
|
|
129
|
+
[
|
|
130
|
+
expect.objectContaining({ payload: { name: 'entityCache', clears: 0, loads: 1, memoryHits: 0, reads: 1, removals: 0, saves: 2, storageHits: 0, writes: 2 } }),
|
|
131
|
+
expect.objectContaining({ payload: { name: 'optionCache', clears: 0, loads: 1, memoryHits: 1, reads: 2, removals: 0, saves: 2, storageHits: 0, writes: 2 } }),
|
|
132
|
+
]
|
|
133
|
+
));
|
|
134
|
+
|
|
135
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
136
|
+
await client.NLWS.xtkSession.logoff();
|
|
137
|
+
expect(assertion.hasObserved("SDK//logoff")).toBe(true);
|
|
138
|
+
|
|
139
|
+
jest.useRealTimers();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('_trackCacheStats should support cache with no stats', async () => {
|
|
143
|
+
const [client, assertion] = await makeObservableClient();
|
|
144
|
+
client._trackEvent = jest.fn();
|
|
145
|
+
client._trackCacheStats('hello', undefined);
|
|
146
|
+
expect(client._trackEvent.mock.calls.length).toBe(0);
|
|
147
|
+
client._trackCacheStats('hello', {});
|
|
148
|
+
expect(client._trackEvent.mock.calls.length).toBe(0);
|
|
149
|
+
client._trackCacheStats('hello', { _stats: {} });
|
|
150
|
+
expect(client._trackEvent.mock.calls.length).toBe(1);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('Should track SOAP calls', async () => {
|
|
154
|
+
const [client, assertion] = await makeObservableClient();
|
|
155
|
+
|
|
156
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
157
|
+
await client.NLWS.xtkSession.logon();
|
|
158
|
+
expect(assertion.hasObserved("SDK//logon")).toBe(true);
|
|
159
|
+
|
|
160
|
+
// Calling get option should generate some cache hits
|
|
161
|
+
client._transport.mockReturnValueOnce(Mock.GET_XTK_SESSION_SCHEMA_RESPONSE);
|
|
162
|
+
client._transport.mockReturnValueOnce(Mock.GET_DATABASEID_RESPONSE);
|
|
163
|
+
var databaseId = await client.getOption("XtkDatabaseId");
|
|
164
|
+
expect(databaseId).toBe("uFE80000000000000F1FA913DD7CC7C480041161C");
|
|
165
|
+
const requests = assertion.getObserved("SOAP//request");
|
|
166
|
+
expect(requests.length).toBe(3);
|
|
167
|
+
expect(requests[0].payload).toMatchObject({ internal: false, urn: 'xtk:session', methodName: 'Logon', retry: false, retryCount: 0 });
|
|
168
|
+
expect(requests[1].payload).toMatchObject({ internal: true, urn: 'xtk:persist', methodName: 'GetEntityIfMoreRecent', retry: false, retryCount: 0 });
|
|
169
|
+
expect(requests[2].payload).toMatchObject({ internal: false, urn: 'xtk:session', methodName: 'GetOption', retry: false, retryCount: 0 });
|
|
170
|
+
|
|
171
|
+
const responses = assertion.getObserved("SOAP//response");
|
|
172
|
+
expect(responses.length).toBe(3);
|
|
173
|
+
expect(responses[0].payload).toMatchObject({ });
|
|
174
|
+
expect(responses[1].payload).toMatchObject({ });
|
|
175
|
+
expect(responses[2].payload).toMatchObject({ });
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('Should track caches auto-refresh (error in GetModifiedEntities API)', async () => {
|
|
179
|
+
jest.useFakeTimers();
|
|
180
|
+
const [client, assertion] = await makeObservableClient();
|
|
181
|
+
|
|
182
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
183
|
+
await client.NLWS.xtkSession.logon();
|
|
184
|
+
expect(assertion.hasObserved("SDK//logon")).toBe(true);
|
|
185
|
+
|
|
186
|
+
client.startRefreshCaches();
|
|
187
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//start")).toBe(true);
|
|
188
|
+
const start = assertion.getFirstObserved("CACHE_REFRESHER//start");
|
|
189
|
+
expect(start.event).toMatchObject({ eventName:"CACHE_REFRESHER//start", payload:{ cacheSchemaId: "xtk:option", refreshFrequency: 10000 } });
|
|
190
|
+
|
|
191
|
+
// No mock implementation => the API call to get modified entities will fail generating a CACHE_REFRESHER//error event
|
|
192
|
+
await client._optionCacheRefresher._safeCallAndRefresh();
|
|
193
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//tick")).toBe(true);
|
|
194
|
+
const tick = assertion.getFirstObserved("CACHE_REFRESHER//tick");
|
|
195
|
+
expect(tick.event).toMatchObject({ eventName:"CACHE_REFRESHER//tick", payload:{ cacheSchemaId: "xtk:option" } });
|
|
196
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//error")).toBe(true);
|
|
197
|
+
const error = assertion.getFirstObserved("CACHE_REFRESHER//error");
|
|
198
|
+
expect(error.event).toMatchObject({ eventName:"CACHE_REFRESHER//error", payload:{ cacheSchemaId: "xtk:option" } });
|
|
199
|
+
// An error in the API should not stop the auto-refresh mechanism
|
|
200
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//stop")).toBe(false);
|
|
201
|
+
|
|
202
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
203
|
+
await client.NLWS.xtkSession.logoff();
|
|
204
|
+
expect(assertion.hasObserved("SDK//logoff")).toBe(true);
|
|
205
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//stop")).toBe(true);
|
|
206
|
+
jest.useRealTimers();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('Should track caches auto-refresh (success in GetModifiedEntities API)', async () => {
|
|
210
|
+
jest.useFakeTimers();
|
|
211
|
+
const [client, assertion] = await makeObservableClient();
|
|
212
|
+
|
|
213
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
214
|
+
await client.NLWS.xtkSession.logon();
|
|
215
|
+
expect(assertion.hasObserved("SDK//logon")).toBe(true);
|
|
216
|
+
|
|
217
|
+
client.startRefreshCaches();
|
|
218
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//start")).toBe(true);
|
|
219
|
+
const start = assertion.getFirstObserved("CACHE_REFRESHER//start");
|
|
220
|
+
expect(start.event).toMatchObject({ eventName:"CACHE_REFRESHER//start", payload:{ cacheSchemaId: "xtk:option", refreshFrequency: 10000 } });
|
|
221
|
+
|
|
222
|
+
client._transport.mockReturnValue(Promise.resolve(Mock.GETMODIFIEDENTITIES_RESPONSE));
|
|
223
|
+
await client._optionCacheRefresher._safeCallAndRefresh();
|
|
224
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//tick")).toBe(true);
|
|
225
|
+
const tick = assertion.getFirstObserved("CACHE_REFRESHER//tick");
|
|
226
|
+
expect(tick.event).toMatchObject({ eventName:"CACHE_REFRESHER//tick", payload:{ cacheSchemaId: "xtk:option" } });
|
|
227
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//error")).toBe(false);
|
|
228
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//abort")).toBe(false);
|
|
229
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//stop")).toBe(false);
|
|
230
|
+
|
|
231
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
232
|
+
await client.NLWS.xtkSession.logoff();
|
|
233
|
+
expect(assertion.hasObserved("SDK//logoff")).toBe(true);
|
|
234
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//stop")).toBe(true);
|
|
235
|
+
jest.useRealTimers();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('Should track caches auto-refresh (GetModifiedEntities API missing)', async () => {
|
|
239
|
+
jest.useFakeTimers();
|
|
240
|
+
const [client, assertion] = await makeObservableClient();
|
|
241
|
+
|
|
242
|
+
client._transport.mockReturnValueOnce(Mock.LOGON_RESPONSE);
|
|
243
|
+
await client.NLWS.xtkSession.logon();
|
|
244
|
+
expect(assertion.hasObserved("SDK//logon")).toBe(true);
|
|
245
|
+
|
|
246
|
+
client.startRefreshCaches();
|
|
247
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//start")).toBe(true);
|
|
248
|
+
const start = assertion.getFirstObserved("CACHE_REFRESHER//start");
|
|
249
|
+
expect(start.event).toMatchObject({ eventName:"CACHE_REFRESHER//start", payload:{ cacheSchemaId: "xtk:option", refreshFrequency: 10000 } });
|
|
250
|
+
|
|
251
|
+
client._transport.mockReturnValue(Promise.resolve(Mock.GETMODIFIEDENTITIES_UNDEFINED_RESPONSE));
|
|
252
|
+
await client._optionCacheRefresher._safeCallAndRefresh();
|
|
253
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//tick")).toBe(true);
|
|
254
|
+
const tick = assertion.getFirstObserved("CACHE_REFRESHER//tick");
|
|
255
|
+
expect(tick.event).toMatchObject({ eventName:"CACHE_REFRESHER//tick", payload:{ cacheSchemaId: "xtk:option" } });
|
|
256
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//error")).toBe(false);
|
|
257
|
+
// Should send an abort event and stop the auto-refresher
|
|
258
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//abort")).toBe(true);
|
|
259
|
+
expect(assertion.hasObserved("CACHE_REFRESHER//stop")).toBe(true);
|
|
260
|
+
|
|
261
|
+
client._transport.mockReturnValueOnce(Mock.LOGOFF_RESPONSE);
|
|
262
|
+
await client.NLWS.xtkSession.logoff();
|
|
263
|
+
jest.useRealTimers();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
});
|
|
267
|
+
|