Skip to content

Commit 5035a9b

Browse files
lengyel-arpad85njlie
authored andcommitted
feat(pos): merchants services (#3528)
* merchant service * jest setup * tests
1 parent 55b86b2 commit 5035a9b

File tree

14 files changed

+372
-3
lines changed

14 files changed

+372
-3
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict'
2+
// eslint-disable-next-line @typescript-eslint/no-var-requires
3+
const baseConfig = require('../../jest.config.base.js')
4+
// eslint-disable-next-line @typescript-eslint/no-var-requires
5+
const packageName = require('./package.json').name
6+
7+
module.exports = {
8+
...baseConfig,
9+
clearMocks: true,
10+
testTimeout: 30000,
11+
roots: [`<rootDir>/packages/${packageName}`],
12+
setupFiles: [`<rootDir>/packages/${packageName}/jest.env.js`],
13+
globalSetup: `<rootDir>/packages/${packageName}/jest.setup.ts`,
14+
globalTeardown: `<rootDir>/packages/${packageName}/jest.teardown.js`,
15+
testRegex: `(packages/${packageName}/.*/__tests__/.*|\\.(test|spec))\\.tsx?$`,
16+
testEnvironment: `<rootDir>/packages/${packageName}/jest.custom-environment.ts`,
17+
moduleDirectories: [
18+
`node_modules`,
19+
`packages/${packageName}/node_modules`,
20+
`<rootDir>/node_modules`
21+
],
22+
modulePaths: [
23+
`node_modules`,
24+
`<rootDir>/packages/${packageName}/src/`,
25+
`<rootDir>/node_modules`
26+
],
27+
id: packageName,
28+
displayName: packageName,
29+
rootDir: '../..'
30+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { TestEnvironment } from 'jest-environment-node'
2+
import nock from 'nock'
3+
4+
export default class CustomEnvironment extends TestEnvironment {
5+
constructor(config, context) {
6+
super(config, context)
7+
this.global.nock = nock
8+
}
9+
}

packages/point-of-sale/jest.env.js

Whitespace-only changes.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { knex } from 'knex'
2+
import { GenericContainer, Wait } from 'testcontainers'
3+
require('./jest.env') // set environment variables
4+
5+
const POSTGRES_PORT = 5432
6+
7+
const setup = async (globalConfig): Promise<void> => {
8+
const workers = globalConfig.maxWorkers
9+
10+
const setupDatabase = async () => {
11+
if (!process.env.DATABASE_URL) {
12+
const postgresContainer = await new GenericContainer('postgres:15')
13+
.withExposedPorts(POSTGRES_PORT)
14+
.withBindMounts([
15+
{
16+
source: __dirname + '/scripts/init.sh',
17+
target: '/docker-entrypoint-initdb.d/init.sh'
18+
}
19+
])
20+
.withEnvironment({
21+
POSTGRES_PASSWORD: 'password'
22+
})
23+
.withHealthCheck({
24+
test: ['CMD-SHELL', 'pg_isready -d testing'],
25+
interval: 10000,
26+
timeout: 5000,
27+
retries: 5
28+
})
29+
.withWaitStrategy(Wait.forHealthCheck())
30+
.start()
31+
32+
process.env.DATABASE_URL = `postgresql://postgres:password@localhost:${postgresContainer.getMappedPort(
33+
POSTGRES_PORT
34+
)}/testing`
35+
36+
global.__POS_POSTGRES__ = postgresContainer
37+
}
38+
39+
const db = knex({
40+
client: 'postgresql',
41+
connection: process.env.DATABASE_URL,
42+
pool: {
43+
min: 2,
44+
max: 10
45+
},
46+
migrations: {
47+
tableName: 'pos_knex_migrations'
48+
}
49+
})
50+
51+
// node pg defaults to returning bigint as string. This ensures it parses to bigint
52+
db.client.driver.types.setTypeParser(
53+
db.client.driver.types.builtins.INT8,
54+
'text',
55+
BigInt
56+
)
57+
await db.migrate.latest({
58+
directory: __dirname + '/migrations'
59+
})
60+
61+
for (let i = 1; i <= workers; i++) {
62+
const workerDatabaseName = `testing_${i}`
63+
64+
await db.raw(`DROP DATABASE IF EXISTS ${workerDatabaseName}`)
65+
await db.raw(`CREATE DATABASE ${workerDatabaseName} TEMPLATE testing`)
66+
}
67+
68+
global.__POS_KNEX__ = db
69+
}
70+
71+
await Promise.all([setupDatabase()])
72+
}
73+
74+
export default setup
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = async () => {
2+
await global.__POS_KNEX__.migrate.rollback(
3+
{ directory: __dirname + '/migrations' },
4+
true
5+
)
6+
await global.__POS_KNEX__.destroy()
7+
if (global.__POS_POSTGRES__) {
8+
await global.__POS_POSTGRES__.stop()
9+
}
10+
}

packages/point-of-sale/package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44
"description": "",
55
"main": "index.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1",
7+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests --maxWorkers=50%",
8+
"test:ci": "NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests --maxWorkers=2",
9+
"test:cov": "pnpm test -- --coverage",
10+
"test:sincemain": "pnpm test -- --changedSince=main",
11+
"test:sincemain:cov": "pnpm test:sincemain --coverage",
812
"knex": "knex",
913
"dev": "ts-node-dev --inspect=0.0.0.0:9229 --respawn --transpile-only src/index.ts",
1014
"build": "pnpm build:deps && pnpm clean && tsc --build tsconfig.json",
@@ -35,6 +39,12 @@
3539
"@types/koa-bodyparser": "^4.3.12",
3640
"@types/koa__cors": "^5.0.0",
3741
"@types/koa__router": "^12.0.4",
38-
"@types/uuid": "^9.0.8"
42+
"@types/uuid": "^9.0.8",
43+
"nock": "14.0.0-beta.19",
44+
"jest-environment-node": "^29.7.0",
45+
"jest-openapi": "^0.14.2",
46+
"testcontainers": "^10.16.0",
47+
"tmp": "^0.2.3",
48+
"@types/tmp": "^0.2.6"
3949
}
4050
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/bin/bash
2+
set -e
3+
4+
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
5+
DROP DATABASE IF EXISTS TESTING;
6+
CREATE DATABASE testing;
7+
CREATE DATABASE development;
8+
EOSQL

packages/point-of-sale/src/config/app.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ export const Config = {
3434
env: envString('NODE_ENV', 'development'),
3535
port: envInt('PORT', 3008),
3636
trustProxy: envBool('TRUST_PROXY', false),
37-
enableManualMigrations: envBool('ENABLE_MANUAl_MIGRATIONS', false)
37+
enableManualMigrations: envBool('ENABLE_MANUAl_MIGRATIONS', false),
38+
dbSchema: undefined as string | undefined
3839
}

packages/point-of-sale/src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Model } from 'objection'
44
import { Config } from './config/app'
55
import { App, AppServices } from './app'
66
import createLogger from 'pino'
7+
import { createMerchantService } from './merchant/service'
78

