forked from elizaOS/eliza
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test configuration and tests for client-lens (elizaOS#2534)
* client-lens: test configuration * client-lens: test utils * client-lens: client tests * client-lens: interaction tests * client-lens: post tests
- Loading branch information
1 parent
227d9de
commit 3a69164
Showing
7 changed files
with
404 additions
and
7 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { describe, it, expect, vi, beforeEach } from 'vitest'; | ||
import { LensClient } from '../src/client'; | ||
import { LensClient as LensClientCore, LimitType, PublicationType } from '@lens-protocol/client'; | ||
|
||
// Mock dependencies | ||
vi.mock('@lens-protocol/client', async () => { | ||
const actual = await vi.importActual('@lens-protocol/client'); | ||
return { | ||
...actual, | ||
LensClient: vi.fn().mockImplementation(() => ({ | ||
authentication: { | ||
generateChallenge: vi.fn().mockResolvedValue({ id: 'challenge-id', text: 'challenge-text' }), | ||
authenticate: vi.fn().mockResolvedValue({ accessToken: 'mock-token', refreshToken: 'mock-refresh' }) | ||
}, | ||
profile: { | ||
fetch: vi.fn().mockResolvedValue({ | ||
id: '0x01', | ||
handle: { localName: 'test.lens' }, | ||
metadata: { | ||
displayName: 'Test User', | ||
bio: 'Test bio', | ||
picture: { | ||
uri: 'https://example.com/pic-raw.jpg' | ||
} | ||
} | ||
}) | ||
}, | ||
publication: { | ||
fetchAll: vi.fn().mockResolvedValue({ | ||
items: [ | ||
{ | ||
id: 'pub-1', | ||
metadata: { content: 'Test post' }, | ||
stats: { reactions: 10 } | ||
} | ||
] | ||
}) | ||
} | ||
})) | ||
}; | ||
}); | ||
|
||
describe('LensClient', () => { | ||
let client: LensClient; | ||
const mockRuntime = { | ||
name: 'test-runtime', | ||
memory: new Map(), | ||
getMemory: vi.fn(), | ||
setMemory: vi.fn(), | ||
clearMemory: vi.fn() | ||
}; | ||
const mockAccount = { | ||
address: '0x123' as `0x${string}`, | ||
privateKey: '0xabc' as `0x${string}`, | ||
signMessage: vi.fn().mockResolvedValue('signed-message'), | ||
signTypedData: vi.fn() | ||
}; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
client = new LensClient({ | ||
runtime: mockRuntime, | ||
cache: new Map(), | ||
account: mockAccount, | ||
profileId: '0x01' as `0x${string}` | ||
}); | ||
}); | ||
|
||
describe('authenticate', () => { | ||
it('should authenticate successfully', async () => { | ||
await client.authenticate(); | ||
expect(client['authenticated']).toBe(true); | ||
expect(client['core'].authentication.generateChallenge).toHaveBeenCalledWith({ | ||
signedBy: mockAccount.address, | ||
for: '0x01' | ||
}); | ||
expect(mockAccount.signMessage).toHaveBeenCalledWith({ message: 'challenge-text' }); | ||
}); | ||
|
||
it('should handle authentication errors', async () => { | ||
const mockError = new Error('Auth failed'); | ||
vi.mocked(client['core'].authentication.generateChallenge).mockRejectedValueOnce(mockError); | ||
|
||
await expect(client.authenticate()).rejects.toThrow('Auth failed'); | ||
expect(client['authenticated']).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('getPublicationsFor', () => { | ||
it('should fetch publications successfully', async () => { | ||
const publications = await client.getPublicationsFor('0x123'); | ||
expect(publications).toHaveLength(1); | ||
expect(publications[0].id).toBe('pub-1'); | ||
expect(client['core'].publication.fetchAll).toHaveBeenCalledWith({ | ||
limit: LimitType.Fifty, | ||
where: { | ||
from: ['0x123'], | ||
publicationTypes: [PublicationType.Post] | ||
} | ||
}); | ||
}); | ||
|
||
it('should handle fetch errors', async () => { | ||
vi.mocked(client['core'].publication.fetchAll).mockRejectedValueOnce(new Error('Fetch failed')); | ||
await expect(client.getPublicationsFor('0x123')).rejects.toThrow('Fetch failed'); | ||
}); | ||
}); | ||
|
||
describe('getProfile', () => { | ||
it('should fetch profile successfully', async () => { | ||
const profile = await client.getProfile('0x123'); | ||
expect(profile).toBeDefined(); | ||
expect(profile.id).toBe('0x01'); | ||
expect(profile.handle).toBe('test.lens'); | ||
expect(profile.pfp).toBe('https://example.com/pic-raw.jpg'); | ||
expect(client['core'].profile.fetch).toHaveBeenCalledWith({ forProfileId: '0x123' }); | ||
}); | ||
|
||
it('should handle profile fetch errors', async () => { | ||
vi.mocked(client['core'].profile.fetch).mockRejectedValueOnce(new Error('Profile fetch failed')); | ||
await expect(client.getProfile('0x123')).rejects.toThrow('Profile fetch failed'); | ||
}); | ||
}); | ||
}); |
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,121 @@ | ||
import { describe, it, expect, vi, beforeEach } from 'vitest'; | ||
import { createTestInteraction, handleTestInteraction } from './test-utils'; | ||
import { LensClient } from '../src/client'; | ||
import type { AnyPublicationFragment, ProfileFragment } from '@lens-protocol/client'; | ||
|
||
// Mock LensClient | ||
vi.mock('../src/client', () => ({ | ||
LensClient: vi.fn().mockImplementation(() => ({ | ||
authenticate: vi.fn().mockResolvedValue(undefined), | ||
mirror: vi.fn().mockResolvedValue({ id: 'mirror-1' }), | ||
comment: vi.fn().mockResolvedValue({ id: 'comment-1' }), | ||
like: vi.fn().mockResolvedValue({ id: 'like-1' }), | ||
follow: vi.fn().mockResolvedValue({ id: 'follow-1' }) | ||
})) | ||
})); | ||
|
||
describe('Interactions', () => { | ||
const mockPublication = { | ||
id: 'pub-1', | ||
metadata: { | ||
content: 'Test publication' | ||
}, | ||
stats: { | ||
totalAmountOfMirrors: 5, | ||
totalAmountOfComments: 3, | ||
totalUpvotes: 10 | ||
} | ||
} as unknown as AnyPublicationFragment; | ||
|
||
const mockProfile = { | ||
id: '0x01', | ||
handle: 'test.lens', | ||
stats: { | ||
totalFollowers: 100, | ||
totalFollowing: 50 | ||
} | ||
} as unknown as ProfileFragment; | ||
|
||
describe('createTestInteraction', () => { | ||
it('should create mirror interaction when conditions are met', () => { | ||
const interaction = createTestInteraction(mockPublication, mockProfile); | ||
expect(interaction).toBeDefined(); | ||
if (interaction) { | ||
expect(['MIRROR', 'COMMENT', 'LIKE', 'FOLLOW']).toContain(interaction.type); | ||
} | ||
}); | ||
|
||
it('should return null when no interaction is needed', () => { | ||
const lowStatsPublication = { | ||
...mockPublication, | ||
stats: { | ||
totalAmountOfMirrors: 0, | ||
totalAmountOfComments: 0, | ||
totalUpvotes: 0 | ||
} | ||
} as unknown as AnyPublicationFragment; | ||
const interaction = createTestInteraction(lowStatsPublication, mockProfile); | ||
expect(interaction).toBeNull(); | ||
}); | ||
}); | ||
|
||
describe('handleTestInteraction', () => { | ||
let client: LensClient; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
client = new LensClient({ | ||
runtime: { | ||
name: 'test-runtime', | ||
memory: new Map(), | ||
getMemory: vi.fn(), | ||
setMemory: vi.fn(), | ||
clearMemory: vi.fn() | ||
}, | ||
cache: new Map(), | ||
account: { | ||
address: '0x123' as `0x${string}`, | ||
privateKey: '0xabc' as `0x${string}`, | ||
signMessage: vi.fn(), | ||
signTypedData: vi.fn() | ||
}, | ||
profileId: '0x01' as `0x${string}` | ||
}); | ||
}); | ||
|
||
it('should handle mirror interaction successfully', async () => { | ||
const interaction = { | ||
type: 'MIRROR' as const, | ||
publicationId: 'pub-1' | ||
}; | ||
|
||
const result = await handleTestInteraction(client, interaction); | ||
expect(result).toBeDefined(); | ||
expect(result.id).toBe('mirror-1'); | ||
expect(client.mirror).toHaveBeenCalledWith('pub-1'); | ||
}); | ||
|
||
it('should handle comment interaction successfully', async () => { | ||
const interaction = { | ||
type: 'COMMENT' as const, | ||
publicationId: 'pub-1', | ||
content: 'Test comment' | ||
}; | ||
|
||
const result = await handleTestInteraction(client, interaction); | ||
expect(result).toBeDefined(); | ||
expect(result.id).toBe('comment-1'); | ||
expect(client.comment).toHaveBeenCalledWith('pub-1', 'Test comment'); | ||
}); | ||
|
||
it('should handle interaction errors', async () => { | ||
const interaction = { | ||
type: 'MIRROR' as const, | ||
publicationId: 'pub-1' | ||
}; | ||
|
||
vi.mocked(client.mirror).mockRejectedValueOnce(new Error('Mirror failed')); | ||
await expect(handleTestInteraction(client, interaction)).rejects.toThrow('Mirror failed'); | ||
}); | ||
}); | ||
}); |
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,64 @@ | ||
import { describe, it, expect, vi, beforeEach } from 'vitest'; | ||
import { createTestPost } from './test-utils'; | ||
import { LensClient } from '../src/client'; | ||
|
||
// Mock dependencies | ||
vi.mock('../src/client', () => ({ | ||
LensClient: vi.fn().mockImplementation(() => ({ | ||
authenticate: vi.fn().mockResolvedValue(undefined), | ||
post: vi.fn().mockResolvedValue({ id: 'post-1' }) | ||
})) | ||
})); | ||
|
||
describe('Post Functions', () => { | ||
let client: LensClient; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
client = new LensClient({ | ||
runtime: { | ||
name: 'test-runtime', | ||
memory: new Map(), | ||
getMemory: vi.fn(), | ||
setMemory: vi.fn(), | ||
clearMemory: vi.fn() | ||
}, | ||
cache: new Map(), | ||
account: { | ||
address: '0x123' as `0x${string}`, | ||
privateKey: '0xabc' as `0x${string}`, | ||
signMessage: vi.fn(), | ||
signTypedData: vi.fn() | ||
}, | ||
profileId: '0x01' as `0x${string}` | ||
}); | ||
}); | ||
|
||
describe('createTestPost', () => { | ||
it('should create a post successfully', async () => { | ||
const content = 'Test post content'; | ||
const result = await createTestPost(client, content); | ||
|
||
expect(result).toBeDefined(); | ||
expect(result.id).toBe('post-1'); | ||
expect(client.post).toHaveBeenCalledWith(content); | ||
}); | ||
|
||
it('should handle post creation errors', async () => { | ||
const content = 'Test post content'; | ||
vi.mocked(client.post).mockRejectedValueOnce(new Error('Post creation failed')); | ||
|
||
await expect(createTestPost(client, content)).rejects.toThrow('Post creation failed'); | ||
}); | ||
|
||
it('should handle empty content', async () => { | ||
const content = ''; | ||
await expect(createTestPost(client, content)).rejects.toThrow('Post content cannot be empty'); | ||
}); | ||
|
||
it('should handle very long content', async () => { | ||
const content = 'a'.repeat(5001); // Assuming max length is 5000 | ||
await expect(createTestPost(client, content)).rejects.toThrow('Post content too long'); | ||
}); | ||
}); | ||
}); |
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,63 @@ | ||
import type { AnyPublicationFragment } from "@lens-protocol/client"; | ||
import type { LensClient } from "../src/client"; | ||
import type { Profile } from "../src/types"; | ||
|
||
export interface TestInteraction { | ||
type: 'MIRROR' | 'COMMENT' | 'LIKE' | 'FOLLOW'; | ||
publicationId?: string; | ||
content?: string; | ||
} | ||
|
||
export function createTestInteraction(publication: AnyPublicationFragment, profile: Profile): TestInteraction | null { | ||
const stats = publication.stats; | ||
|
||
// Simple heuristic: if the publication has good engagement, mirror it | ||
if (stats.totalAmountOfMirrors > 3 || stats.totalAmountOfComments > 2 || stats.totalUpvotes > 5) { | ||
return { | ||
type: 'MIRROR', | ||
publicationId: publication.id | ||
}; | ||
} | ||
|
||
// If the publication is engaging but not viral, comment on it | ||
if (stats.totalAmountOfComments > 0 || stats.totalUpvotes > 2) { | ||
return { | ||
type: 'COMMENT', | ||
publicationId: publication.id, | ||
content: 'Interesting perspective!' | ||
}; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
export async function handleTestInteraction(client: LensClient, interaction: TestInteraction) { | ||
switch (interaction.type) { | ||
case 'MIRROR': | ||
if (!interaction.publicationId) throw new Error('Publication ID required for mirror'); | ||
return await client.mirror(interaction.publicationId); | ||
case 'COMMENT': | ||
if (!interaction.publicationId || !interaction.content) { | ||
throw new Error('Publication ID and content required for comment'); | ||
} | ||
return await client.comment(interaction.publicationId, interaction.content); | ||
case 'LIKE': | ||
if (!interaction.publicationId) throw new Error('Publication ID required for like'); | ||
return await client.like(interaction.publicationId); | ||
case 'FOLLOW': | ||
if (!interaction.publicationId) throw new Error('Profile ID required for follow'); | ||
return await client.follow(interaction.publicationId); | ||
default: | ||
throw new Error('Unknown interaction type'); | ||
} | ||
} | ||
|
||
export async function createTestPost(client: LensClient, content: string) { | ||
if (!content) { | ||
throw new Error('Post content cannot be empty'); | ||
} | ||
if (content.length > 5000) { | ||
throw new Error('Post content too long'); | ||
} | ||
return await client.post(content); | ||
} |
Oops, something went wrong.