mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
chore: last month and month before last
This commit is contained in:
parent
2f584b9064
commit
a4cc1d8daa
132
src/lib/features/instance-stats/getEdgeInstances.e2e.test.ts
Normal file
132
src/lib/features/instance-stats/getEdgeInstances.e2e.test.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import {
|
||||
createGetEdgeInstances,
|
||||
type GetEdgeInstances,
|
||||
} from './getEdgeInstances.js';
|
||||
import dbInit, {
|
||||
type ITestDb,
|
||||
} from '../../../test/e2e/helpers/database-init.js';
|
||||
import getLogger from '../../../test/fixtures/no-logger.js';
|
||||
|
||||
let db: ITestDb;
|
||||
let getEdgeInstances: GetEdgeInstances;
|
||||
|
||||
const TABLE = 'edge_node_presence';
|
||||
|
||||
const firstDayOfMonth = (d: Date) => new Date(d.getFullYear(), d.getMonth(), 1);
|
||||
const addMonths = (d: Date, n: number) =>
|
||||
new Date(d.getFullYear(), d.getMonth() + n, 1);
|
||||
|
||||
const monthWindows = () => {
|
||||
const now = new Date();
|
||||
const thisMonthStart = firstDayOfMonth(now);
|
||||
const lastMonthStart = addMonths(thisMonthStart, -1);
|
||||
const monthBeforeLastStart = addMonths(thisMonthStart, -2);
|
||||
const lastMonthEnd = thisMonthStart;
|
||||
const monthBeforeLastEnd = lastMonthStart;
|
||||
return {
|
||||
monthBeforeLastStart,
|
||||
monthBeforeLastEnd,
|
||||
lastMonthStart,
|
||||
lastMonthEnd,
|
||||
thisMonthStart,
|
||||
};
|
||||
};
|
||||
|
||||
const atMidMonth = (start: Date) =>
|
||||
new Date(start.getFullYear(), start.getMonth(), 15);
|
||||
const atLateMonth = (start: Date) =>
|
||||
new Date(start.getFullYear(), start.getMonth(), 25);
|
||||
|
||||
const rowsForBucket = (count: number, when: Date) =>
|
||||
Array.from({ length: count }, (_, i) => ({
|
||||
bucket_ts: when,
|
||||
node_ephem_id: `node-${when.getTime()}-${i}`,
|
||||
}));
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('edge_instances_e2e', getLogger);
|
||||
getEdgeInstances = createGetEdgeInstances(db.rawDatabase);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await db.rawDatabase(TABLE).delete();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
test('returns 0 for both months when no data', async () => {
|
||||
await expect(getEdgeInstances()).resolves.toEqual({
|
||||
lastMonth: 0,
|
||||
monthBeforeLast: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('counts only last full month', async () => {
|
||||
const { lastMonthStart } = monthWindows();
|
||||
const mid = atMidMonth(lastMonthStart);
|
||||
const late = atLateMonth(lastMonthStart);
|
||||
await db
|
||||
.rawDatabase(TABLE)
|
||||
.insert([...rowsForBucket(3, mid), ...rowsForBucket(7, late)]);
|
||||
await expect(getEdgeInstances()).resolves.toEqual({
|
||||
lastMonth: 5,
|
||||
monthBeforeLast: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('counts only month before last', async () => {
|
||||
const { monthBeforeLastStart } = monthWindows();
|
||||
const mid = atMidMonth(monthBeforeLastStart);
|
||||
const late = atLateMonth(monthBeforeLastStart);
|
||||
await db
|
||||
.rawDatabase(TABLE)
|
||||
.insert([...rowsForBucket(2, mid), ...rowsForBucket(5, late)]);
|
||||
await expect(getEdgeInstances()).resolves.toEqual({
|
||||
lastMonth: 0,
|
||||
monthBeforeLast: 4,
|
||||
});
|
||||
});
|
||||
|
||||
test('separates months correctly when both have data', async () => {
|
||||
const { monthBeforeLastStart, lastMonthStart } = monthWindows();
|
||||
const pMid = atMidMonth(monthBeforeLastStart);
|
||||
const pLate = atLateMonth(monthBeforeLastStart);
|
||||
const lMid = atMidMonth(lastMonthStart);
|
||||
const lLate = atLateMonth(lastMonthStart);
|
||||
|
||||
await db
|
||||
.rawDatabase(TABLE)
|
||||
.insert([
|
||||
...rowsForBucket(4, pMid),
|
||||
...rowsForBucket(6, pLate),
|
||||
...rowsForBucket(3, lMid),
|
||||
...rowsForBucket(7, lLate),
|
||||
]);
|
||||
|
||||
await expect(getEdgeInstances()).resolves.toEqual({
|
||||
lastMonth: 5,
|
||||
monthBeforeLast: 5,
|
||||
});
|
||||
});
|
||||
|
||||
test('ignores current month data', async () => {
|
||||
const { thisMonthStart, lastMonthStart } = monthWindows();
|
||||
const lMid = atMidMonth(lastMonthStart);
|
||||
const lLate = atLateMonth(lastMonthStart);
|
||||
const tMid = atMidMonth(thisMonthStart);
|
||||
|
||||
await db
|
||||
.rawDatabase(TABLE)
|
||||
.insert([
|
||||
...rowsForBucket(10, tMid),
|
||||
...rowsForBucket(2, lMid),
|
||||
...rowsForBucket(4, lLate),
|
||||
]);
|
||||
|
||||
await expect(getEdgeInstances()).resolves.toEqual({
|
||||
lastMonth: 3,
|
||||
monthBeforeLast: 0,
|
||||
});
|
||||
});
|
||||
@ -3,9 +3,8 @@ import type { Db } from '../../types/index.js';
|
||||
const TABLE = 'edge_node_presence';
|
||||
|
||||
export type GetEdgeInstances = () => Promise<{
|
||||
last30: number;
|
||||
last60: number;
|
||||
last90: number;
|
||||
lastMonth: number;
|
||||
monthBeforeLast: number;
|
||||
}>;
|
||||
|
||||
export const createGetEdgeInstances =
|
||||
@ -15,7 +14,9 @@ export const createGetEdgeInstances =
|
||||
.with('buckets', (qb) =>
|
||||
qb
|
||||
.from(TABLE)
|
||||
.whereRaw("bucket_ts >= NOW() - INTERVAL '90 days'")
|
||||
.whereRaw(
|
||||
"bucket_ts >= date_trunc('month', NOW()) - INTERVAL '2 months'",
|
||||
)
|
||||
.groupBy('bucket_ts')
|
||||
.select(
|
||||
db.raw('bucket_ts'),
|
||||
@ -24,31 +25,44 @@ export const createGetEdgeInstances =
|
||||
)
|
||||
.from('buckets')
|
||||
.select({
|
||||
last30: db.raw(
|
||||
`COALESCE(CEIL(AVG(active_nodes) FILTER (WHERE bucket_ts >= NOW() - INTERVAL '30 days'))::int, 0)`,
|
||||
),
|
||||
last60: db.raw(
|
||||
`COALESCE(CEIL(AVG(active_nodes) FILTER (WHERE bucket_ts >= NOW() - INTERVAL '60 days'))::int, 0)`,
|
||||
),
|
||||
last90: db.raw(
|
||||
`COALESCE(CEIL(AVG(active_nodes) FILTER (WHERE bucket_ts >= NOW() - INTERVAL '90 days'))::int, 0)`,
|
||||
),
|
||||
lastMonth: db.raw(`
|
||||
COALESCE(
|
||||
CEIL(
|
||||
AVG(active_nodes)
|
||||
FILTER (
|
||||
WHERE bucket_ts >= date_trunc('month', NOW()) - INTERVAL '1 month'
|
||||
AND bucket_ts < date_trunc('month', NOW())
|
||||
)
|
||||
)::int,
|
||||
0
|
||||
)
|
||||
`),
|
||||
monthBeforeLast: db.raw(`
|
||||
COALESCE(
|
||||
CEIL(
|
||||
AVG(active_nodes)
|
||||
FILTER (
|
||||
WHERE bucket_ts >= date_trunc('month', NOW()) - INTERVAL '2 months'
|
||||
AND bucket_ts < date_trunc('month', NOW()) - INTERVAL '1 month'
|
||||
)
|
||||
)::int,
|
||||
0
|
||||
)
|
||||
`),
|
||||
})
|
||||
.first();
|
||||
|
||||
return {
|
||||
last30: Number(result?.last30 ?? 0),
|
||||
last60: Number(result?.last60 ?? 0),
|
||||
last90: Number(result?.last90 ?? 0),
|
||||
lastMonth: Number(result?.lastMonth ?? 0),
|
||||
monthBeforeLast: Number(result?.monthBeforeLast ?? 0),
|
||||
};
|
||||
};
|
||||
|
||||
export const createFakeGetEdgeInstances =
|
||||
(
|
||||
edgeInstances: Awaited<ReturnType<GetEdgeInstances>> = {
|
||||
last30: 0,
|
||||
last60: 0,
|
||||
last90: 0,
|
||||
lastMonth: 0,
|
||||
monthBeforeLast: 0,
|
||||
},
|
||||
): GetEdgeInstances =>
|
||||
() =>
|
||||
|
||||
@ -545,9 +545,8 @@ export class InstanceStatsService {
|
||||
hostedBy,
|
||||
releaseTemplates,
|
||||
releasePlans,
|
||||
edgeInstances30: edgeInstances.last30,
|
||||
edgeInstances60: edgeInstances.last60,
|
||||
edgeInstances90: edgeInstances.last90,
|
||||
edgeInstancesLastMonth: edgeInstances.lastMonth,
|
||||
edgeInstancesMonthBeforeLast: edgeInstances.monthBeforeLast,
|
||||
};
|
||||
return featureInfo;
|
||||
}
|
||||
|
||||
@ -287,29 +287,22 @@ export const instanceAdminStatsSchema = {
|
||||
edgeInstances: {
|
||||
type: 'object',
|
||||
description:
|
||||
'The billable number of edge instances in the last 30, 60 and 90 days',
|
||||
'The rounded up average number of edge instances in the last month and month before last',
|
||||
properties: {
|
||||
last30: {
|
||||
lastMonth: {
|
||||
type: 'integer',
|
||||
description:
|
||||
'The billable number of edge instances in the last 30 days',
|
||||
'The rounded up average number of edge instances in the last month',
|
||||
example: 10,
|
||||
minimum: 0,
|
||||
},
|
||||
last60: {
|
||||
monthBeforeLast: {
|
||||
type: 'integer',
|
||||
description:
|
||||
'The billable number of edge instances in the last 60 days',
|
||||
'The rounded up average number of edge instances in the month before last',
|
||||
example: 12,
|
||||
minimum: 0,
|
||||
},
|
||||
last90: {
|
||||
type: 'integer',
|
||||
description:
|
||||
'The billable number of edge instances in the last 90 days',
|
||||
example: 15,
|
||||
minimum: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
sum: {
|
||||
|
||||
@ -134,9 +134,8 @@ class InstanceAdminController extends Controller {
|
||||
releaseTemplates: 3,
|
||||
releasePlans: 5,
|
||||
edgeInstances: {
|
||||
last30: 10,
|
||||
last60: 15,
|
||||
last90: 20,
|
||||
lastMonth: 10,
|
||||
monthBeforeLast: 15,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -45,9 +45,8 @@ const fakeTelemetryData = {
|
||||
hostedBy: 'self-hosted',
|
||||
releaseTemplates: 2,
|
||||
releasePlans: 4,
|
||||
edgeInstances30: 0,
|
||||
edgeInstances60: 0,
|
||||
edgeInstances90: 0,
|
||||
edgeInstancesLastMonth: 0,
|
||||
edgeInstancesMonthBeforeLast: 0,
|
||||
};
|
||||
|
||||
test('yields current versions', async () => {
|
||||
|
||||
@ -54,9 +54,8 @@ export interface IFeatureUsageInfo {
|
||||
hostedBy: string;
|
||||
releaseTemplates: number;
|
||||
releasePlans: number;
|
||||
edgeInstances30?: number;
|
||||
edgeInstances60?: number;
|
||||
edgeInstances90?: number;
|
||||
edgeInstancesLastMonth?: number;
|
||||
edgeInstancesMonthBeforeLast?: number;
|
||||
}
|
||||
|
||||
export default class VersionService {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user