mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-04 05:36:13 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			435 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			435 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const semver = require("semver");
 | 
						|
let test;
 | 
						|
const nodeVersion = process.versions.node;
 | 
						|
// Node.js version >= 18
 | 
						|
if (semver.satisfies(nodeVersion, ">= 18")) {
 | 
						|
    test = require("node:test");
 | 
						|
} else {
 | 
						|
    test = require("test");
 | 
						|
}
 | 
						|
 | 
						|
const assert = require("node:assert");
 | 
						|
const { UptimeCalculator } = require("../../server/uptime-calculator");
 | 
						|
const dayjs = require("dayjs");
 | 
						|
const { UP, DOWN, PENDING, MAINTENANCE } = require("../../src/util");
 | 
						|
dayjs.extend(require("dayjs/plugin/utc"));
 | 
						|
dayjs.extend(require("../../server/modules/dayjs/plugin/timezone"));
 | 
						|
dayjs.extend(require("dayjs/plugin/customParseFormat"));
 | 
						|
 | 
						|
test("Test Uptime Calculator - custom date", async (t) => {
 | 
						|
    let c1 = new UptimeCalculator();
 | 
						|
 | 
						|
    // Test custom date
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2021-01-01T00:00:00.000Z");
 | 
						|
    assert.strictEqual(c1.getCurrentDate().unix(), dayjs.utc("2021-01-01T00:00:00.000Z").unix());
 | 
						|
});
 | 
						|
 | 
						|
test("Test update - UP", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59");
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let date = await c2.update(UP);
 | 
						|
    assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:46:59").unix());
 | 
						|
});
 | 
						|
 | 
						|
test("Test update - MAINTENANCE", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20");
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let date = await c2.update(MAINTENANCE);
 | 
						|
    assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix());
 | 
						|
});
 | 
						|
 | 
						|
test("Test update - DOWN", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20");
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let date = await c2.update(DOWN);
 | 
						|
    assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix());
 | 
						|
});
 | 
						|
 | 
						|
test("Test update - PENDING", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20");
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let date = await c2.update(PENDING);
 | 
						|
    assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix());
 | 
						|
});
 | 
						|
 | 
						|
test("Test flatStatus", async (t) => {
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    assert.strictEqual(c2.flatStatus(UP), UP);
 | 
						|
    //assert.strictEqual(c2.flatStatus(MAINTENANCE), UP);
 | 
						|
    assert.strictEqual(c2.flatStatus(DOWN), DOWN);
 | 
						|
    assert.strictEqual(c2.flatStatus(PENDING), DOWN);
 | 
						|
});
 | 
						|
 | 
						|
test("Test getMinutelyKey", async (t) => {
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:00"));
 | 
						|
    assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix());
 | 
						|
 | 
						|
    // Edge case 1
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:01"));
 | 
						|
    assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix());
 | 
						|
 | 
						|
    // Edge case 2
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:59"));
 | 
						|
    assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix());
 | 
						|
});
 | 
						|
 | 
						|
test("Test getDailyKey", async (t) => {
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 20:46:00"));
 | 
						|
    assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix());
 | 
						|
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:45:30"));
 | 
						|
    assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix());
 | 
						|
 | 
						|
    // Edge case 1
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:59:59"));
 | 
						|
    assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix());
 | 
						|
 | 
						|
    // Edge case 2
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 00:00:00"));
 | 
						|
    assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix());
 | 
						|
 | 
						|
    // Test timezone
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    dailyKey = c2.getDailyKey(dayjs("Sat Dec 23 2023 05:38:39 GMT+0800 (Hong Kong Standard Time)"));
 | 
						|
    assert.strictEqual(dailyKey, dayjs.utc("2023-12-22").unix());
 | 
						|
});
 | 
						|
 | 
						|
test("Test lastDailyUptimeData", async (t) => {
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP);
 | 
						|
    assert.strictEqual(c2.lastDailyUptimeData.up, 1);
 | 
						|
});
 | 
						|
 | 
						|
