Skip to content

Commit bbd0616

Browse files
authored
test: add unit tests (#3)
* add more information in the readme Signed-off-by: georgi-l95 <glazarov95@gmail.com> * improve code structure Signed-off-by: georgi-l95 <glazarov95@gmail.com> * add tests Signed-off-by: georgi-l95 <glazarov95@gmail.com> --------- Signed-off-by: georgi-l95 <glazarov95@gmail.com>
1 parent e680a65 commit bbd0616

12 files changed

Lines changed: 1542 additions & 353 deletions

.eslintrc.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"parserOptions": {
5+
"ecmaVersion": 2019,
6+
"sourceType": "module",
7+
"project": "./tsconfig.json"
8+
},
9+
"plugins": ["@typescript-eslint"],
10+
"extends": [
11+
"eslint:recommended",
12+
"plugin:@typescript-eslint/recommended",
13+
"plugin:@typescript-eslint/recommended-requiring-type-checking"
14+
],
15+
"rules": {
16+
"@typescript-eslint/explicit-function-return-type": "off",
17+
"@typescript-eslint/no-explicit-any": "warn",
18+
"@typescript-eslint/no-unused-vars": [
19+
"error",
20+
{ "argsIgnorePattern": "^_" }
21+
],
22+
"@typescript-eslint/consistent-type-imports": "error",
23+
"no-console": ["warn", { "allow": ["warn", "error", "log"] }],
24+
"prefer-const": "error",
25+
"no-var": "error"
26+
},
27+
"ignorePatterns": ["dist", "node_modules", "*.js"]
28+
}

