Merge pull request #3799 from bluewave-labs/chore/domain-refactoring

chore: domain refactoring
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-07-23 10:30:33 -07:00
commit 9540084425
12 files changed
+112 -137

No files matched your search

+1
View File
@@ -15,6 +15,7 @@
"worker:dev": "QUEUE_MODE=worker nodemon --exec tsx src/index.js",
"clean": "rm -rf dist",
"build": "npm run clean && npm run build:openapi && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && cp -r src/templates dist/templates",
"typeCheck": "tsc --noEmit",
"build:openapi": "tsc --noEmit && tsx scripts/buildOpenApi.ts",
"lint": "eslint .",
"lint-fix": "eslint --fix .",
@@ -8,6 +8,7 @@ import type {
} from "@/domain/checks/check.type.js";
import type { MonitorType } from "@/domain/monitors/monitor.types.js";
import type { LatestChecksMap } from "@/domain/checks/check.repository.mongo.js";
import { DateRange } from "@/types/query.js";
export interface IChecksRepository {
// create
@@ -19,7 +20,7 @@ export interface IChecksRepository {
findByMonitorId(
monitorId: string,
sortOrder: string,
dateRange: string,
dateRange: DateRange,
filter: string | undefined,
page: number,
rowsPerPage: number,
@@ -27,7 +28,7 @@ export interface IChecksRepository {
): Promise<ChecksQueryResult>;
findByTeamId(
sortOrder: string,
dateRange: string,
dateRange: DateRange,
filter: string | undefined,
page: number,
rowsPerPage: number,
@@ -36,12 +37,10 @@ export interface IChecksRepository {
findLatestByMonitorIds(monitorIds: string[], options?: { limitPerMonitor?: number }): Promise<LatestChecksMap>;
findByDateRangeAndMonitorId(
monitorId: string,
startDate: Date,
endDate: Date,
dateString: string,
dateRange: DateRange,
options?: { type?: MonitorType }
): Promise<UptimeChecksResult | HardwareChecksResult | PageSpeedChecksResult>;
findSummaryByTeamId(teamId: string, dateRange: string): Promise<ChecksSummary>;
findSummaryByTeamId(teamId: string, dateRange: DateRange): Promise<ChecksSummary>;
findUnevaluatedByMonitorId(monitorId: string, since: number): Promise<Check[]>;
// update
//delete
@@ -15,11 +15,12 @@ import type {
import type { MonitorType } from "@/domain/monitors/monitor.types.js";
import { CheckModel, type CheckDocument } from "@/domain/checks/check.model.js";
import mongoose from "mongoose";
import { getDateForRange } from "@/utils/dataUtils.js";
import { getDateFormat, getDateForRange } from "@/utils/dataUtils.js";
import { ILogger } from "@/utils/logger.js";
import { toStringId, toDateString } from "@/utils/mongoMappers.js";
import { getHardwareUpChecks, getHardwareStats, getHardwareTotalChecks } from "@/domain/checks/check.hardware.aggregations.js";
import { DateRange } from "@/types/query.js";
const SERVICE_NAME = "StatusService";
@@ -207,7 +208,7 @@ class MongoChecksRepository implements IChecksRepository {
findByMonitorId = async (
monitorId: string,
sortOrder: string,
dateRange: string,
dateRange: DateRange,
filter: string | undefined,
page: number,
rowsPerPage: number,
@@ -217,11 +218,7 @@ class MongoChecksRepository implements IChecksRepository {
const matchStage: Record<string, unknown> = {
"metadata.monitorId": new mongoose.Types.ObjectId(monitorId),
...(typeof status !== "undefined" && { status }),
...(getDateForRange(dateRange) && {
createdAt: {
$gte: getDateForRange(dateRange),
},
}),
createdAt: { $gte: getDateForRange(dateRange) },
};
if (filter !== undefined) {
@@ -265,14 +262,10 @@ class MongoChecksRepository implements IChecksRepository {
return { checksCount, checks: this.mapDocuments(checks) };
};
findByTeamId = async (sortOrder: string, dateRange: string, filter: string, page: number, rowsPerPage: number, teamId: string) => {
findByTeamId = async (sortOrder: string, dateRange: DateRange, filter: string, page: number, rowsPerPage: number, teamId: string) => {
const matchStage: Record<string, unknown> = {
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
...(getDateForRange(dateRange) && {
createdAt: {
$gte: getDateForRange(dateRange),
},
}),
createdAt: { $gte: getDateForRange(dateRange) },
};
// Add filter to match stage
if (filter !== undefined) {
@@ -341,25 +334,25 @@ class MongoChecksRepository implements IChecksRepository {
return mapped;
};
findByDateRangeAndMonitorId = async (monitorId: string, startDate: Date, endDate: Date, dateString: string, options?: { type?: MonitorType }) => {
findByDateRangeAndMonitorId = async (monitorId: string, dateRange: DateRange, options?: { type?: MonitorType }) => {
const monitorObjectId = new mongoose.Types.ObjectId(monitorId);
const start = getDateForRange(dateRange);
const dateString = getDateFormat(dateRange);
const end = new Date();
if (options?.type === "hardware") {
return this.findHardwareDateRangeChecks(monitorObjectId, startDate, endDate, dateString);
return this.findHardwareDateRangeChecks(monitorObjectId, start, end, dateString);
}
if (options?.type === "pagespeed") {
return this.findPageSpeedDateRangeChecks(monitorObjectId, startDate, endDate, dateString);
return this.findPageSpeedDateRangeChecks(monitorObjectId, start, end, dateString);
}
return this.findUptimeDateRangeChecks(options?.type ?? "http", monitorObjectId, startDate, endDate, dateString);
return this.findUptimeDateRangeChecks(options?.type ?? "http", monitorObjectId, start, end, dateString);
};
findSummaryByTeamId = async (teamId: string, dateRange: string) => {
findSummaryByTeamId = async (teamId: string, dateRange: DateRange) => {
const baseMatch = {
"metadata.teamId": new mongoose.Types.ObjectId(teamId),
...(getDateForRange(dateRange) && {
createdAt: {
$gte: getDateForRange(dateRange),
},
}),
createdAt: { $gte: getDateForRange(dateRange) },
};
const [totalResult, downResult] = await Promise.all([
+7 -6
View File
@@ -6,6 +6,7 @@ import type { MonitorPayloadMap, MonitorStatusResponse } from "@/types/network.j
import type { HardwareStatusPayload, PageSpeedStatusPayload } from "@/types/network.js";
import { AppError } from "@/utils/AppError.js";
import { ILogger } from "@/utils/logger.js";
import { DateRange } from "@/types/query.js";
const SERVICE_NAME = "checkService";
@@ -18,7 +19,7 @@ export interface ICheckService {
monitorId: string;
teamId: string;
sortOrder: string;
dateRange: string;
dateRange: DateRange;
page: number;
rowsPerPage: number;
filter?: string;
@@ -27,12 +28,12 @@ export interface ICheckService {
getChecksByTeam(params: {
teamId: string;
sortOrder: string;
dateRange: string;
dateRange: DateRange;
page: number;
rowsPerPage: number;
filter?: string;
}): Promise<ChecksQueryResult>;
getChecksSummaryByTeamId(params: { teamId: string; dateRange: string }): Promise<ChecksSummary>;
getChecksSummaryByTeamId(params: { teamId: string; dateRange: DateRange }): Promise<ChecksSummary>;
deleteChecks(params: { monitorId: string; teamId: string }): Promise<number>;
deleteChecksByTeamId(params: { teamId: string }): Promise<number>;
deleteOlderThan(date: Date): Promise<number>;
@@ -164,7 +165,7 @@ export class CheckService implements ICheckService {
monitorId: string;
teamId: string;
sortOrder: string;
dateRange: string;
dateRange: DateRange;
page: number;
rowsPerPage: number;
status?: boolean;
@@ -198,7 +199,7 @@ export class CheckService implements ICheckService {
}: {
teamId: string;
sortOrder: string;
dateRange: string;
dateRange: DateRange;
page: number;
rowsPerPage: number;
filter?: string;
@@ -210,7 +211,7 @@ export class CheckService implements ICheckService {
return checkData;
};
getChecksSummaryByTeamId = async ({ teamId, dateRange }: { teamId: string; dateRange: string }) => {
getChecksSummaryByTeamId = async ({ teamId, dateRange }: { teamId: string; dateRange: DateRange }) => {
const summary = await this.checksRepository.findSummaryByTeamId(teamId, dateRange);
return summary;
};
@@ -1,5 +1,6 @@
import type { GeoCheck, GroupedGeoCheck } from "@/domain/geo-checks/geo-check.type.js";
import type { GeoContinent, FlatGeoCheck } from "@/domain/geo-checks/geo-check.type.js";
import { DateRange } from "@/types/query.js";
export interface GeoChecksQueryResult {
geoChecksCount: number;
@@ -16,19 +17,13 @@ export interface IGeoChecksRepository {
findByMonitorId(
monitorId: string,
sortOrder: string,
dateRange: string,
dateRange: DateRange,
page: number,
rowsPerPage: number,
continents?: GeoContinent[]
): Promise<FlatGeoChecksQueryResult>;
findByMonitorIdAndDateRange(monitorId: string, startDate: Date, endDate: Date): Promise<GeoCheck[]>;
findGroupedByMonitorIdAndDateRange(
monitorId: string,
startDate: Date,
endDate: Date,
dateFormat: string,
continents?: GeoContinent[]
): Promise<GroupedGeoCheck[]>;
findGroupedByMonitorIdAndDateRange(monitorId: string, DateRange: DateRange, continents?: GeoContinent[]): Promise<GroupedGeoCheck[]>;
deleteByMonitorId(monitorId: string): Promise<number>;
deleteByTeamId(teamId: string): Promise<number>;
deleteByMonitorIdsNotIn(monitorIds: string[]): Promise<number>;
@@ -3,9 +3,10 @@ import type { GeoCheck, GeoCheckMetadata, GeoCheckResult, GroupedGeoCheck, GeoCo
import type { FlatGeoChecksQueryResult } from "./geo-check.repository.interface.js";
import { GeoCheckMetadataDocument, GeoCheckModel, type GeoCheckDocument } from "@/domain/geo-checks/geo-check.model.js";
import mongoose, { PipelineStage } from "mongoose";
import { getDateForRange } from "@/utils/dataUtils.js";
import { getDateForRange, getDateFormat } from "@/utils/dataUtils.js";
import { ILogger } from "@/utils/logger.js";
import { toStringId, toDateString } from "@/utils/mongoMappers.js";
import { DateRange } from "@/types/query.js";
const SERVICE_NAME = "GeoChecksRepository";
@@ -90,7 +91,7 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
findByMonitorId = async (
monitorId: string,
sortOrder: string,
dateRange: string,
dateRange: DateRange,
page: number,
rowsPerPage: number,
continents?: GeoContinent[]
@@ -98,11 +99,7 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
try {
const matchStage: Record<string, unknown> = {
"metadata.monitorId": new mongoose.Types.ObjectId(monitorId),
...(getDateForRange(dateRange) && {
createdAt: {
$gte: getDateForRange(dateRange),
},
}),
createdAt: { $gte: getDateForRange(dateRange) },
};
const convertedSortOrder = sortOrder === "asc" ? 1 : -1;
@@ -214,13 +211,11 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
}
};
findGroupedByMonitorIdAndDateRange = async (
monitorId: string,
startDate: Date,
endDate: Date,
dateFormat: string,
continents?: GeoContinent[]
): Promise<GroupedGeoCheck[]> => {
findGroupedByMonitorIdAndDateRange = async (monitorId: string, dateRange: DateRange, continents?: GeoContinent[]): Promise<GroupedGeoCheck[]> => {
const start = getDateForRange(dateRange);
const dateString = getDateFormat(dateRange);
const end = new Date();
try {
const pipeline: PipelineStage[] = [
// Match geo checks for this monitor in date range
@@ -228,8 +223,8 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
$match: {
"metadata.monitorId": new mongoose.Types.ObjectId(monitorId),
createdAt: {
$gte: startDate,
$lte: endDate,
$gte: start,
$lte: end,
},
},
},
@@ -253,7 +248,7 @@ class MongoGeoChecksRepository implements IGeoChecksRepository {
_id: {
bucketDate: {
$dateToString: {
format: dateFormat,
format: dateString,
date: "$createdAt",
},
},
@@ -7,6 +7,7 @@ import type { IMonitorsRepository } from "@/domain/monitors/monitor.repository.i
import type { IGlobalPingService } from "@/service/globalPingService.js";
import type { ILogger } from "@/utils/logger.js";
import { AppError } from "@/utils/AppError.js";
import { DateRange } from "@/types/query.js";
const SERVICE_NAME = "GeoChecksService";
@@ -17,7 +18,7 @@ export interface IGeoChecksService {
monitorId: string;
teamId: string;
sortOrder: string;
dateRange: string;
dateRange: DateRange;
page?: number;
rowsPerPage?: number;
continent?: GeoContinent | GeoContinent[];
@@ -155,7 +156,7 @@ export class GeoChecksService implements IGeoChecksService {
monitorId: string;
teamId: string;
sortOrder: string;
dateRange: string;
dateRange: DateRange;
page?: number;
rowsPerPage?: number;
continent: GeoContinent | GeoContinent[];
@@ -11,6 +11,7 @@ import type { User } from "@/domain/users/user.type.js";
import type { MonitorActionDecision } from "@/worker/worker.helper.js";
import type { INotificationMessageBuilder } from "@/domain/notifications/notification.message-builder.js";
import type { ILogger } from "@/utils/logger.js";
import { DateRange } from "@/types/query.js";
export interface IIncidentService {
handleIncident(
@@ -23,7 +24,7 @@ export interface IIncidentService {
getIncidentsByTeam(
teamId: string,
sortOrder: string,
dateRange: string,
dateRange: DateRange,
page: number,
rowsPerPage: number,
status: boolean | undefined,
@@ -174,7 +175,7 @@ export class IncidentService implements IIncidentService {
getIncidentsByTeam = async (
teamId: string,
sortOrder: string,
dateRange: string,
dateRange: DateRange,
page: number,
rowsPerPage: number,
status: boolean | undefined,
@@ -91,27 +91,26 @@ class MongoMonitorsRepository implements IMonitorsRepository {
{ $project: { stats: 0 } },
];
findByTeamId = async (teamId: string, config: TeamQueryConfig): Promise<Monitor[] | null> => {
private pageOptions = (config: TeamQueryConfig) => {
const { page = 0, rowsPerPage = 0, field = "createdAt", order = "desc" } = config ?? {};
return { sort: { [field]: order === "asc" ? 1 : -1 } as const, skip: Math.max(page, 0) * rowsPerPage, limit: rowsPerPage };
};
findByTeamId = async (teamId: string, config: TeamQueryConfig): Promise<Monitor[] | null> => {
const query = this.queryBuilder(config, teamId);
const sort = { [field]: order === "asc" ? 1 : -1 } as const;
const skip = Math.max(page, 0) * rowsPerPage;
const documents = await MonitorModel.find(query).sort(sort).skip(skip).limit(rowsPerPage);
const { sort, skip, limit } = this.pageOptions(config);
const documents = await MonitorModel.find(query).sort(sort).skip(skip).limit(limit);
return this.mapDocuments(documents);
};
findByTeamIdWithStats = async (teamId: string, config: TeamQueryConfig): Promise<Monitor[] | null> => {
const { page = 0, rowsPerPage = 0, field = "createdAt", order = "desc" } = config ?? {};
const { sort, skip, limit } = this.pageOptions(config);
const query = this.queryBuilder(config, teamId);
const sort = { [field]: order === "asc" ? 1 : -1 } as const;
const skip = Math.max(page, 0) * rowsPerPage;
const pipeline: PipelineStage[] = [
{ $match: query },
{ $sort: sort },
...(rowsPerPage ? [{ $skip: skip }, { $limit: rowsPerPage }] : []),
...(limit ? [{ $skip: skip }, { $limit: limit }] : []),
...this.uptimeStatsLookupStages,
];
+13 -52
View File
@@ -23,9 +23,9 @@ import { AppError } from "@/utils/AppError.js";
import type { ImportedMonitor } from "@/api/validation/monitorValidation.js";
import { ILogger } from "@/utils/logger.js";
import { IJobScheduler } from "@/worker/worker.interface.js";
import { DateRange } from "@/types/query.js";
const SERVICE_NAME = "MonitorService";
type DateRangeKey = "recent" | "day" | "week" | "month" | "all";
const isUptimeChecksResult = (result: UptimeChecksResult | HardwareChecksResult | PageSpeedChecksResult): result is UptimeChecksResult =>
supportsUptimeDetails(result.monitorType);
@@ -37,13 +37,13 @@ export interface IMonitorService {
addDemoMonitors(args: { userId: string; teamId: string }): Promise<Monitor[]>;
// read
getUptimeDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<UptimeDetailsResult>;
getHardwareDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<HardwareDetailsResult>;
getPageSpeedDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<PageSpeedDetailsResult>;
getUptimeDetailsById(args: { teamId: string; monitorId: string; dateRange: DateRange }): Promise<UptimeDetailsResult>;
getHardwareDetailsById(args: { teamId: string; monitorId: string; dateRange: DateRange }): Promise<HardwareDetailsResult>;
getPageSpeedDetailsById(args: { teamId: string; monitorId: string; dateRange: DateRange }): Promise<PageSpeedDetailsResult>;
getGeoChecksByMonitorId(args: {
teamId: string;
monitorId: string;
dateRange: string;
dateRange: DateRange;
continents?: GeoContinent[];
}): Promise<GroupedGeoCheckResult>;
getMonitorById(args: { teamId: string; monitorId: string }): Promise<Monitor>;
@@ -133,31 +133,6 @@ export class MonitorService implements IMonitorService {
this.incidentsRepository = incidentsRepository;
}
private getDateRange = (dateRange: DateRangeKey) => {
const startDates = {
recent: new Date(new Date().setHours(new Date().getHours() - 2)),
day: new Date(new Date().setDate(new Date().getDate() - 1)),
week: new Date(new Date().setDate(new Date().getDate() - 7)),
month: new Date(new Date().setMonth(new Date().getMonth() - 1)),
all: new Date(0),
};
return {
start: startDates[dateRange],
end: new Date(),
};
};
private getDateFormat = (dateRange: DateRangeKey): string => {
const formatLookup = {
recent: "%Y-%m-%dT%H:%M:00Z",
day: "%Y-%m-%dT%H:00:00Z",
week: "%Y-%m-%dT00:00:00Z",
month: "%Y-%m-%dT00:00:00Z",
all: "%Y-%m-%dT00:00:00Z",
};
return formatLookup[dateRange];
};
createMonitor = async (teamId: string, userId: string, body: Monitor): Promise<void> => {
const monitor = await this.monitorsRepository.create(body, teamId, userId);
if (!monitor) {
@@ -200,15 +175,13 @@ export class MonitorService implements IMonitorService {
}: {
teamId: string;
monitorId: string;
dateRange: string;
dateRange: DateRange;
}): Promise<UptimeDetailsResult> => {
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
if (!monitor) {
throw new AppError({ message: `Monitor with ID ${monitorId} not found.`, status: 404 });
}
const rangeKey = dateRange as DateRangeKey;
const { start, end } = this.getDateRange(rangeKey);
const checksData = await this.checksRepository.findByDateRangeAndMonitorId(monitor.id, start, end, this.getDateFormat(rangeKey), {
const checksData = await this.checksRepository.findByDateRangeAndMonitorId(monitor.id, dateRange, {
type: monitor.type,
});
const monitorStats = await this.monitorStatsRepository.findByMonitorId(monitor.id);
@@ -237,7 +210,7 @@ export class MonitorService implements IMonitorService {
}: {
teamId: string;
monitorId: string;
dateRange: string;
dateRange: DateRange;
}): Promise<HardwareDetailsResult> => {
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
if (!monitor) {
@@ -247,9 +220,7 @@ export class MonitorService implements IMonitorService {
throw new AppError({ message: `${monitor.type} monitors are not supported for hardware details`, status: 400 });
}
const rangeKey = dateRange as DateRangeKey;
const { start, end } = this.getDateRange(rangeKey);
const checksData = await this.checksRepository.findByDateRangeAndMonitorId(monitor.id, start, end, this.getDateFormat(rangeKey), {
const checksData = await this.checksRepository.findByDateRangeAndMonitorId(monitor.id, dateRange, {
type: monitor.type,
});
@@ -279,7 +250,7 @@ export class MonitorService implements IMonitorService {
}: {
teamId: string;
monitorId: string;
dateRange: string;
dateRange: DateRange;
}): Promise<PageSpeedDetailsResult> => {
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
if (!monitor) {
@@ -289,9 +260,7 @@ export class MonitorService implements IMonitorService {
throw new AppError({ message: `${monitor.type} monitors are not supported for pagespeed details`, status: 400 });
}
const rangeKey = dateRange as DateRangeKey;
const { start, end } = this.getDateRange(rangeKey);
const checksData = await this.checksRepository.findByDateRangeAndMonitorId(monitor.id, start, end, this.getDateFormat(rangeKey), {
const checksData = await this.checksRepository.findByDateRangeAndMonitorId(monitor.id, dateRange, {
type: monitor.type,
});
@@ -318,7 +287,7 @@ export class MonitorService implements IMonitorService {
}: {
teamId: string;
monitorId: string;
dateRange: string;
dateRange: DateRange;
continents?: GeoContinent[];
}): Promise<GroupedGeoCheckResult> => {
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
@@ -330,15 +299,7 @@ export class MonitorService implements IMonitorService {
return { groupedGeoChecks: [] };
}
const rangeKey = dateRange as DateRangeKey;
const { start, end } = this.getDateRange(rangeKey);
const groupedGeoChecks = await this.geoChecksRepository.findGroupedByMonitorIdAndDateRange(
monitor.id,
start,
end,
this.getDateFormat(rangeKey),
continents
);
const groupedGeoChecks = await this.geoChecksRepository.findGroupedByMonitorIdAndDateRange(monitor.id, dateRange, continents);
return { groupedGeoChecks };
};
+16 -3
View File
@@ -1,6 +1,7 @@
import type { GroupedCheck, HasResponseTime, NormalizedCheck, NormalizedUptimeCheck } from "@/domain/checks/check.type.js";
import { type DateRange } from "@/types/query.js";
export const getDateForRange = (dateRange: string): Date | undefined => {
export const getDateForRange = (dateRange: DateRange): Date => {
const now = Date.now();
switch (dateRange) {
case "recent":
@@ -14,12 +15,24 @@ export const getDateForRange = (dateRange: string): Date | undefined => {
case "month":
return new Date(now - 30 * 24 * 60 * 60 * 1000); // 30 days
case "all":
return undefined;
return new Date(0);
default:
return undefined;
return new Date(0);
}
};
export const getDateFormat = (dateRange: DateRange): string => {
const formatLookup = {
hour: "%Y-%m-%dT%H:%M:00Z",
recent: "%Y-%m-%dT%H:%M:00Z",
day: "%Y-%m-%dT%H:00:00Z",
week: "%Y-%m-%dT00:00:00Z",
month: "%Y-%m-%dT00:00:00Z",
all: "%Y-%m-%dT00:00:00Z",
};
return formatLookup[dateRange];
};
const percentileBy = <T>(arr: T[], percentile: number, value: (item: T) => number): number => {
const sorted = arr.slice().sort((a, b) => value(a) - value(b));
const index = (percentile / 100) * (sorted.length - 1);
+24 -8
View File
@@ -1,6 +1,7 @@
import { describe, expect, it } from "@jest/globals";
import { getDateForRange, NormalizeData, NormalizeDataUptimeDetails } from "../../../src/utils/dataUtils.ts";
import { getDateForRange, getDateFormat, NormalizeData, NormalizeDataUptimeDetails } from "../../../src/utils/dataUtils.ts";
import type { GroupedCheck } from "../../../src/domain/checks/check.type.ts";
import { DateRanges, type DateRange } from "../../../src/types/query.ts";
// ── Helpers ──────────────────────────────────────────────────────────────────
@@ -29,20 +30,35 @@ describe("getDateForRange", () => {
["month", 30 * 24 * 60 * 60 * 1000],
])("returns now minus the %s window", (range, offsetMs) => {
const before = Date.now();
const result = getDateForRange(range as string);
const result = getDateForRange(range as DateRange);
const after = Date.now();
expect(result).toBeInstanceOf(Date);
expect(result!.getTime()).toBeGreaterThanOrEqual(before - offsetMs);
expect(result!.getTime()).toBeLessThanOrEqual(after - offsetMs);
expect(result.getTime()).toBeGreaterThanOrEqual(before - offsetMs);
expect(result.getTime()).toBeLessThanOrEqual(after - offsetMs);
});
it("returns undefined for 'all'", () => {
expect(getDateForRange("all")).toBeUndefined();
it("returns the epoch for 'all'", () => {
expect(getDateForRange("all")).toEqual(new Date(0));
});
});
describe("getDateFormat", () => {
it.each([
["recent", "%Y-%m-%dT%H:%M:00Z"],
["hour", "%Y-%m-%dT%H:%M:00Z"],
["day", "%Y-%m-%dT%H:00:00Z"],
["week", "%Y-%m-%dT00:00:00Z"],
["month", "%Y-%m-%dT00:00:00Z"],
["all", "%Y-%m-%dT00:00:00Z"],
])("returns the %s bucket format", (range, format) => {
expect(getDateFormat(range as DateRange)).toBe(format);
});
it("returns undefined for unknown ranges", () => {
expect(getDateForRange("fortnight")).toBeUndefined();
it("has a format for every member of DateRanges", () => {
for (const range of DateRanges) {
expect(getDateFormat(range)).toMatch(/^%Y-%m-%d/);
}
});
});