2016-05-27 17:57:48 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const request = require('supertest');
|
2016-06-01 13:20:49 +00:00
|
|
|
const Mongo = require('../../src/dao/mongo');
|
|
|
|
const nodemailer = require('nodemailer');
|
2017-08-19 09:52:15 +00:00
|
|
|
const templates = require('../../src/email/templates');
|
2016-06-01 13:20:49 +00:00
|
|
|
const config = require('config');
|
2016-05-27 17:57:48 +00:00
|
|
|
const fs = require('fs');
|
2017-08-18 10:01:34 +00:00
|
|
|
const log = require('winston');
|
2016-05-27 17:57:48 +00:00
|
|
|
|
2016-06-01 13:20:49 +00:00
|
|
|
describe('Koa App (HTTP Server) Integration Tests', function() {
|
2016-05-27 17:57:48 +00:00
|
|
|
this.timeout(20000);
|
|
|
|
|
2019-02-08 16:04:28 +00:00
|
|
|
const sandbox = sinon.createSandbox();
|
2017-08-15 08:03:06 +00:00
|
|
|
let app;
|
|
|
|
let mongo;
|
|
|
|
let sendEmailStub;
|
|
|
|
let publicKeyArmored;
|
|
|
|
let emailParams;
|
2016-06-01 13:20:49 +00:00
|
|
|
|
|
|
|
const DB_TYPE_PUB_KEY = 'publickey';
|
|
|
|
const DB_TYPE_USER_ID = 'userid';
|
|
|
|
const primaryEmail = 'safewithme.testuser@gmail.com';
|
2016-06-02 20:55:32 +00:00
|
|
|
const fingerprint = '4277257930867231CE393FB8DBC0B3D92B1B86E9';
|
2016-05-27 17:57:48 +00:00
|
|
|
|
2017-08-17 09:44:26 +00:00
|
|
|
before(async () => {
|
2017-08-17 11:16:49 +00:00
|
|
|
sandbox.stub(log);
|
|
|
|
|
2019-03-15 15:55:53 +00:00
|
|
|
publicKeyArmored = fs.readFileSync(`${__dirname}/../fixtures/key1.asc`, 'utf8');
|
2016-06-08 12:01:30 +00:00
|
|
|
mongo = new Mongo();
|
2017-08-17 07:34:47 +00:00
|
|
|
await mongo.init(config.mongo);
|
2016-06-01 13:20:49 +00:00
|
|
|
|
2017-08-19 09:52:15 +00:00
|
|
|
const paramMatcher = sinon.match(params => {
|
2016-06-01 13:20:49 +00:00
|
|
|
emailParams = params;
|
2017-08-15 08:03:06 +00:00
|
|
|
return Boolean(params.nonce);
|
2017-08-19 09:52:15 +00:00
|
|
|
});
|
|
|
|
sandbox.spy(templates, 'verifyKey').withArgs(paramMatcher);
|
|
|
|
sandbox.spy(templates, 'verifyRemove').withArgs(paramMatcher);
|
|
|
|
|
|
|
|
sendEmailStub = sandbox.stub().returns(Promise.resolve({response: '250'}));
|
|
|
|
sendEmailStub.withArgs(sinon.match(sendOptions => sendOptions.to.address === primaryEmail));
|
2017-08-17 07:34:47 +00:00
|
|
|
sandbox.stub(nodemailer, 'createTransport').returns({
|
2017-08-19 09:52:15 +00:00
|
|
|
sendMail: sendEmailStub
|
2016-06-01 13:20:49 +00:00
|
|
|
});
|
|
|
|
|
2017-08-15 08:03:06 +00:00
|
|
|
const init = require('../../src/app');
|
2017-08-17 07:34:47 +00:00
|
|
|
app = await init();
|
2016-05-27 17:57:48 +00:00
|
|
|
});
|
|
|
|
|
2017-08-17 09:44:26 +00:00
|
|
|
beforeEach(async () => {
|
2017-08-17 07:34:47 +00:00
|
|
|
await mongo.clear(DB_TYPE_PUB_KEY);
|
|
|
|
await mongo.clear(DB_TYPE_USER_ID);
|
2016-06-01 13:20:49 +00:00
|
|
|
emailParams = null;
|
|
|
|
});
|
|
|
|
|
2017-08-17 09:44:26 +00:00
|
|
|
after(async () => {
|
2017-08-17 07:34:47 +00:00
|
|
|
sandbox.restore();
|
|
|
|
await mongo.clear(DB_TYPE_PUB_KEY);
|
|
|
|
await mongo.clear(DB_TYPE_USER_ID);
|
|
|
|
await mongo.disconnect();
|
2016-06-01 13:20:49 +00:00
|
|
|
});
|
|
|
|
|
2016-05-31 11:52:18 +00:00
|
|
|
describe('REST api', () => {
|
|
|
|
describe('POST /api/v1/key', () => {
|
2016-06-01 13:20:49 +00:00
|
|
|
it('should return 400 for an invalid pgp key', done => {
|
2016-05-27 17:57:48 +00:00
|
|
|
request(app.listen())
|
|
|
|
.post('/api/v1/key')
|
2017-08-15 08:03:06 +00:00
|
|
|
.send({publicKeyArmored: 'foo'})
|
2016-05-27 17:57:48 +00:00
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
2016-06-01 13:20:49 +00:00
|
|
|
|
2017-08-23 10:19:59 +00:00
|
|
|
it('should return 201', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
|
|
|
.post('/api/v1/key')
|
2017-08-15 08:03:06 +00:00
|
|
|
.send({publicKeyArmored})
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(201)
|
|
|
|
.end(() => {
|
|
|
|
expect(emailParams).to.exist;
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
});
|
2016-05-27 17:57:48 +00:00
|
|
|
});
|
|
|
|
|
2016-06-14 10:10:53 +00:00
|
|
|
describe('GET /api/v1/key?op=verify', () => {
|
2016-06-01 13:20:49 +00:00
|
|
|
beforeEach(done => {
|
|
|
|
request(app.listen())
|
|
|
|
.post('/api/v1/key')
|
2017-08-23 10:19:59 +00:00
|
|
|
.send({publicKeyArmored})
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(201)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 200 for valid params', done => {
|
2016-05-27 17:57:48 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?op=verify&keyId=${emailParams.keyId}&nonce=${emailParams.nonce}`)
|
2016-05-27 17:57:48 +00:00
|
|
|
.expect(200)
|
|
|
|
.end(done);
|
|
|
|
});
|
2016-05-29 16:59:14 +00:00
|
|
|
|
2016-06-01 13:20:49 +00:00
|
|
|
it('should return 400 for missing keyid and', done => {
|
2016-05-29 16:59:14 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?op=verify&nonce=${emailParams.nonce}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 400 for missing nonce', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?op=verify&keyId=${emailParams.keyId}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('GET /api/key', () => {
|
|
|
|
beforeEach(done => {
|
|
|
|
request(app.listen())
|
|
|
|
.post('/api/v1/key')
|
2017-08-23 10:19:59 +00:00
|
|
|
.send({publicKeyArmored})
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(201)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Not yet verified', () => {
|
|
|
|
it('should return 404', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?keyId=${emailParams.keyId}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(404).end(done);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Verified', () => {
|
|
|
|
beforeEach(done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?op=verify&keyId=${emailParams.keyId}&nonce=${emailParams.nonce}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(200)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 200 and get key by id', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?keyId=${emailParams.keyId}`)
|
2016-06-02 17:34:24 +00:00
|
|
|
.expect(200)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 200 and get key email address', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?email=${primaryEmail}`)
|
2016-06-02 17:34:24 +00:00
|
|
|
.expect(200)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 400 for missing params', done => {
|
|
|
|
request(app.listen())
|
|
|
|
.get('/api/v1/key')
|
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-07 14:22:17 +00:00
|
|
|
it('should return 400 for short key id', done => {
|
|
|
|
request(app.listen())
|
2016-06-09 16:08:15 +00:00
|
|
|
.get('/api/v1/key?keyId=0123456789ABCDE')
|
2016-06-07 14:22:17 +00:00
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-01 13:20:49 +00:00
|
|
|
it('should return 404 for wrong key id', done => {
|
|
|
|
request(app.listen())
|
2016-06-09 16:08:15 +00:00
|
|
|
.get('/api/v1/key?keyId=0123456789ABCDEF')
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-06-14 14:46:17 +00:00
|
|
|
describe('DELETE /api/v1/key', () => {
|
2016-06-01 13:20:49 +00:00
|
|
|
beforeEach(done => {
|
|
|
|
request(app.listen())
|
|
|
|
.post('/api/v1/key')
|
2017-08-23 10:19:59 +00:00
|
|
|
.send({publicKeyArmored})
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(201)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 202 for key id', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.del(`/api/v1/key?keyId=${emailParams.keyId}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(202)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 202 for email address', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.del(`/api/v1/key?email=${primaryEmail}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(202)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 400 for invalid params', done => {
|
|
|
|
request(app.listen())
|
2016-06-14 14:46:17 +00:00
|
|
|
.del('/api/v1/key')
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 404 for unknown email address', done => {
|
|
|
|
request(app.listen())
|
2016-06-14 14:46:17 +00:00
|
|
|
.del('/api/v1/key?email=a@foo.com')
|
2016-05-29 16:59:14 +00:00
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
2016-05-27 17:57:48 +00:00
|
|
|
});
|
|
|
|
|
2016-06-14 10:10:53 +00:00
|
|
|
describe('GET /api/v1/key?op=verifyRemove', () => {
|
2016-06-01 13:20:49 +00:00
|
|
|
beforeEach(done => {
|
|
|
|
request(app.listen())
|
|
|
|
.post('/api/v1/key')
|
2017-08-23 10:19:59 +00:00
|
|
|
.send({publicKeyArmored})
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(201)
|
2017-08-15 08:03:06 +00:00
|
|
|
.end(() => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.del(`/api/v1/key?keyId=${emailParams.keyId}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(202)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 200 for key id', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?op=verifyRemove&keyId=${emailParams.keyId}&nonce=${emailParams.nonce}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(200)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 400 for invalid params', done => {
|
|
|
|
request(app.listen())
|
2016-06-14 10:10:53 +00:00
|
|
|
.get('/api/v1/key?op=verifyRemove')
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-07 14:22:17 +00:00
|
|
|
it('should return 404 for unknown key id', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?op=verifyRemove&keyId=0123456789ABCDEF&nonce=${emailParams.nonce}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('HKP api', () => {
|
2016-05-31 11:52:18 +00:00
|
|
|
describe('POST /pks/add', () => {
|
|
|
|
it('should return 400 for an invalid body', done => {
|
2016-05-27 17:57:48 +00:00
|
|
|
request(app.listen())
|
|
|
|
.post('/pks/add')
|
|
|
|
.type('form')
|
|
|
|
.send('keytext=asdf')
|
|
|
|
.expect(400)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-05-31 11:52:18 +00:00
|
|
|
it('should return 201 for a valid PGP key', done => {
|
2016-05-27 17:57:48 +00:00
|
|
|
request(app.listen())
|
|
|
|
.post('/pks/add')
|
|
|
|
.type('form')
|
2017-08-15 08:03:06 +00:00
|
|
|
.send(`keytext=${encodeURIComponent(publicKeyArmored)}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(201)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('GET /pks/lookup', () => {
|
|
|
|
beforeEach(done => {
|
|
|
|
request(app.listen())
|
|
|
|
.post('/pks/add')
|
|
|
|
.type('form')
|
2017-08-15 08:03:06 +00:00
|
|
|
.send(`keytext=${encodeURIComponent(publicKeyArmored)}`)
|
2016-05-29 16:59:14 +00:00
|
|
|
.expect(201)
|
2016-05-27 17:57:48 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
2016-06-01 13:20:49 +00:00
|
|
|
|
|
|
|
describe('Not yet verified', () => {
|
|
|
|
it('should return 404', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=get&search=0x${emailParams.keyId}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Verified', () => {
|
|
|
|
beforeEach(done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/api/v1/key?op=verify&keyId=${emailParams.keyId}&nonce=${emailParams.nonce}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(200)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-02 20:55:32 +00:00
|
|
|
it('should return 200 for key id', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=get&search=0x${emailParams.keyId}`)
|
2019-02-14 17:11:37 +00:00
|
|
|
.expect(200)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-02 20:55:32 +00:00
|
|
|
it('should return 200 for fingerprint', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=get&search=0x${fingerprint}`)
|
2019-02-14 17:11:37 +00:00
|
|
|
.expect(200)
|
2016-06-02 20:55:32 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-01 13:20:49 +00:00
|
|
|
it('should return 200 for correct email address', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=get&search=${primaryEmail}`)
|
2019-02-14 17:11:37 +00:00
|
|
|
.expect(200)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-02 17:34:24 +00:00
|
|
|
it('should return 200 for "mr" option', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=get&options=mr&search=${primaryEmail}`)
|
2016-06-02 17:34:24 +00:00
|
|
|
.expect('Content-Type', 'application/pgp-keys; charset=utf-8')
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect('Content-Disposition', 'attachment; filename=openpgpkey.asc')
|
2019-02-14 17:11:37 +00:00
|
|
|
.expect(200)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-02 21:32:23 +00:00
|
|
|
it('should return 200 for "vindex" op', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=vindex&search=0x${emailParams.keyId}`)
|
2016-06-02 21:32:23 +00:00
|
|
|
.expect(200)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-02 17:34:24 +00:00
|
|
|
it('should return 200 for "index" with "mr" option', done => {
|
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=index&options=mr&search=0x${emailParams.keyId}`)
|
2016-06-02 17:34:24 +00:00
|
|
|
.expect('Content-Type', 'text/plain; charset=utf-8')
|
|
|
|
.expect(200)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-07 14:22:17 +00:00
|
|
|
it('should return 501 for invalid email', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
|
|
|
.get('/pks/lookup?op=get&search=a@bco')
|
2016-06-07 14:22:17 +00:00
|
|
|
.expect(501)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 404 for unkown email', done => {
|
|
|
|
request(app.listen())
|
|
|
|
.get('/pks/lookup?op=get&search=a@b.co')
|
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-07 14:22:17 +00:00
|
|
|
it('should return 501 for missing params', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
|
|
|
.get('/pks/lookup?op=get')
|
2016-06-07 14:22:17 +00:00
|
|
|
.expect(501)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-07 14:22:17 +00:00
|
|
|
it('should return 501 for a invalid key id format', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=get&search=${emailParams.keyId}`)
|
2016-06-07 14:22:17 +00:00
|
|
|
.expect(501)
|
2016-06-01 13:20:49 +00:00
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return 404 for unkown key id', done => {
|
|
|
|
request(app.listen())
|
|
|
|
.get('/pks/lookup?op=get&search=0xDBC0B3D92A1B86E9')
|
|
|
|
.expect(404)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-07 14:22:17 +00:00
|
|
|
it('should return 501 (Not implemented) for short key id', done => {
|
|
|
|
request(app.listen())
|
|
|
|
.get('/pks/lookup?op=get&search=0x2A1B86E9')
|
|
|
|
.expect(501)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
|
2016-06-02 21:32:23 +00:00
|
|
|
it('should return 501 (Not implemented) for "x-email" op', done => {
|
2016-06-01 13:20:49 +00:00
|
|
|
request(app.listen())
|
2017-08-15 08:03:06 +00:00
|
|
|
.get(`/pks/lookup?op=x-email&search=0x${emailParams.keyId}`)
|
2016-06-01 13:20:49 +00:00
|
|
|
.expect(501)
|
|
|
|
.end(done);
|
|
|
|
});
|
|
|
|
});
|
2016-05-27 17:57:48 +00:00
|
|
|
});
|
|
|
|
});
|
2017-08-15 08:03:06 +00:00
|
|
|
});
|