@verii/server-mockvendor 1.0.0-pre.1752076816
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/.localdev.env +15 -0
- package/.standalone.env +5 -0
- package/LICENSE +201 -0
- package/docker/compose.yml +25 -0
- package/jest.config.js +20 -0
- package/multilingual-string.schema.json +40 -0
- package/package.json +70 -0
- package/src/config/config.js +55 -0
- package/src/controllers/api/accepted-offers/controller.js +9 -0
- package/src/controllers/api/accepted-offers/repo.js +25 -0
- package/src/controllers/api/applicants/controller.js +9 -0
- package/src/controllers/api/applicants/repo.js +30 -0
- package/src/controllers/api/create_did_key/controller.js +29 -0
- package/src/controllers/api/create_did_key/schemas/index.js +4 -0
- package/src/controllers/api/create_did_key/schemas/jwk-did-request.schema.js +20 -0
- package/src/controllers/api/create_did_key/schemas/jwk-did-response.schema.js +41 -0
- package/src/controllers/api/create_jwk/controller.js +35 -0
- package/src/controllers/api/create_jwk/schemas/index.js +3 -0
- package/src/controllers/api/create_jwk/schemas/jwk-response.schema.js +33 -0
- package/src/controllers/api/credential-submissions/controller.js +59 -0
- package/src/controllers/api/credential-submissions/repo.js +16 -0
- package/src/controllers/api/identifications/controller.js +67 -0
- package/src/controllers/api/identifications/repo.js +22 -0
- package/src/controllers/api/issuing-exchanges/controller.js +218 -0
- package/src/controllers/api/issuing-exchanges/fetchers.js +45 -0
- package/src/controllers/api/issuing-exchanges/repo.js +27 -0
- package/src/controllers/api/jwt/controller.js +69 -0
- package/src/controllers/api/jwt/schemas/index.js +6 -0
- package/src/controllers/api/jwt/schemas/jwt-request.schema.js +40 -0
- package/src/controllers/api/jwt/schemas/jwt-response.schema.js +14 -0
- package/src/controllers/api/jwt/schemas/jwt-verify-request.schema.js +17 -0
- package/src/controllers/api/jwt/schemas/jwt-verify-response.schema.js +17 -0
- package/src/controllers/api/offers/autohooks.js +5 -0
- package/src/controllers/api/offers/controller.js +87 -0
- package/src/controllers/api/offers/new-mockvendor-offer.schema.js +22 -0
- package/src/controllers/api/offers/repo.js +33 -0
- package/src/controllers/api/users/controller.js +20 -0
- package/src/controllers/api/users/repo.js +29 -0
- package/src/controllers/autohooks.js +22 -0
- package/src/controllers/inspection/controller.js +39 -0
- package/src/controllers/issuing/controller.js +158 -0
- package/src/controllers/registrar/controller.js +67 -0
- package/src/controllers/registrar/repo.js +24 -0
- package/src/controllers/root/controller.js +15 -0
- package/src/controllers/schemas/index.js +21 -0
- package/src/controllers/schemas/issuer-data.schema.json +26 -0
- package/src/entities/index.js +4 -0
- package/src/entities/key-pairs/index.js +3 -0
- package/src/entities/key-pairs/key-pairs.js +73 -0
- package/src/entities/offers/index.js +3 -0
- package/src/entities/offers/schemas/generate-offers.schema.js +30 -0
- package/src/entities/offers/schemas/index.js +3 -0
- package/src/index.js +19 -0
- package/src/init-server.js +34 -0
- package/src/main.js +18 -0
- package/src/standalone.js +8 -0
- package/src/start-app-server.js +32 -0
- package/test/accepted-offers.test.js +47 -0
- package/test/api-users.test.js +170 -0
- package/test/create_did_key-controller.test.js +94 -0
- package/test/create_jwk-controller.test.js +86 -0
- package/test/credential-submissions.test.js +331 -0
- package/test/factories/accepted-offers.factory.js +16 -0
- package/test/factories/delayed-offer.factory.js +17 -0
- package/test/factories/identifications.factory.js +33 -0
- package/test/factories/offers.factory.js +64 -0
- package/test/factories/users.factory.js +24 -0
- package/test/helpers/PastEmploymentPosition-2007-2009-Junior-Project-Manager.json +26 -0
- package/test/helpers/PastEmploymentPosition-2009-2015-Project-Manager.json +26 -0
- package/test/helpers/helpers/PastEmploymentPosition-2009-2015-Project-Manager.json +26 -0
- package/test/helpers/latest-Adam-Smith.json +49 -0
- package/test/helpers/legacy-Adam-Smith.json +33 -0
- package/test/helpers/mockvendor-build-fastify.js +16 -0
- package/test/helpers/tools/verifgen/templates/PastEmploymentPosition-2009-2015-Project-Manager.json +26 -0
- package/test/identifications.test.js +56 -0
- package/test/issuing-exchanges.test.js +335 -0
- package/test/issuing-identify.test.js +137 -0
- package/test/jwt-controller.test.js +320 -0
- package/test/offers.test.js +682 -0
- package/test/registrar.test.js +276 -0
- package/test/root.test.js +25 -0
- package/test/swagger.test.js +21 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
const { mongoDb } = require('@spencejs/spence-mongo-repos');
|
|
2
|
+
const { ObjectId } = require('mongodb');
|
|
3
|
+
const {
|
|
4
|
+
ISO_DATETIME_FORMAT,
|
|
5
|
+
OBJECT_ID_FORMAT,
|
|
6
|
+
} = require('@verii/test-regexes');
|
|
7
|
+
const { mongoify } = require('@verii/tests-helpers');
|
|
8
|
+
const { castArray, map, omit, first } = require('lodash/fp');
|
|
9
|
+
const buildFastify = require('./helpers/mockvendor-build-fastify');
|
|
10
|
+
const initOfferFactory = require('./factories/offers.factory');
|
|
11
|
+
const initAcceptedOffersFactory = require('./factories/accepted-offers.factory');
|
|
12
|
+
const offersIO = require('../src/controllers/api/issuing-exchanges/fetchers');
|
|
13
|
+
|
|
14
|
+
jest.mock('../src/controllers/api/issuing-exchanges/fetchers');
|
|
15
|
+
|
|
16
|
+
describe('Offer routes', () => {
|
|
17
|
+
let fastify;
|
|
18
|
+
let newOffer;
|
|
19
|
+
let persistOffer;
|
|
20
|
+
let acceptedOffers;
|
|
21
|
+
|
|
22
|
+
beforeAll(async () => {
|
|
23
|
+
fastify = buildFastify();
|
|
24
|
+
await fastify.ready();
|
|
25
|
+
({ newOffer, persistOffer } = initOfferFactory(fastify));
|
|
26
|
+
initAcceptedOffersFactory(fastify);
|
|
27
|
+
acceptedOffers = mongoDb().collection('acceptedOffers');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
await mongoDb().collection('offers').deleteMany({});
|
|
32
|
+
await mongoDb().collection('acceptedOffers').deleteMany({});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterAll(async () => {
|
|
36
|
+
await fastify.close();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('offer management', () => {
|
|
40
|
+
it('should be able to create a offer', async () => {
|
|
41
|
+
const offer = await newOffer({ label: 'x-label' });
|
|
42
|
+
const response = await fastify.injectJson({
|
|
43
|
+
method: 'POST',
|
|
44
|
+
url: '/api/offers',
|
|
45
|
+
payload: offer,
|
|
46
|
+
});
|
|
47
|
+
expect(response.statusCode).toEqual(200);
|
|
48
|
+
expect(first(response.json)).toEqual({
|
|
49
|
+
...expectedOffer(offer),
|
|
50
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
51
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
52
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
53
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
54
|
+
});
|
|
55
|
+
expect(
|
|
56
|
+
await mongoDb()
|
|
57
|
+
.collection('offers')
|
|
58
|
+
.findOne({ _id: new ObjectId(first(response.json)._id) })
|
|
59
|
+
).toEqual({
|
|
60
|
+
_id: new ObjectId(first(response.json)._id),
|
|
61
|
+
...mongoify(offer),
|
|
62
|
+
createdAt: expect.any(Date),
|
|
63
|
+
updatedAt: expect.any(Date),
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it('should be able to create a offer with resource references', async () => {
|
|
67
|
+
const offer = await newOffer({
|
|
68
|
+
label: 'x-label',
|
|
69
|
+
replaces: [
|
|
70
|
+
{
|
|
71
|
+
id: 'did:ion:123',
|
|
72
|
+
type: ['MockType-1'],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
relatedResource: [
|
|
76
|
+
{
|
|
77
|
+
id: 'http://www.223.com',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'scheme:whatever;12312312',
|
|
81
|
+
type: 'MockType-3',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
const expectedOfferObj = expectedOffer(offer, {
|
|
86
|
+
relatedResource: map(expectedRelatedResource, offer.relatedResource),
|
|
87
|
+
});
|
|
88
|
+
const response = await fastify.injectJson({
|
|
89
|
+
method: 'POST',
|
|
90
|
+
url: '/api/offers',
|
|
91
|
+
payload: offer,
|
|
92
|
+
});
|
|
93
|
+
expect(response.statusCode).toEqual(200);
|
|
94
|
+
expect(first(response.json)).toEqual({
|
|
95
|
+
...expectedOfferObj,
|
|
96
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
97
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
98
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
99
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
100
|
+
});
|
|
101
|
+
expect(
|
|
102
|
+
await mongoDb()
|
|
103
|
+
.collection('offers')
|
|
104
|
+
.findOne({ _id: new ObjectId(first(response.json)._id) })
|
|
105
|
+
).toEqual({
|
|
106
|
+
_id: new ObjectId(first(response.json)._id),
|
|
107
|
+
...mongoify(expectedOfferObj),
|
|
108
|
+
createdAt: expect.any(Date),
|
|
109
|
+
updatedAt: expect.any(Date),
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
it('should be able to create a offer with issuer.name', async () => {
|
|
113
|
+
const offer = await newOffer({ label: 'x-label' });
|
|
114
|
+
const response = await fastify.injectJson({
|
|
115
|
+
method: 'POST',
|
|
116
|
+
url: '/api/offers',
|
|
117
|
+
payload: {
|
|
118
|
+
...offer,
|
|
119
|
+
issuer: { name: 'x', bla: 'o' },
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
expect(response.statusCode).toEqual(200);
|
|
123
|
+
expect(first(response.json)).toEqual({
|
|
124
|
+
...expectedOffer({
|
|
125
|
+
...offer,
|
|
126
|
+
issuer: { name: 'x' },
|
|
127
|
+
}),
|
|
128
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
129
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
130
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
131
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
132
|
+
});
|
|
133
|
+
expect(
|
|
134
|
+
await mongoDb()
|
|
135
|
+
.collection('offers')
|
|
136
|
+
.findOne({ _id: new ObjectId(first(response.json)._id) })
|
|
137
|
+
).toEqual({
|
|
138
|
+
_id: new ObjectId(first(response.json)._id),
|
|
139
|
+
...mongoify({
|
|
140
|
+
...offer,
|
|
141
|
+
issuer: { name: 'x' },
|
|
142
|
+
}),
|
|
143
|
+
createdAt: expect.any(Date),
|
|
144
|
+
updatedAt: expect.any(Date),
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
it('should be able to create an offer', async () => {
|
|
148
|
+
const offer = await newOffer({ label: 'x-label' });
|
|
149
|
+
const offer2 = await newOffer();
|
|
150
|
+
offersIO.submitOffer.mockResolvedValue(Promise.resolve());
|
|
151
|
+
offersIO.completeSubmitOffer.mockResolvedValue(Promise.resolve());
|
|
152
|
+
const response = await fastify.injectJson({
|
|
153
|
+
method: 'POST',
|
|
154
|
+
url: '/api/offers',
|
|
155
|
+
payload: [offer, offer2],
|
|
156
|
+
});
|
|
157
|
+
expect(response.statusCode).toEqual(200);
|
|
158
|
+
expect(response.json).toEqual(
|
|
159
|
+
expect.arrayContaining([
|
|
160
|
+
{
|
|
161
|
+
...expectedOffer(offer),
|
|
162
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
163
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
164
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
165
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
...expectedOffer(offer2),
|
|
169
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
170
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
171
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
172
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
173
|
+
},
|
|
174
|
+
])
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
it('should be able to get a offer', async () => {
|
|
178
|
+
const offer = await persistOffer();
|
|
179
|
+
const response = await fastify.injectJson({
|
|
180
|
+
method: 'GET',
|
|
181
|
+
url: `/api/offers/${offer._id}`,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
expect(response.statusCode).toEqual(200);
|
|
185
|
+
expect(response.json).toEqual({
|
|
186
|
+
...offer,
|
|
187
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
188
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
189
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
190
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
it('should exist linkedCredentials in an offer', async () => {
|
|
194
|
+
const offer = await persistOffer({
|
|
195
|
+
linkedCredentials: [{ linkType: 'REPLACE', linkedCredentialId: '' }],
|
|
196
|
+
});
|
|
197
|
+
const response = await fastify.injectJson({
|
|
198
|
+
method: 'GET',
|
|
199
|
+
url: `/api/offers/${offer._id}`,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(response.statusCode).toEqual(200);
|
|
203
|
+
expect(response.json).toEqual({
|
|
204
|
+
...offer,
|
|
205
|
+
linkedCredentials: [{ linkType: 'REPLACE', linkedCredentialId: '' }],
|
|
206
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
207
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
208
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
209
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
it('should be able to get all offers', async () => {
|
|
213
|
+
const [offer1, offer2] = await Promise.all([
|
|
214
|
+
persistOffer(),
|
|
215
|
+
persistOffer(),
|
|
216
|
+
]);
|
|
217
|
+
const response = await fastify.injectJson({
|
|
218
|
+
method: 'GET',
|
|
219
|
+
url: '/api/offers',
|
|
220
|
+
});
|
|
221
|
+
expect(response.statusCode).toEqual(200);
|
|
222
|
+
expect(response.json).toEqual(
|
|
223
|
+
expect.arrayContaining([
|
|
224
|
+
{
|
|
225
|
+
...offer1,
|
|
226
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
227
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
228
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
229
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
...offer2,
|
|
233
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
234
|
+
_id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
235
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
236
|
+
createdAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
237
|
+
},
|
|
238
|
+
])
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
it('should be able to update a offer', async () => {
|
|
242
|
+
const [offer1] = await Promise.all([persistOffer(), persistOffer()]);
|
|
243
|
+
const response = await fastify.injectJson({
|
|
244
|
+
method: 'PUT',
|
|
245
|
+
url: `/api/offers/${offer1._id}`,
|
|
246
|
+
payload: expectedOffer(offer1),
|
|
247
|
+
});
|
|
248
|
+
expect(response.statusCode).toEqual(200);
|
|
249
|
+
expect(response.json).toEqual({
|
|
250
|
+
...offer1,
|
|
251
|
+
id: expect.stringMatching(OBJECT_ID_FORMAT),
|
|
252
|
+
updatedAt: expect.stringMatching(ISO_DATETIME_FORMAT),
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
it('should be able to delete a offer', async () => {
|
|
256
|
+
const [, offer2] = await Promise.all([persistOffer(), persistOffer()]);
|
|
257
|
+
const getResponsePre = await fastify.injectJson({
|
|
258
|
+
method: 'GET',
|
|
259
|
+
url: '/api/offers',
|
|
260
|
+
});
|
|
261
|
+
expect(getResponsePre.json).toEqual(
|
|
262
|
+
expect.arrayContaining([
|
|
263
|
+
{ ...offer2, id: expect.stringMatching(OBJECT_ID_FORMAT) },
|
|
264
|
+
])
|
|
265
|
+
);
|
|
266
|
+
const response = await fastify.injectJson({
|
|
267
|
+
method: 'DELETE',
|
|
268
|
+
url: `/api/offers/${offer2._id}`,
|
|
269
|
+
});
|
|
270
|
+
expect(response.statusCode).toEqual(200);
|
|
271
|
+
const getResponsePost = await fastify.injectJson({
|
|
272
|
+
method: 'GET',
|
|
273
|
+
url: '/api/offers',
|
|
274
|
+
});
|
|
275
|
+
expect(getResponsePost.json).not.toEqual(
|
|
276
|
+
expect.arrayContaining([
|
|
277
|
+
{ ...offer2, id: expect.stringMatching(OBJECT_ID_FORMAT) },
|
|
278
|
+
])
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe('generate offers', () => {
|
|
284
|
+
let offers;
|
|
285
|
+
beforeEach(async () => {
|
|
286
|
+
fastify.resetOverrides();
|
|
287
|
+
offers = await Promise.all([
|
|
288
|
+
persistOffer(),
|
|
289
|
+
persistOffer({ type: ['CurrentEmploymentPosition'] }),
|
|
290
|
+
persistOffer({ type: ['Badge'] }),
|
|
291
|
+
persistOffer({
|
|
292
|
+
credentialSubject: { vendorUserId: 'maria.williams@example.com' },
|
|
293
|
+
}),
|
|
294
|
+
persistOffer({
|
|
295
|
+
credentialSubject: { vendorUserId: 'maria.williams2@example.com' },
|
|
296
|
+
exchangeId: '123', // should be ignored
|
|
297
|
+
}),
|
|
298
|
+
persistOffer({ issuer: { vendorOrganizationId: '123' } }),
|
|
299
|
+
persistOffer({ issuer: { id: '123' } }),
|
|
300
|
+
]);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should generate offers for a specific user, type, and tenantDID', async () => {
|
|
304
|
+
const payload = {
|
|
305
|
+
vendorUserId: 'maria.williams@example.com',
|
|
306
|
+
types: ['Course'],
|
|
307
|
+
tenantDID: offers[0].issuer.id,
|
|
308
|
+
exchangeId: new ObjectId(),
|
|
309
|
+
};
|
|
310
|
+
const response = await fastify.injectJson({
|
|
311
|
+
method: 'POST',
|
|
312
|
+
url: '/issuing/generate-offers',
|
|
313
|
+
payload,
|
|
314
|
+
});
|
|
315
|
+
expect(response.statusCode).toEqual(200);
|
|
316
|
+
expect(response.json).toEqual({
|
|
317
|
+
offers: [expectedOffer(offers[3])],
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should generate offers with issuer.logo and issuer.name', async () => {
|
|
322
|
+
const offer = await persistOffer({
|
|
323
|
+
issuer: {
|
|
324
|
+
id: 'uniq-1',
|
|
325
|
+
name: 'uniq-1',
|
|
326
|
+
image: 'uniq-1',
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
const payload = {
|
|
330
|
+
vendorUserId: 'adam.smith@example.com',
|
|
331
|
+
types: ['Course'],
|
|
332
|
+
tenantDID: offer.issuer.id,
|
|
333
|
+
exchangeId: new ObjectId(),
|
|
334
|
+
};
|
|
335
|
+
const response = await fastify.injectJson({
|
|
336
|
+
method: 'POST',
|
|
337
|
+
url: '/issuing/generate-offers',
|
|
338
|
+
payload,
|
|
339
|
+
});
|
|
340
|
+
expect(response.statusCode).toEqual(200);
|
|
341
|
+
expect(response.json).toEqual({
|
|
342
|
+
offers: [
|
|
343
|
+
expectedOffer(offer, {
|
|
344
|
+
issuer: {
|
|
345
|
+
id: 'uniq-1',
|
|
346
|
+
name: 'uniq-1',
|
|
347
|
+
image: 'uniq-1',
|
|
348
|
+
},
|
|
349
|
+
}),
|
|
350
|
+
],
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should generate offers and stript not needed properties', async () => {
|
|
355
|
+
const offer = await persistOffer({
|
|
356
|
+
issuer: {
|
|
357
|
+
id: 'uniq-1',
|
|
358
|
+
name: 'uniq-1',
|
|
359
|
+
bla: 'uniq-1',
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
const payload = {
|
|
363
|
+
vendorUserId: 'adam.smith@example.com',
|
|
364
|
+
types: ['Course'],
|
|
365
|
+
tenantDID: offer.issuer.id,
|
|
366
|
+
exchangeId: new ObjectId(),
|
|
367
|
+
};
|
|
368
|
+
const response = await fastify.injectJson({
|
|
369
|
+
method: 'POST',
|
|
370
|
+
url: '/issuing/generate-offers',
|
|
371
|
+
payload,
|
|
372
|
+
});
|
|
373
|
+
expect(response.statusCode).toEqual(200);
|
|
374
|
+
expect(response.json).toEqual({
|
|
375
|
+
offers: [
|
|
376
|
+
expectedOffer(offer, {
|
|
377
|
+
issuer: {
|
|
378
|
+
id: 'uniq-1',
|
|
379
|
+
name: 'uniq-1',
|
|
380
|
+
},
|
|
381
|
+
}),
|
|
382
|
+
],
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('should return 200 with empty array if no offer and config noOffers200 turn on', async () => {
|
|
387
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
388
|
+
...config,
|
|
389
|
+
noOffers200: true,
|
|
390
|
+
});
|
|
391
|
+
const payload = {
|
|
392
|
+
vendorUserId: 'no-vendor@example.com',
|
|
393
|
+
types: ['NO-TYPES'],
|
|
394
|
+
tenantDID: offers[0].issuer.id,
|
|
395
|
+
exchangeId: new ObjectId(),
|
|
396
|
+
};
|
|
397
|
+
const response = await fastify.injectJson({
|
|
398
|
+
method: 'POST',
|
|
399
|
+
url: '/issuing/generate-offers',
|
|
400
|
+
payload,
|
|
401
|
+
});
|
|
402
|
+
expect(response.statusCode).toEqual(200);
|
|
403
|
+
expect(response.json).toEqual({
|
|
404
|
+
offers: [],
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('should generate offers without offerId if omitOfferId config is true', async () => {
|
|
409
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
410
|
+
...config,
|
|
411
|
+
omitOfferId: true,
|
|
412
|
+
});
|
|
413
|
+
const payload = {
|
|
414
|
+
vendorUserId: 'maria.williams@example.com',
|
|
415
|
+
types: ['Course'],
|
|
416
|
+
tenantDID: offers[0].issuer.id,
|
|
417
|
+
exchangeId: new ObjectId(),
|
|
418
|
+
};
|
|
419
|
+
const response = await fastify.injectJson({
|
|
420
|
+
method: 'POST',
|
|
421
|
+
url: '/issuing/generate-offers',
|
|
422
|
+
payload,
|
|
423
|
+
});
|
|
424
|
+
expect(response.statusCode).toEqual(200);
|
|
425
|
+
expect(response.json).toEqual({
|
|
426
|
+
offers: [omit(['offerId'], expectedOffer(offers[3]))],
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should generate offers for a specific user, and vendorOrganizationId', async () => {
|
|
431
|
+
const payload = {
|
|
432
|
+
vendorUserId: 'adam.smith@example.com',
|
|
433
|
+
types: [],
|
|
434
|
+
vendorOrganizationId: offers[5].issuer.vendorOrganizationId,
|
|
435
|
+
exchangeId: new ObjectId(),
|
|
436
|
+
};
|
|
437
|
+
const response = await fastify.injectJson({
|
|
438
|
+
method: 'POST',
|
|
439
|
+
url: '/issuing/generate-offers',
|
|
440
|
+
payload,
|
|
441
|
+
});
|
|
442
|
+
expect(response.statusCode).toEqual(200);
|
|
443
|
+
expect(response.json).toEqual({
|
|
444
|
+
offers: [expectedOffer(offers[5], { issuer: {} })],
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('should generate offers for a specific user, and vendorOrganizationId or tenantDID', async () => {
|
|
449
|
+
const payload = {
|
|
450
|
+
vendorUserId: 'adam.smith@example.com',
|
|
451
|
+
types: ['Course'],
|
|
452
|
+
tenantDID: offers[0].issuer.id,
|
|
453
|
+
vendorOrganizationId: offers[5].issuer.vendorOrganizationId,
|
|
454
|
+
exchangeId: new ObjectId(),
|
|
455
|
+
};
|
|
456
|
+
const response = await fastify.injectJson({
|
|
457
|
+
method: 'POST',
|
|
458
|
+
url: '/issuing/generate-offers',
|
|
459
|
+
payload,
|
|
460
|
+
});
|
|
461
|
+
expect(response.statusCode).toEqual(200);
|
|
462
|
+
expect(response.json).toEqual({
|
|
463
|
+
offers: expect.arrayContaining([
|
|
464
|
+
expectedOffer(offers[0], { issuer: {} }),
|
|
465
|
+
expectedOffer(offers[5], { issuer: {} }),
|
|
466
|
+
]),
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
expect(response.json.offers).toHaveLength(2);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it('should reply with 202', async () => {
|
|
473
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
474
|
+
...config,
|
|
475
|
+
noOffers200: false,
|
|
476
|
+
});
|
|
477
|
+
const payload = {
|
|
478
|
+
vendorUserId: 'maria.williams@example.com',
|
|
479
|
+
types: ['CurrentEmploymentPosition'],
|
|
480
|
+
tenantDID: '123',
|
|
481
|
+
exchangeId: new ObjectId(),
|
|
482
|
+
};
|
|
483
|
+
const response = await fastify.injectJson({
|
|
484
|
+
method: 'POST',
|
|
485
|
+
url: '/issuing/generate-offers',
|
|
486
|
+
payload,
|
|
487
|
+
});
|
|
488
|
+
expect(response.statusCode).toEqual(202);
|
|
489
|
+
expect(response.json).toEqual({});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('should reply with 200 with delay', async () => {
|
|
493
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
494
|
+
...config,
|
|
495
|
+
noOffers200: true,
|
|
496
|
+
generateOffersDelaySec: 3000,
|
|
497
|
+
});
|
|
498
|
+
const startTimestamp = Date.now().valueOf();
|
|
499
|
+
const payload = {
|
|
500
|
+
vendorUserId: 'maria.williams@example.com',
|
|
501
|
+
types: ['CurrentEmploymentPosition'],
|
|
502
|
+
tenantDID: '123',
|
|
503
|
+
exchangeId: new ObjectId(),
|
|
504
|
+
};
|
|
505
|
+
const response = await fastify.injectJson({
|
|
506
|
+
method: 'POST',
|
|
507
|
+
url: '/issuing/generate-offers',
|
|
508
|
+
payload,
|
|
509
|
+
});
|
|
510
|
+
expect(response.statusCode).toEqual(200);
|
|
511
|
+
expect(response.json).toEqual({
|
|
512
|
+
offers: [],
|
|
513
|
+
});
|
|
514
|
+
expect(Date.now().valueOf() - startTimestamp).toBeGreaterThanOrEqual(
|
|
515
|
+
3000
|
|
516
|
+
);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should reply with 202 based on vendorOrganizationId', async () => {
|
|
520
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
521
|
+
...config,
|
|
522
|
+
noOffers200: false,
|
|
523
|
+
});
|
|
524
|
+
const payload = {
|
|
525
|
+
vendorUserId: 'maria.williams@example.com',
|
|
526
|
+
types: ['CurrentEmploymentPosition'],
|
|
527
|
+
vendorOrganizationId: '123',
|
|
528
|
+
exchangeId: new ObjectId(),
|
|
529
|
+
};
|
|
530
|
+
const response = await fastify.injectJson({
|
|
531
|
+
method: 'POST',
|
|
532
|
+
url: '/issuing/generate-offers',
|
|
533
|
+
payload,
|
|
534
|
+
});
|
|
535
|
+
expect(response.statusCode).toEqual(202);
|
|
536
|
+
expect(response.json).toEqual({});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('should reply with 202 based on tenantDID', async () => {
|
|
540
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
541
|
+
...config,
|
|
542
|
+
noOffers200: false,
|
|
543
|
+
});
|
|
544
|
+
const payload = {
|
|
545
|
+
vendorUserId: 'maria.williams@example.com',
|
|
546
|
+
types: ['CurrentEmploymentPosition'],
|
|
547
|
+
tenantDID: '123',
|
|
548
|
+
exchangeId: new ObjectId(),
|
|
549
|
+
};
|
|
550
|
+
const response = await fastify.injectJson({
|
|
551
|
+
method: 'POST',
|
|
552
|
+
url: '/issuing/generate-offers',
|
|
553
|
+
payload,
|
|
554
|
+
});
|
|
555
|
+
expect(response.statusCode).toEqual(202);
|
|
556
|
+
expect(response.json).toEqual({});
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('should generate offers for a specific user, many types, and tenantDID', async () => {
|
|
560
|
+
fastify.overrides.reqConfig = (config) => ({
|
|
561
|
+
...config,
|
|
562
|
+
noOffers200: false,
|
|
563
|
+
});
|
|
564
|
+
const payload = {
|
|
565
|
+
vendorUserId: 'adam.smith@example.com',
|
|
566
|
+
tenantDID: offers[0].issuer.id,
|
|
567
|
+
types: ['Course', 'Badge'],
|
|
568
|
+
exchangeId: new ObjectId(),
|
|
569
|
+
};
|
|
570
|
+
const response = await fastify.injectJson({
|
|
571
|
+
method: 'POST',
|
|
572
|
+
url: '/issuing/generate-offers',
|
|
573
|
+
payload,
|
|
574
|
+
});
|
|
575
|
+
expect(response.statusCode).toEqual(200);
|
|
576
|
+
expect(response.json).toEqual({
|
|
577
|
+
offers: expect.arrayContaining(
|
|
578
|
+
map(expectedOffer, [offers[0], offers[2]])
|
|
579
|
+
),
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('should generate offers for a specific user, many types, and tenantDID and no vendorOrganizationId', async () => {
|
|
584
|
+
const payload = {
|
|
585
|
+
vendorUserId: 'adam.smith@example.com',
|
|
586
|
+
tenantDID: offers[0].issuer.id,
|
|
587
|
+
types: ['Course', 'Badge'],
|
|
588
|
+
exchangeId: new ObjectId(),
|
|
589
|
+
};
|
|
590
|
+
const response = await fastify.injectJson({
|
|
591
|
+
method: 'POST',
|
|
592
|
+
url: '/issuing/generate-offers',
|
|
593
|
+
payload,
|
|
594
|
+
});
|
|
595
|
+
expect(response.statusCode).toEqual(200);
|
|
596
|
+
expect(response.json).toEqual({
|
|
597
|
+
offers: expect.arrayContaining(
|
|
598
|
+
map(expectedOffer, [offers[0], offers[2]])
|
|
599
|
+
),
|
|
600
|
+
});
|
|
601
|
+
expect(response.json.offers).toHaveLength(2);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('should generate offers with resource reference data', async () => {
|
|
605
|
+
const offer = await persistOffer({
|
|
606
|
+
type: ['CourseTest'],
|
|
607
|
+
credentialSubject: { vendorUserId: 'maria.williams@example.com' },
|
|
608
|
+
replaces: [
|
|
609
|
+
{
|
|
610
|
+
id: 'did:ion:123',
|
|
611
|
+
type: 'MockType-1',
|
|
612
|
+
},
|
|
613
|
+
],
|
|
614
|
+
relatedResource: [
|
|
615
|
+
{
|
|
616
|
+
id: 'http://www.223.com',
|
|
617
|
+
type: 'MockType-2',
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
id: 'scheme:whatever;12312312',
|
|
621
|
+
type: 'MockType-3',
|
|
622
|
+
},
|
|
623
|
+
],
|
|
624
|
+
});
|
|
625
|
+
const payload = {
|
|
626
|
+
vendorUserId: 'maria.williams@example.com',
|
|
627
|
+
types: ['CourseTest'],
|
|
628
|
+
tenantDID: offer.issuer.id,
|
|
629
|
+
exchangeId: new ObjectId(),
|
|
630
|
+
};
|
|
631
|
+
const response = await fastify.injectJson({
|
|
632
|
+
method: 'POST',
|
|
633
|
+
url: '/issuing/generate-offers',
|
|
634
|
+
payload,
|
|
635
|
+
});
|
|
636
|
+
expect(response.statusCode).toEqual(200);
|
|
637
|
+
expect(response.json).toEqual({
|
|
638
|
+
offers: [expectedOffer(offer)],
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('should insert accepted offers', async () => {
|
|
643
|
+
const payload = {
|
|
644
|
+
offerIds: [offers[0].offerId],
|
|
645
|
+
exchangeId: new ObjectId(),
|
|
646
|
+
};
|
|
647
|
+
const response = await fastify.injectJson({
|
|
648
|
+
method: 'POST',
|
|
649
|
+
url: '/issuing/receive-issued-credentials',
|
|
650
|
+
payload,
|
|
651
|
+
});
|
|
652
|
+
expect(response.statusCode).toEqual(200);
|
|
653
|
+
const exchangeDBResult = await acceptedOffers.findOne({
|
|
654
|
+
exchangeId: payload.exchangeId.toString(),
|
|
655
|
+
});
|
|
656
|
+
expect(exchangeDBResult).toEqual({
|
|
657
|
+
_id: expect.any(ObjectId),
|
|
658
|
+
createdAt: expect.any(Date),
|
|
659
|
+
exchangeId: payload.exchangeId.toString(),
|
|
660
|
+
offerIds: [offers[0].offerId],
|
|
661
|
+
updatedAt: expect.any(Date),
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
const expectedOffer = (payload, overrides = {}) =>
|
|
668
|
+
omit(['_id', 'createdAt', 'exchangeId', 'updatedAt'], {
|
|
669
|
+
...payload,
|
|
670
|
+
...overrides,
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
const expectedRelatedResource = (relatedResource) => {
|
|
674
|
+
if (relatedResource.type == null) {
|
|
675
|
+
return relatedResource;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return {
|
|
679
|
+
...relatedResource,
|
|
680
|
+
type: castArray(relatedResource.type),
|
|
681
|
+
};
|
|
682
|
+
};
|