Skip to content

Instantly share code, notes, and snippets.

@clasense4
Created November 7, 2023 03:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clasense4/f9c383a62340692cc3e19a6b9a6061f0 to your computer and use it in GitHub Desktop.
Save clasense4/f9c383a62340692cc3e19a6b9a6061f0 to your computer and use it in GitHub Desktop.
Daily Rewards basic mechanism

Daily Rewards Mechanism

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.

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
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