89
export function initIocContainer(
910
config: typeof Config
@@ -55,6 +56,15 @@ export function initIocContainer(
5556
)
5657
return db
5758
})
59+
60+
container.singleton('merchantService', async (deps) => {
61+
const [logger, knex] = await Promise.all([
62+
deps.use('logger'),
63+
deps.use('knex')
64+
])
65+
return createMerchantService({ logger, knex })
66+
})
67+
5868
return container
5969
}
6070

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { IocContract } from '@adonisjs/fold'
2+
3+
import { Merchant } from './model'
4+
import { MerchantService } from './service'
5+
6+
import { createTestApp, TestContainer } from '../tests/app'
7+
import { truncateTables } from '../tests/tableManager'
8+
import { Config } from '../config/app'
9+
10+
import { initIocContainer } from '../'
11+
import { AppServices } from '../app'
12+
13+
describe('Merchant Service', (): void => {
14+
let deps: IocContract<AppServices>
15+
let appContainer: TestContainer
16+
let merchantService: MerchantService
17+
18+
beforeAll(async (): Promise<void> => {
19+
deps = initIocContainer({
20+
...Config
21+
})
22+
23+
appContainer = await createTestApp(deps)
24+
merchantService = await deps.use('merchantService')
25+
})
26+
27+
afterEach(async (): Promise<void> => {
28+
jest.useRealTimers()
29+
await truncateTables(deps)
30+
})
31+
32+
afterAll(async (): Promise<void> => {
33+
await appContainer.shutdown()
34+
})
35+
36+
describe('create', (): void => {
37+
test('creates a merchant', async (): Promise<void> => {
38+
const merchant = await merchantService.create('Test merchant')
39+
expect(merchant).toEqual({ id: merchant.id, name: 'Test merchant' })
40+
})
41+
})
42+
43+
describe('delete', (): void => {
44+
test('soft deletes an existing merchant', async (): Promise<void> => {
45+
const created = await merchantService.create('Test merchant')
46+
47+
const result = await merchantService.delete(created.id)
48+
expect(result).toBe(true)
49+
50+
const deletedMerchant = await Merchant.query()
51+
.findById(created.id)
52+
.whereNotNull('deletedAt')
53+
expect(deletedMerchant).toBeDefined()
54+
expect(deletedMerchant?.deletedAt).toBeDefined()
55+
})
56+
57+
test('returns false for already deleted merchant', async (): Promise<void> => {
58+
const created = await merchantService.create('Test merchant')
59+
60+
await merchantService.delete(created.id)
61+
const secondDelete = await merchantService.delete(created.id)
62+
expect(secondDelete).toBe(false)
63+
})
64+
})
65+
})

0 commit comments

Comments
 (0)