mirror of
https://github.com/Cameri/nostream.git
synced 2025-05-03 06:30:15 +02:00
feat: add integration tests w/ docker
This commit is contained in:
parent
82225c47b1
commit
851693a966
39
.github/workflows/checks.yml
vendored
39
.github/workflows/checks.yml
vendored
@ -27,7 +27,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:18-alpine3.16
|
||||
needs: [lint]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
@ -38,12 +37,11 @@ jobs:
|
||||
run: npm ci
|
||||
- name: Run ESLint
|
||||
run: npm run build
|
||||
test:
|
||||
name: Tests
|
||||
test-and-cover:
|
||||
name: Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:18-alpine3.16
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
@ -52,23 +50,9 @@ jobs:
|
||||
cache: npm
|
||||
- name: Install package dependencies
|
||||
run: npm ci
|
||||
- name: Run tests
|
||||
- name: Run unit tests
|
||||
run: npm run test:unit
|
||||
coverage:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:18-alpine3.16
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Install package dependencies
|
||||
run: npm ci
|
||||
- name: Run coverage
|
||||
- name: Run coverage for unit tests
|
||||
run: npm run cover
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
@ -76,3 +60,18 @@ jobs:
|
||||
path-to-lcov: ./.coverage/lcov.info
|
||||
flag-name: Unit
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
test-integration:
|
||||
name: Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:18-alpine3.16
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
cache: npm
|
||||
- name: Install package dependencies
|
||||
run: npm ci
|
||||
- name: Run integration tests
|
||||
run: npm run docker:test:integration
|
||||
|
@ -1,10 +1,10 @@
|
||||
const config = [
|
||||
'test/integration/features/**/*.feature',
|
||||
'--require-module ts-node/register',
|
||||
'--require tests/integration/features/**/*.ts',
|
||||
'--format progress-bar',
|
||||
'--format json:report.json',
|
||||
'--publish-quiet',
|
||||
'--require test/integration/features/**/*.ts',
|
||||
'--require test/integration/features/*.ts',
|
||||
'--format @cucumber/pretty-formatter',
|
||||
'--publish',
|
||||
].join(' ')
|
||||
|
||||
module.exports = {
|
||||
|
@ -8,6 +8,8 @@ services:
|
||||
DB_USER: nostr_ts_relay
|
||||
DB_PASSWORD: nostr_ts_relay
|
||||
DB_NAME: nostr_ts_relay
|
||||
DB_MIN_POOL_SIZE: 1
|
||||
DB_MAX_POOL_SIZE: 2
|
||||
NOSTR_CONFIG_DIR: /home/node/
|
||||
user: node:node
|
||||
volumes:
|
||||
|
@ -7,7 +7,6 @@ module.exports = {
|
||||
password: process.env.DB_PASSWORD ?? 'postgres',
|
||||
database: process.env.DB_NAME ?? 'nostr-ts-relay',
|
||||
},
|
||||
pool: { min: 4, max: 16 },
|
||||
seeds: {
|
||||
directory: './seeds',
|
||||
},
|
||||
|
64
package-lock.json
generated
64
package-lock.json
generated
@ -19,6 +19,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cucumber/cucumber": "8.7.0",
|
||||
"@cucumber/pretty-formatter": "1.0.0",
|
||||
"@types/chai": "^4.3.1",
|
||||
"@types/chai-as-promised": "^7.1.5",
|
||||
"@types/mocha": "^9.1.1",
|
||||
@ -674,6 +675,34 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@cucumber/pretty-formatter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@cucumber/pretty-formatter/-/pretty-formatter-1.0.0.tgz",
|
||||
"integrity": "sha512-wcnIMN94HyaHGsfq72dgCvr1d8q6VGH4Y6Gl5weJ2TNZw1qn2UY85Iki4c9VdaLUONYnyYH3+178YB+9RFe/Hw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^5.0.0",
|
||||
"cli-table3": "^0.6.0",
|
||||
"figures": "^3.2.0",
|
||||
"ts-dedent": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cucumber/cucumber": ">=7.0.0",
|
||||
"@cucumber/messages": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@cucumber/pretty-formatter/node_modules/ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@cucumber/tag-expressions": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-4.1.0.tgz",
|
||||
@ -5104,6 +5133,15 @@
|
||||
"tree-kill": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-dedent": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
|
||||
"integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.10"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
@ -6136,6 +6174,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@cucumber/pretty-formatter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@cucumber/pretty-formatter/-/pretty-formatter-1.0.0.tgz",
|
||||
"integrity": "sha512-wcnIMN94HyaHGsfq72dgCvr1d8q6VGH4Y6Gl5weJ2TNZw1qn2UY85Iki4c9VdaLUONYnyYH3+178YB+9RFe/Hw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^5.0.0",
|
||||
"cli-table3": "^0.6.0",
|
||||
"figures": "^3.2.0",
|
||||
"ts-dedent": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@cucumber/tag-expressions": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-4.1.0.tgz",
|
||||
@ -9499,6 +9557,12 @@
|
||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-dedent": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
|
||||
"integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
|
@ -16,7 +16,7 @@
|
||||
],
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"dev": "ts-node src/index.ts",
|
||||
"dev": "node -r ts-node/register src/index.ts",
|
||||
"clean": "rimraf ./dist",
|
||||
"build": "tsc --project tsconfig.build.json",
|
||||
"prestart": "npm run build",
|
||||
@ -35,7 +35,8 @@
|
||||
"predocker:compose:up": "[ -d \"$HOME/.nostr\" ] || mkdir -p $HOME/.nostr",
|
||||
"docker:compose:up": "docker compose up --build",
|
||||
"docker:compose:down": "docker compose down",
|
||||
"docker:compose:rm": "docker compose rm"
|
||||
"docker:compose:rm": "docker compose rm",
|
||||
"docker:test:integration": "docker compose -f ./test/integration/docker-compose.yml up tests --build --exit-code-from tests"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -53,6 +54,7 @@
|
||||
"homepage": "https://github.com/Cameri/nostr-ts-relay#readme",
|
||||
"devDependencies": {
|
||||
"@cucumber/cucumber": "8.7.0",
|
||||
"@cucumber/pretty-formatter": "1.0.0",
|
||||
"@types/chai": "^4.3.1",
|
||||
"@types/chai-as-promised": "^7.1.5",
|
||||
"@types/mocha": "^9.1.1",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import cluster from 'cluster'
|
||||
import { EventEmitter } from 'stream'
|
||||
import { IncomingMessage as IncomingHttpMessage } from 'http'
|
||||
import { WebSocket } from 'ws'
|
||||
@ -60,10 +61,12 @@ export class WebSocketAdapter extends EventEmitter implements IWebSocketAdapter
|
||||
|
||||
public onBroadcast(event: Event): void {
|
||||
this.webSocketServer.emit(WebSocketServerAdapterEvent.Broadcast, event)
|
||||
process.send({
|
||||
eventName: WebSocketServerAdapterEvent.Broadcast,
|
||||
event,
|
||||
})
|
||||
if (cluster.isWorker) {
|
||||
process.send({
|
||||
eventName: WebSocketServerAdapterEvent.Broadcast,
|
||||
event,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public onSendEvent(event: Event): void {
|
||||
|
@ -44,6 +44,7 @@ export class WebSocketServerAdapter extends WebServerAdapter implements IWebSock
|
||||
}
|
||||
|
||||
public close(callback: () => void): void {
|
||||
this.onClose()
|
||||
this.webSocketServer.close(() => {
|
||||
this.webServer.close(callback)
|
||||
})
|
||||
|
@ -6,7 +6,7 @@ export class AppWorker implements IRunnable {
|
||||
private readonly process: NodeJS.Process,
|
||||
private readonly adapter: IWebSocketServerAdapter
|
||||
) {
|
||||
process
|
||||
this.process
|
||||
.on('message', this.onMessage.bind(this))
|
||||
.on('SIGINT', this.onExit.bind(this))
|
||||
.on('SIGHUP', this.onExit.bind(this))
|
||||
@ -34,11 +34,12 @@ export class AppWorker implements IRunnable {
|
||||
|
||||
private onExit() {
|
||||
console.log(`worker ${process.pid} - exiting`)
|
||||
this.adapter.close(() => {
|
||||
// dbClient.destroy(() => {
|
||||
// process.exit(0)
|
||||
// })
|
||||
process.exit(0)
|
||||
this.close(() => {
|
||||
this.process.exit(0)
|
||||
})
|
||||
}
|
||||
|
||||
public close(callback?: () => void) {
|
||||
this.adapter.close(callback)
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { getDbClient } from '../database/client'
|
||||
import { webSocketAdapterFactory } from './websocket-adapter-factory'
|
||||
import { WebSocketServerAdapter } from '../adapters/web-socket-server-adapter'
|
||||
|
||||
export const workerFactory = () => {
|
||||
export const workerFactory = (): AppWorker => {
|
||||
const dbClient = getDbClient()
|
||||
const eventRepository = new EventRepository(dbClient)
|
||||
|
||||
|
@ -90,7 +90,7 @@ export class EventRepository implements IEventRepository {
|
||||
)
|
||||
)
|
||||
),
|
||||
}),
|
||||
} as any),
|
||||
),
|
||||
],
|
||||
])(currentFilter[filterName] as string[])
|
||||
|
64
test/integration/docker-compose.yml
Normal file
64
test/integration/docker-compose.yml
Normal file
@ -0,0 +1,64 @@
|
||||
services:
|
||||
tests:
|
||||
build:
|
||||
context: ../../
|
||||
dockerfile: Dockerfile.test
|
||||
env_file:
|
||||
- ../../test.env
|
||||
environment:
|
||||
NOSTR_CONFIG_DIR: /code
|
||||
volumes:
|
||||
- ../../src:/code/src
|
||||
- ../../test:/code/test
|
||||
working_dir: /code
|
||||
ports:
|
||||
- "8008:8008"
|
||||
command:
|
||||
["sh", "-c", "whoami && pwd && ls -hall test/integration && npm run test:integration"]
|
||||
depends_on:
|
||||
db-test:
|
||||
condition: service_healthy
|
||||
migrations-test:
|
||||
condition: service_completed_successfully
|
||||
networks:
|
||||
- nostr-ts-relay-test
|
||||
links:
|
||||
- db-test
|
||||
db-test:
|
||||
image: postgres
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: nostr_ts_relay_test
|
||||
networks:
|
||||
- nostr-ts-relay-test
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
timeout: 5s
|
||||
start_period: 5s
|
||||
retries: 0
|
||||
migrations-test:
|
||||
image: node:18-alpine3.16
|
||||
environment:
|
||||
DB_HOST: db-test
|
||||
DB_PORT: 5432
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_NAME: nostr_ts_relay_test
|
||||
entrypoint:
|
||||
- sh
|
||||
- -c
|
||||
- 'cd code && npm install -g knex@2.3.0 && npm install knex --quiet && npm run db:migrate'
|
||||
volumes:
|
||||
- ../../package.json:/code/package.json
|
||||
- ../../migrations:/code/migrations
|
||||
- ../../knexfile.js:/code/knexfile.js
|
||||
depends_on:
|
||||
- db-test
|
||||
networks:
|
||||
- nostr-ts-relay-test
|
||||
links:
|
||||
- db-test
|
||||
|
||||
networks:
|
||||
nostr-ts-relay-test:
|
6
test/integration/features/nip-01/nip-01.feature
Normal file
6
test/integration/features/nip-01/nip-01.feature
Normal file
@ -0,0 +1,6 @@
|
||||
Feature: NIP-01
|
||||
Scenario: Alice posts set_metadata event
|
||||
Given I am Alice
|
||||
And I subscribe to author Alice
|
||||
When I send a set_metadata event as Alice
|
||||
Then I receive a set_metadata event from Alice
|
201
test/integration/features/nip-01/nip-01.feature-step.ts
Normal file
201
test/integration/features/nip-01/nip-01.feature-step.ts
Normal file
@ -0,0 +1,201 @@
|
||||
import * as secp256k1 from '@noble/secp256k1'
|
||||
import {
|
||||
After,
|
||||
Before,
|
||||
Given,
|
||||
Then,
|
||||
When,
|
||||
World,
|
||||
} from '@cucumber/cucumber'
|
||||
import { RawData, WebSocket } from 'ws'
|
||||
import chai from 'chai'
|
||||
import { createHmac } from 'crypto'
|
||||
import sinonChai from 'sinon-chai'
|
||||
|
||||
import { Event } from '../../../../src/@types/event'
|
||||
import { MessageType } from '../../../../src/@types/messages'
|
||||
import { serializeEvent } from '../../../../src/utils/event'
|
||||
import { SubscriptionFilter } from '../../../../src/@types/subscription'
|
||||
|
||||
chai.use(sinonChai)
|
||||
const { expect } = chai
|
||||
|
||||
Before(async function () {
|
||||
const ws = new WebSocket('ws://localhost:8008')
|
||||
this.parameters.ws = ws
|
||||
await new Promise((resolve, reject) => {
|
||||
ws
|
||||
.once('open', resolve)
|
||||
.once('error', reject)
|
||||
})
|
||||
})
|
||||
|
||||
After(function () {
|
||||
const ws = this.parameters.ws as WebSocket
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.close()
|
||||
}
|
||||
})
|
||||
|
||||
Given(/I am (\w+)/, function(name: string) {
|
||||
this.parameters.authors = this.parameters.authors ?? {}
|
||||
this.parameters.authors[name] = this.parameters.authors[name] ?? createIdentity(name)
|
||||
})
|
||||
|
||||
When(/I subscribe to author (\w+)/, async function(this: World<Record<string, any>>, name: string) {
|
||||
const ws = this.parameters.ws as WebSocket
|
||||
const pubkey = this.parameters.authors[name].pubkey
|
||||
this.parameters.subscriptions = this.parameters.subscriptions ?? []
|
||||
const subscription = { name: `test-${Math.random()}`, filters: [{ authors: [pubkey] }] }
|
||||
this.parameters.subscriptions.push(subscription)
|
||||
|
||||
await createSubscription(ws, subscription.name, subscription.filters)
|
||||
|
||||
await waitForEOSE(ws, subscription.name)
|
||||
})
|
||||
|
||||
When(/I send a set_metadata event as (\w+)/, async function(name: string) {
|
||||
const ws = this.parameters.ws as WebSocket
|
||||
const { pubkey, privkey } = this.parameters.authors[name]
|
||||
|
||||
const content = JSON.stringify({ name })
|
||||
const event: Event = await createEvent({ pubkey, kind: 0, content }, privkey)
|
||||
|
||||
await sendEvent(ws, event)
|
||||
|
||||
this.parameters.events = this.parameters.events ?? []
|
||||
this.parameters.events.push(event)
|
||||
})
|
||||
|
||||
Then(/I receive a set_metadata event from (\w+)/, async function(author: string) {
|
||||
const expectedEvent = this.parameters.events.pop()
|
||||
const subscription = this.parameters.subscriptions[this.parameters.subscriptions.length - 1]
|
||||
const receivedEvent = await waitForNextEvent(this.parameters.ws, subscription.name)
|
||||
expect(receivedEvent.pubkey).to.equal(this.parameters.authors[author].pubkey)
|
||||
expect(receivedEvent).to.deep.equal(expectedEvent)
|
||||
})
|
||||
|
||||
async function createEvent(input: Partial<Event>, privkey: any): Promise<Event> {
|
||||
const event: Event = {
|
||||
pubkey: input.pubkey,
|
||||
kind: input.kind,
|
||||
created_at: input.created_at ?? Math.floor(Date.now() / 1000),
|
||||
content: input.content ?? '',
|
||||
tags: input.tags ?? [],
|
||||
} as any
|
||||
|
||||
const id = Buffer.from(
|
||||
await secp256k1.utils.sha256(
|
||||
Buffer.from(JSON.stringify(serializeEvent(event)))
|
||||
)
|
||||
).toString('hex')
|
||||
|
||||
const sig = Buffer.from(
|
||||
await secp256k1.schnorr.sign(id, privkey)
|
||||
).toString('hex')
|
||||
|
||||
return { id, ...event, sig }
|
||||
}
|
||||
|
||||
function createIdentity(name: string) {
|
||||
const hmac = createHmac('sha256', process.env.SECRET ?? Math.random().toString())
|
||||
hmac.update(name)
|
||||
const privkey = hmac.digest().toString('hex')
|
||||
const pubkey = Buffer.from(secp256k1.getPublicKey(privkey, true)).toString('hex').substring(2)
|
||||
const author = {
|
||||
name,
|
||||
privkey,
|
||||
pubkey,
|
||||
}
|
||||
return author
|
||||
}
|
||||
|
||||
async function createSubscription(
|
||||
ws: WebSocket,
|
||||
subscriptionName: string,
|
||||
subscriptionFilters: SubscriptionFilter[],
|
||||
): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const message = JSON.stringify([
|
||||
'REQ',
|
||||
subscriptionName,
|
||||
...subscriptionFilters,
|
||||
])
|
||||
|
||||
ws.send(message, (error: Error) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function waitForEOSE(ws: WebSocket, subscription: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
function cleanup() {
|
||||
ws.removeListener('message', onMessage)
|
||||
ws.removeListener('error', onError)
|
||||
}
|
||||
|
||||
function onError(error: Error) {
|
||||
reject(error)
|
||||
cleanup()
|
||||
}
|
||||
ws.once('error', onError)
|
||||
|
||||
function onMessage(raw: RawData) {
|
||||
const message = JSON.parse(raw.toString('utf-8'))
|
||||
if (message[0] === MessageType.EOSE && message[1] === subscription) {
|
||||
resolve()
|
||||
cleanup()
|
||||
} else if (message[0] === MessageType.NOTICE) {
|
||||
reject(new Error(message[1]))
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
ws.on('message', onMessage)
|
||||
})
|
||||
}
|
||||
|
||||
async function sendEvent(ws: WebSocket, event: Event) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
ws.send(JSON.stringify(['EVENT', event]), (err) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
return
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function waitForNextEvent(ws: WebSocket, subscription: string): Promise<Event> {
|
||||
return new Promise((resolve, reject) => {
|
||||
function cleanup() {
|
||||
ws.removeListener('message', onMessage)
|
||||
ws.removeListener('error', onError)
|
||||
}
|
||||
|
||||
function onError(error: Error) {
|
||||
reject(error)
|
||||
cleanup()
|
||||
}
|
||||
ws.once('error', onError)
|
||||
|
||||
function onMessage(raw: RawData) {
|
||||
ws.removeListener('error', onError)
|
||||
const message = JSON.parse(raw.toString('utf-8'))
|
||||
if (message[0] === MessageType.EVENT && message[1] === subscription) {
|
||||
resolve(message[2])
|
||||
cleanup()
|
||||
} else if (message[0] === MessageType.NOTICE) {
|
||||
reject(new Error(message[1]))
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
ws.on('message', onMessage)
|
||||
})
|
||||
}
|
23
test/integration/features/shared.ts
Normal file
23
test/integration/features/shared.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { AfterAll, BeforeAll } from '@cucumber/cucumber'
|
||||
|
||||
import { AppWorker } from '../../../src/app/worker'
|
||||
import { DatabaseClient } from '../../../src/@types/base'
|
||||
import { getDbClient } from '../../../src/database/client'
|
||||
import { workerFactory } from '../../../src/factories/worker-factory'
|
||||
|
||||
let worker: AppWorker
|
||||
|
||||
let dbClient: DatabaseClient
|
||||
|
||||
BeforeAll({ timeout: 6000 }, async function () {
|
||||
dbClient = getDbClient()
|
||||
await dbClient.raw('SELECT 1=1')
|
||||
worker = workerFactory()
|
||||
worker.run()
|
||||
})
|
||||
|
||||
AfterAll(async function() {
|
||||
worker.close(async () => {
|
||||
await dbClient.destroy()
|
||||
})
|
||||
})
|
@ -9,7 +9,7 @@
|
||||
"target": "es6",
|
||||
"outDir": "./dist",
|
||||
"moduleResolution": "Node",
|
||||
"types": ["node", "mocha"],
|
||||
"types": ["node", "mocha", "@cucumber/cucumber"],
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"incremental": true,
|
||||
"declarationMap": true,
|
||||
|
Loading…
x
Reference in New Issue
Block a user