UNPKG

@myria/leaderboard-ts-sdk

Version:

Myria Leaderboard typescripts SDK which will manage leaderboard for game developers

449 lines 30.1 kB
import { LeaderboardManager } from '../modules/LeaderboardManager'; import { LeaderboardAPI } from '../core/apis/LeaderboardAPI'; import { EOrderBy, } from '../types/ILeaderboard.types'; import { EnvTypes } from 'myria-core-sdk'; // Mock the LeaderboardAPI class jest.mock('../core/apis/LeaderboardAPI'); const mockLeaderboardsData = { items: [ { name: 'Leaderboard 1', description: 'Test leaderboard 1', updateScoreStrategy: 'ACCUMULATE', gameName: 'Test Game', totalPeriods: 10, livePeriodInDays: 30, availableAt: '2024-01-01T00:00:00Z', status: 'active', currentPeriod: 1, dataPersistenceInDays: 365, expireAt: '2025-01-01T00:00:00Z', id: 1, airdropContractAddress: '', daysToNextPeriod: '', gameMetadata: {}, configMetadata: {}, isApplyRewardAllPeriods: false, }, ], meta: { totalItems: 1, itemCount: 1, itemsPerPage: 10, totalPages: 1, currentPage: 1, }, }; const mockApiResponse = { status: 'success', data: mockLeaderboardsData, }; describe('LeaderboardManager', () => { let leaderboardManager; let mockLeaderboardAPI; const env = EnvTypes.STAGING; // Example environment const developerApiKey = 'Developer_Api_Key'; beforeEach(() => { mockLeaderboardAPI = new LeaderboardAPI(env); const leaderboardParams = { env: EnvTypes.STAGING, apiKey: developerApiKey, }; leaderboardManager = new LeaderboardManager(leaderboardParams); leaderboardManager['leaderboardAPI'] = mockLeaderboardAPI; }); afterEach(() => { jest.clearAllMocks(); // Reset mocks }); // Get List score by leaderId it('should getScoreByLeaderboardId a leaderboard successfully', async () => { // Given const queryScoreParams = { page: 1, limit: 10, leaderboardId: 1, sortingField: 'score', orderBy: EOrderBy.DESC, filters: { period: 1, userId: '0x5eb1a04db9190230f231627c1b673cdb19f77374ff4780158322f23177e801', }, }; const queryScoreLeaderboardData = { items: [ { id: 5, userId: '0x5eb1a04db9190230f231627c1b673cdb19f77374ff4780158322f23177e801', leaderboardId: 21, period: 1, score: 130, createdAt: '2024-08-12T08:34:32.291Z', updatedAt: '2024-08-16T03:27:59.919Z', rank: 1, }, ], meta: { totalItems: 1, itemCount: 1, itemsPerPage: 10, totalPages: 1, currentPage: 1, }, }; // Act mockLeaderboardAPI.getScoresByLeaderboardId.mockResolvedValue({ status: 'success', data: queryScoreLeaderboardData, }); // Act const result = await leaderboardManager.queryScoresByLeaderboardId(queryScoreParams); // Assert expect(result.data).toEqual(queryScoreLeaderboardData); }); it('should throw an error if leaderboard query fails', async () => { // Given const queryScoreParams = { page: 1, limit: 10, leaderboardId: 19, sortingField: 'score', orderBy: EOrderBy.DESC, filters: { period: 1, userId: '2', }, }; mockLeaderboardAPI.getScoresByLeaderboardId.mockRejectedValue({ status: 'error', data: null, }); // Act & Assert await expect(leaderboardManager.queryScoresByLeaderboardId(queryScoreParams)).rejects.toThrow('Failed to query scores'); }); it('should validate missing query parameters', async () => { const invalidParams = [ { page: 0, leaderboardId: 1, }, { page: 1, leaderboardId: 0, }, ]; for (const params of invalidParams) { await expect(leaderboardManager.queryScoresByLeaderboardId(params)).rejects.toThrow(); } }); it('should validate missing query filters userId parameters', async () => { const invalidParams = [ { page: 1, leaderboardId: 21, filters: { userId: '', score: 0, }, }, ]; for (const params of invalidParams) { await expect(leaderboardManager.queryScoresByLeaderboardId(params)).rejects.toThrow(); } }); // Get list scores by leaderboard id and user id it('should getScoreByLeaderboardIdAndUserId a leaderboard successfully', async () => { // Given const queryScoreParams = { leaderboardId: 21, userId: '0x5eb1a04db9190230f231627c1b673cdb19f77374ff4780158322f23177e801', period: 1, }; const queryScoreLeaderboardData = { leaderboardId: 21, userId: '0x5eb1a04db9190230f231627c1b673cdb19f77374ff4780158322f23177e803', score: 24, period: 1, leaderboard: { id: 21, name: 'weekly-demo-function', description: 'Testing weekly-demo-function', updateScoreStrategy: 'ACCUMULATE', gameName: 'Demo', status: 'ACTIVE', livePeriodInDays: 7, enablePeriodRefresh: false, currentPeriod: 1, dataPersistenceInDays: 14, expireAt: '2024-08-19T08:31:54.891Z', availableAt: '2024-08-12T08:31:54.891Z', airdropContractAddress: '', daysToNextPeriod: '', gameMetadata: {}, configMetadata: {}, isApplyRewardAllPeriods: false, }, user: { userId: '0x5eb1a04db9190230f231627c1b673cdb19f77374ff4780158322f23177e803', username: '', displayName: '', }, }; // Act mockLeaderboardAPI.getScoresByLeaderboardIdAndUserId.mockResolvedValue({ status: 'success', data: queryScoreLeaderboardData, }); // Act const result = await leaderboardManager.getScoresByPlayerId(queryScoreParams); // Assert expect(result.data).toEqual(queryScoreLeaderboardData); }); it('should throw an error if leaderboard id and userId query fails', async () => { // Given const queryScoreParams = { leaderboardId: 19, userId: '0x5eb1a04db9190230f231627c1b673cdb19f77374ff4780158322f23177e801', }; mockLeaderboardAPI.getScoresByLeaderboardIdAndUserId.mockRejectedValue({ status: 'error', data: null, }); // Act & Assert await expect(leaderboardManager.getScoresByPlayerId(queryScoreParams)).rejects.toThrow('Failed to query scores by player id: error'); }); it('should validate missing query score by leaderboard and user id parameters', async () => { const invalidParams = [ { leaderboardId: 0, userId: '0x5eb1a04db9190230f231627c1b673cdb19f77374ff4780158322f23177e801', }, { leaderboardId: 21, userId: '', }, ]; for (const params of invalidParams) { await expect(leaderboardManager.getScoresByPlayerId(params)).rejects.toThrow(); } }); // Create leaderboard it('should create a leaderboard successfully', async () => { // Given const leaderboardParams = { name: 'Test Leaderboard', description: 'A test leaderboard', gameName: 'Test Game', livePeriodInDays: 10, totalPeriods: 5, updateScoreStrategy: 'ACCUMULATE', availableAt: '', enableMetadata: false, }; const leaderboardData = { id: 1, ...leaderboardParams, status: 'ACTIVE', currentPeriod: 5, dataPersistenceInDays: 10, expireAt: '', updatedAt: '', createdAt: '', enablePeriodRefresh: false, developerId: '', metadata: undefined, airdropContractAddress: '', daysToNextPeriod: '', whitelist: undefined, rewards: undefined, gameMetadata: {}, configMetadata: {}, isApplyRewardAllPeriods: false, }; mockLeaderboardAPI.createLeaderboard.mockResolvedValue({ status: 'success', data: leaderboardData, }); // Act const result = await leaderboardManager.createLeaderboard(leaderboardParams); // Assert expect(result).toEqual(leaderboardData); expect(mockLeaderboardAPI.createLeaderboard).toHaveBeenCalledWith(leaderboardParams, developerApiKey); }); it('should throw an error if leaderboard creation fails', async () => { // Given const leaderboardParams = { name: 'Test Leaderboard', description: 'A test leaderboard', gameName: 'Test Game', livePeriodInDays: 10, totalPeriods: 5, updateScoreStrategy: 'OVERWRITE', availableAt: '', enableMetadata: false, }; mockLeaderboardAPI.createLeaderboard.mockResolvedValue({ status: 'error', data: null, }); // Act & Assert await expect(leaderboardManager.createLeaderboard(leaderboardParams)).rejects.toThrow('Create leaderboard failed: {"status":"error","data":null}'); }); it('should validate missing parameters', async () => { const invalidParams = [ { name: '', description: 'desc', gameName: 'game', livePeriodInDays: 10, totalPeriods: 5, updateScoreStrategy: 'ACCUMULATE', }, { name: 'name', description: '', gameName: 'game', livePeriodInDays: 10, totalPeriods: 5, updateScoreStrategy: 'ACCUMULATE', }, { name: 'name', description: 'desc', gameName: '', livePeriodInDays: 10, totalPeriods: 5, updateScoreStrategy: 'ACCUMULATE', }, { name: 'name', description: 'desc', gameName: 'game', livePeriodInDays: 0, totalPeriods: 5, updateScoreStrategy: 'ACCUMULATE', }, { name: 'name', description: 'desc', gameName: 'game', livePeriodInDays: 10, totalPeriods: 0, updateScoreStrategy: 'ACCUMULATE', }, { name: 'name', description: 'desc', gameName: 'game', livePeriodInDays: 10, totalPeriods: 5, updateScoreStrategy: '', }, ]; for (const params of invalidParams) { await expect(leaderboardManager.createLeaderboard(params)).rejects.toThrow(); } }); // Post score it('should postscore a leaderboard successfully', async () => { // Given const newScoreParams = { leaderboardId: 21, items: [ { score: 240, displayName: 'userDisplayname', userId: 'userId', username: 'username', }, ], }; const newScoreLeaderboardData = { success: [ { score: 240, userId: '0x724f337bf0fa934db9aa3ec6dea49b03c54ad3cc', username: 'vule', id: 39, displayName: 'Test', }, ], failed: [], }; // Act mockLeaderboardAPI.postNewScore.mockResolvedValue({ status: 'success', data: newScoreLeaderboardData, }); // Act const result = await leaderboardManager.postPlayerScore(newScoreParams); // Assert expect(result.data).toEqual(newScoreLeaderboardData); }); it('should throw an error if post score fails', async () => { // Given const newScoreParams = { leaderboardId: 19, items: [ { score: 0, userId: 'userId', displayName: 'username', username: 'username', }, ], }; mockLeaderboardAPI.postNewScore.mockRejectedValue({ status: 'error', data: null, }); // Act & Assert await expect(leaderboardManager.postPlayerScore(newScoreParams)).rejects.toThrow('Failed to post score for player'); }); it('should validate missing post score parameters', async () => { const invalidParams = [ { leaderboardId: 0, items: [ { score: 20, userId: '0x724f337bF0Fa934dB9aa3ec6dEa49B03c54AD3cc', username: 'username', displayName: 'displayname', }, ], }, { leaderboardId: 21, items: [ { score: 20, userId: '', username: 'username', displayName: 'displayname', }, ], }, ]; for (const params of invalidParams) { await expect(leaderboardManager.postPlayerScore(params)).rejects.toThrow(); } }); it('should retrieve leaderboards successfully', async () => { mockLeaderboardAPI.getLeaderboards.mockResolvedValue(mockApiResponse); const params = { page: 1, limit: 10 }; // Act const result = await leaderboardManager.getLeaderboards(params); // Assert expect(mockLeaderboardAPI.getLeaderboards).toHaveBeenCalledWith(params, 'Developer_Api_Key'); expect(result).toEqual(mockApiResponse); }); it('should throw an error if the API call for retrieve leaderboards fails', async () => { const errorMessage = 'Failed to retrieve leaderboard with Params {"page":1,"limit":10}'; mockLeaderboardAPI.getLeaderboards.mockRejectedValue(new Error(errorMessage)); const params = { page: 1, limit: 10 }; // Act & Assert await expect(leaderboardManager.getLeaderboards(params)).rejects.toThrow(`Failed to retrieve leaderboard with Params ${JSON.stringify(params)}`); expect(mockLeaderboardAPI.getLeaderboards).toHaveBeenCalledWith(params, 'Developer_Api_Key'); }); }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTGVhZGVyYm9hcmQudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy90ZXN0cy9MZWFkZXJib2FyZC50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ25FLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUM3RCxPQUFPLEVBSUwsUUFBUSxHQU9ULE1BQU0sNkJBQTZCLENBQUM7QUFDckMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBSTFDLGdDQUFnQztBQUNoQyxJQUFJLENBQUMsSUFBSSxDQUFDLDZCQUE2QixDQUFDLENBQUM7QUFFekMsTUFBTSxvQkFBb0IsR0FBK0M7SUFDdkUsS0FBSyxFQUFFO1FBQ0w7WUFDRSxJQUFJLEVBQUUsZUFBZTtZQUNyQixXQUFXLEVBQUUsb0JBQW9CO1lBQ2pDLG1CQUFtQixFQUFFLFlBQVk7WUFDakMsUUFBUSxFQUFFLFdBQVc7WUFDckIsWUFBWSxFQUFFLEVBQUU7WUFDaEIsZ0JBQWdCLEVBQUUsRUFBRTtZQUNwQixXQUFXLEVBQUUsc0JBQXNCO1lBQ25DLE1BQU0sRUFBRSxRQUFRO1lBQ2hCLGFBQWEsRUFBRSxDQUFDO1lBQ2hCLHFCQUFxQixFQUFFLEdBQUc7WUFDMUIsUUFBUSxFQUFFLHNCQUFzQjtZQUNoQyxFQUFFLEVBQUUsQ0FBQztZQUNMLHNCQUFzQixFQUFFLEVBQUU7WUFDMUIsZ0JBQWdCLEVBQUUsRUFBRTtZQUNwQixZQUFZLEVBQUUsRUFBRTtZQUNoQixjQUFjLEVBQUUsRUFBRTtZQUNsQix1QkFBdUIsRUFBRSxLQUFLO1NBQy9CO0tBQ0Y7SUFDRCxJQUFJLEVBQUU7UUFDSixVQUFVLEVBQUUsQ0FBQztRQUNiLFNBQVMsRUFBRSxDQUFDO1FBQ1osWUFBWSxFQUFFLEVBQUU7UUFDaEIsVUFBVSxFQUFFLENBQUM7UUFDYixXQUFX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