-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Agent Manager to utils package (#731)
# Motivation It is a simple but useful class to manage multiple HTTP agents associated with identities. # Credits The original scripts were created by @peterpeterparker in the [OISY repo](https://github.com/dfinity/oisy-wallet). This is just a transposition to a re-usable utility. # Changes New `AgentManager` class with: - `create` method: static constructor. - `createAgent` method: as the name suggests, it creates a new HTTP but do not cache it. - `getAgent` method: it fetches the HTTP agent among the cached, and if does note exists, it creates it and cache it. - `clearAgents` method: clean slate. # Tests I created a few tests for the few usecases. # Todos - [x] Maybe add more mocked identity, to check for concurrent multiple agents. --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
7ed25cc
commit 6c79ecc
Showing
6 changed files
with
289 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import type { HttpAgent } from "@dfinity/agent"; | ||
import { AgentManagerConfig } from "../utils/agent.utils"; | ||
|
||
export const mockHttpAgent = { | ||
call: jest.fn().mockResolvedValue({ | ||
ok: true, | ||
status: 200, | ||
statusText: "OK", | ||
json: async () => ({ data: "mocked call result" }), | ||
}), | ||
query: jest.fn().mockResolvedValue({ | ||
ok: true, | ||
status: 200, | ||
statusText: "OK", | ||
json: async () => ({ data: "mocked query result" }), | ||
}), | ||
fetchRootKey: jest.fn().mockResolvedValue(undefined), | ||
} as unknown as HttpAgent; | ||
|
||
export const mockHttpAgent2 = { | ||
call: jest.fn().mockResolvedValue({ | ||
ok: true, | ||
status: 200, | ||
statusText: "OK", | ||
json: async () => ({ data: "mocked call result" }), | ||
}), | ||
query: jest.fn().mockResolvedValue({ | ||
ok: true, | ||
status: 200, | ||
statusText: "OK", | ||
json: async () => ({ data: "mocked query result" }), | ||
}), | ||
fetchRootKey: jest.fn().mockResolvedValue(undefined), | ||
} as unknown as HttpAgent; | ||
|
||
export const mockAgentManagerConfig: AgentManagerConfig = { | ||
fetchRootKey: false, | ||
host: "https://icp-api.io", | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { Identity } from "@dfinity/agent"; | ||
import { Principal } from "@dfinity/principal"; | ||
|
||
export const mockPrincipalText = | ||
"xlmdg-vkosz-ceopx-7wtgu-g3xmd-koiyc-awqaq-7modz-zf6r6-364rh-oqe"; | ||
|
||
export const mockPrincipal = Principal.fromText(mockPrincipalText); | ||
|
||
export const mockIdentity = { | ||
getPrincipal: () => mockPrincipal, | ||
} as unknown as Identity; | ||
|
||
export const mockPrincipalText2 = | ||
"5uuwe-ggtgm-fonrs-rblmx-cfc23-pb3dg-iyfk2-dle5w-j5uev-ggmep-6ae"; | ||
|
||
export const mockPrincipal2 = Principal.fromText(mockPrincipalText2); | ||
|
||
export const mockIdentity2 = { | ||
getPrincipal: () => mockPrincipal2, | ||
} as unknown as Identity; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { HttpAgent } from "@dfinity/agent"; | ||
import { describe, expect } from "@jest/globals"; | ||
import { | ||
mockAgentManagerConfig, | ||
mockHttpAgent, | ||
mockHttpAgent2, | ||
} from "../mocks/agent.mock"; | ||
import { mockIdentity, mockIdentity2 } from "../mocks/identity.mock"; | ||
import { AgentManager } from "./agent.utils"; | ||
|
||
jest.mock("@dfinity/agent", () => ({ | ||
HttpAgent: { | ||
create: jest.fn(), | ||
}, | ||
})); | ||
|
||
describe("AgentManager", () => { | ||
let agentManager: AgentManager; | ||
const mockHttpAgentCreate = HttpAgent.create as jest.Mock; | ||
|
||
beforeEach(() => { | ||
agentManager = AgentManager.create(mockAgentManagerConfig); | ||
jest.clearAllMocks(); | ||
|
||
mockHttpAgentCreate.mockResolvedValueOnce(mockHttpAgent); | ||
}); | ||
|
||
describe("getAgent", () => { | ||
it("should create a new agent when there is none", async () => { | ||
const agent = await agentManager.getAgent({ identity: mockIdentity }); | ||
|
||
expect(mockHttpAgentCreate).toHaveBeenCalledWith( | ||
expect.objectContaining({ identity: mockIdentity }), | ||
); | ||
expect(agent).toBe(mockHttpAgent); | ||
}); | ||
|
||
it("should return cached agent if already created", async () => { | ||
await agentManager.getAgent({ identity: mockIdentity }); | ||
|
||
const agent = await agentManager.getAgent({ identity: mockIdentity }); | ||
|
||
expect(mockHttpAgentCreate).toHaveBeenCalledTimes(1); | ||
expect(agent).toBe(mockHttpAgent); | ||
}); | ||
|
||
it("should handle multiple agents for multiple identities", async () => { | ||
await agentManager.getAgent({ identity: mockIdentity }); | ||
|
||
mockHttpAgentCreate.mockResolvedValueOnce(mockHttpAgent2); | ||
|
||
const agent2 = await agentManager.getAgent({ identity: mockIdentity2 }); | ||
const agent1 = await agentManager.getAgent({ identity: mockIdentity }); | ||
|
||
expect(mockHttpAgentCreate).toHaveBeenCalledTimes(2); | ||
|
||
expect(agent1).toBe(mockHttpAgent); | ||
expect(agent1).not.toBe(mockHttpAgent2); | ||
|
||
expect(agent2).toBe(mockHttpAgent2); | ||
expect(agent2).not.toBe(mockHttpAgent); | ||
}); | ||
}); | ||
|
||
describe("clearAgents", () => { | ||
it("should clear cached agents", async () => { | ||
await agentManager.getAgent({ identity: mockIdentity }); | ||
|
||
const agentBefore = await agentManager.getAgent({ | ||
identity: mockIdentity, | ||
}); | ||
|
||
expect(agentBefore).toBe(mockHttpAgent); | ||
|
||
agentManager.clearAgents(); | ||
|
||
const agentAfter = await agentManager.getAgent({ | ||
identity: mockIdentity, | ||
}); | ||
|
||
expect(mockHttpAgentCreate).toHaveBeenCalledTimes(2); | ||
expect(agentAfter).not.toBe(mockHttpAgent); | ||
expect(agentAfter).toBeUndefined(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters