Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indexer-service: Server integration tests #901

Merged
merged 1 commit into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions packages/indexer-service/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testPathIgnorePatterns: ['/node_modules/', '/dist/', '.yalc'],
globals: {
'ts-jest': {
transform: {
'^.+\\.tsx?$': ['ts-jest', {
isolatedModules: true,
},
tsconfig: 'tsconfig.json'
}],
},
globals: {
__DATABASE__: {
host: process.env.POSTGRES_TEST_HOST || bail('POSTGRES_TEST_HOST is not defined'),
port: parseInt(process.env.POSTGRES_TEST_PORT || '5432'),
Expand Down
6 changes: 3 additions & 3 deletions packages/indexer-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
"compile": "tsc --build",
"prepare": "yarn format && yarn lint && yarn compile",
"start": "node ./dist/index.js start",
"test": "jest --colors --verbose --detectOpenHandles",
"test:ci": "jest --verbose --ci",
"test:watch": "jest --watch --passWithNoTests --detectOpenHandles --verbose",
"test": "jest --colors --verbose --detectOpenHandles --runInBand",
"test:ci": "jest --verbose --ci --runInBand",
"test:watch": "jest --watch --passWithNoTests --detectOpenHandles --verbose --runInBand",
"clean": "rm -rf ./node_modules ./dist ./tsconfig.tsbuildinfo"
},
"bin": {
Expand Down
141 changes: 123 additions & 18 deletions packages/indexer-service/src/server/__tests__/server.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Assumes a graph-node is running
* Assumes a graph-node is running and is accessible at http://127.0.0.1
*/

import http from 'http'
Expand Down Expand Up @@ -28,11 +28,11 @@ import {
defineQueryFeeModels,
IndexerManagementClient,
IndexerManagementModels,
IndexingStatusResolver,
monitorEligibleAllocations,
NetworkSubgraph,
QueryFeeModels,
getTestProvider,
GraphNode,
} from '@graphprotocol/indexer-common'

// Make global Jest variable available
Expand All @@ -48,7 +48,6 @@ let models: IndexerManagementModels
let queryFeeModels: QueryFeeModels
let address: string
let contracts: NetworkContracts
let indexingStatusResolver: IndexingStatusResolver
let networkSubgraph: NetworkSubgraph
let client: IndexerManagementClient
let receiptManager: AllocationReceiptManager
Expand All @@ -65,25 +64,28 @@ const setup = async () => {
contracts = await connectContracts(getTestProvider('sepolia'), 11155111, undefined)
sequelize = await sequelize.sync({ force: true })
const statusEndpoint = 'http://127.0.0.1:8030/graphql'
indexingStatusResolver = new IndexingStatusResolver({
logger: logger,
statusEndpoint,
})
const queryEndpoint = 'http://127.0.0.1:8000'
networkSubgraph = await NetworkSubgraph.create({
logger,
endpoint:
'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-testnet',
'https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-sepolia',
deployment: undefined,
})
const indexNodeIDs = ['node_1']

const graphNode = new GraphNode(
logger,
// We can use a fake Graph Node admin endpoint here because Indexer Service
// doesn't need to perform management actions on Graph Node.
'http://fake-graph-node-admin-endpoint',
queryEndpoint,
statusEndpoint,
indexNodeIDs,
)
client = await createIndexerManagementClient({
models,
address,
contracts,
indexingStatusResolver,
graphNode,
indexNodeIDs,
deploymentManagementEndpoint: statusEndpoint,
networkSubgraph,
logger,
defaults: {
// This is just a dummy, since we're never writing to the management
Expand All @@ -92,16 +94,15 @@ const setup = async () => {
allocationAmount: BigNumber.from('0'),
},
},
features: {
injectDai: true,
},
multiNetworks: undefined,
})

receiptManager = new AllocationReceiptManager(
sequelize,
queryFeeModels,
logger,
toAddress(address), //update maybe
'eip155:11155111',
)

const release = {
Expand All @@ -113,6 +114,7 @@ const setup = async () => {

// Monitor indexer allocations that we may receive traffic for
const allocations = monitorEligibleAllocations({
protocolNetwork: 'eip155:11155111',
indexer: toAddress(address),
logger,
networkSubgraph,
Expand Down Expand Up @@ -141,10 +143,10 @@ const setup = async () => {
})

server = await createServer({
logger,
logger: logger.child({ component: 'Server' }),
port: 9600,
queryProcessor,
graphNodeStatusEndpoint: 'http://127.0.0.1:8030/graphql',
graphNodeStatusEndpoint: statusEndpoint,
metrics,
freeQueryAuthToken: '',
indexerManagementClient: client,
Expand All @@ -153,6 +155,9 @@ const setup = async () => {
networkSubgraph,
networkSubgraphAuthToken: 'superdupersecrettoken',
serveNetworkSubgraph: false,
infoRateLimit: 3,
statusRateLimit: 2,
bodySizeLimit: 0.1,
})
server.on('connection', socket => {
logger.debug('Connection established', { socket })
Expand Down Expand Up @@ -180,11 +185,111 @@ const teardown = async () => {
await sequelize.drop({})
}

// Helpers for sending test requests
const testGetRequest = async (
path: string,
expectedStatusCode: number,
expectedResponse: object | string,
) => {
const response = await supertest(server).get(path)
expect(response.status).toEqual(expectedStatusCode)
if ((response.status === 429) | (response.status === 500)) {
expect(response.text).toEqual(expectedResponse)
} else if (response.status === 200) {
expect(response.body).toEqual(expectedResponse)
}
}

const testGraphQLRequest = async (
path: string,
query: object,
expectedStatusCode: number,
expectedResponse: object | string,
) => {
const response = await supertest(server)
.post(path)
.send(query)
.set('Accept', 'application/json')

expect(response.status).toEqual(expectedStatusCode)
if ((response.status === 429) | (response.status === 500)) {
expect(response.text).toEqual(expectedResponse)
} else if (response.status === 200) {
expect(response.body).toEqual(expectedResponse)
}
}

describe('Server', () => {
beforeAll(setup)
afterAll(teardown)

it('is ready to roll', done => {
supertest(server).get('/').expect(200, done)
})

it('Operator info endpoint returns expected data', async () => {
const expectedResponses: [number, object | string][] = [
[
200,
{
publicKey:
'0x04e68acfc0253a10620dff706b0a1b1f1f5833ea3beb3bde2250d5f271f3563606672ebc45e0b7ea2e816ecb70ca03137b1c9476eec63d4632e990020b7b6fba39',
},
],
]
for (const [expectedStatus, expectedResponse] of expectedResponses) {
await testGetRequest('/operator/info', expectedStatus, expectedResponse)
}
})

it('Subgraph deployment health endpoint returns expected data', async () => {
const expectedResponses: [number, string][] = [[500, 'Invalid indexing status']]
for (const [expectedStatus, expectedResponse] of expectedResponses) {
await testGetRequest(
'/subgraphs/health/Qmxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
expectedStatus,
expectedResponse,
)
}
})

// Note: the rate limiting part of this test assumes the tests are run in sequence (suggest using --runInBand)
it('Cost endpoint returns expected data and is rate limited correctly', async () => {
const expectedResponses: [number, object | string][] = [
[200, { data: { costModels: [] } }],
[429, 'Too many requests, please try again later.'],
]

for (const [expectedStatus, expectedResponse] of expectedResponses) {
await testGraphQLRequest(
'/cost',
{
query:
'{costModels(deployments: ["Qmxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]){ deployment model } }',
},
expectedStatus,
expectedResponse,
)
}
})

it('Status endpoint returns expected data and is rate limited correctly', async () => {
const expectedResponses: [number, object | string][] = [
[200, { data: { indexingStatuses: [] } }],
[200, { data: { indexingStatuses: [] } }],
[429, 'Too many requests, please try again later.'],
]

for (const [expectedStatus, expectedResponse] of expectedResponses) {
await testGraphQLRequest(
'/status',
{
query:
'{indexingStatuses(subgraphs: ["Qmxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]){ subgraph health } }',
},
expectedStatus,
expectedResponse,
)
}
})
})
Loading