test("Test get24Hour Uptime and Avg Ping", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59");
 | 
						|
 | 
						|
    // No data
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let data = c2.get24Hour();
 | 
						|
    assert.strictEqual(data.uptime, 0);
 | 
						|
    assert.strictEqual(data.avgPing, null);
 | 
						|
 | 
						|
    // 1 Up
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP, 100);
 | 
						|
    let uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 1);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 100);
 | 
						|
 | 
						|
    // 2 Up
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP, 100);
 | 
						|
    await c2.update(UP, 200);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 1);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 150);
 | 
						|
 | 
						|
    // 3 Up
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP, 0);
 | 
						|
    await c2.update(UP, 100);
 | 
						|
    await c2.update(UP, 400);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 1);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 166.66666666666666);
 | 
						|
 | 
						|
    // 1 MAINTENANCE
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(MAINTENANCE);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, null);
 | 
						|
 | 
						|
    // 1 PENDING
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(PENDING);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, null);
 | 
						|
 | 
						|
    // 1 DOWN
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, null);
 | 
						|
 | 
						|
    // 2 DOWN
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(DOWN);
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, null);
 | 
						|
 | 
						|
    // 1 DOWN, 1 UP
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(DOWN);
 | 
						|
    await c2.update(UP, 0.5);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.5);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 0.5);
 | 
						|
 | 
						|
    // 1 UP, 1 DOWN
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP, 123);
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.5);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 123);
 | 
						|
 | 
						|
    // Add 24 hours
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP, 0);
 | 
						|
    await c2.update(UP, 0);
 | 
						|
    await c2.update(UP, 0);
 | 
						|
    await c2.update(UP, 1);
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.8);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 0.25);
 | 
						|
 | 
						|
    UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour");
 | 
						|
 | 
						|
    // After 24 hours, even if there is no data, the uptime should be still 80%
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.8);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 0.25);
 | 
						|
 | 
						|
    // Add more 24 hours (48 hours)
 | 
						|
    UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour");
 | 
						|
 | 
						|
    // After 48 hours, even if there is no data, the uptime should be still 80%
 | 
						|
    uptime = c2.get24Hour().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.8);
 | 
						|
    assert.strictEqual(c2.get24Hour().avgPing, 0.25);
 | 
						|
});
 | 
						|
 | 
						|
test("Test get7DayUptime", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59");
 | 
						|
 | 
						|
    // No data
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
 | 
						|
    // 1 Up
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 1);
 | 
						|
 | 
						|
    // 2 Up
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(UP);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 1);
 | 
						|
 | 
						|
    // 3 Up
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(UP);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 1);
 | 
						|
 | 
						|
    // 1 MAINTENANCE
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(MAINTENANCE);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
 | 
						|
    // 1 PENDING
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(PENDING);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
 | 
						|
    // 1 DOWN
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
 | 
						|
    // 2 DOWN
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(DOWN);
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
 | 
						|
    // 1 DOWN, 1 UP
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(DOWN);
 | 
						|
    await c2.update(UP);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.5);
 | 
						|
 | 
						|
    // 1 UP, 1 DOWN
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.5);
 | 
						|
 | 
						|
    // Add 7 days
 | 
						|
    c2 = new UptimeCalculator();
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(UP);
 | 
						|
    await c2.update(DOWN);
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.8);
 | 
						|
    UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(7, "day");
 | 
						|
 | 
						|
    // After 7 days, even if there is no data, the uptime should be still 80%
 | 
						|
    uptime = c2.get7Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0.8);
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
test("Test get30DayUptime (1 check per day)", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59");
 | 
						|
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let uptime = c2.get30Day().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
 | 
						|
    let up = 0;
 | 
						|
    let down = 0;
 | 
						|
    let flip = true;
 | 
						|
    for (let i = 0; i < 30; i++) {
 | 
						|
        UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day");
 | 
						|
 | 
						|
        if (flip) {
 | 
						|
            await c2.update(UP);
 | 
						|
            up++;
 | 
						|
        } else {
 | 
						|
            await c2.update(DOWN);
 | 
						|
            down++;
 | 
						|
        }
 | 
						|
 | 
						|
        uptime = c2.get30Day().uptime;
 | 
						|
        assert.strictEqual(uptime, up / (up + down));
 | 
						|
 | 
						|
        flip = !flip;
 | 
						|
    }
 | 
						|
 | 
						|
    // Last 7 days
 | 
						|
    // Down, Up, Down, Up, Down, Up, Down
 | 
						|
    // So 3 UP
 | 
						|
    assert.strictEqual(c2.get7Day().uptime, 3 / 7);
 | 
						|
});
 | 
						|
 | 
						|
