0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

test(core): add api response guard and error case tests to dashboard api (#3789)

* test(core): add dashboard api response guard

add dashboard api response guard

* test(core): add dashboard integration tests

add dashboard integration tests
This commit is contained in:
simeng-li 2023-05-02 20:58:48 +08:00 committed by GitHub
parent f4f8224ff9
commit 5c0ed8e79e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 40 deletions

View file

@ -1,6 +1,7 @@
import { dateRegex } from '@logto/core-kit'; import { dateRegex } from '@logto/core-kit';
import { getNewUsersResponseGuard, getActiveUsersResponseGuard } from '@logto/schemas';
import { endOfDay, format, subDays } from 'date-fns'; import { endOfDay, format, subDays } from 'date-fns';
import { object, string } from 'zod'; import { number, object, string } from 'zod';
import koaGuard from '#src/middleware/koa-guard.js'; import koaGuard from '#src/middleware/koa-guard.js';
@ -20,56 +21,74 @@ export default function dashboardRoutes<T extends AuthedRouter>(
users: { countUsers, getDailyNewUserCountsByTimeInterval }, users: { countUsers, getDailyNewUserCountsByTimeInterval },
} = queries; } = queries;
router.get('/dashboard/users/total', async (ctx, next) => { router.get(
const { count: totalUserCount } = await countUsers(); '/dashboard/users/total',
ctx.body = { totalUserCount }; koaGuard({
response: object({
totalUserCount: number(),
}),
status: [200, 401, 403],
}),
async (ctx, next) => {
const { count: totalUserCount } = await countUsers();
ctx.body = { totalUserCount };
return next(); return next();
}); }
);
router.get('/dashboard/users/new', async (ctx, next) => { router.get(
const today = Date.now(); '/dashboard/users/new',
const dailyNewUserCounts = await getDailyNewUserCountsByTimeInterval( koaGuard({
// (14 days ago 23:59:59.999, today 23:59:59.999] response: getNewUsersResponseGuard,
getEndOfDayTimestamp(subDays(today, 14)), status: [200, 401, 403],
getEndOfDayTimestamp(today) }),
); async (ctx, next) => {
const today = Date.now();
const dailyNewUserCounts = await getDailyNewUserCountsByTimeInterval(
// (14 days ago 23:59:59.999, today 23:59:59.999]
getEndOfDayTimestamp(subDays(today, 14)),
getEndOfDayTimestamp(today)
);
const last14DaysNewUserCounts = new Map( const last14DaysNewUserCounts = new Map(
dailyNewUserCounts.map(({ date, count }) => [date, count]) dailyNewUserCounts.map(({ date, count }) => [date, count])
); );
const todayNewUserCount = last14DaysNewUserCounts.get(getDateString(today)) ?? 0; const todayNewUserCount = last14DaysNewUserCounts.get(getDateString(today)) ?? 0;
const yesterday = subDays(today, 1); const yesterday = subDays(today, 1);
const yesterdayNewUserCount = last14DaysNewUserCounts.get(getDateString(yesterday)) ?? 0; const yesterdayNewUserCount = last14DaysNewUserCounts.get(getDateString(yesterday)) ?? 0;
const todayDelta = todayNewUserCount - yesterdayNewUserCount; const todayDelta = todayNewUserCount - yesterdayNewUserCount;
const last7DaysNewUserCount = indices(7) const last7DaysNewUserCount = indices(7)
.map((index) => getDateString(subDays(today, index))) .map((index) => getDateString(subDays(today, index)))
.reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0); .reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0);
const newUserCountFrom13DaysAgoTo7DaysAgo = indices(7) const newUserCountFrom13DaysAgoTo7DaysAgo = indices(7)
.map((index) => getDateString(subDays(today, index + 7))) .map((index) => getDateString(subDays(today, index + 7)))
.reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0); .reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0);
const last7DaysDelta = last7DaysNewUserCount - newUserCountFrom13DaysAgoTo7DaysAgo; const last7DaysDelta = last7DaysNewUserCount - newUserCountFrom13DaysAgoTo7DaysAgo;
ctx.body = { ctx.body = {
today: { today: {
count: todayNewUserCount, count: todayNewUserCount,
delta: todayDelta, delta: todayDelta,
}, },
last7Days: { last7Days: {
count: last7DaysNewUserCount, count: last7DaysNewUserCount,
delta: last7DaysDelta, delta: last7DaysDelta,
}, },
}; };
return next(); return next();
}); }
);
router.get( router.get(
'/dashboard/users/active', '/dashboard/users/active',
koaGuard({ koaGuard({
query: object({ date: string().regex(dateRegex).optional() }), query: object({ date: string().regex(dateRegex).optional() }),
response: getActiveUsersResponseGuard,
status: [200, 401, 403],
}), }),
async (ctx, next) => { async (ctx, next) => {
const { const {

View file

@ -1,7 +1,8 @@
import { SignInIdentifier } from '@logto/schemas'; import { SignInIdentifier } from '@logto/schemas';
import type { StatisticsData } from '#src/api/index.js'; import type { StatisticsData } from '#src/api/index.js';
import { getTotalUsersCount, getNewUsersData, getActiveUsersData } from '#src/api/index.js'; import { api, getTotalUsersCount, getNewUsersData, getActiveUsersData } from '#src/api/index.js';
import { createResponseWithCode } from '#src/helpers/admin-tenant.js';
import { createUserByAdmin } from '#src/helpers/index.js'; import { createUserByAdmin } from '#src/helpers/index.js';
import { registerNewUser, signInWithPassword } from '#src/helpers/interactions.js'; import { registerNewUser, signInWithPassword } from '#src/helpers/interactions.js';
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js'; import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
@ -16,6 +17,16 @@ describe('admin console dashboard', () => {
}); });
}); });
it('non authorized request should return 401', async () => {
await expect(api.get('dashboard/users/total')).rejects.toMatchObject(
createResponseWithCode(401)
);
await expect(api.get('dashboard/users/new')).rejects.toMatchObject(createResponseWithCode(401));
await expect(api.get('dashboard/users/active')).rejects.toMatchObject(
createResponseWithCode(401)
);
});
it('should get total user count successfully', async () => { it('should get total user count successfully', async () => {
const { totalUserCount: originTotalUserCount } = await getTotalUsersCount(); const { totalUserCount: originTotalUserCount } = await getTotalUsersCount();

View file

@ -0,0 +1,23 @@
import { number, object, array, string } from 'zod';
const dashboardUsersDataGuard = object({
count: number(),
delta: number(),
});
export const getNewUsersResponseGuard = object({
today: dashboardUsersDataGuard,
last7Days: dashboardUsersDataGuard,
});
export const getActiveUsersResponseGuard = object({
dauCurve: array(
object({
date: string(),
count: number(),
})
),
dau: dashboardUsersDataGuard,
wau: dashboardUsersDataGuard,
mau: dashboardUsersDataGuard,
});

View file

@ -17,3 +17,4 @@ export * from './hook.js';
export * from './service-log.js'; export * from './service-log.js';
export * from './theme.js'; export * from './theme.js';
export * from './cookie.js'; export * from './cookie.js';
export * from './dashboard.js';