This gist is showing how to do a daily rewards like it is usually implemented on some games. For example, if you own a specific item, you are allowed to claim a specific item within a time limit.
Created
November 7, 2023 03:15
-
-
Save clasense4/f9c383a62340692cc3e19a6b9a6061f0 to your computer and use it in GitHub Desktop.
Daily Rewards basic mechanism
This file contains hidden or 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
const express = require('express'); | |
const { Pool } = require('pg'); | |
const app = express(); | |
const port = 3000; | |
const pool = new Pool({ | |
host: 'localhost', | |
user: 'project_rewards', | |
password: 'project_rewards', | |
database: 'project_rewards', | |
port: 5432, | |
}); | |
app.use(express.json()); | |
// DB Schema | |
// -- Create table for user items | |
// CREATE TABLE user_items ( | |
// id SERIAL PRIMARY KEY, | |
// user_id SERIAL, | |
// item_name TEXT | |
// ); | |
// Route to claim reward | |
app.post('/claim', async (req, res) => { | |
const userId = req.body.userId; | |
try { | |
const client = await pool.connect(); | |
// Check if user owns "Blue Gemstone" | |
const checkOwnership = await client.query('SELECT * FROM user_items WHERE user_id = $1 AND item_name = $2', [userId, 'Blue Gemstone']); | |
if (checkOwnership.rowCount === 0) { | |
res.status(403).send('User does not own the required Blue Gemstone.'); | |
client.release(); | |
return; | |
} | |
// Get the server's current time in GMT+7 | |
const currentServerTime = new Date(); | |
currentServerTime.setHours(currentServerTime.getHours() + 7); | |
console.log("Server time:", new Date()); | |
// Zero-out the hours, minutes, seconds, and milliseconds | |
currentServerTime.setHours(0, 0, 0, 0); | |
// Check if a reward has been claimed today | |
const checkLastClaim = await client.query('SELECT * FROM reward_history WHERE user_id = $1 AND reward_name = $2 AND claimed_date >= $3::timestamptz ORDER BY claimed_date DESC', | |
[userId, 'Red Gemstone', currentServerTime.toISOString()] | |
); | |
if (checkLastClaim.rowCount > 0) { | |
res.status(403).send('Reward already claimed today. You can claim again at 00:00 GMT+7.'); | |
client.release(); | |
return; | |
} | |
// Insert claim into history | |
await client.query('INSERT INTO reward_history (user_id, reward_name, claimed_amount, claimed_date) VALUES ($1, $2, $3, $4)', [userId, 'Red Gemstone', 5, currentServerTime]); | |
// Insert 5 Red Gemstones into user inventory (done 5 times for simplicity) | |
for (let i = 0; i < 5; i++) { | |
await client.query('INSERT INTO user_items (user_id, item_name) VALUES ($1, $2)', [userId, 'Red Gemstone']); | |
} | |
res.status(200).send('Successfully claimed 5 Red Gemstones.'); | |
client.release(); | |
} catch (err) { | |
console.error(err); | |
res.status(500).send('Internal Server Error'); | |
} | |
}); | |
app.listen(port, () => { | |
console.log(`Server running at http://localhost:${port}/`); | |
}); | |
module.exports = app; // Export the app instance for testing |
This file contains hidden or 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
const request = require('supertest'); | |
const app = require('./daily_rewards'); | |
const mockDate = require('jest-date-mock'); | |
describe('Test case for user 1 with date mocking', () => { | |
beforeAll(() => { | |
mockDate.advanceTo(new Date()); | |
}); | |
it('test the date mocking', () => { | |
mockDate.advanceTo(new Date(2018, 5, 27, 0, 0, 0)); // reset to date time. | |
const now = Date.now(); | |
mockDate.advanceBy(3000); // advance time 3 seconds | |
expect(+new Date() - now).toBe(3000); | |
mockDate.advanceBy(-1000); // advance time -1 second | |
expect(+new Date() - now).toBe(2000); | |
mockDate.clear(); | |
Date.now(); // will got current timestamp | |
}); | |
it('User 1 claims and succeeds', async () => { | |
const response = await request(app).post('/claim').send({ userId: 1 }); | |
expect(response.status).toBe(200); | |
// Further checks like verifying the database state can be added here | |
}); | |
it('User 1 tries to claim again after 3 minutes and fails', async () => { | |
// Move time forward by 3 minutes | |
mockDate.advanceBy(3 * 60 * 1000); | |
const response = await request(app).post('/claim').send({ userId: 1 }); | |
expect(response.status).toBe(403); | |
expect(response.text).toBe('Reward already claimed today. You can claim again at 00:00 GMT+7.'); | |
}); | |
it('User 1 tries to claim again the next day and succeeds', async () => { | |
// Move time forward by 1 day | |
mockDate.advanceBy(24 * 60 * 60 * 1000); | |
const response = await request(app).post('/claim').send({ userId: 1 }); | |
expect(response.status).toBe(200); | |
}); | |
it('User 1 tries to claim again the next day + 1 hour and fails', async () => { | |
// Move time forward by 1 hour | |
mockDate.advanceBy(1 * 60 * 60 * 1000); | |
const response = await request(app).post('/claim').send({ userId: 1 }); | |
expect(response.status).toBe(403); | |
expect(response.text).toBe('Reward already claimed today. You can claim again at 00:00 GMT+7.'); | |
}); | |
afterAll(() => { | |
// Reset the Date object to the current time | |
mockDate.clear(); | |
}); | |
}); | |
describe('Test case for user 2', () => { | |
it("User 2 can't claim because they don't have a Blue Gemstone", async () => { | |
const response = await request(app).post('/claim').send({ userId: 2 }); | |
expect(response.status).toBe(403); | |
expect(response.text).toBe('User does not own the required Blue Gemstone.'); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment