Skip to content

Commit 8f9e154

Browse files
authored
feat: Enhance DePINScan tool with advanced project filtering and optional fields (#66)
- Updated DepinScanProjectSchema to allow optional market cap field. - Improved GetProjectsToolSchema with detailed descriptions for filtering parameters, including minimum market cap, token price, daily earnings, and days to breakeven. - Implemented a new filterProjects function to streamline project filtering logic based on multiple criteria. - Expanded unit tests to cover new filtering capabilities and ensure robust functionality.
1 parent 58ceb50 commit 8f9e154

File tree

2 files changed

+341
-42
lines changed

2 files changed

+341
-42
lines changed

src/tools/__tests__/depinscan.test.ts

Lines changed: 211 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,7 @@ describe("DePINProjectsTool", () => {
252252

253253
it("should initialize with correct properties", () => {
254254
expect(projectsTool.name).toBe("get_depin_projects");
255-
expect(projectsTool.description).toBe(
256-
"Fetches DePINScan projects and their metrics"
257-
);
255+
expect(projectsTool.description).toContain("Fetches DePINScan projects");
258256
expect(projectsTool.schema).toHaveLength(1);
259257
expect(projectsTool.schema[0].name).toBe("get_depin_projects");
260258
});
@@ -393,7 +391,9 @@ describe("DePINProjectsTool", () => {
393391

394392
it("should return all projects with transformed data", async () => {
395393
const result = await projectsTool.schema[0].tool.execute(
396-
{},
394+
{
395+
requireDescription: true,
396+
},
397397
executionOptions
398398
);
399399

@@ -484,5 +484,212 @@ describe("DePINProjectsTool", () => {
484484
expect(solana!.estimatedDailyEarnings).toBe("0");
485485
expect(solana!.daysToBreakeven).toBe(0);
486486
});
487+
488+
it("should filter out projects with undefined token when requireToken is true", async () => {
489+
const mockProjectsWithNullToken = [
490+
{ ...mockProjects[0], token: null },
491+
{ ...mockProjects[1], token: undefined },
492+
{ ...mockProjects[2] }, // has token
493+
];
494+
495+
vi.mocked(fetch).mockResolvedValueOnce({
496+
ok: true,
497+
json: () => Promise.resolve(mockProjectsWithNullToken),
498+
} as Response);
499+
500+
const result = await projectsTool.schema[0].tool.execute(
501+
{ requireToken: true },
502+
executionOptions
503+
);
504+
505+
expect(result.totalProjects).toBe(1);
506+
expect(result.projects[0].name).toBe("IoTeX");
507+
});
508+
509+
it("should filter projects by minimum token price", async () => {
510+
const result = await projectsTool.schema[0].tool.execute(
511+
{ minTokenPrice: 1.0 },
512+
executionOptions
513+
);
514+
515+
expect(result.totalProjects).toBe(1);
516+
expect(result.projects[0].name).toBe("Solana");
517+
});
518+
519+
it("should filter projects by minimum estimated daily earnings", async () => {
520+
const result = await projectsTool.schema[0].tool.execute(
521+
{ minDailyEarnings: 1.5 },
522+
executionOptions
523+
);
524+
525+
expect(result.totalProjects).toBe(2);
526+
expect(result.projects.map((p) => p.name)).toEqual(["Filecoin", "IoTeX"]);
527+
});
528+
529+
it("should filter projects by maximum days to breakeven", async () => {
530+
const result = await projectsTool.schema[0].tool.execute(
531+
{ maxDaysToBreakeven: 50 },
532+
executionOptions
533+
);
534+
535+
expect(result.totalProjects).toBe(1);
536+
expect(result.projects[0].name).toBe("Filecoin");
537+
});
538+
539+
it("should combine multiple filters correctly", async () => {
540+
const result = await projectsTool.schema[0].tool.execute(
541+
{
542+
requireToken: true,
543+
minTokenPrice: 0.02,
544+
minDailyEarnings: 1.0,
545+
maxDaysToBreakeven: 100,
546+
},
547+
executionOptions
548+
);
549+
550+
expect(result.totalProjects).toBe(1);
551+
expect(result.projects[0].name).toBe("IoTeX");
552+
});
553+
554+
it("should exclude descriptions when requireDescription is false", async () => {
555+
const result = await projectsTool.schema[0].tool.execute(
556+
{ requireDescription: false },
557+
executionOptions
558+
);
559+
560+
expect(result.totalProjects).toBe(3);
561+
expect(result.projects[0]).not.toHaveProperty("description");
562+
expect(result.projects[1]).not.toHaveProperty("description");
563+
expect(result.projects[2]).not.toHaveProperty("description");
564+
});
565+
566+
it("should include descriptions when requireDescription is true", async () => {
567+
const result = await projectsTool.schema[0].tool.execute(
568+
{ requireDescription: true },
569+
executionOptions
570+
);
571+
572+
expect(result.totalProjects).toBe(3);
573+
expect(result.projects[0].description).toBe(
574+
"Solana is a general purpose layer 1 blockchain..."
575+
);
576+
expect(result.projects[1].description).toBe(
577+
"Filecoin is a peer-to-peer network..."
578+
);
579+
expect(result.projects[2].description).toBe("IoTeX network...");
580+
});
581+
582+
it("should exclude descriptions by default", async () => {
583+
const result = await projectsTool.schema[0].tool.execute(
584+
{},
585+
executionOptions
586+
);
587+
588+
expect(result.totalProjects).toBe(3);
589+
expect(result.projects[0]).not.toHaveProperty("description");
590+
expect(result.projects[1]).not.toHaveProperty("description");
591+
expect(result.projects[2]).not.toHaveProperty("description");
592+
});
593+
594+
describe("numeric filters", () => {
595+
const mockProjectsWithZeros = [
596+
{
597+
...mockProjects[0],
598+
estimated_daily_earnings: "0",
599+
market_cap: "0",
600+
token_price: "0",
601+
total_devices: "0",
602+
days_to_breakeven: "0",
603+
},
604+
{
605+
...mockProjects[1],
606+
estimated_daily_earnings: "10",
607+
market_cap: "1000000",
608+
token_price: "1.5",
609+
total_devices: "100",
610+
days_to_breakeven: "30",
611+
},
612+
{
613+
...mockProjects[2],
614+
estimated_daily_earnings: null,
615+
market_cap: undefined,
616+
token_price: null,
617+
total_devices: "0",
618+
days_to_breakeven: undefined,
619+
},
620+
];
621+
622+
beforeEach(() => {
623+
vi.mocked(fetch).mockResolvedValue({
624+
ok: true,
625+
json: () => Promise.resolve(mockProjectsWithZeros),
626+
} as Response);
627+
});
628+
629+
it("should filter out zero and null/undefined values for minDailyEarnings", async () => {
630+
const result = await projectsTool.schema[0].tool.execute(
631+
{ minDailyEarnings: 0 },
632+
executionOptions
633+
);
634+
635+
expect(result.totalProjects).toBe(1);
636+
expect(result.projects[0].name).toBe("Filecoin");
637+
});
638+
639+
it("should filter out zero and null/undefined values for minMarketCap", async () => {
640+
const result = await projectsTool.schema[0].tool.execute(
641+
{ minMarketCap: 0 },
642+
executionOptions
643+
);
644+
645+
expect(result.totalProjects).toBe(1);
646+
expect(result.projects[0].name).toBe("Filecoin");
647+
});
648+
649+
it("should filter out zero and null/undefined values for minTokenPrice", async () => {
650+
const result = await projectsTool.schema[0].tool.execute(
651+
{ minTokenPrice: 0 },
652+
executionOptions
653+
);
654+
655+
expect(result.totalProjects).toBe(1);
656+
expect(result.projects[0].name).toBe("Filecoin");
657+
});
658+
659+
it("should filter out zero and null/undefined values for minDevices", async () => {
660+
const result = await projectsTool.schema[0].tool.execute(
661+
{ minDevices: 0 },
662+
executionOptions
663+
);
664+
665+
expect(result.totalProjects).toBe(1);
666+
expect(result.projects[0].name).toBe("Filecoin");
667+
});
668+
669+
it("should filter out zero and null/undefined values for maxDaysToBreakeven", async () => {
670+
const result = await projectsTool.schema[0].tool.execute(
671+
{ maxDaysToBreakeven: 100 },
672+
executionOptions
673+
);
674+
675+
expect(result.totalProjects).toBe(1);
676+
expect(result.projects[0].name).toBe("Filecoin");
677+
});
678+
679+
it("should combine multiple numeric filters correctly", async () => {
680+
const result = await projectsTool.schema[0].tool.execute(
681+
{
682+
minDailyEarnings: 0,
683+
minMarketCap: 0,
684+
minTokenPrice: 0,
685+
maxDaysToBreakeven: 100,
686+
},
687+
executionOptions
688+
);
689+
690+
expect(result.totalProjects).toBe(1);
691+
expect(result.projects[0].name).toBe("Filecoin");
692+
});
693+
});
487694
});
488695
});

0 commit comments

Comments
 (0)