test("Test get1YearUptime (1 check per day)", async (t) => {
 | 
						|
    UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59");
 | 
						|
 | 
						|
    let c2 = new UptimeCalculator();
 | 
						|
    let uptime = c2.get1Year().uptime;
 | 
						|
    assert.strictEqual(uptime, 0);
 | 
						|
 | 
						|
    let flip = true;
 | 
						|
    for (let i = 0; i < 365; i++) {
 | 
						|
        UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day");
 | 
						|
 | 
						|
        if (flip) {
 | 
						|
            await c2.update(UP);
 | 
						|
        } else {
 | 
						|
            await c2.update(DOWN);
 | 
						|
        }
 | 
						|
 | 
						|
        uptime = c2.get30Day().time;
 | 
						|
        flip = !flip;
 | 
						|
    }
 | 
						|
 | 
						|
    assert.strictEqual(c2.get1Year().uptime, 183 / 365);
 | 
						|
    assert.strictEqual(c2.get30Day().uptime, 15 / 30);
 | 
						|
    assert.strictEqual(c2.get7Day().uptime, 4 / 7);
 | 
						|
});
 | 
						|
 | 
						|
/**
 | 
						|
 * Code from here: https://stackoverflow.com/a/64550489/1097815
 | 
						|
 * @returns {{rss: string, heapTotal: string, heapUsed: string, external: string}} Current memory usage
 | 
						|
 */
 | 
						|
function memoryUsage() {
 | 
						|
    const formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
 | 
						|
    const memoryData = process.memoryUsage();
 | 
						|
 | 
						|
    return {
 | 
						|
        rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated for the process execution`,
 | 
						|
        heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`,
 | 
						|
        heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`,
 | 
						|
        external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`,
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
test("Worst case", async (t) => {
 | 
						|
 | 
						|
    // Disable on GitHub Actions, as it is not stable on it
 | 
						|
    if (process.env.GITHUB_ACTIONS) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    console.log("Memory usage before preparation", memoryUsage());
 | 
						|
 | 
						|
    let c = new UptimeCalculator();
 | 
						|
    let up = 0;
 | 
						|
    let down = 0;
 | 
						|
    let interval = 20;
 | 
						|
 | 
						|
    await t.test("Prepare data", async () => {
 | 
						|
        UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59");
 | 
						|
 | 
						|
        // Since 2023-08-12 will be out of 365 range, it starts from 2023-08-13 actually
 | 
						|
        let actualStartDate = dayjs.utc("2023-08-13 00:00:00").unix();
 | 
						|
 | 
						|
        // Simulate 1s interval for a year
 | 
						|
        for (let i = 0; i < 365 * 24 * 60 * 60; i += interval) {
 | 
						|
            UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(interval, "second");
 | 
						|
 | 
						|
            //Randomly UP, DOWN, MAINTENANCE, PENDING
 | 
						|
            let rand = Math.random();
 | 
						|
            if (rand < 0.25) {
 | 
						|
                c.update(UP);
 | 
						|
                if (UptimeCalculator.currentDate.unix() > actualStartDate) {
 | 
						|
                    up++;
 | 
						|
                }
 | 
						|
            } else if (rand < 0.5) {
 | 
						|
                c.update(DOWN);
 | 
						|
                if (UptimeCalculator.currentDate.unix() > actualStartDate) {
 | 
						|
                    down++;
 | 
						|
                }
 | 
						|
            } else if (rand < 0.75) {
 | 
						|
                c.update(MAINTENANCE);
 | 
						|
                if (UptimeCalculator.currentDate.unix() > actualStartDate) {
 | 
						|
                    //up++;
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                c.update(PENDING);
 | 
						|
                if (UptimeCalculator.currentDate.unix() > actualStartDate) {
 | 
						|
                    down++;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        console.log("Final Date: ", UptimeCalculator.currentDate.format("YYYY-MM-DD HH:mm:ss"));
 | 
						|
        console.log("Memory usage before preparation", memoryUsage());
 | 
						|
 | 
						|
        assert.strictEqual(c.minutelyUptimeDataList.length(), 1440);
 | 
						|
        assert.strictEqual(c.dailyUptimeDataList.length(), 365);
 | 
						|
    });
 | 
						|
 | 
						|
    await t.test("get1YearUptime()", async () => {
 | 
						|
        assert.strictEqual(c.get1Year().uptime, up / (up + down));
 | 
						|
    });
 | 
						|
 | 
						|
});
 |