Skip to content

Commit

Permalink
chore(test): setup github workflow to monitor process state
Browse files Browse the repository at this point in the history
  • Loading branch information
dtfiedler committed Aug 27, 2024
1 parent 9e3d25c commit 64b1d88
Show file tree
Hide file tree
Showing 13 changed files with 291 additions and 111 deletions.
115 changes: 115 additions & 0 deletions .github/workflows/monitor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# run on a cron every hour and call yarn monitor

name: Montior IO Process

on:
push:
workflow_dispatch:
schedule:
- cron: '0 * * * *' # Run every hour

jobs:
monitor:
permissions:
contents: read
actions: read
strategy:
matrix:
network: [testnet, devnet]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'

- name: Setup
run: yarn

- name: Monitor
run: yarn monitor | tee results.txt
continue-on-error: true
id: monitor
env:
IO_PROCESS_ID: ${{ matrix.network == 'testnet' && 'agYcCFJtrMG6cqMuZfskIkFTGvUPddICmtQSBIoPdiA' || 'GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc' }}

- name: Create test output
id: test-outputs
run: |
TEST_RESULTS=$(cat results.txt)
echo "TEST_RESULTS<<EOF" >> $GITHUB_ENV
echo "$TEST_RESULTS" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: Notify Failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'IO Process Observation Failed!'
custom_payload: |
{
text: "IO Process Observation Failed",
attachments: [{
fallback: 'IO Process Observation Failed',
color: 'danger',
title: 'Test Results',
text: 'The IO Process Observation test has failed.',
fields: [{
title: 'Network',
value: '${{ matrix.network }}',
short: true
},
{
title: 'Process ID',
value: '${{ matrix.network == 'testnet' && 'agYcCFJtrMG6cqMuZfskIkFTGvUPddICmtQSBIoPdiA' || 'GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc' }}',
short: true
},
{
title: 'Error Details',
value: '${{ steps.monitor.outputs.stderr }}',
short: false
}],
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

# on sucess send a slack message
- name: Notify Success
if: success()
uses: rtCamp/action-slack-notify@v2
env:
SLACK_COLOR: ${{ job.status }}
SLACK_TITLE: IO Process Observation Success!
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_CUSTOM_PAYLOAD: |
{
"text": "IO Process Observation Success!",
"attachments": [{
"fallback": "IO Process Observation Success!",
"color": "good",
"title": "Test Results",
"text": "The IO Process Observation test has succeeded.",
"fields": [{
"title": "Network",
"value": "${{ matrix.network }}",
"short": true
},
{
"title": "Process ID",
"value": "${{ matrix.network == 'testnet' && 'agYcCFJtrMG6cqMuZfskIkFTGvUPddICmtQSBIoPdiA' || 'GaQrvEMKBpkjofgnBi_B3IgIDmY_XYelVLB6GcRGrHc' }}",
"short": true
},
{
"title": "Test Output",
"value": "```\n${{ env.TEST_RESULTS }}\n```",
"short": false
}
]
}]
}
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"type": "module",
"scripts": {
"build": "node tools/bundle-aos.js",
"test:integration": "yarn build && node --test --experimental-wasm-memory64 **/*.test.js",
"build": "node tools/bundle-aos.mjs",
"test:integration": "yarn build && node --test --experimental-wasm-memory64 **/*.test.mjs",
"test:unit": "busted . && luacov",
"test": "yarn test:unit && yarn test:integration",
"monitor": "node tests/monitor/monitor.test.mjs",
"evolve": "yarn build && node tools/evolve.mjs"
},
"devDependencies": {
"@ar.io/sdk": "^2.1.0-alpha.6",
"@ar.io/sdk": "alpha",
"@permaweb/ao-loader": "^0.0.35",
"@permaweb/aoconnect": "^0.0.55",
"arweave": "^1.15.1",
Expand Down
2 changes: 1 addition & 1 deletion src/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ constants.MAX_ALLOWED_UNDERNAMES = 10000
constants.UNDERNAME_LEASE_FEE_PERCENTAGE = 0.001
constants.UNDERNAME_PERMABUY_FEE_PERCENTAGE = 0.005
constants.oneYearMs = 31536000 * 1000
constants.gracePeriodMs = 3 * 14 * 24 * 60 * 60 * 1000
constants.gracePeriodMs = 14 * 24 * 60 * 60 * 1000 -- 2 weeks
constants.maxLeaseLengthYears = 5

-- DEMAND
Expand Down
10 changes: 5 additions & 5 deletions test/arns.test.js → tests/arns.test.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const { createAosLoader } = require('./utils');
const { describe, it } = require('node:test');
const assert = require('node:assert');
const {
import { createAosLoader } from './utils.mjs';
import { describe, it } from 'node:test';
import assert from 'node:assert';
import {
AO_LOADER_HANDLER_ENV,
DEFAULT_HANDLE_OPTIONS,
STUB_ADDRESS,
} = require('../tools/constants');
} from '../tools/constants.mjs';

// EIP55-formatted test address
const testEthAddress = '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa';
Expand Down
10 changes: 5 additions & 5 deletions test/gar.test.js → tests/gar.test.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const { createAosLoader } = require('./utils');
const { describe, it } = require('node:test');
const assert = require('node:assert');
const {
import { createAosLoader } from './utils.mjs';
import { describe, it } from 'node:test';
import assert from 'node:assert';
import {
AO_LOADER_HANDLER_ENV,
DEFAULT_HANDLE_OPTIONS,
STUB_ADDRESS,
} = require('../tools/constants');
} from '../tools/constants.mjs';

describe('GatewayRegistry', async () => {
const { handle: originalHandle, memory: startMemory } =
Expand Down
113 changes: 113 additions & 0 deletions tests/monitor/monitor.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { IO_DEVNET_PROCESS_ID, IO, IO_TESTNET_PROCESS_ID } from '@ar.io/sdk';
import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';

const io = IO.init({
processId: process.env.IO_PROCESS_ID || IO_TESTNET_PROCESS_ID,
});

describe('distribution totals', () => {
it('should always have correct eligible rewards for the current epoch (within 10 mIO)', async () => {
const { distributions: currentEpochDistributions } =
await io.getCurrentEpoch();

// TODO: for now pass if distributions are empty
if (Object.keys(currentEpochDistributions).length === 0) {
return;
}

// add up all the eligible operators and delegates
const assignedRewards = Object.values(
currentEpochDistributions.rewards.eligible,
).reduce((acc, curr) => {
const delegateRewards = Object.values(curr.delegateRewards).reduce(
(d, c) => d + c,
0,
);
return acc + curr.operatorReward + delegateRewards;
}, 0);

// handle any rounding errors
const roundingError =
assignedRewards - currentEpochDistributions.totalEligibleRewards;
// assert total eligible rewards rounding is less than 10
assert(
roundingError < 10,
`Rounding for eligible distributions is too large: ${roundingError}`,
);
});
});

describe('token supply', () => {
it('should always be 1 billion IO', async () => {
const totalSupply = await io.getTokenSupply();
assert(
totalSupply === 1000000000 * 1000000,
`Total supply is not 1 billion IO: ${totalSupply}`,
);
});
});

// TODO: arns - ensure no invalid arns names

describe('gateway registry', () => {
it('should only have valid gateways', async () => {
let cursor = '';
do {
const { items: gateways, nextCursor } = await io.getGateways({
cursor,
});
for (const gateway of gateways) {
assert(gateway.operatorStake >= 50_000_000_000);
}
cursor = nextCursor;
} while (cursor !== undefined);
});
});

// Gateway registry - ensure no invalid gateways
describe('arns names', () => {
const twoWeeks = 2 * 7 * 24 * 60 * 60 * 1000;
it('should not have any arns records older than two weeks', async () => {
let cursor = '';
do {
const { items: arns, nextCursor } = await io.getArNSRecords({
cursor,
});
for (const arn of arns) {
assert(arn.processId, `ARNs name '${arn.name}' has no processId`);
assert(arn.type, `ARNs name '${arn.name}' has no type`);
assert(
arn.startTimestamp,
`ARNs name '${arn.name}' has no start timestamp`,
);
assert(
arn.purchasePrice >= 0,
`ARNs name '${arn.name}' has no purchase price`,
);
assert(
arn.undernameLimit >= 10,
`ARNs name '${arn.name}' has no undername limit`,
);
if (arns.type === 'lease') {
assert(
arn.endTimestamp,
`ARNs name '${arn.name}' has no end timestamp`,
);
assert(
arn.endTimestamp > Date.now() - twoWeeks,
`ARNs name '${arn.name}' is older than two weeks`,
);
}
// if permabuy, assert no endTimestamp
if (arn.type === 'permabuy') {
assert(
!arn.endTimestamp,
`ARNs name '${arn.name}' has an end timestamp`,
);
}
}
cursor = nextCursor;
} while (cursor !== undefined);
});
});
10 changes: 5 additions & 5 deletions test/transfer.test.js → tests/transfer.test.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const { createAosLoader } = require('./utils');
const { describe, it } = require('node:test');
const assert = require('node:assert');
const {
import { createAosLoader } from './utils.mjs';
import { describe, it } from 'node:test';
import assert from 'node:assert';
import {
AO_LOADER_HANDLER_ENV,
DEFAULT_HANDLE_OPTIONS,
STUB_ADDRESS,
} = require('../tools/constants');
} from '../tools/constants.mjs';

describe('Transfers', async () => {
const { handle: originalHandle, memory: startMemory } =
Expand Down
12 changes: 4 additions & 8 deletions test/utils.js → tests/utils.mjs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
const AoLoader = require('@permaweb/ao-loader');
const {
import AoLoader from '@permaweb/ao-loader';
import {
AOS_WASM,
AO_LOADER_HANDLER_ENV,
AO_LOADER_OPTIONS,
DEFAULT_HANDLE_OPTIONS,
BUNDLED_SOURCE_CODE,
} = require('../tools/constants');
} from '../tools/constants.mjs';

/**
* Loads the aos wasm binary and returns the handle function with program memory
* @returns {Promise<{handle: Function, memory: WebAssembly.Memory}>}
*/
async function createAosLoader() {
export async function createAosLoader() {
const handle = await AoLoader(AOS_WASM, AO_LOADER_OPTIONS);
const evalRes = await handle(
null,
Expand All @@ -30,7 +30,3 @@ async function createAosLoader() {
memory: evalRes.Memory,
};
}

module.exports = {
createAosLoader,
};
9 changes: 6 additions & 3 deletions tools/bundle-aos.js → tools/bundle-aos.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const path = require('path');
const fs = require('fs');
const { bundle } = require('./lua-bundler.js');
import { bundle } from './lua-bundler.mjs';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

async function main() {
console.log('Bundling Lua...');
Expand Down
Loading

0 comments on commit 64b1d88

Please sign in to comment.