package.json

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hashscan-verify",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Hardhat plugin for verifying smart contracts on HashScan (Hedera's contract verification service)",
55
"license": "MIT",
66
"author": "LimeChain",
@@ -9,7 +9,8 @@
99
"main": "dist/index.js",
1010
"types": "dist/index.d.ts",
1111
"files": [
12-
"dist"
12+
"dist",
13+
"!dist/**/__tests__"
1314
],
1415
"exports": {
1516
".": {
@@ -19,14 +20,27 @@
1920
},
2021
"scripts": {
2122
"build": "tsc -p tsconfig.json",
22-
"prepublishOnly": "npm run build"
23+
"clean": "rm -rf dist",
24+
"prebuild": "npm run clean",
25+
"test": "jest",
26+
"test:watch": "jest --watch",
27+
"test:coverage": "jest --coverage",
28+
"lint": "eslint src --ext .ts",
29+
"lint:fix": "eslint src --ext .ts --fix",
30+
"prepublishOnly": "npm run test && npm run build"
2331
},
2432
"peerDependencies": {
2533
"hardhat": "^3.0.0"
2634
},
2735
"devDependencies": {
36+
"@types/jest": "^29.5.0",
2837
"@types/node": "^24.3.1",
38+
"@typescript-eslint/eslint-plugin": "^6.0.0",
39+
"@typescript-eslint/parser": "^6.0.0",
40+
"eslint": "^8.45.0",
2941
"hardhat": "^3.0.6",
42+
"jest": "^29.5.0",
43+
"ts-jest": "^29.1.0",
3044
"typescript": "5.9.2"
3145
},
3246
"keywords": [
@@ -35,5 +49,41 @@
3549
"hedera",
3650
"hashscan",
3751
"sourcify"
38-
]
52+
],
53+
"jest": {
54+
"preset": "ts-jest/presets/default-esm",
55+
"extensionsToTreatAsEsm": [".ts"],
56+
"moduleNameMapper": {
57+
"^(\\.{1,2}/.*)\\.js$": "$1"
58+
},
59+
"transform": {
60+
"^.+\\.tsx?$": [
61+
"ts-jest",
62+
{
63+
"useESM": true,
64+
"tsconfig": {
65+
"module": "ES2022",
66+
"moduleResolution": "node16",
67+
"types": ["node", "jest"]
68+
}
69+
}
70+
]
71+
},
72+
"testEnvironment": "node",
73+
"roots": ["<rootDir>/src"],
74+
"testMatch": ["**/__tests__/**/*.test.ts"],
75+
"collectCoverageFrom": [
76+
"src/**/*.ts",
77+
"!src/**/__tests__/**",
78+
"!src/index.ts"
79+
],
80+
"coverageThreshold": {
81+
"global": {
82+
"branches": 80,
83+
"functions": 80,
84+
"lines": 80,
85+
"statements": 80
86+
}
87+
}
88+
}
3989
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { NetworkConfig, CHAIN_IDS, HEDERA_NETWORKS } from '../networks.js';
2+
3+
describe('NetworkConfig', () => {
4+
describe('fromChainId', () => {
5+
it('should return correct network for mainnet', () => {
6+
const network = NetworkConfig.fromChainId(CHAIN_IDS.MAINNET);
7+
expect(network.networkName).toBe('mainnet');
8+
expect(network.chainId).toBe(295);
9+
});
10+
11+
it('should return correct network for testnet', () => {
12+
const network = NetworkConfig.fromChainId(CHAIN_IDS.TESTNET);
13+
expect(network.networkName).toBe('testnet');
14+
expect(network.chainId).toBe(296);
15+
});
16+
17+
it('should return correct network for previewnet', () => {
18+
const network = NetworkConfig.fromChainId(CHAIN_IDS.PREVIEWNET);
19+
expect(network.networkName).toBe('previewnet');
20+
expect(network.chainId).toBe(297);
21+
});
22+
23+
it('should return correct network for local', () => {
24+
const network = NetworkConfig.fromChainId(CHAIN_IDS.LOCAL);
25+
expect(network.networkName).toBe('local');
26+
expect(network.chainId).toBe(298);
27+
});
28+
29+
it('should return default config for unknown chain', () => {
30+
const network = NetworkConfig.fromChainId(999);
31+
expect(network.networkName).toBe('unknown');
32+
expect(network.chainId).toBe(999);
33+
});
34+
});
35+
36+
describe('getNetworkName', () => {
37+
it('should return network name for known chains', () => {
38+
expect(NetworkConfig.getNetworkName(CHAIN_IDS.MAINNET)).toBe('mainnet');
39+
expect(NetworkConfig.getNetworkName(CHAIN_IDS.TESTNET)).toBe('testnet');
40+
expect(NetworkConfig.getNetworkName(CHAIN_IDS.PREVIEWNET)).toBe('previewnet');
41+
expect(NetworkConfig.getNetworkName(CHAIN_IDS.LOCAL)).toBe('local');
42+
});
43+
44+
it('should return undefined for unknown chains', () => {
45+
expect(NetworkConfig.getNetworkName(999)).toBeUndefined();
46+
});
47+
});
48+
49+
describe('isHederaNetwork', () => {
50+
it('should identify Hedera networks', () => {
51+
expect(NetworkConfig.isHederaNetwork(295)).toBe(true);
52+
expect(NetworkConfig.isHederaNetwork(296)).toBe(true);
53+
expect(NetworkConfig.isHederaNetwork(297)).toBe(true);
54+
expect(NetworkConfig.isHederaNetwork(298)).toBe(true);
55+
});
56+
57+
it('should reject non-Hedera networks', () => {
58+
expect(NetworkConfig.isHederaNetwork(1)).toBe(false);
59+
expect(NetworkConfig.isHederaNetwork(999)).toBe(false);
60+
});
61+
});
62+
63+
describe('getHashScanUrl', () => {
64+
it('should generate correct URLs for each network', () => {
65+
const address = '0x1234567890123456789012345678901234567890';
66+
67+
const mainnet = NetworkConfig.fromChainId(CHAIN_IDS.MAINNET);
68+
expect(mainnet.getHashScanUrl(address)).toBe(
69+
`https://hashscan.io/mainnet/contract/${address}`
70+
);
71+
72+
const testnet = NetworkConfig.fromChainId(CHAIN_IDS.TESTNET);
73+
expect(testnet.getHashScanUrl(address)).toBe(
74+
`https://hashscan.io/testnet/contract/${address}`
75+
);
76+
77+
const previewnet = NetworkConfig.fromChainId(CHAIN_IDS.PREVIEWNET);
78+
expect(previewnet.getHashScanUrl(address)).toBe(
79+
`https://hashscan.io/previewnet/contract/${address}`
80+
);
81+
82+
const local = NetworkConfig.fromChainId(CHAIN_IDS.LOCAL);
83+
expect(local.getHashScanUrl(address)).toBe(
84+
`http://localhost:8080/local/contract/${address}`
85+
);
86+
});
87+
88+
it('should return undefined for unknown networks', () => {
89+
const unknown = NetworkConfig.fromChainId(999);
90+
expect(unknown.getHashScanUrl('0x123')).toBeUndefined();
91+
});
92+
});
93+
94+
describe('HEDERA_NETWORKS', () => {
95+
it('should export all network configurations', () => {
96+
expect(HEDERA_NETWORKS).toHaveLength(4);
97+
expect(HEDERA_NETWORKS.map(n => n.chainId)).toEqual([295, 296, 297, 298]);
98+
});
99+
100+
it('should have correct network names', () => {
101+
const networkNames = HEDERA_NETWORKS.map(n => n.networkName);
102+
expect(networkNames).toEqual(['mainnet', 'testnet', 'previewnet', 'local']);
103+
});
104+
105+
it('should have multiple name aliases for each network', () => {
106+
HEDERA_NETWORKS.forEach(network => {
107+
expect(network.names.length).toBeGreaterThanOrEqual(2);
108+
expect(network.names).toContain(network.networkName);
109+
});
110+
});
111+
});
112+
});

