@myria/leaderboard-ts-sdk
Version:
Myria Leaderboard typescripts SDK which will manage leaderboard for game developers
449 lines • 30.1 kB
JavaScript
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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