Skip to content

Commit

Permalink
Merge pull request #92 from pshenmic/feat/api-integration-tests
Browse files Browse the repository at this point in the history
Add API integration tests
  • Loading branch information
pshenmic authored Mar 14, 2024
2 parents 3d41cc6 + a9118bf commit 6d3fd2a
Show file tree
Hide file tree
Showing 43 changed files with 7,954 additions and 255 deletions.
51 changes: 50 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,61 @@ name: Build and push packages
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
test_api:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write

services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'

- name: NPM install
run: cd packages/api && npm install

- name: Run unit tests
run: cd packages/api && npm run test:unit

- name: Migrate DB
run: cd packages/api && POSTGRES_HOST=127.0.0.1 POSTGRES_DB=postgres POSTGRES_USER=postgres POSTGRES_PASS=postgres POSTGRES_DB=postgres npm run db:migrate

- name: Run integration tests
run: cd packages/api && POSTGRES_HOST=127.0.0.1 POSTGRES_DB=postgres POSTGRES_USER=postgres POSTGRES_PASS=postgres POSTGRES_DB=postgres npm run test:integration

build_api:
runs-on: ubuntu-latest
needs: test_api
permissions:
contents: read
packages: write
Expand All @@ -35,7 +82,8 @@ jobs:
- name: Build and push API package Docker image
uses: docker/build-push-action@v4
with:
context: ./packages/api
context: ./
file: ./packages/api/Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:api
build_indexer:
Expand Down Expand Up @@ -70,6 +118,7 @@ jobs:
deploy:
runs-on: ubuntu-latest
needs: [build_api, build_indexer]
if: github.event.pull_request.merged == true
steps:
- name: Deploy to the server
uses: appleboy/[email protected]
Expand Down
7 changes: 4 additions & 3 deletions packages/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
FROM node:18-alpine
COPY index.js /app/index.js
COPY src /app/src
FROM node:20-alpine
COPY packages/api/index.js /app/index.js
COPY packages/api/src /app/src
COPY packages/indexer/migrations /app/migrations
COPY package.json /app/package.json
WORKDIR /app
RUN npm install
Expand Down
88 changes: 4 additions & 84 deletions packages/api/index.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,7 @@
require('dotenv').config()

const Dash = require('dash')
const Fastify = require('fastify')
const cors = require('@fastify/cors')
const Routes = require('./src/routes')
const server = require('./src/server')

const ServiceNotAvailableError = require("./src/errors/ServiceNotAvailableError");
const MainController = require("./src/controllers/MainController");
const TransactionsController = require("./src/controllers/TransactionsController");
const BlocksController = require("./src/controllers/BlocksController");
const DocumentsController = require("./src/controllers/DocumentsController");
const IdentitiesController = require("./src/controllers/IdentitiesController");
const packageVersion = require('./package.json').version
const Worker = require('./src/worker/index')
const {BLOCK_TIME} = require("./src/constants");
const DataContractsController = require("./src/controllers/DataContractsController");

function errorHandler(err, req, reply) {
if (err instanceof ServiceNotAvailableError) {
return reply.status(403).send({error: 'tenderdash backend is not available'})
}

if (err?.constructor?.name === 'InvalidStateTransitionError') {
const [error] = err.getErrors()
const {code, message} = error

return reply.status(500).send({error: message, code})
}

console.error(err)
reply.status(500)

reply.send({error: err.message})
}

let status;

const init = async () => {
const client = new Dash.Client()
await client.platform.initialize()

const worker = new Worker()

worker.setHandler(async () => {
try {
status = true
} catch (e) {
}
})
worker.start(BLOCK_TIME)

const fastify = Fastify()

await fastify.register(cors, {
// put your options here
})

const knex = require('knex')({
client: 'pg',
connection: {
host: process.env["POSTGRES_HOST"],
port: process.env["POSTGRES_PORT"],
user: process.env["POSTGRES_USER"],
database: process.env["POSTGRES_NAME"],
password: process.env["POSTGRES_PASS"],
ssl: process.env["POSTGRES_SSL"] ? { rejectUnauthorized: false } : false,
}
});

await knex.raw('select 1+1');

const mainController = new MainController(knex)
const blocksController = new BlocksController(knex)
const transactionsController = new TransactionsController(client, knex)
const dataContractsController = new DataContractsController(knex)
const documentsController = new DocumentsController(knex)
const identitiesController = new IdentitiesController(knex)

Routes({fastify, mainController, blocksController, transactionsController, dataContractsController, documentsController, identitiesController})

fastify.setErrorHandler(errorHandler)
fastify.listen({ host: "0.0.0.0", port: 3005, listenTextResolver: (address) => console.log(`Platform indexer API has started on the ${address}`)});
}


