@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.
Files changed (114) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +60 -0
  3. package/.github/workflows/npmpublish.yml +11 -0
  4. package/.github/workflows/prs.yml +13 -0
  5. package/dist/package-lock.json +2 -2
  6. package/dist/package.json +1 -1
  7. package/jest.config.ts +33 -0
  8. package/jest.smoke.config.ts +35 -0
  9. package/package.json +1 -1
  10. package/tests/API/Request.test.ts +273 -0
  11. package/tests/API/Response.test.ts +367 -0
  12. package/tests/API/Utils.test.ts +167 -0
  13. package/tests/BaseEvent/EventProcessor.test.ts +261 -0
  14. package/tests/BaseEvent/Process.test.ts +49 -0
  15. package/tests/BaseEvent/Transaction.test.ts +408 -0
  16. package/tests/Cache/Redis-client.test.ts +90 -0
  17. package/tests/Cache/Redis-cluster.test.ts +100 -0
  18. package/tests/Config/Config.test.ts +205 -0
  19. package/tests/Config/EnvironmentVar.test.ts +250 -0
  20. package/tests/Crypto/Crypto.test.ts +88 -0
  21. package/tests/Crypto/JWT.test.ts +92 -0
  22. package/tests/Database/DatabaseManager.test.ts +71 -0
  23. package/tests/Database/integrations/knex/KnexDatabase.test.ts +76 -0
  24. package/tests/Database/integrations/knex/KnexTransaction.test.ts +149 -0
  25. package/tests/Database/integrations/kysely/KyselyDatabase.test.ts +113 -0
  26. package/tests/Database/integrations/kysely/KyselyTransaction.test.ts +119 -0
  27. package/tests/Database/integrations/pg/PostgresDatabase.test.ts +76 -0
  28. package/tests/Database/integrations/pg/PostgresTransaction.test.ts +118 -0
  29. package/tests/Logger/Logger.test.ts +219 -0
  30. package/tests/Mailer/Mailer.test.ts +59 -0
  31. package/tests/Publisher/Publisher.test.ts +94 -0
  32. package/tests/Server/RouteResolver.test.ts +102 -0
  33. package/tests/Server/Router.test.ts +39 -0
  34. package/tests/Server/lib/ContainerServer.test.ts +531 -0
  35. package/tests/Server/lib/Server.test.ts +12 -0
  36. package/tests/Server/lib/container/GenericHandler.test.ts +131 -0
  37. package/tests/Server/lib/container/GenericHandlerEvent.test.ts +103 -0
  38. package/tests/Server/lib/container/HealthHandler.test.ts +30 -0
  39. package/tests/Server/lib/container/Proxy.test.ts +268 -0
  40. package/tests/Server/lib/container/Utils.test.ts +47 -0
  41. package/tests/Test.utils.ts +74 -0
  42. package/tests/Validation/Validator.test.ts +76 -0
  43. package/tsconfig.json +26 -0
  44. package/tsconfig.smoke.json +26 -0
  45. package/coverage/clover.xml +0 -1088
  46. package/coverage/coverage/coverage.txt +0 -40
  47. package/coverage/coverage-final.json +0 -37
  48. package/coverage/coverage-summary.json +0 -38
  49. package/coverage/coverage.txt +0 -59
  50. package/coverage/lcov-report/base.css +0 -224
  51. package/coverage/lcov-report/block-navigation.js +0 -87
  52. package/coverage/lcov-report/favicon.png +0 -0
  53. package/coverage/lcov-report/index.html +0 -371
  54. package/coverage/lcov-report/prettify.css +0 -1
  55. package/coverage/lcov-report/prettify.js +0 -2
  56. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  57. package/coverage/lcov-report/sorter.js +0 -196
  58. package/coverage/lcov-report/src/API/Request.ts.html +0 -727
  59. package/coverage/lcov-report/src/API/Response.ts.html +0 -1189
  60. package/coverage/lcov-report/src/API/Utils.ts.html +0 -313
  61. package/coverage/lcov-report/src/API/index.html +0 -131
  62. package/coverage/lcov-report/src/BaseEvent/EventProcessor.ts.html +0 -496
  63. package/coverage/lcov-report/src/BaseEvent/Process.ts.html +0 -346
  64. package/coverage/lcov-report/src/BaseEvent/Transaction.ts.html +0 -1015
  65. package/coverage/lcov-report/src/BaseEvent/index.html +0 -146
  66. package/coverage/lcov-report/src/Cache/Redis.ts.html +0 -367
  67. package/coverage/lcov-report/src/Cache/index.html +0 -116
  68. package/coverage/lcov-report/src/Config/Configuration.ts.html +0 -700
  69. package/coverage/lcov-report/src/Config/EnvironmentVar.ts.html +0 -526
  70. package/coverage/lcov-report/src/Config/index.html +0 -131
  71. package/coverage/lcov-report/src/Crypto/Crypto.ts.html +0 -352
  72. package/coverage/lcov-report/src/Crypto/JWT.ts.html +0 -337
  73. package/coverage/lcov-report/src/Crypto/index.html +0 -131
  74. package/coverage/lcov-report/src/Database/Database.ts.html +0 -151
  75. package/coverage/lcov-report/src/Database/DatabaseManager.ts.html +0 -289
  76. package/coverage/lcov-report/src/Database/DatabaseTransaction.ts.html +0 -595
  77. package/coverage/lcov-report/src/Database/index.html +0 -161
  78. package/coverage/lcov-report/src/Database/index.ts.html +0 -169
  79. package/coverage/lcov-report/src/Database/integrations/knex/KnexDatabase.ts.html +0 -283
  80. package/coverage/lcov-report/src/Database/integrations/knex/KnexTransaction.ts.html +0 -337
  81. package/coverage/lcov-report/src/Database/integrations/knex/index.html +0 -131
  82. package/coverage/lcov-report/src/Database/integrations/kysely/KyselyDatabase.ts.html +0 -376
  83. package/coverage/lcov-report/src/Database/integrations/kysely/KyselyTransaction.ts.html +0 -601
  84. package/coverage/lcov-report/src/Database/integrations/kysely/index.html +0 -131
  85. package/coverage/lcov-report/src/Database/integrations/pgsql/PostgresDatabase.ts.html +0 -250
  86. package/coverage/lcov-report/src/Database/integrations/pgsql/PostgresTransaction.ts.html +0 -346
  87. package/coverage/lcov-report/src/Database/integrations/pgsql/index.html +0 -131
  88. package/coverage/lcov-report/src/Database/types.d.ts.html +0 -232
  89. package/coverage/lcov-report/src/Globals.ts.html +0 -394
  90. package/coverage/lcov-report/src/Logger/Logger.ts.html +0 -1138
  91. package/coverage/lcov-report/src/Logger/index.html +0 -116
  92. package/coverage/lcov-report/src/Mailer/Mailer.ts.html +0 -754
  93. package/coverage/lcov-report/src/Mailer/index.html +0 -116
  94. package/coverage/lcov-report/src/Publisher/Publisher.ts.html +0 -460
  95. package/coverage/lcov-report/src/Publisher/index.html +0 -116
  96. package/coverage/lcov-report/src/Server/RouteResolver.ts.html +0 -442
  97. package/coverage/lcov-report/src/Server/Router.ts.html +0 -616
  98. package/coverage/lcov-report/src/Server/index.html +0 -131
  99. package/coverage/lcov-report/src/Server/lib/ContainerServer.ts.html +0 -280
  100. package/coverage/lcov-report/src/Server/lib/Server.ts.html +0 -403
  101. package/coverage/lcov-report/src/Server/lib/container/GenericHandler.ts.html +0 -331
  102. package/coverage/lcov-report/src/Server/lib/container/GenericHandlerEvent.ts.html +0 -547
  103. package/coverage/lcov-report/src/Server/lib/container/HealthHandler.ts.html +0 -118
  104. package/coverage/lcov-report/src/Server/lib/container/Proxy.ts.html +0 -619
  105. package/coverage/lcov-report/src/Server/lib/container/Utils.ts.html +0 -184
  106. package/coverage/lcov-report/src/Server/lib/container/index.html +0 -176
  107. package/coverage/lcov-report/src/Server/lib/index.html +0 -131
  108. package/coverage/lcov-report/src/Util/AsyncSingleton.ts.html +0 -343
  109. package/coverage/lcov-report/src/Util/Utils.ts.html +0 -313
  110. package/coverage/lcov-report/src/Util/index.html +0 -131
  111. package/coverage/lcov-report/src/Validation/Validator.ts.html +0 -217
  112. package/coverage/lcov-report/src/Validation/index.html +0 -116
  113. package/coverage/lcov-report/src/index.html +0 -116
  114. 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
+ })