src/config/networks.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
export interface HederaNetwork {
2+
chainId: number;
3+
networkName: string;
4+
names: string[];
5+
hashScanBaseUrl?: string;
6+
defaultApiUrl: string;
7+
getHashScanUrl(address: string): string | undefined;
8+
}
9+
10+
// Chain ID constants
11+
export const CHAIN_IDS = {
12+
MAINNET: 295,
13+
TESTNET: 296,
14+
PREVIEWNET: 297,
15+
LOCAL: 298,
16+
} as const;
17+
18+
// Helper class to create HederaNetwork objects with methods
19+
class HederaNetworkImpl implements HederaNetwork {
20+
constructor(
21+
public chainId: number,
22+
public networkName: string,
23+
public names: string[],
24+
public defaultApiUrl: string,
25+
public hashScanBaseUrl?: string,
26+
) {}
27+
28+
getHashScanUrl(address: string): string | undefined {
29+
return this.hashScanBaseUrl
30+
? `${this.hashScanBaseUrl}/contract/${address}`
31+
: undefined;
32+
}
33+
}
34+
35+
// Network configurations
36+
export const HEDERA_NETWORKS: HederaNetwork[] = [
37+
new HederaNetworkImpl(
38+
CHAIN_IDS.MAINNET,
39+
"mainnet",
40+
["mainnet", "hedera_mainnet"],
41+
"https://server-verify.hashscan.io",
42+
"https://hashscan.io/mainnet",
43+
),
44+
new HederaNetworkImpl(
45+
CHAIN_IDS.TESTNET,
46+
"testnet",
47+
["testnet", "hedera_testnet"],
48+
"https://server-verify.hashscan.io",
49+
"https://hashscan.io/testnet",
50+
),
51+
new HederaNetworkImpl(
52+
CHAIN_IDS.PREVIEWNET,
53+
"previewnet",
54+
["previewnet", "hedera_previewnet"],
55+
"https://server-verify.hashscan.io",
56+
"https://hashscan.io/previewnet",
57+
),
58+
new HederaNetworkImpl(
59+
CHAIN_IDS.LOCAL,
60+
"local",
61+
["local", "hedera_local", "localhost"],
62+
"http://localhost:8080",
63+
"http://localhost:8080/local",
64+
),
65+
];
66+
67+
export class NetworkConfig {
68+
private static readonly networks = new Map<number, HederaNetwork>(
69+
HEDERA_NETWORKS.map(network => [network.chainId, network])
70+
);
71+
72+
static fromChainId(chainId: number): HederaNetwork {
73+
const network = this.networks.get(chainId);
74+
if (!network) {
75+
// Return a default configuration for unknown networks
76+
return new HederaNetworkImpl(
77+
chainId,
78+
"unknown",
79+
[],
80+
"https://server-verify.hashscan.io",
81+
);
82+
}
83+
return network;
84+
}
85+
86+
static getNetworkName(chainId: number): string | undefined {
87+
return this.networks.get(chainId)?.networkName;
88+
}
89+
90+
static isHederaNetwork(chainId: number): boolean {
91+
return this.networks.has(chainId);
92+
}
93+
}

0 commit comments

Comments
 (0)