Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ package-lock.json
/node_modules
.env
dist
coverage
9 changes: 9 additions & 0 deletions __tests__/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import "dotenv/config"

jest.mock("viem", ()=> ({
createPublicClient: jest.fn(() => ({
getBlockNumber: jest.fn(),
getBlock: jest.fn(),
})),
http: jest.fn(),
}))
95 changes: 95 additions & 0 deletions __tests__/transaction.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { getLatestTransaction } from "../web3-utils";
import { createPublicClient, http, PublicClient, Hash } from "viem";

type MockPublicClient = jest.Mocked<PublicClient>;

jest.mock("viem", () => ({
createPublicClient: jest.fn(),
http: jest.fn(),
}));

describe("getLatestTransaction", () => {
const mockAddress = "0x742d35Cc6634C0532925a3b844Bc454e4438f44e";
const mockCommandId = "test-command";
let mockClient: MockPublicClient;

beforeEach(() => {
jest.clearAllMocks();

mockClient = {
getBlockNumber: jest.fn(),
getBlock: jest.fn(),
} as unknown as MockPublicClient;

(createPublicClient as jest.Mock).mockReturnValue(mockClient);
});

test("should return transaction hash on first attempt", async () => {
const mockHash = "0x123" as Hash;

mockClient.getBlockNumber.mockResolvedValue(123n);
mockClient.getBlock.mockResolvedValue({
transactions: [
{
from: mockAddress.toLowerCase(),
hash: mockHash,
},
],
} as unknown as Awaited<ReturnType<PublicClient["getBlock"]>>);

const result = await getLatestTransaction(mockAddress, mockCommandId);
expect(result).toBe(mockHash);
expect(mockClient.getBlockNumber).toHaveBeenCalledTimes(1);
expect(mockClient.getBlock).toHaveBeenCalledTimes(1);
});

test("should retry and return hash on second attempt", async () => {
const mockHash = "0x456" as Hash;

mockClient.getBlockNumber
.mockResolvedValueOnce(123n)
.mockResolvedValueOnce(124n);

mockClient.getBlock
.mockResolvedValueOnce({
transactions: [],
} as unknown as Awaited<ReturnType<PublicClient["getBlock"]>>)
.mockResolvedValueOnce({
transactions: [
{
from: mockAddress.toLowerCase(),
hash: mockHash,
},
],
} as unknown as Awaited<ReturnType<PublicClient["getBlock"]>>);

const result = await getLatestTransaction(mockAddress, mockCommandId);
expect(result).toBe(mockHash);
expect(mockClient.getBlockNumber).toHaveBeenCalledTimes(2);
expect(mockClient.getBlock).toHaveBeenCalledTimes(2);
});

test("should return null if no transaction found", async () => {
mockClient.getBlockNumber
.mockResolvedValueOnce(123n)
.mockResolvedValueOnce(124n);

mockClient.getBlock
.mockResolvedValueOnce({
transactions: [],
} as unknown as Awaited<ReturnType<PublicClient["getBlock"]>>)
.mockResolvedValueOnce({
transactions: [],
} as unknown as Awaited<ReturnType<PublicClient["getBlock"]>>);

const result = await getLatestTransaction(mockAddress, mockCommandId);
expect(result).toBeNull();
});

test("should handle errors gracefully", async () => {
mockClient.getBlockNumber.mockRejectedValue(new Error("Network error"));

const result = await getLatestTransaction(mockAddress, mockCommandId);
expect(result).toBeNull();
});
});
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
["@babel/preset-env", { targets: { node: "current" } }],
"@babel/preset-typescript",
],
};
38 changes: 38 additions & 0 deletions coverage/clover.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1738577162889" clover="3.2.0">
<project timestamp="1738577162889" name="All files">
<metrics statements="29" coveredstatements="9" conditionals="6" coveredconditionals="0" methods="4" coveredmethods="1" elements="39" coveredelements="10" complexity="0" loc="29" ncloc="29" packages="1" files="1" classes="1"/>
<file name="web3-utils.js" path="/home/seven/Documents/codes/oss/akavelink/web3-utils.js">
<metrics statements="29" coveredstatements="9" conditionals="6" coveredconditionals="0" methods="4" coveredmethods="1"/>
<line num="1" count="1" type="stmt"/>
<line num="2" count="1" type="stmt"/>
<line num="5" count="1" type="stmt"/>
<line num="21" count="1" type="stmt"/>
<line num="33" count="4" type="stmt"/>
<line num="35" count="4" type="stmt"/>
<line num="36" count="0" type="stmt"/>
<line num="38" count="0" type="stmt"/>
<line num="43" count="0" type="stmt"/>
<line num="44" count="0" type="stmt"/>
<line num="47" count="0" type="cond" truecount="0" falsecount="2"/>
<line num="48" count="0" type="stmt"/>
<line num="49" count="0" type="stmt"/>
<line num="50" count="0" type="stmt"/>
<line num="53" count="0" type="stmt"/>
<line num="54" count="0" type="stmt"/>
<line num="57" count="0" type="stmt"/>
<line num="58" count="0" type="stmt"/>
<line num="60" count="0" type="stmt"/>
<line num="65" count="0" type="stmt"/>
<line num="66" count="0" type="stmt"/>
<line num="69" count="0" type="stmt"/>
<line num="70" count="0" type="cond" truecount="0" falsecount="2"/>
<line num="71" count="0" type="stmt"/>
<line num="73" count="0" type="stmt"/>
<line num="76" count="0" type="cond" truecount="0" falsecount="2"/>
<line num="79" count="4" type="stmt"/>
<line num="80" count="4" type="stmt"/>
<line num="84" count="1" type="stmt"/>
</file>
</project>
</coverage>
2 changes: 2 additions & 0 deletions coverage/coverage-final.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"/home/seven/Documents/codes/oss/akavelink/web3-utils.js": {"path":"/home/seven/Documents/codes/oss/akavelink/web3-utils.js","statementMap":{"0":{"start":{"line":1,"column":37},"end":{"line":1,"column":52}},"1":{"start":{"line":2,"column":32},"end":{"line":2,"column":56}},"2":{"start":{"line":5,"column":18},"end":{"line":18,"column":1}},"3":{"start":{"line":21,"column":21},"end":{"line":24,"column":2}},"4":{"start":{"line":33,"column":2},"end":{"line":81,"column":3}},"5":{"start":{"line":35,"column":24},"end":{"line":35,"column":59}},"6":{"start":{"line":36,"column":4},"end":{"line":36,"column":84}},"7":{"start":{"line":38,"column":18},"end":{"line":41,"column":6}},"8":{"start":{"line":43,"column":23},"end":{"line":45,"column":5}},"9":{"start":{"line":44,"column":6},"end":{"line":44,"column":54}},"10":{"start":{"line":47,"column":4},"end":{"line":51,"column":5}},"11":{"start":{"line":48,"column":19},"end":{"line":48,"column":61}},"12":{"start":{"line":49,"column":6},"end":{"line":49,"column":87}},"13":{"start":{"line":50,"column":6},"end":{"line":50,"column":18}},"14":{"start":{"line":53,"column":4},"end":{"line":53,"column":79}},"15":{"start":{"line":54,"column":4},"end":{"line":54,"column":60}},"16":{"start":{"line":54,"column":33},"end":{"line":54,"column":58}},"17":{"start":{"line":57,"column":27},"end":{"line":57,"column":62}},"18":{"start":{"line":58,"column":4},"end":{"line":58,"column":87}},"19":{"start":{"line":60,"column":21},"end":{"line":63,"column":6}},"20":{"start":{"line":65,"column":4},"end":{"line":67,"column":6}},"21":{"start":{"line":66,"column":6},"end":{"line":66,"column":54}},"22":{"start":{"line":69,"column":17},"end":{"line":69,"column":60}},"23":{"start":{"line":70,"column":4},"end":{"line":74,"column":5}},"24":{"start":{"line":71,"column":6},"end":{"line":71,"column":88}},"25":{"start":{"line":73,"column":6},"end":{"line":73,"column":74}},"26":{"start":{"line":76,"column":4},"end":{"line":76,"column":24}},"27":{"start":{"line":79,"column":4},"end":{"line":79,"column":80}},"28":{"start":{"line":80,"column":4},"end":{"line":80,"column":16}},"29":{"start":{"line":84,"column":0},"end":{"line":86,"column":2}}},"fnMap":{"0":{"name":"getLatestTransaction","decl":{"start":{"line":32,"column":15},"end":{"line":32,"column":35}},"loc":{"start":{"line":32,"column":56},"end":{"line":82,"column":1}},"line":32},"1":{"name":"(anonymous_1)","decl":{"start":{"line":43,"column":49},"end":{"line":43,"column":50}},"loc":{"start":{"line":44,"column":6},"end":{"line":44,"column":54}},"line":44},"2":{"name":"(anonymous_2)","decl":{"start":{"line":54,"column":22},"end":{"line":54,"column":23}},"loc":{"start":{"line":54,"column":33},"end":{"line":54,"column":58}},"line":54},"3":{"name":"(anonymous_3)","decl":{"start":{"line":65,"column":48},"end":{"line":65,"column":49}},"loc":{"start":{"line":66,"column":6},"end":{"line":66,"column":54}},"line":66}},"branchMap":{"0":{"loc":{"start":{"line":47,"column":4},"end":{"line":51,"column":5}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":51,"column":5}},{"start":{},"end":{}}],"line":47},"1":{"loc":{"start":{"line":70,"column":4},"end":{"line":74,"column":5}},"type":"if","locations":[{"start":{"line":70,"column":4},"end":{"line":74,"column":5}},{"start":{"line":72,"column":11},"end":{"line":74,"column":5}}],"line":70},"2":{"loc":{"start":{"line":76,"column":11},"end":{"line":76,"column":23}},"type":"binary-expr","locations":[{"start":{"line":76,"column":11},"end":{"line":76,"column":15}},{"start":{"line":76,"column":19},"end":{"line":76,"column":23}}],"line":76}},"s":{"0":1,"1":1,"2":1,"3":1,"4":4,"5":4,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":4,"28":4,"29":1},"f":{"0":4,"1":0,"2":0,"3":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0]},"inputSourceMap":null,"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"c4fd85cb3bf7b212da94ed27c364e83ce970565a"}
}
224 changes: 224 additions & 0 deletions coverage/lcov-report/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
body, html {
margin:0; padding: 0;
height: 100%;
}
body {
font-family: Helvetica Neue, Helvetica, Arial;
font-size: 14px;
color:#333;
}
.small { font-size: 12px; }
*, *:after, *:before {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
h1 { font-size: 20px; margin: 0;}
h2 { font-size: 14px; }
pre {
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
margin: 0;
padding: 0;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
a { color:#0074D9; text-decoration:none; }
a:hover { text-decoration:underline; }
.strong { font-weight: bold; }
.space-top1 { padding: 10px 0 0 0; }
.pad2y { padding: 20px 0; }
.pad1y { padding: 10px 0; }
.pad2x { padding: 0 20px; }
.pad2 { padding: 20px; }
.pad1 { padding: 10px; }
.space-left2 { padding-left:55px; }
.space-right2 { padding-right:20px; }
.center { text-align:center; }
.clearfix { display:block; }
.clearfix:after {
content:'';
display:block;
height:0;
clear:both;
visibility:hidden;
}
.fl { float: left; }
@media only screen and (max-width:640px) {
.col3 { width:100%; max-width:100%; }
.hide-mobile { display:none!important; }
}

.quiet {
color: #7f7f7f;
color: rgba(0,0,0,0.5);
}
.quiet a { opacity: 0.7; }

.fraction {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 10px;
color: #555;
background: #E8E8E8;
padding: 4px 5px;
border-radius: 3px;
vertical-align: middle;
}

div.path a:link, div.path a:visited { color: #333; }
table.coverage {
border-collapse: collapse;
margin: 10px 0 0 0;
padding: 0;
}

table.coverage td {
margin: 0;
padding: 0;
vertical-align: top;
}
table.coverage td.line-count {
text-align: right;
padding: 0 5px 0 20px;
}
table.coverage td.line-coverage {
text-align: right;
padding-right: 10px;
min-width:20px;
}

table.coverage td span.cline-any {
display: inline-block;
padding: 0 5px;
width: 100%;
}
.missing-if-branch {
display: inline-block;
margin-right: 5px;
border-radius: 3px;
position: relative;
padding: 0 4px;
background: #333;
color: yellow;
}

.skip-if-branch {
display: none;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: #ccc;
color: white;
}
.missing-if-branch .typ, .skip-if-branch .typ {
color: inherit !important;
}
.coverage-summary {
border-collapse: collapse;
width: 100%;
}
.coverage-summary tr { border-bottom: 1px solid #bbb; }
.keyline-all { border: 1px solid #ddd; }
.coverage-summary td, .coverage-summary th { padding: 10px; }
.coverage-summary tbody { border: 1px solid #bbb; }
.coverage-summary td { border-right: 1px solid #bbb; }
.coverage-summary td:last-child { border-right: none; }
.coverage-summary th {
text-align: left;
font-weight: normal;
white-space: nowrap;
}
.coverage-summary th.file { border-right: none !important; }
.coverage-summary th.pct { }
.coverage-summary th.pic,
.coverage-summary th.abs,
.coverage-summary td.pct,
.coverage-summary td.abs { text-align: right; }
.coverage-summary td.file { white-space: nowrap; }
.coverage-summary td.pic { min-width: 120px !important; }
.coverage-summary tfoot td { }

.coverage-summary .sorter {
height: 10px;
width: 7px;
display: inline-block;
margin-left: 0.5em;
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
.coverage-summary .sorted .sorter {
background-position: 0 -20px;
}
.coverage-summary .sorted-desc .sorter {
background-position: 0 -10px;
}
.status-line { height: 10px; }
/* yellow */
.cbranch-no { background: yellow !important; color: #111; }
/* dark red */
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
.low .chart { border:1px solid #C21F39 }
.highlighted,
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
background: #C21F39 !important;
}
/* medium red */
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
/* light red */
.low, .cline-no { background:#FCE1E5 }
/* light green */
.high, .cline-yes { background:rgb(230,245,208) }
/* medium green */
.cstat-yes { background:rgb(161,215,106) }
/* dark green */
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
.high .chart { border:1px solid rgb(77,146,33) }
/* dark yellow (gold) */
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
.medium .chart { border:1px solid #f9cd0b; }
/* light yellow */
.medium { background: #fff4c2; }

.cstat-skip { background: #ddd; color: #111; }
.fstat-skip { background: #ddd; color: #111 !important; }
.cbranch-skip { background: #ddd !important; color: #111; }

span.cline-neutral { background: #eaeaea; }

.coverage-summary td.empty {
opacity: .5;
padding-top: 4px;
padding-bottom: 4px;
line-height: 1;
color: #888;
}

.cover-fill, .cover-empty {
display:inline-block;
height: 12px;
}
.chart {
line-height: 0;
}
.cover-empty {
background: white;
}
.cover-full {
border-right: none !important;
}
pre.prettyprint {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.com { color: #999 !important; }
.ignore-none { color: #999; font-weight: normal; }

.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -48px;
}
.footer, .push {
height: 48px;
}
Loading