@adobe/spacecat-shared-tier-client 1.0.0
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/.mocha-multi.json +6 -0
- package/.releaserc.cjs +17 -0
- package/CHANGELOG.md +21 -0
- package/CODE_OF_CONDUCT.md +74 -0
- package/CONTRIBUTING.md +74 -0
- package/LICENSE.txt +263 -0
- package/README.md +221 -0
- package/package.json +51 -0
- package/src/index.d.ts +51 -0
- package/src/index.js +16 -0
- package/src/tier-client.js +228 -0
- package/test/setup-env.js +14 -0
- package/test/tier-client.test.js +424 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 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
|
+
/* eslint-env mocha */
|
|
14
|
+
|
|
15
|
+
import { use, expect } from 'chai';
|
|
16
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
17
|
+
import sinonChai from 'sinon-chai';
|
|
18
|
+
import sinon from 'sinon';
|
|
19
|
+
|
|
20
|
+
import { Organization, Site } from '@adobe/spacecat-shared-data-access';
|
|
21
|
+
import TierClient from '../src/tier-client.js';
|
|
22
|
+
|
|
23
|
+
use(chaiAsPromised);
|
|
24
|
+
use(sinonChai);
|
|
25
|
+
|
|
26
|
+
describe('TierClient', () => {
|
|
27
|
+
const sandbox = sinon.createSandbox();
|
|
28
|
+
const orgId = '123e4567-e89b-12d3-a456-426614174000';
|
|
29
|
+
const siteId = '456e7890-e89b-12d3-a456-426614174000';
|
|
30
|
+
const productCode = 'LLMO';
|
|
31
|
+
|
|
32
|
+
const mockEntitlement = {
|
|
33
|
+
getId: () => 'entitlement-123',
|
|
34
|
+
getOrganizationId: () => orgId,
|
|
35
|
+
getProductCode: () => productCode,
|
|
36
|
+
getTier: () => 'FREE_TRIAL',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const mockSiteEnrollment = {
|
|
40
|
+
getId: () => 'enrollment-123',
|
|
41
|
+
getSiteId: () => siteId,
|
|
42
|
+
getEntitlementId: () => 'entitlement-123',
|
|
43
|
+
getStatus: () => 'ACTIVE',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const mockOrganization = {
|
|
47
|
+
getId: () => orgId,
|
|
48
|
+
getImsOrgId: () => 'ims-org-123',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Create actual Organization instance for instanceof checks
|
|
52
|
+
const organizationInstance = Object.create(Organization.prototype);
|
|
53
|
+
Object.assign(organizationInstance, mockOrganization);
|
|
54
|
+
|
|
55
|
+
const mockSite = {
|
|
56
|
+
getId: () => siteId,
|
|
57
|
+
getName: () => 'Test Site',
|
|
58
|
+
getOrganization: () => organizationInstance,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Create actual Site instance for instanceof checks
|
|
62
|
+
const siteInstance = Object.create(Site.prototype);
|
|
63
|
+
Object.assign(siteInstance, mockSite);
|
|
64
|
+
|
|
65
|
+
const mockDataAccess = {
|
|
66
|
+
Entitlement: {
|
|
67
|
+
findByOrganizationIdAndProductCode: sandbox.stub(),
|
|
68
|
+
findById: sandbox.stub(),
|
|
69
|
+
create: sandbox.stub(),
|
|
70
|
+
},
|
|
71
|
+
SiteEnrollment: {
|
|
72
|
+
allBySiteId: sandbox.stub(),
|
|
73
|
+
create: sandbox.stub(),
|
|
74
|
+
},
|
|
75
|
+
Organization: {
|
|
76
|
+
findById: sandbox.stub(),
|
|
77
|
+
},
|
|
78
|
+
Site: {
|
|
79
|
+
findById: sandbox.stub(),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const mockContext = {
|
|
84
|
+
dataAccess: mockDataAccess,
|
|
85
|
+
log: {
|
|
86
|
+
info: sandbox.stub(),
|
|
87
|
+
error: sandbox.stub(),
|
|
88
|
+
},
|
|
89
|
+
attributes: {
|
|
90
|
+
authInfo: {
|
|
91
|
+
getProfile: () => ({ provider: 'IMS' }),
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
let tierClient;
|
|
97
|
+
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
sandbox.restore();
|
|
100
|
+
|
|
101
|
+
// Reset all stubs
|
|
102
|
+
Object.values(mockDataAccess).forEach((service) => {
|
|
103
|
+
Object.values(service).forEach((method) => {
|
|
104
|
+
if (typeof method === 'function' && method.reset) {
|
|
105
|
+
method.reset();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
tierClient = new TierClient(mockContext, organizationInstance, siteInstance, productCode);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
afterEach(() => {
|
|
114
|
+
sandbox.restore();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('Static Factory Methods', () => {
|
|
118
|
+
const testOrganization = Object.create(Organization.prototype);
|
|
119
|
+
Object.assign(testOrganization, { getId: () => orgId });
|
|
120
|
+
|
|
121
|
+
const testSite = Object.create(Site.prototype);
|
|
122
|
+
Object.assign(testSite, {
|
|
123
|
+
getId: () => siteId,
|
|
124
|
+
getOrganizationId: () => orgId,
|
|
125
|
+
getOrganization: () => testOrganization,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const testSiteWithOrgRef = Object.create(Site.prototype);
|
|
129
|
+
Object.assign(testSiteWithOrgRef, {
|
|
130
|
+
getId: () => siteId,
|
|
131
|
+
getOrganization: () => testOrganization,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('createForOrg', () => {
|
|
135
|
+
it('should create TierClient for organization', () => {
|
|
136
|
+
const client = TierClient.createForOrg(mockContext, testOrganization, productCode);
|
|
137
|
+
|
|
138
|
+
expect(client).to.be.an('object');
|
|
139
|
+
expect(client.checkValidEntitlement).to.be.a('function');
|
|
140
|
+
expect(client.createEntitlement).to.be.a('function');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should throw error when organization is not provided', () => {
|
|
144
|
+
expect(() => TierClient.createForOrg(mockContext, null, productCode)).to.throw('Entity must be an instance of Organization');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should throw error when organization has no getId method', () => {
|
|
148
|
+
const invalidOrg = { name: 'test' };
|
|
149
|
+
expect(() => TierClient.createForOrg(mockContext, invalidOrg, productCode)).to.throw('Entity must be an instance of Organization');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should throw error when context is invalid', () => {
|
|
153
|
+
expect(() => TierClient.createForOrg(null, testOrganization, productCode)).to.throw('Context is required');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should throw error when productCode is not provided', () => {
|
|
157
|
+
expect(() => TierClient.createForOrg(mockContext, testOrganization, '')).to.throw('Product code is required');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should throw error when dataAccess is missing', () => {
|
|
161
|
+
const invalidContext = { log: {} };
|
|
162
|
+
expect(() => TierClient.createForOrg(invalidContext, testOrganization, productCode)).to.throw('Cannot destructure property');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('createForSite', () => {
|
|
167
|
+
it('should create TierClient for site with getOrganizationId', async () => {
|
|
168
|
+
const client = await TierClient.createForSite(mockContext, testSite, productCode);
|
|
169
|
+
|
|
170
|
+
expect(client).to.be.an('object');
|
|
171
|
+
expect(client.checkValidEntitlement).to.be.a('function');
|
|
172
|
+
expect(client.createEntitlement).to.be.a('function');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should create TierClient for site with getOrganization', async () => {
|
|
176
|
+
const client = await TierClient.createForSite(mockContext, testSiteWithOrgRef, productCode);
|
|
177
|
+
|
|
178
|
+
expect(client).to.be.an('object');
|
|
179
|
+
expect(client.checkValidEntitlement).to.be.a('function');
|
|
180
|
+
expect(client.createEntitlement).to.be.a('function');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should throw error when site is not provided', async () => {
|
|
184
|
+
await expect(TierClient.createForSite(mockContext, null, productCode)).to.be.rejectedWith('Entity must be an instance of Site');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should throw error when site has no getId method', async () => {
|
|
188
|
+
const invalidSite = { name: 'test' };
|
|
189
|
+
await expect(TierClient.createForSite(mockContext, invalidSite, productCode)).to.be.rejectedWith('Entity must be an instance of Site');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should throw error when context is invalid', async () => {
|
|
193
|
+
await expect(TierClient.createForSite(null, testSite, productCode)).to.be.rejectedWith('Context is required');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should throw error when productCode is not provided', async () => {
|
|
197
|
+
await expect(TierClient.createForSite(mockContext, testSite, '')).to.be.rejectedWith('Product code is required');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should throw error when dataAccess is missing', async () => {
|
|
201
|
+
const invalidContext = { log: {} };
|
|
202
|
+
await expect(TierClient.createForSite(invalidContext, testSite, productCode)).to.be.rejectedWith('Cannot destructure property');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('Constructor Validation', () => {
|
|
208
|
+
it('should allow direct constructor usage', () => {
|
|
209
|
+
const client = new TierClient(mockContext, organizationInstance, siteInstance, productCode);
|
|
210
|
+
expect(client).to.be.an('object');
|
|
211
|
+
expect(client.checkValidEntitlement).to.be.a('function');
|
|
212
|
+
expect(client.createEntitlement).to.be.a('function');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should allow site to be null', () => {
|
|
216
|
+
// site is now optional, so this should not throw an error
|
|
217
|
+
expect(() => new TierClient(mockContext, organizationInstance, null, productCode))
|
|
218
|
+
.to.not.throw();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('checkValidEntitlement', () => {
|
|
223
|
+
it('should return empty object when no entitlement exists', async () => {
|
|
224
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(null);
|
|
225
|
+
|
|
226
|
+
const result = await tierClient.checkValidEntitlement();
|
|
227
|
+
|
|
228
|
+
expect(result).to.deep.equal({});
|
|
229
|
+
expect(mockDataAccess.Entitlement.findByOrganizationIdAndProductCode)
|
|
230
|
+
.to.have.been.calledWith(orgId, productCode);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should return only entitlement when site enrollment is missing', async () => {
|
|
234
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
|
|
235
|
+
mockDataAccess.SiteEnrollment.allBySiteId.resolves([]);
|
|
236
|
+
|
|
237
|
+
const result = await tierClient.checkValidEntitlement();
|
|
238
|
+
|
|
239
|
+
expect(result).to.deep.equal({ entitlement: mockEntitlement });
|
|
240
|
+
expect(mockDataAccess.SiteEnrollment.allBySiteId).to.have.been.calledWith(siteId);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('should return both entitlement and site enrollment when both exist', async () => {
|
|
244
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
|
|
245
|
+
mockDataAccess.SiteEnrollment.allBySiteId.resolves([mockSiteEnrollment]);
|
|
246
|
+
|
|
247
|
+
const result = await tierClient.checkValidEntitlement();
|
|
248
|
+
|
|
249
|
+
expect(result).to.deep.equal({
|
|
250
|
+
entitlement: mockEntitlement,
|
|
251
|
+
siteEnrollment: mockSiteEnrollment,
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should handle database errors', async () => {
|
|
256
|
+
const error = new Error('Database error');
|
|
257
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.rejects(error);
|
|
258
|
+
|
|
259
|
+
await expect(tierClient.checkValidEntitlement()).to.be.rejectedWith('Database error');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should return only entitlement when no site is provided', async () => {
|
|
263
|
+
// Create a TierClient without site
|
|
264
|
+
const tierClientWithoutSite = new TierClient(
|
|
265
|
+
mockContext,
|
|
266
|
+
organizationInstance,
|
|
267
|
+
null,
|
|
268
|
+
productCode,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
|
|
272
|
+
|
|
273
|
+
const result = await tierClientWithoutSite.checkValidEntitlement();
|
|
274
|
+
|
|
275
|
+
expect(result).to.deep.equal({ entitlement: mockEntitlement });
|
|
276
|
+
expect(mockDataAccess.Entitlement.findByOrganizationIdAndProductCode)
|
|
277
|
+
.to.have.been.calledWith(orgId, productCode);
|
|
278
|
+
// SiteEnrollment.allBySiteId should not be called when site is null
|
|
279
|
+
expect(mockDataAccess.SiteEnrollment.allBySiteId).to.not.have.been.called;
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe('createEntitlement', () => {
|
|
284
|
+
beforeEach(() => {
|
|
285
|
+
mockDataAccess.Organization.findById.resolves(mockOrganization);
|
|
286
|
+
mockDataAccess.Site.findById.resolves(mockSite);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should return existing entitlement and site enrollment when both exist', async () => {
|
|
290
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
|
|
291
|
+
mockDataAccess.SiteEnrollment.allBySiteId.resolves([mockSiteEnrollment]);
|
|
292
|
+
|
|
293
|
+
const result = await tierClient.createEntitlement('FREE_TRIAL');
|
|
294
|
+
|
|
295
|
+
expect(result).to.deep.equal({
|
|
296
|
+
entitlement: mockEntitlement,
|
|
297
|
+
siteEnrollment: mockSiteEnrollment,
|
|
298
|
+
});
|
|
299
|
+
expect(mockDataAccess.Entitlement.create).to.not.have.been.called;
|
|
300
|
+
expect(mockDataAccess.SiteEnrollment.create).to.not.have.been.called;
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should create site enrollment when only entitlement exists', async () => {
|
|
304
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
|
|
305
|
+
mockDataAccess.SiteEnrollment.allBySiteId.resolves([]);
|
|
306
|
+
mockDataAccess.SiteEnrollment.create.resolves(mockSiteEnrollment);
|
|
307
|
+
|
|
308
|
+
const result = await tierClient.createEntitlement('FREE_TRIAL');
|
|
309
|
+
|
|
310
|
+
expect(result).to.deep.equal({
|
|
311
|
+
entitlement: mockEntitlement,
|
|
312
|
+
siteEnrollment: mockSiteEnrollment,
|
|
313
|
+
});
|
|
314
|
+
expect(mockDataAccess.Entitlement.create).to.not.have.been.called;
|
|
315
|
+
expect(mockDataAccess.SiteEnrollment.create).to.have.been.calledWith({
|
|
316
|
+
siteId,
|
|
317
|
+
entitlementId: mockEntitlement.getId(),
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should create everything when nothing exists', async () => {
|
|
322
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(null);
|
|
323
|
+
mockDataAccess.Entitlement.create.resolves(mockEntitlement);
|
|
324
|
+
mockDataAccess.SiteEnrollment.create.resolves(mockSiteEnrollment);
|
|
325
|
+
|
|
326
|
+
const result = await tierClient.createEntitlement('FREE_TRIAL');
|
|
327
|
+
|
|
328
|
+
expect(result).to.deep.equal({
|
|
329
|
+
entitlement: mockEntitlement,
|
|
330
|
+
siteEnrollment: mockSiteEnrollment,
|
|
331
|
+
});
|
|
332
|
+
expect(mockDataAccess.Entitlement.create).to.have.been.calledWith({
|
|
333
|
+
organizationId: orgId,
|
|
334
|
+
productCode,
|
|
335
|
+
tier: 'FREE_TRIAL',
|
|
336
|
+
quotas: { llmo_trial_prompts: 200, llmo_trial_prompts_consumed: 0 },
|
|
337
|
+
});
|
|
338
|
+
expect(mockDataAccess.SiteEnrollment.create).to.have.been.calledWith({
|
|
339
|
+
siteId,
|
|
340
|
+
entitlementId: mockEntitlement.getId(),
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should throw error for invalid tier', async () => {
|
|
345
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(null);
|
|
346
|
+
|
|
347
|
+
await expect(tierClient.createEntitlement('INVALID_TIER')).to.be.rejectedWith('Invalid tier: INVALID_TIER');
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should throw error when site is not provided for createEntitlement', async () => {
|
|
351
|
+
// Create a TierClient without site
|
|
352
|
+
const tierClientWithoutSite = new TierClient(
|
|
353
|
+
mockContext,
|
|
354
|
+
organizationInstance,
|
|
355
|
+
null,
|
|
356
|
+
productCode,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
await expect(tierClientWithoutSite.createEntitlement('FREE_TRIAL')).to.be.rejectedWith('Site required for creating entitlements');
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should throw error when organization not found', async () => {
|
|
363
|
+
mockDataAccess.Organization.findById.resolves(null);
|
|
364
|
+
|
|
365
|
+
await expect(tierClient.createEntitlement('FREE_TRIAL')).to.be.rejectedWith('Cannot read properties of undefined');
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('should throw error when site not found', async () => {
|
|
369
|
+
mockDataAccess.Site.findById.resolves(null);
|
|
370
|
+
|
|
371
|
+
await expect(tierClient.createEntitlement('FREE_TRIAL')).to.be.rejectedWith('Cannot read properties of undefined');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should handle database errors during creation', async () => {
|
|
375
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(null);
|
|
376
|
+
mockDataAccess.Entitlement.create.rejects(new Error('Database error'));
|
|
377
|
+
|
|
378
|
+
await expect(tierClient.createEntitlement('FREE_TRIAL')).to.be.rejectedWith('Database error');
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
describe('Edge Cases', () => {
|
|
383
|
+
it('should handle context without authInfo', async () => {
|
|
384
|
+
const contextWithoutAuth = {
|
|
385
|
+
...mockContext,
|
|
386
|
+
attributes: {},
|
|
387
|
+
};
|
|
388
|
+
const clientWithoutAuth = new TierClient(
|
|
389
|
+
contextWithoutAuth,
|
|
390
|
+
organizationInstance,
|
|
391
|
+
siteInstance,
|
|
392
|
+
productCode,
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
mockDataAccess.Organization.findById.resolves(mockOrganization);
|
|
396
|
+
mockDataAccess.Site.findById.resolves(mockSite);
|
|
397
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(null);
|
|
398
|
+
mockDataAccess.Entitlement.create.resolves(mockEntitlement);
|
|
399
|
+
mockDataAccess.SiteEnrollment.create.resolves(mockSiteEnrollment);
|
|
400
|
+
|
|
401
|
+
const result = await clientWithoutAuth.createEntitlement('FREE_TRIAL');
|
|
402
|
+
|
|
403
|
+
expect(result).to.deep.equal({
|
|
404
|
+
entitlement: mockEntitlement,
|
|
405
|
+
siteEnrollment: mockSiteEnrollment,
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
it('should handle multiple site enrollments with different entitlements', async () => {
|
|
410
|
+
const otherSiteEnrollment = {
|
|
411
|
+
getId: () => 'other-enrollment-123',
|
|
412
|
+
getSiteId: () => siteId,
|
|
413
|
+
getEntitlementId: () => 'other-entitlement-123',
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
|
|
417
|
+
mockDataAccess.SiteEnrollment.allBySiteId.resolves([otherSiteEnrollment]);
|
|
418
|
+
|
|
419
|
+
const result = await tierClient.checkValidEntitlement();
|
|
420
|
+
|
|
421
|
+
expect(result).to.deep.equal({ entitlement: mockEntitlement });
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
});
|