@creator.co/wapi 1.7.1-alpha3 → 1.7.1
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/.eslintignore +3 -0
- package/.eslintrc.cjs +60 -0
- package/.github/workflows/npmpublish.yml +11 -0
- package/.github/workflows/prs.yml +13 -0
- package/dist/package-lock.json +2 -2
- package/dist/package.json +1 -1
- package/jest.config.ts +33 -0
- package/jest.smoke.config.ts +35 -0
- package/package.json +1 -1
- package/tests/API/Request.test.ts +273 -0
- package/tests/API/Response.test.ts +367 -0
- package/tests/API/Utils.test.ts +167 -0
- package/tests/BaseEvent/EventProcessor.test.ts +261 -0
- package/tests/BaseEvent/Process.test.ts +49 -0
- package/tests/BaseEvent/Transaction.test.ts +408 -0
- package/tests/Cache/Redis-client.test.ts +90 -0
- package/tests/Cache/Redis-cluster.test.ts +100 -0
- package/tests/Config/Config.test.ts +205 -0
- package/tests/Config/EnvironmentVar.test.ts +250 -0
- package/tests/Crypto/Crypto.test.ts +88 -0
- package/tests/Crypto/JWT.test.ts +92 -0
- package/tests/Database/DatabaseManager.test.ts +71 -0
- package/tests/Database/integrations/knex/KnexDatabase.test.ts +76 -0
- package/tests/Database/integrations/knex/KnexTransaction.test.ts +149 -0
- package/tests/Database/integrations/kysely/KyselyDatabase.test.ts +113 -0
- package/tests/Database/integrations/kysely/KyselyTransaction.test.ts +119 -0
- package/tests/Database/integrations/pg/PostgresDatabase.test.ts +76 -0
- package/tests/Database/integrations/pg/PostgresTransaction.test.ts +118 -0
- package/tests/Logger/Logger.test.ts +219 -0
- package/tests/Mailer/Mailer.test.ts +59 -0
- package/tests/Publisher/Publisher.test.ts +94 -0
- package/tests/Server/RouteResolver.test.ts +102 -0
- package/tests/Server/Router.test.ts +39 -0
- package/tests/Server/lib/ContainerServer.test.ts +531 -0
- package/tests/Server/lib/Server.test.ts +12 -0
- package/tests/Server/lib/container/GenericHandler.test.ts +131 -0
- package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
- package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
- package/tests/Server/lib/container/Proxy.test.ts +268 -0
- package/tests/Server/lib/container/Utils.test.ts +47 -0
- package/tests/Test.utils.ts +74 -0
- package/tests/Validation/Validator.test.ts +76 -0
- package/tsconfig.json +26 -0
- package/tsconfig.smoke.json +26 -0
- package/coverage/clover.xml +0 -1088
- package/coverage/coverage/coverage.txt +0 -40
- package/coverage/coverage-final.json +0 -37
- package/coverage/coverage-summary.json +0 -38
- package/coverage/coverage.txt +0 -59
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -371
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/src/API/Request.ts.html +0 -727
- package/coverage/lcov-report/src/API/Response.ts.html +0 -1189
- package/coverage/lcov-report/src/API/Utils.ts.html +0 -313
- package/coverage/lcov-report/src/API/index.html +0 -131
- package/coverage/lcov-report/src/BaseEvent/EventProcessor.ts.html +0 -496
- package/coverage/lcov-report/src/BaseEvent/Process.ts.html +0 -346
- package/coverage/lcov-report/src/BaseEvent/Transaction.ts.html +0 -1015
- package/coverage/lcov-report/src/BaseEvent/index.html +0 -146
- package/coverage/lcov-report/src/Cache/Redis.ts.html +0 -367
- package/coverage/lcov-report/src/Cache/index.html +0 -116
- package/coverage/lcov-report/src/Config/Configuration.ts.html +0 -700
- package/coverage/lcov-report/src/Config/EnvironmentVar.ts.html +0 -526
- package/coverage/lcov-report/src/Config/index.html +0 -131
- package/coverage/lcov-report/src/Crypto/Crypto.ts.html +0 -352
- package/coverage/lcov-report/src/Crypto/JWT.ts.html +0 -337
- package/coverage/lcov-report/src/Crypto/index.html +0 -131
- package/coverage/lcov-report/src/Database/Database.ts.html +0 -151
- package/coverage/lcov-report/src/Database/DatabaseManager.ts.html +0 -289
- package/coverage/lcov-report/src/Database/DatabaseTransaction.ts.html +0 -595
- package/coverage/lcov-report/src/Database/index.html +0 -161
- package/coverage/lcov-report/src/Database/index.ts.html +0 -169
- package/coverage/lcov-report/src/Database/integrations/knex/KnexDatabase.ts.html +0 -283
- package/coverage/lcov-report/src/Database/integrations/knex/KnexTransaction.ts.html +0 -337
- package/coverage/lcov-report/src/Database/integrations/knex/index.html +0 -131
- package/coverage/lcov-report/src/Database/integrations/kysely/KyselyDatabase.ts.html +0 -376
- package/coverage/lcov-report/src/Database/integrations/kysely/KyselyTransaction.ts.html +0 -601
- package/coverage/lcov-report/src/Database/integrations/kysely/index.html +0 -131
- package/coverage/lcov-report/src/Database/integrations/pgsql/PostgresDatabase.ts.html +0 -250
- package/coverage/lcov-report/src/Database/integrations/pgsql/PostgresTransaction.ts.html +0 -346
- package/coverage/lcov-report/src/Database/integrations/pgsql/index.html +0 -131
- package/coverage/lcov-report/src/Database/types.d.ts.html +0 -232
- package/coverage/lcov-report/src/Globals.ts.html +0 -394
- package/coverage/lcov-report/src/Logger/Logger.ts.html +0 -1138
- package/coverage/lcov-report/src/Logger/index.html +0 -116
- package/coverage/lcov-report/src/Mailer/Mailer.ts.html +0 -754
- package/coverage/lcov-report/src/Mailer/index.html +0 -116
- package/coverage/lcov-report/src/Publisher/Publisher.ts.html +0 -460
- package/coverage/lcov-report/src/Publisher/index.html +0 -116
- package/coverage/lcov-report/src/Server/RouteResolver.ts.html +0 -442
- package/coverage/lcov-report/src/Server/Router.ts.html +0 -616
- package/coverage/lcov-report/src/Server/index.html +0 -131
- package/coverage/lcov-report/src/Server/lib/ContainerServer.ts.html +0 -280
- package/coverage/lcov-report/src/Server/lib/Server.ts.html +0 -403
- package/coverage/lcov-report/src/Server/lib/container/GenericHandler.ts.html +0 -331
- package/coverage/lcov-report/src/Server/lib/container/GenericHandlerEvent.ts.html +0 -547
- package/coverage/lcov-report/src/Server/lib/container/HealthHandler.ts.html +0 -118
- package/coverage/lcov-report/src/Server/lib/container/Proxy.ts.html +0 -619
- package/coverage/lcov-report/src/Server/lib/container/Utils.ts.html +0 -184
- package/coverage/lcov-report/src/Server/lib/container/index.html +0 -176
- package/coverage/lcov-report/src/Server/lib/index.html +0 -131
- package/coverage/lcov-report/src/Util/AsyncSingleton.ts.html +0 -343
- package/coverage/lcov-report/src/Util/Utils.ts.html +0 -313
- package/coverage/lcov-report/src/Util/index.html +0 -131
- package/coverage/lcov-report/src/Validation/Validator.ts.html +0 -217
- package/coverage/lcov-report/src/Validation/index.html +0 -116
- package/coverage/lcov-report/src/index.html +0 -116
- package/coverage/lcov.info +0 -2326
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { DecryptCommand, EncryptCommand, KMSClient } from '@aws-sdk/client-kms'
|
|
2
|
+
import { mockClient } from 'aws-sdk-client-mock'
|
|
3
|
+
import { expect } from 'chai'
|
|
4
|
+
|
|
5
|
+
import Crypto from '../../src/Crypto/Crypto'
|
|
6
|
+
|
|
7
|
+
const KMSMock = mockClient(KMSClient)
|
|
8
|
+
|
|
9
|
+
function fakeEncryption(v) {
|
|
10
|
+
const f = new TextEncoder().encode(v)
|
|
11
|
+
return Buffer.from(f as any, 'utf8').toString('hex')
|
|
12
|
+
}
|
|
13
|
+
function fakeDecryption(v) {
|
|
14
|
+
return new TextEncoder().encode(v)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('Encryption', () => {
|
|
18
|
+
// reset mock
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
KMSMock.reset()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const provider = new Crypto('ca-central-1', 'abc123')
|
|
24
|
+
test('Encrypts json object', async () => {
|
|
25
|
+
const encryptionObj = { userID: 123 }
|
|
26
|
+
KMSMock.on(EncryptCommand).resolves({
|
|
27
|
+
CiphertextBlob: new TextEncoder().encode(JSON.stringify(encryptionObj)),
|
|
28
|
+
})
|
|
29
|
+
const token = await provider.encryptData(encryptionObj)
|
|
30
|
+
expect(token).is.not.null
|
|
31
|
+
expect(token).to.be.equals(fakeEncryption(JSON.stringify(encryptionObj)))
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('Encrypts string', async () => {
|
|
35
|
+
const encryptionText = 'Hello encryption!'
|
|
36
|
+
KMSMock.on(EncryptCommand).resolves({
|
|
37
|
+
CiphertextBlob: new TextEncoder().encode(encryptionText),
|
|
38
|
+
})
|
|
39
|
+
const token = await provider.encryptData(encryptionText)
|
|
40
|
+
expect(token).is.not.null
|
|
41
|
+
expect(token).to.be.equals(fakeEncryption(encryptionText))
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('Fails to encrypt a number', async () => {
|
|
45
|
+
const encryptionText = 123
|
|
46
|
+
KMSMock.on(EncryptCommand).rejects(new Error('failed'))
|
|
47
|
+
const token = await provider.encryptData(encryptionText)
|
|
48
|
+
expect(token).is.null
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('Decryption', () => {
|
|
53
|
+
// reset mock
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
KMSMock.reset()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const provider = new Crypto('ca-central-1', 'abc123')
|
|
59
|
+
test('Decrypts json object', async () => {
|
|
60
|
+
const encryptionObj = { userID: 123 }
|
|
61
|
+
KMSMock.on(DecryptCommand).resolves({
|
|
62
|
+
Plaintext: fakeDecryption(JSON.stringify(encryptionObj)),
|
|
63
|
+
})
|
|
64
|
+
const token = await provider.decryptData(fakeEncryption(JSON.stringify(encryptionObj)))
|
|
65
|
+
expect(token).is.not.null
|
|
66
|
+
expect(token).to.be.equals(JSON.stringify(encryptionObj))
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('Decrypts string', async () => {
|
|
70
|
+
const encryptionText = 'abc123'
|
|
71
|
+
KMSMock.on(DecryptCommand).resolves({
|
|
72
|
+
Plaintext: fakeDecryption(encryptionText),
|
|
73
|
+
})
|
|
74
|
+
const token = await provider.decryptData(fakeEncryption(encryptionText))
|
|
75
|
+
expect(token).is.not.null
|
|
76
|
+
expect(token).to.be.equals(encryptionText)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('Fails to decrypt a number', async () => {
|
|
80
|
+
const encryptionText = 123
|
|
81
|
+
KMSMock.on(DecryptCommand).rejects(new Error('failed'))
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
const token = await provider.decryptData(encryptionText)
|
|
84
|
+
expect(token).is.null
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
export {}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { expect } from 'chai'
|
|
2
|
+
|
|
3
|
+
import JWT from '../../src/Crypto/JWT'
|
|
4
|
+
import { foreignToken, privateKey } from '../Test.utils'
|
|
5
|
+
|
|
6
|
+
describe('Creates token', () => {
|
|
7
|
+
const provider = new JWT(privateKey, '7d')
|
|
8
|
+
test('Creates token without explicity expiration', () => {
|
|
9
|
+
const token = provider.createToken({ userID: 123 })
|
|
10
|
+
expect(token).to.be.a('string')
|
|
11
|
+
expect(token).is.not.null
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('Creates token with explicity expiration', () => {
|
|
15
|
+
const token = provider.createToken({ userID: 123 }, '7d')
|
|
16
|
+
expect(token).to.be.a('string')
|
|
17
|
+
expect(token).is.not.null
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('Creates token with explicity token override', () => {
|
|
21
|
+
const token = provider.createToken({ userID: 123 }, '7d', privateKey)
|
|
22
|
+
expect(token).to.be.a('string')
|
|
23
|
+
expect(token).is.not.null
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test('Creates token with explicity token options', () => {
|
|
27
|
+
const token = provider.createToken({ userID: 123 }, undefined, undefined, {
|
|
28
|
+
expiresIn: '7d',
|
|
29
|
+
})
|
|
30
|
+
expect(token).to.be.a('string')
|
|
31
|
+
expect(token).is.not.null
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('Creates token with no expiration', () => {
|
|
35
|
+
const localProvider = new JWT(privateKey)
|
|
36
|
+
const token = localProvider.createToken({ userID: 123 })
|
|
37
|
+
expect(token).to.be.a('string')
|
|
38
|
+
expect(token).is.not.null
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('Validates token', () => {
|
|
43
|
+
const provider = new JWT(privateKey, '7d')
|
|
44
|
+
test('Succeed to validate just created token', () => {
|
|
45
|
+
// Creates token
|
|
46
|
+
const token = provider.createToken({ userID: 123 }, '7d', privateKey)
|
|
47
|
+
expect(token).to.be.a('string')
|
|
48
|
+
expect(token).is.not.null
|
|
49
|
+
// Validates token
|
|
50
|
+
const validationResp = provider.validateToken(token)
|
|
51
|
+
expect(validationResp).is.not.null
|
|
52
|
+
expect(validationResp.isValid).is.true
|
|
53
|
+
expect(validationResp['isExpired']).is.undefined
|
|
54
|
+
expect(validationResp['decodedToken']).is.not.null
|
|
55
|
+
expect(validationResp['decodedToken'].userID).to.be.equals(123)
|
|
56
|
+
expect(validationResp['decodedToken'].exp).to.be.a('number')
|
|
57
|
+
expect(validationResp['decodedToken'].exp).to.be.above(Date.now() / 1000)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('Fails to validate invalid token', () => {
|
|
61
|
+
// Creates token
|
|
62
|
+
const token = provider.createToken({ userID: 123 }, '7d', privateKey)
|
|
63
|
+
// Validates token
|
|
64
|
+
const validationResp = provider.validateToken(token + '123')
|
|
65
|
+
expect(validationResp).is.not.null
|
|
66
|
+
expect(validationResp.isValid).is.false
|
|
67
|
+
expect(validationResp['isExpired']).is.undefined
|
|
68
|
+
expect(validationResp['decodedToken']).to.undefined
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('Fails to validate expired token', () => {
|
|
72
|
+
// Creates token
|
|
73
|
+
const token = provider.createToken({ userID: 123 }, '-7d', privateKey)
|
|
74
|
+
// Validates token
|
|
75
|
+
const validationResp = provider.validateToken(token)
|
|
76
|
+
expect(validationResp).is.not.null
|
|
77
|
+
expect(validationResp.isValid).is.false
|
|
78
|
+
expect(validationResp['isExpired']).is.true
|
|
79
|
+
expect(validationResp['decodedToken']).to.undefined
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('Fails to validate foreign token', () => {
|
|
83
|
+
// Validates token
|
|
84
|
+
const validationResp = provider.validateToken(foreignToken)
|
|
85
|
+
expect(validationResp).is.not.null
|
|
86
|
+
expect(validationResp.isValid).is.false
|
|
87
|
+
expect(validationResp['isExpired']).is.undefined
|
|
88
|
+
expect(validationResp['decodedToken']).to.undefined
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
export {}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import { expect } from 'chai'
|
|
3
|
+
|
|
4
|
+
import { DatabaseManager } from '../../src/Database'
|
|
5
|
+
import type { DatabaseType, DbConfig } from '../../src/Database/types'
|
|
6
|
+
|
|
7
|
+
type FakeDatabase = { host: string; type: 'knex' | 'pg' | 'kysely' }
|
|
8
|
+
|
|
9
|
+
const fakeConfig = (host: string, type: DatabaseType) => {
|
|
10
|
+
return {
|
|
11
|
+
type,
|
|
12
|
+
host,
|
|
13
|
+
} as DbConfig<typeof type>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('Database Manager', () => {
|
|
17
|
+
test('should correctly create and cache database instances', () => {
|
|
18
|
+
const underTest = new DatabaseManager()
|
|
19
|
+
|
|
20
|
+
underTest['databases'] = {
|
|
21
|
+
knex: jest.fn((config: any) => {
|
|
22
|
+
return { host: config.host, type: 'knex' } as any
|
|
23
|
+
}) as any,
|
|
24
|
+
pg: jest.fn((config: any) => {
|
|
25
|
+
return { host: config.host, type: 'pg' } as any
|
|
26
|
+
}) as any,
|
|
27
|
+
kysely: jest.fn((config: any) => {
|
|
28
|
+
return { host: config.host, type: 'kysely' } as any
|
|
29
|
+
}) as any,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const create = config => underTest.create({ ...config }) as unknown as FakeDatabase
|
|
33
|
+
|
|
34
|
+
const knexConfig1 = fakeConfig('localhost', 'knex')
|
|
35
|
+
const knexConfig2 = fakeConfig('other', 'knex')
|
|
36
|
+
const pgConfig1 = fakeConfig('localhost', 'pg')
|
|
37
|
+
const pgConfig2 = fakeConfig('other', 'pg')
|
|
38
|
+
const kyselyConfig1 = fakeConfig('localhost', 'pg')
|
|
39
|
+
const kyselyConfig2 = fakeConfig('other', 'pg')
|
|
40
|
+
|
|
41
|
+
const knex1 = create(knexConfig1)
|
|
42
|
+
const knex2 = create(knexConfig1)
|
|
43
|
+
const knex3 = create(knexConfig2)
|
|
44
|
+
|
|
45
|
+
const pg1 = create(pgConfig1)
|
|
46
|
+
const pg2 = create(pgConfig1)
|
|
47
|
+
const pg3 = create(pgConfig2)
|
|
48
|
+
|
|
49
|
+
const kysely1 = create(kyselyConfig1)
|
|
50
|
+
const kysely2 = create(kyselyConfig1)
|
|
51
|
+
const kysely3 = create(kyselyConfig2)
|
|
52
|
+
|
|
53
|
+
expect(knex1).to.equal(knex2).to.not.equal(knex3).to.not.equal(pg1)
|
|
54
|
+
|
|
55
|
+
expect(knexConfig1).to.be.deep.equal(knex1).to.be.deep.equal(knex2)
|
|
56
|
+
|
|
57
|
+
expect(knex3).to.be.deep.equal(knexConfig2)
|
|
58
|
+
|
|
59
|
+
expect(pg1).to.equal(pg2).to.not.equal(pg3)
|
|
60
|
+
|
|
61
|
+
expect(pgConfig1).to.be.deep.equal(pg1).to.be.deep.equal(pg2)
|
|
62
|
+
|
|
63
|
+
expect(pg3).to.be.deep.equal(pgConfig2)
|
|
64
|
+
|
|
65
|
+
expect(kysely1).to.equal(kysely2).to.not.equal(kysely3)
|
|
66
|
+
|
|
67
|
+
expect(kyselyConfig1).to.be.deep.equal(kysely1).to.be.deep.equal(kysely2)
|
|
68
|
+
|
|
69
|
+
expect(kysely3).to.be.deep.equal(kyselyConfig2)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import { Knex } from 'knex'
|
|
3
|
+
|
|
4
|
+
import type { DbConfig } from '../../../../src/Database'
|
|
5
|
+
import { KnexDatabase } from '../../../../src/Database/integrations/knex/KnexDatabase'
|
|
6
|
+
|
|
7
|
+
const config: DbConfig<'knex'> = {
|
|
8
|
+
type: 'knex',
|
|
9
|
+
username: 'username',
|
|
10
|
+
password: 'password',
|
|
11
|
+
host: 'host',
|
|
12
|
+
port: 1234,
|
|
13
|
+
database: 'database',
|
|
14
|
+
driver: 'driver',
|
|
15
|
+
maxConnections: 1,
|
|
16
|
+
autoCommit: true,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const expectedImplConfig = {
|
|
20
|
+
client: config.driver,
|
|
21
|
+
connection: {
|
|
22
|
+
host: config.host,
|
|
23
|
+
port: config.port,
|
|
24
|
+
user: config.username,
|
|
25
|
+
password: config.password,
|
|
26
|
+
database: config.database,
|
|
27
|
+
},
|
|
28
|
+
pool: {
|
|
29
|
+
min: 1,
|
|
30
|
+
max: config.maxConnections,
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('KnexDatabase', () => {
|
|
35
|
+
test('KnexDatabase', async () => {
|
|
36
|
+
const mockTrans = { a: 'b' } as any
|
|
37
|
+
const mockClient = jest.fn(
|
|
38
|
+
() =>
|
|
39
|
+
({
|
|
40
|
+
transaction: async () => mockTrans,
|
|
41
|
+
}) as Knex
|
|
42
|
+
)
|
|
43
|
+
KnexDatabase['knexProvider'] = mockClient
|
|
44
|
+
|
|
45
|
+
const underTest = new KnexDatabase(config)
|
|
46
|
+
expect(mockClient).toBeCalledWith(expectedImplConfig)
|
|
47
|
+
|
|
48
|
+
const trans = await underTest.transaction()
|
|
49
|
+
|
|
50
|
+
expect(trans).toBeInstanceOf(Function)
|
|
51
|
+
expect(trans.transaction).toBe(mockTrans)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('KnexDatabase - convert camel to snake', async () => {
|
|
55
|
+
const mockTrans = { a: 'b' } as any
|
|
56
|
+
const mockClient = jest.fn(
|
|
57
|
+
() =>
|
|
58
|
+
({
|
|
59
|
+
transaction: async () => mockTrans,
|
|
60
|
+
}) as Knex
|
|
61
|
+
)
|
|
62
|
+
KnexDatabase['knexProvider'] = mockClient
|
|
63
|
+
|
|
64
|
+
const underTest = new KnexDatabase({ ...config, convertCamelToSnake: true })
|
|
65
|
+
expect(mockClient).toBeCalledWith({
|
|
66
|
+
...expectedImplConfig,
|
|
67
|
+
postProcessResponse: expect.any(Function),
|
|
68
|
+
wrapIdentifier: expect.any(Function),
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const trans = await underTest.transaction()
|
|
72
|
+
|
|
73
|
+
expect(trans).toBeInstanceOf(Function)
|
|
74
|
+
expect(trans.transaction).toBe(mockTrans)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import { Knex } from 'knex'
|
|
3
|
+
|
|
4
|
+
import { DbConfig } from '../../../../src/Database'
|
|
5
|
+
import { KnexDatabase } from '../../../../src/Database/integrations/knex/KnexDatabase'
|
|
6
|
+
import { KnexTransactionImpl } from '../../../../src/Database/integrations/knex/KnexTransaction'
|
|
7
|
+
|
|
8
|
+
const testResources = async (config: Partial<DbConfig<'knex'>>) => {
|
|
9
|
+
const database = {
|
|
10
|
+
config: {
|
|
11
|
+
autoCommit: true,
|
|
12
|
+
...config,
|
|
13
|
+
} as DbConfig<'knex'>,
|
|
14
|
+
} as KnexDatabase
|
|
15
|
+
|
|
16
|
+
const txMock = jest.fn() as any
|
|
17
|
+
txMock.commit = jest.fn()
|
|
18
|
+
txMock.rollback = jest.fn()
|
|
19
|
+
txMock.select = jest.fn()
|
|
20
|
+
|
|
21
|
+
const wMock = jest.fn() as any
|
|
22
|
+
wMock.transaction = jest.fn(() => txMock)
|
|
23
|
+
|
|
24
|
+
const writer = wMock as Knex
|
|
25
|
+
const transaction = txMock as Knex.Transaction
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
transaction,
|
|
29
|
+
writer,
|
|
30
|
+
underTest: await KnexTransactionImpl.newTransaction(writer, database),
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('KnexTransaction', () => {
|
|
35
|
+
test('Can only commit once', async () => {
|
|
36
|
+
const { writer, transaction, underTest } = await testResources({})
|
|
37
|
+
await underTest.commit()
|
|
38
|
+
expect(underTest.isOpen()).toBe(false)
|
|
39
|
+
expect(writer.transaction).toBeCalled()
|
|
40
|
+
expect(transaction.commit).toBeCalled()
|
|
41
|
+
|
|
42
|
+
await expect(underTest.commit).rejects.toThrowError(
|
|
43
|
+
'Cannot commit, transaction is already closed!'
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test("Can't commit after rollback", async () => {
|
|
48
|
+
const { writer, transaction, underTest } = await testResources({})
|
|
49
|
+
await underTest.rollback()
|
|
50
|
+
expect(underTest.isOpen()).toBe(false)
|
|
51
|
+
expect(writer.transaction).toBeCalled()
|
|
52
|
+
expect(transaction.rollback).toBeCalled()
|
|
53
|
+
|
|
54
|
+
await expect(underTest.commit).rejects.toThrowError(
|
|
55
|
+
'Cannot commit, transaction is already closed!'
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('Forwards calls to transaction', async () => {
|
|
60
|
+
const { transaction, underTest } = await testResources({})
|
|
61
|
+
await underTest.select('name')
|
|
62
|
+
expect(transaction.select).toBeCalledWith('name')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('Blocks transaction calls after commit', async () => {
|
|
66
|
+
const { transaction, underTest } = await testResources({})
|
|
67
|
+
expect(underTest.select).toBeDefined()
|
|
68
|
+
|
|
69
|
+
await underTest.commit()
|
|
70
|
+
expect(transaction.commit).toBeCalled()
|
|
71
|
+
expect(underTest.isOpen()).toBe(false)
|
|
72
|
+
|
|
73
|
+
expect(underTest.select).toBeUndefined()
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('KnexTransaction - closeSuccess', () => {
|
|
78
|
+
test('Close success commits', async () => {
|
|
79
|
+
const { transaction, underTest } = await testResources({})
|
|
80
|
+
await underTest.closeSuccess()
|
|
81
|
+
expect(transaction.commit).toBeCalled()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("Close success, doesn't commit again if already committed", async () => {
|
|
85
|
+
const { transaction, underTest } = await testResources({})
|
|
86
|
+
await underTest.commit()
|
|
87
|
+
expect(transaction.commit).toBeCalledTimes(1)
|
|
88
|
+
await underTest.closeSuccess()
|
|
89
|
+
expect(transaction.commit).toBeCalledTimes(1)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('Close success, rolls back if not autocommit', async () => {
|
|
93
|
+
const { transaction, underTest } = await testResources({ autoCommit: false })
|
|
94
|
+
await underTest.closeSuccess()
|
|
95
|
+
expect(transaction.rollback).toBeCalled()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test("Close success, doesn't rollback if already committed", async () => {
|
|
99
|
+
const { transaction, underTest } = await testResources({ autoCommit: false })
|
|
100
|
+
await underTest.commit()
|
|
101
|
+
expect(transaction.commit).toBeCalledTimes(1)
|
|
102
|
+
await underTest.closeSuccess()
|
|
103
|
+
expect(transaction.rollback).not.toBeCalled()
|
|
104
|
+
expect(transaction.commit).toBeCalledTimes(1)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test("Close success, doesn't rollback if already rolled back", async () => {
|
|
108
|
+
const { transaction, underTest } = await testResources({ autoCommit: false })
|
|
109
|
+
await underTest.rollback()
|
|
110
|
+
expect(transaction.rollback).toBeCalledTimes(1)
|
|
111
|
+
await underTest.closeSuccess()
|
|
112
|
+
expect(transaction.rollback).toBeCalledTimes(1)
|
|
113
|
+
expect(transaction.commit).not.toBeCalled()
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe('KnexTransaction - closeFailure', () => {
|
|
118
|
+
test('Close failure rolls back', async () => {
|
|
119
|
+
const { transaction, underTest } = await testResources({})
|
|
120
|
+
await underTest.closeFailure()
|
|
121
|
+
expect(transaction.rollback).toBeCalled()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test("Close failure, doesn't rollback again if already rolled back", async () => {
|
|
125
|
+
const { transaction, underTest } = await testResources({})
|
|
126
|
+
await underTest.rollback()
|
|
127
|
+
expect(transaction.rollback).toBeCalledTimes(1)
|
|
128
|
+
await underTest.closeFailure()
|
|
129
|
+
expect(transaction.rollback).toBeCalledTimes(1)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test("Close failure, doesn't rollback if already committed", async () => {
|
|
133
|
+
const { transaction, underTest } = await testResources({ autoCommit: false })
|
|
134
|
+
await underTest.commit()
|
|
135
|
+
expect(transaction.commit).toBeCalledTimes(1)
|
|
136
|
+
await underTest.closeFailure()
|
|
137
|
+
expect(transaction.rollback).not.toBeCalled()
|
|
138
|
+
expect(transaction.commit).toBeCalledTimes(1)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('KnexTransaction - call direct', () => {
|
|
143
|
+
test('Calls transaction', async () => {
|
|
144
|
+
const { transaction, underTest } = await testResources({ autoCommit: false })
|
|
145
|
+
|
|
146
|
+
underTest('test')
|
|
147
|
+
expect(transaction).toBeCalledWith('test')
|
|
148
|
+
})
|
|
149
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import { PostgresDialect } from 'kysely'
|
|
3
|
+
import * as pg from 'pg'
|
|
4
|
+
|
|
5
|
+
import type { DbConfig } from '../../../../src/Database'
|
|
6
|
+
import { KyselyDatabase } from '../../../../src/Database/integrations/kysely/KyselyDatabase'
|
|
7
|
+
|
|
8
|
+
const config: DbConfig<'kysely'> = {
|
|
9
|
+
type: 'kysely',
|
|
10
|
+
username: 'username',
|
|
11
|
+
password: 'password',
|
|
12
|
+
host: 'host',
|
|
13
|
+
port: 1234,
|
|
14
|
+
database: 'database',
|
|
15
|
+
maxConnections: 1,
|
|
16
|
+
autoCommit: true,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const config2: DbConfig<'kysely'> = {
|
|
20
|
+
...config,
|
|
21
|
+
readReplica: config,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const expectedImplConfig = {
|
|
25
|
+
host: config.host,
|
|
26
|
+
port: config.port,
|
|
27
|
+
user: config.username,
|
|
28
|
+
password: config.password,
|
|
29
|
+
database: config.database,
|
|
30
|
+
max: config.maxConnections,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('KyselyDatabase', () => {
|
|
34
|
+
let mockTrans
|
|
35
|
+
let mockClient
|
|
36
|
+
let mockDriver
|
|
37
|
+
let mockDialect
|
|
38
|
+
let mockPgTrans
|
|
39
|
+
let mockPgClient
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
mockTrans = {
|
|
42
|
+
execute: jest.fn(async (cb: any) => {
|
|
43
|
+
cb(mockTrans)
|
|
44
|
+
}),
|
|
45
|
+
} as any
|
|
46
|
+
mockClient = jest.fn(
|
|
47
|
+
() =>
|
|
48
|
+
({
|
|
49
|
+
transaction: () => mockTrans,
|
|
50
|
+
}) as any as PostgresDialect
|
|
51
|
+
)
|
|
52
|
+
KyselyDatabase['kyselyProvider'] = mockClient as any
|
|
53
|
+
|
|
54
|
+
mockDriver = {
|
|
55
|
+
execute: jest.fn(async () => {}),
|
|
56
|
+
} as any
|
|
57
|
+
mockDialect = jest.fn(
|
|
58
|
+
() =>
|
|
59
|
+
({
|
|
60
|
+
createDriver: () => mockDriver,
|
|
61
|
+
}) as any as PostgresDialect
|
|
62
|
+
)
|
|
63
|
+
KyselyDatabase['kyselyPgProvider'] = mockDialect as any
|
|
64
|
+
|
|
65
|
+
mockPgTrans = {
|
|
66
|
+
query: jest.fn(() => {}),
|
|
67
|
+
} as any
|
|
68
|
+
mockPgClient = jest.fn(
|
|
69
|
+
() =>
|
|
70
|
+
({
|
|
71
|
+
connect: async () => mockPgTrans,
|
|
72
|
+
}) as pg.PoolClient
|
|
73
|
+
)
|
|
74
|
+
KyselyDatabase['pgProvider'] = mockPgClient as any
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('KyselyDatabase', async () => {
|
|
78
|
+
const underTest = new KyselyDatabase(config)
|
|
79
|
+
expect(mockPgClient).toHaveBeenNthCalledWith(1, expectedImplConfig)
|
|
80
|
+
expect(mockDialect).toHaveBeenCalledTimes(1)
|
|
81
|
+
expect(mockClient).toHaveBeenCalledTimes(1)
|
|
82
|
+
expect(mockClient.mock.calls[0][0].dialect).toEqual(underTest['pgClient'])
|
|
83
|
+
expect(mockClient.mock.calls[0][0].plugins).toBeInstanceOf(Array)
|
|
84
|
+
const trans = await underTest.transaction()
|
|
85
|
+
|
|
86
|
+
expect(mockTrans.execute).toHaveBeenCalledTimes(1)
|
|
87
|
+
expect(trans.reader).toBeUndefined()
|
|
88
|
+
expect(trans.writer).toBeInstanceOf(Object)
|
|
89
|
+
expect(trans.writer.transaction).toBeInstanceOf(Function)
|
|
90
|
+
expect(trans).toBeInstanceOf(Function)
|
|
91
|
+
expect(trans['transaction']).toBe(mockTrans)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('KyselyDatabase - read replica', async () => {
|
|
95
|
+
const underTest = new KyselyDatabase(config2)
|
|
96
|
+
expect(mockPgClient).toHaveBeenNthCalledWith(2, expectedImplConfig)
|
|
97
|
+
expect(mockDialect).toHaveBeenCalledTimes(2)
|
|
98
|
+
expect(mockClient).toHaveBeenCalledTimes(2)
|
|
99
|
+
expect(mockClient.mock.calls[0][0].dialect).toEqual(underTest['pgClient'])
|
|
100
|
+
expect(mockClient.mock.calls[0][0].plugins).toBeInstanceOf(Array)
|
|
101
|
+
expect(mockClient.mock.calls[1][0].dialect).toEqual(underTest['pgReadClient'])
|
|
102
|
+
expect(mockClient.mock.calls[1][0].plugins).toBeInstanceOf(Array)
|
|
103
|
+
|
|
104
|
+
const trans = await underTest.transaction()
|
|
105
|
+
|
|
106
|
+
expect(mockTrans.execute).toHaveBeenCalledTimes(1)
|
|
107
|
+
expect(trans.writer).toBeInstanceOf(Object)
|
|
108
|
+
expect(trans.writer.transaction).toBeInstanceOf(Function)
|
|
109
|
+
expect(trans.reader).toBeInstanceOf(Object)
|
|
110
|
+
expect(trans).toBeInstanceOf(Function)
|
|
111
|
+
expect(trans['transaction']).toBe(mockTrans)
|
|
112
|
+
})
|
|
113
|
+
})
|