Skip to content

Commit

Permalink
added testing suite
Browse files Browse the repository at this point in the history
  • Loading branch information
Admin committed Dec 5, 2024
1 parent 70426ac commit e414625
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 15 deletions.
14 changes: 14 additions & 0 deletions indexer/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.test.ts'],
moduleFileExtensions: ['ts', 'js', 'json', 'node'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/__tests__/**'
],
coverageDirectory: 'coverage',
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts']
};
15 changes: 12 additions & 3 deletions indexer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,27 @@
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts"
"dev": "ts-node src/index.ts",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"dependencies": {
"dotenv": "^16.3.1",
"ethers": "^5.7.2",
"express": "^4.18.2",
"dotenv": "^16.3.1",
"mongodb": "^6.3.0"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/jest": "^29.5.14",
"@types/node": "^20.10.4",
"@types/supertest": "^2.0.16",
"jest": "^29.7.0",
"mongodb-memory-server": "^9.1.1",
"supertest": "^6.3.3",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.3.3"
}
}
}
106 changes: 106 additions & 0 deletions indexer/src/__tests__/db/mongodb.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { MongoDBAdapter } from '../../db/mongodb';
import { BlockData, ContractConfig, EventData } from '../../db/adapter';
import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals';

describe('MongoDBAdapter', () => {
let adapter: MongoDBAdapter;

beforeAll(async () => {
adapter = new MongoDBAdapter(process.env.MONGODB_URI!);
await adapter.connect();
});

afterAll(async () => {
await adapter.disconnect();
});

beforeEach(async () => {
// Clear collections before each test
const db = (adapter as any).client.db();
await db.collection('blocks').deleteMany({});
await db.collection('contracts').deleteMany({});
await db.collection('events').deleteMany({});
});

describe('Blocks', () => {
const mockBlock: BlockData = {
number: 1,
hash: '0x123',
timestamp: 1000,
transactions: ['0xabc']
};

it('should save and retrieve a block', async () => {
await adapter.saveBlock(mockBlock);
const retrieved = await adapter.getBlock(mockBlock.number);
expect(retrieved).toEqual(mockBlock);
});

it('should get latest block', async () => {
await adapter.saveBlock({ ...mockBlock, number: 1 });
await adapter.saveBlock({ ...mockBlock, number: 2 });
const latest = await adapter.getLatestBlock();
expect(latest?.number).toBe(2);
});
});

describe('Contracts', () => {
const mockContract: ContractConfig = {
address: '0x123',
name: 'Test Contract',
abi: '[]',
events: [],
isActive: true,
createdAt: new Date(),
updatedAt: new Date()
};

it('should add and retrieve a contract', async () => {
await adapter.addContract(mockContract);
const retrieved = await adapter.getContract(mockContract.address);
expect(retrieved?.address).toBe(mockContract.address);
});

it('should list active contracts', async () => {
await adapter.addContract(mockContract);
await adapter.addContract({ ...mockContract, address: '0x456', isActive: false });
const activeContracts = await adapter.listContracts(true);
expect(activeContracts.length).toBe(1);
expect(activeContracts[0].address).toBe('0x123');
});
});

describe('Events', () => {
const mockEvent: EventData = {
id: '1',
contractAddress: '0x123',
eventName: 'Transfer',
blockNumber: 1,
transactionHash: '0xabc',
timestamp: 1000,
returnValues: { from: '0x123', to: '0x456', value: '1000' },
raw: { data: '0x', topics: [] }
};

it('should save and retrieve events', async () => {
await adapter.saveEvent(mockEvent);
const events = await adapter.getEvents(mockEvent.contractAddress);
expect(events.length).toBe(1);
expect(events[0].id).toBe(mockEvent.id);
});

it('should query events with filters', async () => {
await adapter.saveEvent(mockEvent);
await adapter.saveEvent({ ...mockEvent, id: '2', blockNumber: 2 });

const result = await adapter.queryEvents({
contractAddress: '0x123',
fromBlock: 1,
toBlock: 1
});

expect(result.events.length).toBe(1);
expect(result.total).toBe(1);
});
});
});
138 changes: 138 additions & 0 deletions indexer/src/__tests__/routes/api.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals';
import express from 'express';
import request from 'supertest';
import { MongoDBAdapter } from '../../db/mongodb';
import { createContractRoutes } from '../../routes/contracts';
import { createEventRoutes } from '../../routes/events';
import { createBlockRoutes } from '../../routes/blocks';

describe('API Routes', () => {
let app: express.Application;
let db: MongoDBAdapter;

beforeAll(async () => {
db = new MongoDBAdapter(process.env.MONGODB_URI!);
await db.connect();

app = express();
app.use(express.json());
app.use('/contracts', createContractRoutes(db, '../out', '../deployments'));
app.use('/events', createEventRoutes(db));
app.use('/blocks', createBlockRoutes(db));
});

afterAll(async () => {
await db.disconnect();
});

beforeEach(async () => {
const dbClient = (db as any).client.db();
await dbClient.collection('blocks').deleteMany({});
await dbClient.collection('contracts').deleteMany({});
await dbClient.collection('events').deleteMany({});
});

describe('Contract Routes', () => {
const mockContract = {
address: '0x123',
name: 'Test Contract',
abi: '[]'
};

it('should add a new contract', async () => {
const response = await request(app)
.post('/contracts')
.send(mockContract);

expect(response.status).toBe(200);
expect(response.body.message).toBe('Contract added successfully');
});

it('should list contracts', async () => {
await db.addContract({
...mockContract,
events: [],
isActive: true
});

const response = await request(app)
.get('/contracts');

expect(response.status).toBe(200);
expect(response.body.length).toBe(1);
expect(response.body[0].address).toBe(mockContract.address);
});
});

describe('Event Routes', () => {
const mockEvent = {
name: 'Transfer',
signature: 'Transfer(address,address,uint256)',
abi: 'event Transfer(address indexed from, address indexed to, uint256 value)'
};

beforeEach(async () => {
await db.addContract({
address: '0x123',
name: 'Test Contract',
abi: '[]',
events: [],
isActive: true
});
});

it('should add an event to a contract', async () => {
const response = await request(app)
.post('/events/contracts/0x123/events')
.send(mockEvent);

expect(response.status).toBe(200);
expect(response.body.message).toBe('Event added successfully');
});

it('should query events', async () => {
const response = await request(app)
.post('/events/query')
.send({
contractAddress: '0x123',
fromBlock: 1,
toBlock: 100
});

expect(response.status).toBe(200);
expect(response.body).toHaveProperty('events');
expect(response.body).toHaveProperty('total');
});
});

describe('Block Routes', () => {
const mockBlock = {
number: 1,
hash: '0x123',
timestamp: 1000,
transactions: []
};

beforeEach(async () => {
await db.saveBlock(mockBlock);
});

it('should get block by number', async () => {
const response = await request(app)
.get('/blocks/1');

expect(response.status).toBe(200);
expect(response.body.number).toBe(1);
expect(response.body.hash).toBe('0x123');
});

it('should get indexing status', async () => {
const response = await request(app)
.get('/blocks/status');

expect(response.status).toBe(200);
expect(response.body).toHaveProperty('latestIndexedBlock');
expect(response.body).toHaveProperty('totalIndexedBlocks');
});
});
});
26 changes: 26 additions & 0 deletions indexer/src/__tests__/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MongoMemoryServer } from 'mongodb-memory-server';
import { MongoClient } from 'mongodb';
import '@jest/globals';

let mongoServer: MongoMemoryServer;
let mongoClient: MongoClient;

beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const mongoUri = mongoServer.getUri();
mongoClient = new MongoClient(mongoUri);
await mongoClient.connect();

// Set environment variables for testing
process.env.MONGODB_URI = mongoUri;
process.env.ETHEREUM_RPC_URL = 'http://localhost:8545'; // For local testing
});

afterAll(async () => {
if (mongoClient) {
await mongoClient.close();
}
if (mongoServer) {
await mongoServer.stop();
}
});
24 changes: 12 additions & 12 deletions indexer/src/routes/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ import { DatabaseAdapter } from '../db/adapter';
export function createBlockRoutes(db: DatabaseAdapter) {
const router = Router();

// Get block by number
router.get('/:number', async (req, res) => {
const blockNumber = parseInt(req.params.number);
const block = await db.getBlock(blockNumber);

if (!block) {
return res.status(404).json({ error: 'Block not found' });
}

res.json(block);
});

// Get indexing status
router.get('/status', async (req, res) => {
const totalBlocks = await db.getTotalBlocks();
Expand All @@ -33,5 +21,17 @@ export function createBlockRoutes(db: DatabaseAdapter) {
});
});

// Get block by number
router.get('/:number', async (req, res) => {
const blockNumber = parseInt(req.params.number);
const block = await db.getBlock(blockNumber);

if (!block) {
return res.status(404).json({ error: 'Block not found' });
}

res.json(block);
});

return router;
}

0 comments on commit e414625

Please sign in to comment.