init().then((`Platform Explorer backend v${packageVersion}`))
server.start()
.then((_server) => server.listen(_server))
.then(() => console.log(`Platform Explorer API started`))
10 changes: 8 additions & 2 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
"license": "MIT",
"scripts": {
"start": "node index.js",
"test": "node --test"
"test:unit": "node --test test/unit/*.spec.js",
"test:integration": "node --test --test-concurrency=1 test/integration/*.spec.js",
"db:migrate": "node ./test/utils/migrate.js ../indexer/migrations"
},
"dependencies": {
"@fastify/cors": "^8.3.0",
"dash": "4.0.0-dev.5",
"@scure/base": "^1.1.5",
"dash": "4.0.0-dev.7",
"dotenv": "^16.3.1",
"fastify": "^4.21.0",
"knex": "^2.5.1",
"node-fetch": "^2.6.11",
"pg": "^8.11.3"
},
"devDependencies": {
"supertest": "^6.3.4"
}
}
12 changes: 11 additions & 1 deletion packages/api/src/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
module.exports = {
BLOCK_TIME: 5000
BLOCK_TIME: 5000,
StateTransitionEnum: {
DATA_CONTRACT_CREATE: 0,
DOCUMENTS_BATCH: 1,
IDENTITY_CREATE: 2,
IDENTITY_TOP_UP: 3,
DATA_CONTRACT_UPDATE: 4,
IDENTITY_UPDATE: 5,
IDENTITY_CREDIT_WITHDRAWAL: 6,
IDENTITY_CREDIT_TRANSFER: 7
}
}
24 changes: 12 additions & 12 deletions packages/api/src/controllers/DataContractsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,6 @@ class DataContractsController {
this.dataContractsDAO = new DataContractsDAO(knex)
}

getDataContracts = async (request, response) => {
const {page = 1, limit = 10, order = 'asc'} = request.query

if (order !== 'asc' && order !== 'desc') {
return response.status(400).send({message: `invalid ordering value ${order}. only 'asc' or 'desc' is valid values`})
}

const dataContracts = await this.dataContractsDAO.getDataContracts(Number(page), Number(limit), order);

response.send(dataContracts)
}

getDataContractByIdentifier = async (request, response) => {
const {identifier} = request.params

Expand All @@ -29,6 +17,18 @@ class DataContractsController {

response.send(dataContract);
}

getDataContracts = async (request, response) => {
const {page = 1, limit = 10, order = 'asc'} = request.query

if (order !== 'asc' && order !== 'desc') {
return response.status(400).send({message: `invalid ordering value ${order}. only 'asc' or 'desc' is valid values`})
}

const dataContracts = await this.dataContractsDAO.getDataContracts(Number(page), Number(limit), order);

response.send(dataContracts)
}
}

module.exports = DataContractsController
17 changes: 13 additions & 4 deletions packages/api/src/controllers/MainController.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const BlocksDAO = require('../dao/BlocksDAO')
const DataContractsDAO = require('../dao/DataContractsDAO')
const TransactionsDAO = require('../dao/TransactionsDAO')
const DocumentsDAO = require('../dao/DocumentsDAO')
const IdentitiesDAO = require('../dao/IdentitiesDAO')
const TenderdashRPC = require("../tenderdashRpc");

class MainController {
Expand All @@ -10,6 +11,7 @@ class MainController {
this.dataContractsDAO = new DataContractsDAO(knex)
this.documentsDAO = new DocumentsDAO(knex)
this.transactionsDAO = new TransactionsDAO(knex)
this.identitiesDAO = new IdentitiesDAO(knex)
}

getStatus = async (request, response) => {
Expand All @@ -20,7 +22,7 @@ class MainController {
stats = await this.blocksDAO.getStats()
tdStatus = await TenderdashRPC.getStatus();
} catch (e) {

console.error(e)
}

response.send({
Expand All @@ -32,8 +34,8 @@ class MainController {
transfersCount: stats?.transfersCount,
dataContractsCount: stats?.dataContractsCount,
documentsCount: stats?.documentsCount,
network: tdStatus?.network,
tenderdashVersion: tdStatus?.tenderdashVersion
network: tdStatus?.network ?? null,
tenderdashVersion: tdStatus?.tenderdashVersion ?? null
});
}

Expand Down Expand Up @@ -70,8 +72,15 @@ class MainController {
}
}

// check for any Identifiers (data contracts, documents)
// check for any Identifiers (identities, data contracts, documents)
if (query.length >= 43 && query.length <= 44) {
// search identites
const identity = await this.identitiesDAO.getIdentityByIdentifier(query)

if (identity) {
return response.send({identity})
}

// search data contracts
const dataContract = await this.dataContractsDAO.getDataContractByIdentifier(query)

Expand Down
24 changes: 12 additions & 12 deletions packages/api/src/controllers/TransactionsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,6 @@ class TransactionsController {
this.transactionsDAO = new TransactionsDAO(knex)
}

getTransactions = async (request, response) => {
const {page = 1, limit = 10, order = 'asc'} = request.query

if (order !== 'asc' && order !== 'desc') {
return response.status(400).send({message: `invalid ordering value ${order}. only 'asc' or 'desc' is valid values`})
}

const transactions = await this.transactionsDAO.getTransactions(Number(page), Number(limit), order)

response.send(transactions);
}

getTransactionByHash = async (request, reply) => {
const {txHash} = request.params;

Expand All @@ -31,6 +19,18 @@ class TransactionsController {
reply.send(transaction)
}


getTransactions = async (request, response) => {
const {page = 1, limit = 10, order = 'asc'} = request.query

if (order !== 'asc' && order !== 'desc') {
return response.status(400).send({message: `invalid ordering value ${order}. only 'asc' or 'desc' is valid values`})
}

const transactions = await this.transactionsDAO.getTransactions(Number(page), Number(limit), order)

response.send(transactions);
}
decode = async (request, reply) => {
const {base64} = request.body;

Expand Down
Loading

0 comments on commit 6d3fd2a

Please sign in to comment.