From d79901aa420f2677a9c0708902a0e5084ee44255 Mon Sep 17 00:00:00 2001 From: Henry Tsai Date: Thu, 2 Nov 2023 13:42:29 -0700 Subject: [PATCH] Time and timestamp utility refactoring (#593) 1. Exported all time utilities for downstream external usage 2. Renamed `getCurrentTimeInHighPrecision()` -> `getCurrentTimestamp()` for dev friendliness 3. Removed redundant `createDateString()` 4. Grouped all time utilities into a class 5. Replaced all direct usage of temporal library --- src/index.ts | 2 +- src/interfaces/events-get.ts | 6 +- src/interfaces/messages-get.ts | 6 +- src/interfaces/permissions-grant.ts | 8 +- src/interfaces/permissions-request.ts | 6 +- src/interfaces/permissions-revoke.ts | 6 +- src/interfaces/protocols-configure.ts | 6 +- src/interfaces/protocols-query.ts | 6 +- src/interfaces/records-delete.ts | 6 +- src/interfaces/records-query.ts | 6 +- src/interfaces/records-read.ts | 6 +- src/interfaces/records-write.ts | 12 +- src/utils/time.ts | 127 ++++++++++--------- tests/core/message.spec.ts | 12 +- tests/handlers/permissions-grant.spec.ts | 6 +- tests/handlers/permissions-revoke.spec.ts | 12 +- tests/handlers/protocols-configure.spec.ts | 6 +- tests/handlers/protocols-query.spec.ts | 12 +- tests/handlers/records-delete.spec.ts | 4 +- tests/handlers/records-query.spec.ts | 89 +++++++------ tests/handlers/records-write.spec.ts | 14 +- tests/interfaces/permissions-grant.spec.ts | 21 ++- tests/interfaces/protocols-configure.spec.ts | 4 +- tests/interfaces/protocols-query.spec.ts | 4 +- tests/interfaces/records-delete.spec.ts | 4 +- tests/interfaces/records-query.spec.ts | 4 +- tests/interfaces/records-read.spec.ts | 4 +- tests/interfaces/records-write.spec.ts | 8 +- tests/store/index-level.spec.ts | 6 +- tests/utils/test-data-generator.ts | 14 +- tests/utils/time.spec.ts | 23 ++-- 31 files changed, 225 insertions(+), 225 deletions(-) diff --git a/src/index.ts b/src/index.ts index 197bc443b..70bbfa13d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,12 +41,12 @@ export { Protocols } from './utils/protocols.js'; export { ProtocolsConfigure, ProtocolsConfigureOptions } from './interfaces/protocols-configure.js'; export { ProtocolsQuery, ProtocolsQueryOptions } from './interfaces/protocols-query.js'; export { Records } from './utils/records.js'; -export { createTimestamp } from './utils/time.js'; export { RecordsDelete, RecordsDeleteOptions } from './interfaces/records-delete.js'; export { RecordsRead, RecordsReadOptions } from './interfaces/records-read.js'; export { Secp256k1 } from './utils/secp256k1.js'; export { Signer } from './types/signer.js'; export { SortOrder } from './types/message-types.js'; +export { Time } from './utils/time.js'; // store interfaces export { DataStoreLevel } from './store/data-store-level.js'; diff --git a/src/interfaces/events-get.ts b/src/interfaces/events-get.ts index 5a5ecf206..b3df1e26d 100644 --- a/src/interfaces/events-get.ts +++ b/src/interfaces/events-get.ts @@ -1,9 +1,9 @@ import type { Signer } from '../types/signer.js'; import type { EventsGetDescriptor, EventsGetMessage } from '../types/event-types.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; export type EventsGetOptions = { watermark?: string; @@ -16,7 +16,7 @@ export class EventsGet extends Message { public static async parse(message: EventsGetMessage): Promise { Message.validateJsonSchema(message); await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); return new EventsGet(message); } @@ -25,7 +25,7 @@ export class EventsGet extends Message { const descriptor: EventsGetDescriptor = { interface : DwnInterfaceName.Events, method : DwnMethodName.Get, - messageTimestamp : options.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), }; if (options.watermark) { diff --git a/src/interfaces/messages-get.ts b/src/interfaces/messages-get.ts index c95bcb4b6..ced08c41f 100644 --- a/src/interfaces/messages-get.ts +++ b/src/interfaces/messages-get.ts @@ -2,10 +2,10 @@ import type { Signer } from '../types/signer.js'; import type { MessagesGetDescriptor, MessagesGetMessage } from '../types/messages-types.js'; import { Cid } from '../utils/cid.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../index.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; export type MessagesGetOptions = { messageCids: string[]; @@ -19,7 +19,7 @@ export class MessagesGet extends Message { this.validateMessageCids(message.descriptor.messageCids); await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); return new MessagesGet(message); } @@ -29,7 +29,7 @@ export class MessagesGet extends Message { interface : DwnInterfaceName.Messages, method : DwnMethodName.Get, messageCids : options.messageCids, - messageTimestamp : options?.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options?.messageTimestamp ?? Time.getCurrentTimestamp(), }; const authorization = await Message.createAuthorization(descriptor, options.signer); diff --git a/src/interfaces/permissions-grant.ts b/src/interfaces/permissions-grant.ts index 5b2b0b67e..2f9598289 100644 --- a/src/interfaces/permissions-grant.ts +++ b/src/interfaces/permissions-grant.ts @@ -4,10 +4,10 @@ import type { DelegatedGrantMessage, PermissionConditions, PermissionScope, Reco import type { PermissionsGrantDescriptor, PermissionsGrantMessage } from '../types/permissions-types.js'; import { removeUndefinedProperties } from '../utils/object.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../core/dwn-error.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; import { normalizeProtocolUrl, normalizeSchemaUrl } from '../utils/url.js'; export type PermissionsGrantOptions = { @@ -39,8 +39,8 @@ export class PermissionsGrant extends Message { public static async parse(message: PermissionsGrantMessage): Promise { await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); PermissionsGrant.validateScope(message); - validateTimestamp(message.descriptor.messageTimestamp); - validateTimestamp(message.descriptor.dateExpires); + Time.validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.dateExpires); return new PermissionsGrant(message); } @@ -53,7 +53,7 @@ export class PermissionsGrant extends Message { const descriptor: PermissionsGrantDescriptor = { interface : DwnInterfaceName.Permissions, method : DwnMethodName.Grant, - messageTimestamp : options.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), dateExpires : options.dateExpires, description : options.description, grantedTo : options.grantedTo, diff --git a/src/interfaces/permissions-request.ts b/src/interfaces/permissions-request.ts index 166c7d310..24888d0cb 100644 --- a/src/interfaces/permissions-request.ts +++ b/src/interfaces/permissions-request.ts @@ -3,9 +3,9 @@ import type { PermissionConditions, PermissionScope } from '../types/permissions import type { PermissionsRequestDescriptor, PermissionsRequestMessage } from '../types/permissions-types.js'; import { removeUndefinedProperties } from '../utils/object.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; export type PermissionsRequestOptions = { messageTimestamp?: string; @@ -22,7 +22,7 @@ export class PermissionsRequest extends Message { public static async parse(message: PermissionsRequestMessage): Promise { await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); return new PermissionsRequest(message); } @@ -31,7 +31,7 @@ export class PermissionsRequest extends Message { const descriptor: PermissionsRequestDescriptor = { interface : DwnInterfaceName.Permissions, method : DwnMethodName.Request, - messageTimestamp : options.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), description : options.description, grantedTo : options.grantedTo, grantedBy : options.grantedBy, diff --git a/src/interfaces/permissions-revoke.ts b/src/interfaces/permissions-revoke.ts index 4060f1af3..c100aace7 100644 --- a/src/interfaces/permissions-revoke.ts +++ b/src/interfaces/permissions-revoke.ts @@ -1,10 +1,10 @@ import type { Signer } from '../types/signer.js'; import type { PermissionsGrantMessage, PermissionsRevokeDescriptor, PermissionsRevokeMessage } from '../types/permissions-types.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../core/dwn-error.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; export type PermissionsRevokeOptions = { messageTimestamp?: string; @@ -15,7 +15,7 @@ export type PermissionsRevokeOptions = { export class PermissionsRevoke extends Message { public static async parse(message: PermissionsRevokeMessage): Promise { await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); return new PermissionsRevoke(message); } @@ -24,7 +24,7 @@ export class PermissionsRevoke extends Message { const descriptor: PermissionsRevokeDescriptor = { interface : DwnInterfaceName.Permissions, method : DwnMethodName.Revoke, - messageTimestamp : options.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), permissionsGrantId : options.permissionsGrantId, }; diff --git a/src/interfaces/protocols-configure.ts b/src/interfaces/protocols-configure.ts index 0eb8ca0cc..833d1937f 100644 --- a/src/interfaces/protocols-configure.ts +++ b/src/interfaces/protocols-configure.ts @@ -1,10 +1,10 @@ import type { Signer } from '../types/signer.js'; import type { ProtocolDefinition, ProtocolRuleSet, ProtocolsConfigureDescriptor, ProtocolsConfigureMessage } from '../types/protocols-types.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../index.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; import { normalizeProtocolUrl, normalizeSchemaUrl, validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../utils/url.js'; export type ProtocolsConfigureOptions = { @@ -22,7 +22,7 @@ export class ProtocolsConfigure extends Message { Message.validateJsonSchema(message); ProtocolsConfigure.validateProtocolDefinition(message.descriptor.definition); await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); return new ProtocolsConfigure(message); } @@ -31,7 +31,7 @@ export class ProtocolsConfigure extends Message { const descriptor: ProtocolsConfigureDescriptor = { interface : DwnInterfaceName.Protocols, method : DwnMethodName.Configure, - messageTimestamp : options.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), definition : ProtocolsConfigure.normalizeDefinition(options.definition) }; diff --git a/src/interfaces/protocols-query.ts b/src/interfaces/protocols-query.ts index e92b18b36..7ee8a9059 100644 --- a/src/interfaces/protocols-query.ts +++ b/src/interfaces/protocols-query.ts @@ -5,9 +5,9 @@ import type { ProtocolsQueryDescriptor, ProtocolsQueryFilter, ProtocolsQueryMess import { GrantAuthorization } from '../core/grant-authorization.js'; import { removeUndefinedProperties } from '../utils/object.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; import { normalizeProtocolUrl, validateProtocolUrlNormalized } from '../utils/url.js'; import { DwnError, DwnErrorCode } from '../core/dwn-error.js'; @@ -29,7 +29,7 @@ export class ProtocolsQuery extends Message { if (message.descriptor.filter !== undefined) { validateProtocolUrlNormalized(message.descriptor.filter.protocol); } - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); return new ProtocolsQuery(message); } @@ -38,7 +38,7 @@ export class ProtocolsQuery extends Message { const descriptor: ProtocolsQueryDescriptor = { interface : DwnInterfaceName.Protocols, method : DwnMethodName.Query, - messageTimestamp : options.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), filter : ProtocolsQuery.normalizeFilter(options.filter), }; diff --git a/src/interfaces/records-delete.ts b/src/interfaces/records-delete.ts index b54abf19b..110827da5 100644 --- a/src/interfaces/records-delete.ts +++ b/src/interfaces/records-delete.ts @@ -6,10 +6,10 @@ import type { RecordsDeleteDescriptor, RecordsDeleteMessage } from '../types/rec import { Message } from '../core/message.js'; import { ProtocolAuthorization } from '../core/protocol-authorization.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../index.js'; import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; export type RecordsDeleteOptions = { recordId: string; @@ -22,7 +22,7 @@ export class RecordsDelete extends Message { public static async parse(message: RecordsDeleteMessage): Promise { await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); const recordsDelete = new RecordsDelete(message); return recordsDelete; @@ -35,7 +35,7 @@ export class RecordsDelete extends Message { */ public static async create(options: RecordsDeleteOptions): Promise { const recordId = options.recordId; - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const descriptor: RecordsDeleteDescriptor = { interface : DwnInterfaceName.Records, diff --git a/src/interfaces/records-query.ts b/src/interfaces/records-query.ts index c1f9ffff4..1dc2331b6 100644 --- a/src/interfaces/records-query.ts +++ b/src/interfaces/records-query.ts @@ -5,10 +5,10 @@ import type { RecordsFilter, RecordsQueryDescriptor, RecordsQueryMessage } from import { Message } from '../core/message.js'; import { Records } from '../utils/records.js'; import { removeUndefinedProperties } from '../utils/object.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../core/dwn-error.js'; import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; import { validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../utils/url.js'; export enum DateSort { @@ -49,7 +49,7 @@ export class RecordsQuery extends Message { if (message.descriptor.filter.schema !== undefined) { validateSchemaUrlNormalized(message.descriptor.filter.schema); } - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); return new RecordsQuery(message); } @@ -58,7 +58,7 @@ export class RecordsQuery extends Message { const descriptor: RecordsQueryDescriptor = { interface : DwnInterfaceName.Records, method : DwnMethodName.Query, - messageTimestamp : options.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : options.messageTimestamp ?? Time.getCurrentTimestamp(), filter : Records.normalizeFilter(options.filter), dateSort : options.dateSort, pagination : options.pagination, diff --git a/src/interfaces/records-read.ts b/src/interfaces/records-read.ts index 90b3c1b4c..7137d79d9 100644 --- a/src/interfaces/records-read.ts +++ b/src/interfaces/records-read.ts @@ -8,10 +8,10 @@ import { ProtocolAuthorization } from '../core/protocol-authorization.js'; import { Records } from '../utils/records.js'; import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js'; import { removeUndefinedProperties } from '../utils/object.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../index.js'; import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; export type RecordsReadOptions = { filter: RecordsFilter; @@ -31,7 +31,7 @@ export class RecordsRead extends Message { if (message.authorization !== undefined) { await validateMessageSignatureIntegrity(message.authorization.signature, message.descriptor); } - validateTimestamp(message.descriptor.messageTimestamp); + Time.validateTimestamp(message.descriptor.messageTimestamp); const recordsRead = new RecordsRead(message); return recordsRead; @@ -46,7 +46,7 @@ export class RecordsRead extends Message { */ public static async create(options: RecordsReadOptions): Promise { const { filter, signer, permissionsGrantId, protocolRole } = options; - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const descriptor: RecordsReadDescriptor = { interface : DwnInterfaceName.Records, diff --git a/src/interfaces/records-write.ts b/src/interfaces/records-write.ts index 0a8f0465e..9038f1ec2 100644 --- a/src/interfaces/records-write.ts +++ b/src/interfaces/records-write.ts @@ -26,10 +26,10 @@ import { ProtocolAuthorization } from '../core/protocol-authorization.js'; import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js'; import { removeUndefinedProperties } from '../utils/object.js'; import { Secp256k1 } from '../utils/secp256k1.js'; +import { Time } from '../utils/time.js'; import { validateMessageSignatureIntegrity } from '../core/auth.js'; import { DwnError, DwnErrorCode } from '../core/dwn-error.js'; import { DwnInterfaceName, DwnMethodName } from '../core/message.js'; -import { getCurrentTimeInHighPrecision, validateTimestamp } from '../utils/time.js'; import { normalizeProtocolUrl, normalizeSchemaUrl, validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../utils/url.js'; export type RecordsWriteOptions = { @@ -273,7 +273,7 @@ export class RecordsWrite { const dataCid = options.dataCid ?? await Cid.computeDagPbCidFromBytes(options.data!); const dataSize = options.dataSize ?? options.data!.length; - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const descriptor: RecordsWriteDescriptor = { interface : DwnInterfaceName.Records, @@ -356,7 +356,7 @@ export class RecordsWrite { */ public static async createFrom(options: CreateFromOptions): Promise { const sourceMessage = options.recordsWriteMessage; - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); // inherit published value from parent if neither published nor datePublished is specified const published = options.published ?? (options.datePublished ? true : sourceMessage.descriptor.published); @@ -634,10 +634,10 @@ export class RecordsWrite { validateSchemaUrlNormalized(this.message.descriptor.schema); } - validateTimestamp(this.message.descriptor.messageTimestamp); - validateTimestamp(this.message.descriptor.dateCreated); + Time.validateTimestamp(this.message.descriptor.messageTimestamp); + Time.validateTimestamp(this.message.descriptor.dateCreated); if (this.message.descriptor.datePublished) { - validateTimestamp(this.message.descriptor.datePublished); + Time.validateTimestamp(this.message.descriptor.datePublished); } } diff --git a/src/utils/time.ts b/src/utils/time.ts index eca878032..c703bc0e2 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -2,72 +2,77 @@ import { Temporal } from '@js-temporal/polyfill'; import { DwnError, DwnErrorCode } from '../index.js'; /** - * sleeps for the desired duration - * @param durationInMillisecond the desired amount of sleep time - * @returns when the provided duration has passed + * Time related utilities. */ -export function sleep(durationInMillisecond: number): Promise { - return new Promise(resolve => setTimeout(resolve, durationInMillisecond)); -} +export class Time { + /** + * sleeps for the desired duration + * @param durationInMillisecond the desired amount of sleep time + * @returns when the provided duration has passed + */ + public static async sleep(durationInMillisecond: number): Promise { + return new Promise(resolve => setTimeout(resolve, durationInMillisecond)); + } -/** - * returns an UTC ISO-8601 timestamp with microsecond precision - * using @js-temporal/polyfill - */ -export function getCurrentTimeInHighPrecision(): string { - return Temporal.Now.instant().toString({ smallestUnit: 'microseconds' }); -} + /** + * We must sleep for at least 2ms to avoid timestamp collisions during testing. + * https://github.com/TBD54566975/dwn-sdk-js/issues/481 + */ + public static async minimalSleep(): Promise { + await Time.sleep(2); + } -/** - * Creates a UTC ISO-8601 timestamp in microsecond precision accepted by DWN. - * @param options - Options for creating the timestamp. - * @returns string - */ -export function createTimestamp( - options: {year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number, millisecond?: number, microsecond?: number} -): string { - const { year, month, day, hour, minute, second, millisecond, microsecond } = options; - return Temporal.ZonedDateTime.from({ - timeZone: 'UTC', - year, - month, - day, - hour, - minute, - second, - millisecond, - microsecond - }).toInstant().toString({ smallestUnit: 'microseconds' }); -} + /** + * Returns an UTC ISO-8601 timestamp with microsecond precision accepted by DWN. + * using @js-temporal/polyfill + */ + public static getCurrentTimestamp(): string { + return Temporal.Now.instant().toString({ smallestUnit: 'microseconds' }); + } -/** - * We must sleep for at least 2ms to avoid timestamp collisions during testing. - * https://github.com/TBD54566975/dwn-sdk-js/issues/481 - */ -export async function minimalSleep(): Promise { - await sleep(2); -} + /** + * Creates a UTC ISO-8601 timestamp in microsecond precision accepted by DWN. + * @param options - Options for creating the timestamp. + * @returns string + */ + public static createTimestamp(options: { + year?: number, month?: number, day?: number, hour?: number, minute?: number, second?: number, millisecond?: number, microsecond?: number + }): string { + const { year, month, day, hour, minute, second, millisecond, microsecond } = options; + return Temporal.ZonedDateTime.from({ + timeZone: 'UTC', + year, + month, + day, + hour, + minute, + second, + millisecond, + microsecond + }).toInstant().toString({ smallestUnit: 'microseconds' }); + } -/** - * Validates that the provided timestamp is a valid number - * @param timestamp the timestamp to validate - * @throws DwnError if timestamp is not a valid number - */ -export function validateTimestamp(timestamp: string): void { - try { - Temporal.Instant.from(timestamp); - } catch { - throw new DwnError(DwnErrorCode.TimestampInvalid, `Invalid timestamp: ${timestamp}`); + /** + * Creates a UTC ISO-8601 timestamp offset from now or given timestamp accepted by DWN. + * @param offset Negative number means offset into the past. + */ + public static createOffsetTimestamp(offset: { seconds: number }, timestamp?: string): string { + const timestampInstant = timestamp ? Temporal.Instant.from(timestamp) : Temporal.Now.instant(); + const offsetDuration = Temporal.Duration.from(offset); + const offsetInstant = timestampInstant.add(offsetDuration); + return offsetInstant.toString({ smallestUnit: 'microseconds' }); } -} -/** - * Creates a UTC ISO-8601 timestamp offset from now or given timestamp accepted by DWN. - * @param offset Negative number means offset into the past. - */ -export function createOffsetTimestamp(offset: { seconds: number }, timestamp?: string): string { - const timestampInstant = timestamp ? Temporal.Instant.from(timestamp) : Temporal.Now.instant(); - const offsetDuration = Temporal.Duration.from(offset); - const offsetInstant = timestampInstant.add(offsetDuration); - return offsetInstant.toString({ smallestUnit: 'microseconds' }); + /** + * Validates that the provided timestamp is a valid number + * @param timestamp the timestamp to validate + * @throws DwnError if timestamp is not a valid number + */ + public static validateTimestamp(timestamp: string): void { + try { + Temporal.Instant.from(timestamp); + } catch { + throw new DwnError(DwnErrorCode.TimestampInvalid, `Invalid timestamp: ${timestamp}`); + } + } } diff --git a/tests/core/message.spec.ts b/tests/core/message.spec.ts index 6db3c8e89..011c2d42c 100644 --- a/tests/core/message.spec.ts +++ b/tests/core/message.spec.ts @@ -4,7 +4,7 @@ import { expect } from 'chai'; import { Message } from '../../src/core/message.js'; import { RecordsRead } from '../../src/index.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; -import { getCurrentTimeInHighPrecision, minimalSleep } from '../../src/utils/time.js'; +import { Time } from '../../src/utils/time.js'; describe('Message', () => { describe('getSigner()', () => { @@ -37,7 +37,7 @@ describe('Message', () => { describe('compareMessageTimestamp', () => { it('should return 0 if age is same', async () => { - const dateModified = getCurrentTimeInHighPrecision(); + const dateModified = Time.getCurrentTimestamp(); const a = (await TestDataGenerator.generateRecordsWrite({ messageTimestamp: dateModified })).message; const b = JSON.parse(JSON.stringify(a)); // create a deep copy of `a` @@ -49,9 +49,9 @@ describe('Message', () => { describe('getNewestMessage', () => { it('should return the newest message', async () => { const a = (await TestDataGenerator.generateRecordsWrite()).message; - await minimalSleep(); + await Time.minimalSleep(); const b = (await TestDataGenerator.generateRecordsWrite()).message; - await minimalSleep(); + await Time.minimalSleep(); const c = (await TestDataGenerator.generateRecordsWrite()).message; // c is the newest since its created last const newestMessage = await Message.getNewestMessage([b, c, a]); @@ -62,9 +62,9 @@ describe('Message', () => { describe('getOldestMessage', () => { it('should return the newest message', async () => { const a = (await TestDataGenerator.generateRecordsWrite()).message; - await minimalSleep(); + await Time.minimalSleep(); const b = (await TestDataGenerator.generateRecordsWrite()).message; - await minimalSleep(); + await Time.minimalSleep(); const c = (await TestDataGenerator.generateRecordsWrite()).message; // c is the newest since its created last const newestMessage = await Message.getOldestMessage([b, c, a]); diff --git a/tests/handlers/permissions-grant.spec.ts b/tests/handlers/permissions-grant.spec.ts index 6268c6643..f36da5ddb 100644 --- a/tests/handlers/permissions-grant.spec.ts +++ b/tests/handlers/permissions-grant.spec.ts @@ -11,12 +11,12 @@ import { DidResolver } from '../../src/did/did-resolver.js'; import { Dwn } from '../../src/dwn.js'; import { DwnErrorCode } from '../../src/core/dwn-error.js'; import { expect } from 'chai'; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { Jws } from '../../src/index.js'; import { PermissionsGrant } from '../../src/interfaces/permissions-grant.js'; import { PermissionsGrantHandler } from '../../src/handlers/permissions-grant.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestStores } from '../test-stores.js'; +import { Time } from '../../src/utils/time.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../../src/core/message.js'; export function testPermissionsGrantHandler(): void { @@ -149,7 +149,7 @@ export function testPermissionsGrantHandler(): void { // Options to create a grant with `schema` in its `scope` const permissionsGrantBaseOptions = { author : alice, - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', grantedFor : 'did:jank:bob', @@ -219,7 +219,7 @@ export function testPermissionsGrantHandler(): void { const contextIdAndProtocolPathGrant = await TestDataGenerator.generatePermissionsGrant({ author : alice, - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', grantedFor : 'did:jank:bob', diff --git a/tests/handlers/permissions-revoke.spec.ts b/tests/handlers/permissions-revoke.spec.ts index 2004bdae7..5a38590bf 100644 --- a/tests/handlers/permissions-revoke.spec.ts +++ b/tests/handlers/permissions-revoke.spec.ts @@ -11,7 +11,7 @@ import { Message } from '../../src/core/message.js'; import { MessageStoreLevel } from '../../src/store/message-store-level.js'; import { PermissionsRevoke } from '../../src/interfaces/permissions-revoke.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; -import { getCurrentTimeInHighPrecision, minimalSleep, sleep } from '../../src/utils/time.js'; +import { Time } from '../../src/utils/time.js'; describe('PermissionsRevokeHandler.handle()', () => { let didResolver: DidResolver; @@ -118,8 +118,8 @@ describe('PermissionsRevokeHandler.handle()', () => { it('should reject with 400 if the associated grant was issued after the revoke was created', async () => { const alice = await DidKeyResolver.generate(); - const preGrantTimeStamp = getCurrentTimeInHighPrecision(); - await sleep(10); + const preGrantTimeStamp = Time.getCurrentTimestamp(); + await Time.sleep(10); // Create grant const { permissionsGrant } = await TestDataGenerator.generatePermissionsGrant({ @@ -205,7 +205,7 @@ describe('PermissionsRevokeHandler.handle()', () => { expect(permissionsGrantReply.status.code).to.eq(202); // Create two revokes with same timestamp - const revokeTimestamp = getCurrentTimeInHighPrecision(); + const revokeTimestamp = Time.getCurrentTimestamp(); const { permissionsRevoke: revoke1 } = await TestDataGenerator.generatePermissionsRevoke({ author : alice, permissionsGrantId : await Message.getCid(permissionsGrant.message), @@ -255,7 +255,7 @@ describe('PermissionsRevokeHandler.handle()', () => { permissionsGrantId : await Message.getCid(permissionsGrant.message), }); - await minimalSleep(); + await Time.minimalSleep(); // Revoke the grant using a later timestamp than the pre-created revoke const { permissionsRevoke: permissionsRevoke2 } = await TestDataGenerator.generatePermissionsRevoke({ @@ -323,7 +323,7 @@ describe('PermissionsRevokeHandler.handle()', () => { permissionsGrantId : await Message.getCid(permissionsGrant.message), }); - await sleep(10); + await Time.sleep(10); // Revoke the grant using a later timestamp than the pre-created revoke const { permissionsRevoke: permissionsRevoke2 } = await TestDataGenerator.generatePermissionsRevoke({ diff --git a/tests/handlers/protocols-configure.spec.ts b/tests/handlers/protocols-configure.spec.ts index 930f8631a..c8736f37d 100644 --- a/tests/handlers/protocols-configure.spec.ts +++ b/tests/handlers/protocols-configure.spec.ts @@ -16,10 +16,10 @@ import { DidKeyResolver } from '../../src/did/did-key-resolver.js'; import { GeneralJwsBuilder } from '../../src/jose/jws/general/builder.js'; import { lexicographicalCompare } from '../../src/utils/string.js'; import { Message } from '../../src/core/message.js'; -import { minimalSleep } from '../../src/utils/time.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestStores } from '../test-stores.js'; import { TestStubGenerator } from '../utils/test-stub-generator.js'; +import { Time } from '../../src/utils/time.js'; import { DidResolver, Dwn, DwnErrorCode, Encoder, Jws } from '../../src/index.js'; @@ -115,7 +115,7 @@ export function testProtocolsConfigureHandler(): void { author: alice, protocolDefinition, }); - await minimalSleep(); + await Time.minimalSleep(); const middleProtocolsConfigure = await TestDataGenerator.generateProtocolsConfigure({ author: alice, protocolDefinition, @@ -299,7 +299,7 @@ export function testProtocolsConfigureHandler(): void { it('should delete older ProtocolsConfigure events when one is overwritten', async () => { const alice = await DidKeyResolver.generate(); const oldestWrite = await TestDataGenerator.generateProtocolsConfigure({ author: alice, protocolDefinition: minimalProtocolDefinition }); - await minimalSleep(); + await Time.minimalSleep(); const newestWrite = await TestDataGenerator.generateProtocolsConfigure({ author: alice, protocolDefinition: minimalProtocolDefinition }); let reply = await dwn.processMessage(alice.did, oldestWrite.message); diff --git a/tests/handlers/protocols-query.spec.ts b/tests/handlers/protocols-query.spec.ts index 13467769c..18789a9d1 100644 --- a/tests/handlers/protocols-query.spec.ts +++ b/tests/handlers/protocols-query.spec.ts @@ -14,9 +14,9 @@ import { GeneralJwsBuilder } from '../../src/jose/jws/general/builder.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestStores } from '../test-stores.js'; import { TestStubGenerator } from '../utils/test-stub-generator.js'; +import { Time } from '../../src/utils/time.js'; import { DidResolver, Dwn, DwnErrorCode, Encoder, Jws, ProtocolsQuery } from '../../src/index.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../../src/core/message.js'; -import { getCurrentTimeInHighPrecision, sleep } from '../../src/utils/time.js'; chai.use(chaiAsPromised); @@ -238,8 +238,8 @@ export function testProtocolsQueryHandler(): void { const bob = await DidKeyResolver.generate(); // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure and an expiry time - const dateGranted = getCurrentTimeInHighPrecision(); - const dateExpires = getCurrentTimeInHighPrecision(); + const dateGranted = Time.getCurrentTimestamp(); + const dateExpires = Time.getCurrentTimestamp(); const permissionsGrant = await TestDataGenerator.generatePermissionsGrant({ author : alice, messageTimestamp : dateGranted, @@ -272,9 +272,9 @@ export function testProtocolsQueryHandler(): void { const bob = await DidKeyResolver.generate(); // Set up timestamps - const protocolsQueryTimestamp = getCurrentTimeInHighPrecision(); - await sleep(2); - const dateGranted = getCurrentTimeInHighPrecision(); + const protocolsQueryTimestamp = Time.getCurrentTimestamp(); + await Time.minimalSleep(); + const dateGranted = Time.getCurrentTimestamp(); // Alice gives Bob a PermissionsGrant with scope ProtocolsConfigure const permissionsGrant = await TestDataGenerator.generatePermissionsGrant({ diff --git a/tests/handlers/records-delete.spec.ts b/tests/handlers/records-delete.spec.ts index d8a9f1917..a754060c6 100644 --- a/tests/handlers/records-delete.spec.ts +++ b/tests/handlers/records-delete.spec.ts @@ -21,12 +21,12 @@ import { ArrayUtility } from '../../src/utils/array.js'; import { DidKeyResolver } from '../../src/did/did-key-resolver.js'; import { DwnErrorCode } from '../../src/index.js'; import { Message } from '../../src/core/message.js'; -import { minimalSleep } from '../../src/utils/time.js'; import { RecordsDeleteHandler } from '../../src/handlers/records-delete.js'; import { stubInterface } from 'ts-sinon'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestStores } from '../test-stores.js'; import { TestStubGenerator } from '../utils/test-stub-generator.js'; +import { Time } from '../../src/utils/time.js'; import { DataStream, DidResolver, Dwn, Encoder, Jws, RecordsDelete, RecordsRead, RecordsWrite } from '../../src/index.js'; chai.use(chaiAsPromised); @@ -210,7 +210,7 @@ export function testRecordsDeleteHandler(): void { recordId : initialWriteData.message.recordId, signer : Jws.createSigner(alice) }); - await minimalSleep(); + await Time.minimalSleep(); const subsequentWriteData = await TestDataGenerator.generateFromRecordsWrite({ existingWrite : initialWriteData.recordsWrite, author : alice diff --git a/tests/handlers/records-query.spec.ts b/tests/handlers/records-query.spec.ts index c370b2ee2..54522cedc 100644 --- a/tests/handlers/records-query.spec.ts +++ b/tests/handlers/records-query.spec.ts @@ -21,19 +21,13 @@ import { stubInterface } from 'ts-sinon'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestStores } from '../test-stores.js'; import { TestStubGenerator } from '../utils/test-stub-generator.js'; -import { toTemporalInstant } from '@js-temporal/polyfill'; import { constructRecordsWriteIndexes, RecordsWriteHandler } from '../../src/handlers/records-write.js'; import { DateSort, RecordsQuery } from '../../src/interfaces/records-query.js'; -import { DidResolver, Dwn, RecordsWrite } from '../../src/index.js'; +import { DidResolver, Dwn, RecordsWrite, Time } from '../../src/index.js'; import { DwnErrorCode, MessageStoreLevel } from '../../src/index.js'; chai.use(chaiAsPromised); -function createDateString(d: Date): string { - return toTemporalInstant.call(d).toString({ smallestUnit: 'microseconds' }); -} - - export function testRecordsQueryHandler(): void { describe('RecordsQueryHandler.handle()', () => { describe('functional tests', () => { @@ -497,10 +491,11 @@ export function testRecordsQueryHandler(): void { }); it('should be able to range query by `dateCreated`', async () => { - // scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, only the first 2 records share the same schema - const firstDayOf2021 = createDateString(new Date(2021, 1, 1)); - const firstDayOf2022 = createDateString(new Date(2022, 1, 1)); - const firstDayOf2023 = createDateString(new Date(2023, 1, 1)); + // scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, + // only the first 2 records share the same schema + const firstDayOf2021 = Time.createTimestamp({ year: 2021, month: 1, day: 1 }); + const firstDayOf2022 = Time.createTimestamp({ year: 2022, month: 1, day: 1 }); + const firstDayOf2023 = Time.createTimestamp({ year: 2023, month: 1, day: 1 }); const alice = await DidKeyResolver.generate(); const write1 = await TestDataGenerator.generateRecordsWrite({ author: alice, dateCreated: firstDayOf2021, messageTimestamp: firstDayOf2021 }); const write2 = await TestDataGenerator.generateRecordsWrite({ author: alice, dateCreated: firstDayOf2022, messageTimestamp: firstDayOf2022 }); @@ -515,7 +510,7 @@ export function testRecordsQueryHandler(): void { expect(writeReply3.status.code).to.equal(202); // testing `from` range - const lastDayOf2021 = createDateString(new Date(2021, 12, 31)); + const lastDayOf2021 = Time.createTimestamp({ year: 2021, month: 12, day: 31 }); const recordsQuery1 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { dateCreated: { from: lastDayOf2021 } }, @@ -527,7 +522,7 @@ export function testRecordsQueryHandler(): void { expect(reply1.entries![1].encodedData).to.equal(Encoder.bytesToBase64Url(write3.dataBytes!)); // testing `to` range - const lastDayOf2022 = createDateString(new Date(2022, 12, 31)); + const lastDayOf2022 = Time.createTimestamp({ year: 2022, month: 12, day: 31 }); const recordsQuery2 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { dateCreated: { to: lastDayOf2022 } }, @@ -539,7 +534,7 @@ export function testRecordsQueryHandler(): void { expect(reply2.entries![1].encodedData).to.equal(Encoder.bytesToBase64Url(write2.dataBytes!)); // testing `from` and `to` range - const lastDayOf2023 = createDateString(new Date(2023, 12, 31)); + const lastDayOf2023 = Time.createTimestamp({ year: 2023, month: 12, day: 31 }); const recordsQuery3 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { dateCreated: { from: lastDayOf2022, to: lastDayOf2023 } }, @@ -561,13 +556,13 @@ export function testRecordsQueryHandler(): void { }); it('should not return records that were published and then unpublished ', async () => { - // scenario: 3 records authored by alice, published on first of 2021, 2022, and 2023 respectively - // then the records are unpublished and tested to not return when filtering for published records + // scenario: 3 records authored by alice, published on first of 2021, 2022, and 2023 respectively + // then the records are unpublished and tested to not return when filtering for published records - const firstDayOf2020 = createDateString(new Date(2020, 1, 1)); - const firstDayOf2021 = createDateString(new Date(2021, 1, 1)); - const firstDayOf2022 = createDateString(new Date(2022, 1, 1)); - const firstDayOf2023 = createDateString(new Date(2023, 1, 1)); + const firstDayOf2020 = Time.createTimestamp({ year: 2020, month: 1, day: 1 }); + const firstDayOf2021 = Time.createTimestamp({ year: 2021, month: 1, day: 1 }); + const firstDayOf2022 = Time.createTimestamp({ year: 2022, month: 1, day: 1 }); + const firstDayOf2023 = Time.createTimestamp({ year: 2023, month: 1, day: 1 }); const alice = await DidKeyResolver.generate(); const write1 = await TestDataGenerator.generateRecordsWrite({ author: alice, published: true, dateCreated: firstDayOf2020, datePublished: firstDayOf2021, messageTimestamp: firstDayOf2020 @@ -588,7 +583,7 @@ export function testRecordsQueryHandler(): void { expect(writeReply3.status.code).to.equal(202); // confirm range before un-publishing. - const lastDayOf2021 = createDateString(new Date(2021, 12, 31)); + const lastDayOf2021 = Time.createTimestamp({ year: 2021, month: 12, day: 31 }); const ownerRangeQuery = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { datePublished: { from: lastDayOf2021 } }, @@ -679,13 +674,13 @@ export function testRecordsQueryHandler(): void { }); it('should be able to range query by `datePublished`', async () => { - // scenario: 3 records authored by alice, published on first of 2021, 2022, and 2023 respectively - // all 3 records are created on first of 2020 + // scenario: 3 records authored by alice, published on first of 2021, 2022, and 2023 respectively + // all 3 records are created on first of 2020 - const firstDayOf2020 = createDateString(new Date(2020, 1, 1)); - const firstDayOf2021 = createDateString(new Date(2021, 1, 1)); - const firstDayOf2022 = createDateString(new Date(2022, 1, 1)); - const firstDayOf2023 = createDateString(new Date(2023, 1, 1)); + const firstDayOf2020 = Time.createTimestamp({ year: 2020, month: 1, day: 1 }); + const firstDayOf2021 = Time.createTimestamp({ year: 2021, month: 1, day: 1 }); + const firstDayOf2022 = Time.createTimestamp({ year: 2022, month: 1, day: 1 }); + const firstDayOf2023 = Time.createTimestamp({ year: 2023, month: 1, day: 1 }); const alice = await DidKeyResolver.generate(); const write1 = await TestDataGenerator.generateRecordsWrite({ author: alice, published: true, dateCreated: firstDayOf2020, datePublished: firstDayOf2021, messageTimestamp: firstDayOf2020 @@ -706,7 +701,7 @@ export function testRecordsQueryHandler(): void { expect(writeReply3.status.code).to.equal(202); // testing `from` range - const lastDayOf2021 = createDateString(new Date(2021, 12, 31)); + const lastDayOf2021 = Time.createTimestamp({ year: 2021, month: 12, day: 31 }); const recordsQuery1 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { datePublished: { from: lastDayOf2021 } }, @@ -718,7 +713,7 @@ export function testRecordsQueryHandler(): void { expect(reply1RecordIds).to.have.members([ write2.message.recordId, write3.message.recordId ]); // testing `to` range - const lastDayOf2022 = createDateString(new Date(2022, 12, 31)); + const lastDayOf2022 = Time.createTimestamp({ year: 2022, month: 12, day: 31 }); const recordsQuery2 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { datePublished: { to: lastDayOf2022 } }, @@ -730,7 +725,7 @@ export function testRecordsQueryHandler(): void { expect(reply2RecordIds).to.have.members([ write1.message.recordId, write2.message.recordId ]); // testing `from` and `to` range - const lastDayOf2023 = createDateString(new Date(2023, 12, 31)); + const lastDayOf2023 = Time.createTimestamp({ year: 2023, month: 12, day: 31 }); const recordsQuery3 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { datePublished: { from: lastDayOf2022, to: lastDayOf2023 } }, @@ -782,10 +777,10 @@ export function testRecordsQueryHandler(): void { // alice then updates these records to published on first of 2021, 2022, and 2023 respectively // this should update the messageTimestamp on the respective messages - const firstDayOf2020 = createDateString(new Date(2020, 1, 1)); - const firstDayOf2021 = createDateString(new Date(2021, 1, 1)); - const firstDayOf2022 = createDateString(new Date(2022, 1, 1)); - const firstDayOf2023 = createDateString(new Date(2023, 1, 1)); + const firstDayOf2020 = Time.createTimestamp({ year: 2020, month: 1, day: 1 }); + const firstDayOf2021 = Time.createTimestamp({ year: 2021, month: 1, day: 1 }); + const firstDayOf2022 = Time.createTimestamp({ year: 2022, month: 1, day: 1 }); + const firstDayOf2023 = Time.createTimestamp({ year: 2023, month: 1, day: 1 }); const alice = await DidKeyResolver.generate(); const write1 = await TestDataGenerator.generateRecordsWrite({ @@ -838,7 +833,7 @@ export function testRecordsQueryHandler(): void { expect(writeReplyUpdate3.status.code).to.equal(202); // testing `from` range - const lastDayOf2021 = createDateString(new Date(2021, 12, 31)); + const lastDayOf2021 = Time.createTimestamp({ year: 2021, month: 12, day: 31 }); const recordsQuery1 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { dateUpdated: { from: lastDayOf2021 } }, @@ -850,7 +845,7 @@ export function testRecordsQueryHandler(): void { expect(reply1RecordIds).to.have.members([ write2.message.recordId, write3.message.recordId ]); // testing `to` range - const lastDayOf2022 = createDateString(new Date(2022, 12, 31)); + const lastDayOf2022 = Time.createTimestamp({ year: 2022, month: 12, day: 31 }); const recordsQuery2 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { dateUpdated: { to: lastDayOf2022 } }, @@ -862,7 +857,7 @@ export function testRecordsQueryHandler(): void { expect(reply2RecordIds).to.have.members([ write1.message.recordId, write2.message.recordId ]); // testing `from` and `to` range - const lastDayOf2023 = createDateString(new Date(2023, 12, 31)); + const lastDayOf2023 = Time.createTimestamp({ year: 2023, month: 12, day: 31 }); const recordsQuery3 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { dateUpdated: { from: lastDayOf2022, to: lastDayOf2023 } }, @@ -884,10 +879,12 @@ export function testRecordsQueryHandler(): void { }); it('should be able use range and exact match queries at the same time', async () => { - // scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, only the first 2 records share the same schema - const firstDayOf2021 = createDateString(new Date(2021, 1, 1)); - const firstDayOf2022 = createDateString(new Date(2022, 1, 1)); - const firstDayOf2023 = createDateString(new Date(2023, 1, 1)); + // scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, + // only the first 2 records share the same schema + + const firstDayOf2021 = Time.createTimestamp({ year: 2021, month: 1, day: 1 }); + const firstDayOf2022 = Time.createTimestamp({ year: 2022, month: 1, day: 1 }); + const firstDayOf2023 = Time.createTimestamp({ year: 2023, month: 1, day: 1 }); const alice = await DidKeyResolver.generate(); const schema = '2021And2022Schema'; const write1 = await TestDataGenerator.generateRecordsWrite({ @@ -909,8 +906,8 @@ export function testRecordsQueryHandler(): void { expect(writeReply3.status.code).to.equal(202); // testing range criterion with another exact match - const lastDayOf2021 = createDateString(new Date(2021, 12, 31)); - const lastDayOf2023 = createDateString(new Date(2023, 12, 31)); + const lastDayOf2021 = Time.createTimestamp({ year: 2021, month: 12, day: 31 }); + const lastDayOf2023 = Time.createTimestamp({ year: 2023, month: 12, day: 31 }); const recordsQuery5 = await TestDataGenerator.generateRecordsQuery({ author : alice, filter : { @@ -1084,7 +1081,7 @@ export function testRecordsQueryHandler(): void { it('should tiebreak using `messageCid` when sorting encounters identical values', async () => { // setup: 3 messages with the same `dateCreated` value - const dateCreated = createDateString(new Date()); + const dateCreated = Time.getCurrentTimestamp(); const messageTimestamp = dateCreated; const alice = await DidKeyResolver.generate(); const schema = 'aSchema'; @@ -1608,12 +1605,12 @@ export function testRecordsQueryHandler(): void { }); it('should return 400 if published is set to false and a datePublished range is provided', async () => { - const firstDayOf2021 = createDateString(new Date(2021, 1, 1)); + const fromDatePublished = Time.getCurrentTimestamp(); const alice = await DidKeyResolver.generate(); // set to true so create does not fail const recordQuery = await TestDataGenerator.generateRecordsQuery({ author : alice, - filter : { datePublished: { from: firstDayOf2021 }, published: true } + filter : { datePublished: { from: fromDatePublished }, published: true } }); // set to false diff --git a/tests/handlers/records-write.spec.ts b/tests/handlers/records-write.spec.ts index 1b39e8252..3595cfc9e 100644 --- a/tests/handlers/records-write.spec.ts +++ b/tests/handlers/records-write.spec.ts @@ -40,8 +40,8 @@ import { stubInterface } from 'ts-sinon'; import { TestDataGenerator } from '../utils/test-data-generator.js'; import { TestStores } from '../test-stores.js'; import { TestStubGenerator } from '../utils/test-stub-generator.js'; +import { Time } from '../../src/utils/time.js'; -import { createOffsetTimestamp, getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { DwnConstant, KeyDerivationScheme, PermissionsGrant, RecordsDelete } from '../../src/index.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../../src/core/message.js'; import { Encryption, EncryptionAlgorithm } from '../../src/utils/encryption.js'; @@ -156,7 +156,7 @@ export function testRecordsWriteHandler(): void { expect(originatingMessageWriteReply.status.code).to.equal(202); // generate two new RecordsWrite messages with the same `messageTimestamp` value - const dateModified = getCurrentTimeInHighPrecision(); + const dateModified = Time.getCurrentTimestamp(); const recordsWrite1 = await TestDataGenerator.generateFromRecordsWrite({ author, existingWrite : originatingMessageData.recordsWrite, @@ -243,7 +243,7 @@ export function testRecordsWriteHandler(): void { author : initialWriteData.author, recordId, schema, - dateCreated : getCurrentTimeInHighPrecision(), // should not be allowed to be modified + dateCreated : Time.getCurrentTimestamp(), // should not be allowed to be modified dataFormat : initialWriteData.message.descriptor.dataFormat }); @@ -505,7 +505,7 @@ export function testRecordsWriteHandler(): void { const deviceXGrant = await PermissionsGrant.create({ delegated : true, // this is a delegated grant - dateExpires : createOffsetTimestamp({ seconds: 100 }), + dateExpires : Time.createOffsetTimestamp({ seconds: 100 }), description : 'Allow to write to message protocol', grantedBy : alice.did, grantedTo : deviceX.did, @@ -516,7 +516,7 @@ export function testRecordsWriteHandler(): void { const deviceYGrant = await PermissionsGrant.create({ delegated : true, // this is a delegated grant - dateExpires : createOffsetTimestamp({ seconds: 100 }), + dateExpires : Time.createOffsetTimestamp({ seconds: 100 }), description : 'Allow to write to message protocol', grantedBy : alice.did, grantedTo : deviceY.did, @@ -654,7 +654,7 @@ export function testRecordsWriteHandler(): void { const grantToBob = await PermissionsGrant.create({ delegated : true, // this is a delegated grant - dateExpires : createOffsetTimestamp({ seconds: 100 }), + dateExpires : Time.createOffsetTimestamp({ seconds: 100 }), description : 'Allow to Bob write as me in chat protocol', grantedBy : alice.did, grantedTo : bob.did, @@ -1205,7 +1205,7 @@ export function testRecordsWriteHandler(): void { it('should return 400 if `dateCreated` and `messageTimestamp` are not the same in an initial write', async () => { const { author, message, dataStream } = await TestDataGenerator.generateRecordsWrite({ dateCreated : '2023-01-10T10:20:30.405060Z', - messageTimestamp : getCurrentTimeInHighPrecision() // this always generate a different timestamp + messageTimestamp : Time.getCurrentTimestamp() // this always generate a different timestamp }); const tenant = author.did; diff --git a/tests/interfaces/permissions-grant.spec.ts b/tests/interfaces/permissions-grant.spec.ts index 3c4b846ad..ea14a3bc0 100644 --- a/tests/interfaces/permissions-grant.spec.ts +++ b/tests/interfaces/permissions-grant.spec.ts @@ -2,14 +2,13 @@ import { expect } from 'chai'; import type { CreateFromPermissionsRequestOverrides } from '../../src/interfaces/permissions-grant.js'; import type { PermissionScope } from '../../src/index.js'; +import type { RecordsPermissionScope } from '../../src/types/permissions-types.js'; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { PermissionsConditionPublication } from '../../src/types/permissions-types.js'; import { PermissionsGrant } from '../../src/interfaces/permissions-grant.js'; -import type { RecordsPermissionScope } from '../../src/types/permissions-types.js'; import { Secp256k1 } from '../../src/utils/secp256k1.js'; -import { Temporal } from '@js-temporal/polyfill'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/utils/time.js'; import { DidKeyResolver, DwnErrorCode, Jws, PrivateKeySigner } from '../../src/index.js'; import { DwnInterfaceName, DwnMethodName, Message } from '../../src/core/message.js'; @@ -20,7 +19,7 @@ describe('PermissionsGrant', () => { const signer = new PrivateKeySigner({ privateJwk, keyId: 'did:jank:bob' }); const { message } = await PermissionsGrant.create({ - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), description : 'drugs', grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', @@ -48,7 +47,7 @@ describe('PermissionsGrant', () => { }; const { message } = await PermissionsGrant.create({ - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), description : 'schema normalization test', grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', @@ -72,7 +71,7 @@ describe('PermissionsGrant', () => { }; const { message } = await PermissionsGrant.create({ - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), description : 'protocol normalization test', grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', @@ -92,7 +91,7 @@ describe('PermissionsGrant', () => { const signer = new PrivateKeySigner({ privateJwk, keyId: 'did:jank:bob' }); const permissionsGrantOptions = { - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', grantedFor : 'did:jank:bob', @@ -135,7 +134,7 @@ describe('PermissionsGrant', () => { const signer = new PrivateKeySigner({ privateJwk, keyId: 'did:jank:bob' }); const permissionsGrantOptions = { - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), grantedBy : 'did:jank:bob', grantedTo : 'did:jank:alice', grantedFor : 'did:jank:bob', @@ -172,7 +171,7 @@ describe('PermissionsGrant', () => { grantedTo : bob.did, }); - const dateExpires = Temporal.Now.instant().add({ hours: 24 }).toString({ smallestUnit: 'microseconds' }); + const dateExpires = Time.createOffsetTimestamp({ seconds: 60 * 60 * 24 }); const permissionsGrant = await PermissionsGrant.createFromPermissionsRequest(permissionsRequest, signer, { dateExpires }); expect(permissionsGrant.author).to.eq(alice.did); @@ -196,7 +195,7 @@ describe('PermissionsGrant', () => { const { permissionsRequest } = await TestDataGenerator.generatePermissionsRequest(); const description = 'friendship'; - const dateExpires = getCurrentTimeInHighPrecision(); + const dateExpires = Time.getCurrentTimestamp(); const overrides: CreateFromPermissionsRequestOverrides = { dateExpires, description, @@ -230,7 +229,7 @@ describe('PermissionsGrant', () => { const alice = await TestDataGenerator.generatePersona(); const { message } = await PermissionsGrant.create({ - dateExpires : getCurrentTimeInHighPrecision(), + dateExpires : Time.getCurrentTimestamp(), grantedBy : alice.did, grantedTo : 'did:example:bob', grantedFor : alice.did, diff --git a/tests/interfaces/protocols-configure.spec.ts b/tests/interfaces/protocols-configure.spec.ts index c34c0f1c4..d89503ffa 100644 --- a/tests/interfaces/protocols-configure.spec.ts +++ b/tests/interfaces/protocols-configure.spec.ts @@ -5,10 +5,10 @@ import type { ProtocolsConfigureMessage } from '../../src/index.js'; import dexProtocolDefinition from '../vectors/protocol-definitions/dex.json' assert { type: 'json' }; import { DwnErrorCode } from '../../src/index.js'; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { Jws } from '../../src/utils/jws.js'; import { ProtocolsConfigure } from '../../src/interfaces/protocols-configure.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/utils/time.js'; chai.use(chaiAsPromised); @@ -17,7 +17,7 @@ describe('ProtocolsConfigure', () => { it('should use `messageTimestamp` as is if given', async () => { const alice = await TestDataGenerator.generatePersona(); - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const definition = { ...dexProtocolDefinition }; const protocolsConfigure = await ProtocolsConfigure.create({ messageTimestamp : currentTime, diff --git a/tests/interfaces/protocols-query.spec.ts b/tests/interfaces/protocols-query.spec.ts index 7ce753a72..91ddf5ea0 100644 --- a/tests/interfaces/protocols-query.spec.ts +++ b/tests/interfaces/protocols-query.spec.ts @@ -4,10 +4,10 @@ import chai, { expect } from 'chai'; import type { ProtocolsQueryMessage } from '../../src/index.js'; import dexProtocolDefinition from '../vectors/protocol-definitions/dex.json' assert { type: 'json' }; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { Jws } from '../../src/index.js'; import { ProtocolsQuery } from '../../src/interfaces/protocols-query.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/utils/time.js'; chai.use(chaiAsPromised); @@ -16,7 +16,7 @@ describe('ProtocolsQuery', () => { it('should use `messageTimestamp` as is if given', async () => { const alice = await TestDataGenerator.generatePersona(); - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const protocolsQuery = await ProtocolsQuery.create({ filter : { protocol: 'anyValue' }, messageTimestamp : currentTime, diff --git a/tests/interfaces/records-delete.spec.ts b/tests/interfaces/records-delete.spec.ts index 908225de1..4382ffebb 100644 --- a/tests/interfaces/records-delete.spec.ts +++ b/tests/interfaces/records-delete.spec.ts @@ -1,10 +1,10 @@ import chaiAsPromised from 'chai-as-promised'; import chai, { expect } from 'chai'; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { Jws } from '../../src/index.js'; import { RecordsDelete } from '../../src/interfaces/records-delete.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/utils/time.js'; chai.use(chaiAsPromised); @@ -13,7 +13,7 @@ describe('RecordsDelete', () => { it('should use `messageTimestamp` as is if given', async () => { const alice = await TestDataGenerator.generatePersona(); - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const recordsDelete = await RecordsDelete.create({ recordId : 'anything', signer : Jws.createSigner(alice), diff --git a/tests/interfaces/records-query.spec.ts b/tests/interfaces/records-query.spec.ts index 0765d7575..43bdfaf3a 100644 --- a/tests/interfaces/records-query.spec.ts +++ b/tests/interfaces/records-query.spec.ts @@ -2,10 +2,10 @@ import chaiAsPromised from 'chai-as-promised'; import chai, { expect } from 'chai'; import dexProtocolDefinition from '../vectors/protocol-definitions/dex.json' assert { type: 'json' }; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { Jws } from '../../src/index.js'; import { RecordsQuery } from '../../src/interfaces/records-query.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/utils/time.js'; chai.use(chaiAsPromised); @@ -29,7 +29,7 @@ describe('RecordsQuery', () => { it('should use `messageTimestamp` as is if given', async () => { const alice = await TestDataGenerator.generatePersona(); - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const recordsQuery = await RecordsQuery.create({ filter : { schema: 'anything' }, messageTimestamp : currentTime, diff --git a/tests/interfaces/records-read.spec.ts b/tests/interfaces/records-read.spec.ts index fa57e5160..e1924164e 100644 --- a/tests/interfaces/records-read.spec.ts +++ b/tests/interfaces/records-read.spec.ts @@ -2,10 +2,10 @@ import chaiAsPromised from 'chai-as-promised'; import chai, { expect } from 'chai'; import dexProtocolDefinition from '../vectors/protocol-definitions/dex.json' assert { type: 'json' }; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { Jws } from '../../src/index.js'; import { RecordsRead } from '../../src/interfaces/records-read.js'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/utils/time.js'; chai.use(chaiAsPromised); @@ -14,7 +14,7 @@ describe('RecordsRead', () => { it('should use `messageTimestamp` as is if given', async () => { const alice = await TestDataGenerator.generatePersona(); - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const recordsRead = await RecordsRead.create({ filter: { recordId: 'anything', diff --git a/tests/interfaces/records-write.spec.ts b/tests/interfaces/records-write.spec.ts index 3fe09b095..2d015bbe6 100644 --- a/tests/interfaces/records-write.spec.ts +++ b/tests/interfaces/records-write.spec.ts @@ -9,8 +9,8 @@ import { DwnErrorCode } from '../../src/core/dwn-error.js'; import { RecordsWrite } from '../../src/interfaces/records-write.js'; import { stubInterface } from 'ts-sinon'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/utils/time.js'; -import { createOffsetTimestamp, getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { DidKeyResolver, DwnInterfaceName, DwnMethodName, Encoder, Jws, KeyDerivationScheme, PermissionsGrant } from '../../src/index.js'; @@ -287,7 +287,7 @@ describe('RecordsWrite', () => { }; const grantToBob = await PermissionsGrant.create({ delegated : true, // this is a delegated grant - dateExpires : createOffsetTimestamp({ seconds: 100 }), + dateExpires : Time.createOffsetTimestamp({ seconds: 100 }), description : 'Allow to Bob write as me in chat protocol', grantedBy : alice.did, grantedTo : bob.did, @@ -314,7 +314,7 @@ describe('RecordsWrite', () => { const write = await RecordsWrite.createFrom({ recordsWriteMessage : recordsWrite.message, - datePublished : getCurrentTimeInHighPrecision(), + datePublished : Time.getCurrentTimestamp(), signer : Jws.createSigner(author) }); @@ -333,7 +333,7 @@ describe('RecordsWrite', () => { }; const grantToBob = await PermissionsGrant.create({ delegated : true, // this is a delegated grant - dateExpires : createOffsetTimestamp({ seconds: 100 }), + dateExpires : Time.createOffsetTimestamp({ seconds: 100 }), description : 'Allow to Bob write as me in chat protocol', grantedBy : alice.did, grantedTo : bob.did, diff --git a/tests/store/index-level.spec.ts b/tests/store/index-level.spec.ts index fe1bc425b..872590b35 100644 --- a/tests/store/index-level.spec.ts +++ b/tests/store/index-level.spec.ts @@ -6,8 +6,8 @@ import chai, { expect } from 'chai'; import { ArrayUtility } from '../../src/utils/array.js'; import { IndexLevel } from '../../src/store/index-level.js'; import { lexicographicalCompare } from '../../src/utils/string.js'; -import { Temporal } from '@js-temporal/polyfill'; import { TestDataGenerator } from '../utils/test-data-generator.js'; +import { Time } from '../../src/index.js'; import { v4 as uuid } from 'uuid'; chai.use(chaiAsPromised); @@ -210,7 +210,7 @@ describe('Index Level', () => { for (let i = -5; i < 5; ++i) { const id = uuid(); const doc = { - dateCreated: Temporal.PlainDateTime.from({ year: 2023, month: 1, day: 15 + i }).toString({ smallestUnit: 'microseconds' }) + dateCreated: Time.createTimestamp({ year: 2023, month: 1, day: 15 + i }) }; await index.put(tenant, id, doc); @@ -218,7 +218,7 @@ describe('Index Level', () => { const resp = await index.query(tenant, [{ dateCreated: { - gte: Temporal.PlainDateTime.from({ year: 2023, month: 1, day: 15 }).toString({ smallestUnit: 'microseconds' }) + gte: Time.createTimestamp({ year: 2023, month: 1, day: 15 }) } }]); diff --git a/tests/utils/test-data-generator.ts b/tests/utils/test-data-generator.ts index fccae14e8..6a27ce065 100644 --- a/tests/utils/test-data-generator.ts +++ b/tests/utils/test-data-generator.ts @@ -37,16 +37,14 @@ import type { PrivateJwk, PublicJwk } from '../../src/types/jose-types.js'; import * as cbor from '@ipld/dag-cbor'; import { CID } from 'multiformats/cid'; -import { createTimestamp } from '../../src/index.js'; import { DataStream } from '../../src/utils/data-stream.js'; -import { getCurrentTimeInHighPrecision } from '../../src/utils/time.js'; import { PermissionsGrant } from '../../src/interfaces/permissions-grant.js'; import { PermissionsRequest } from '../../src/interfaces/permissions-request.js'; import { PermissionsRevoke } from '../../src/interfaces/permissions-revoke.js'; import { removeUndefinedProperties } from '../../src/utils/object.js'; import { Secp256k1 } from '../../src/utils/secp256k1.js'; import { sha256 } from 'multiformats/hashes/sha2'; -import { Temporal } from '@js-temporal/polyfill'; +import { Time } from '../../src/utils/time.js'; import { DidKeyResolver, @@ -565,7 +563,7 @@ export class TestDataGenerator { */ public static async generateFromRecordsWrite(input: GenerateFromRecordsWriteInput): Promise { const existingMessage = input.existingWrite.message; - const currentTime = getCurrentTimeInHighPrecision(); + const currentTime = Time.getCurrentTimestamp(); const published = input.published ?? existingMessage.descriptor.published ? false : true; // toggle from the parent value if not given explicitly const datePublished = input.datePublished ?? (published ? currentTime : undefined); @@ -657,7 +655,7 @@ export class TestDataGenerator { public static async generatePermissionsRequest(input?: GeneratePermissionsRequestInput): Promise { const author = input?.author ?? await TestDataGenerator.generatePersona(); const permissionsRequest = await PermissionsRequest.create({ - messageTimestamp : getCurrentTimeInHighPrecision(), + messageTimestamp : Time.getCurrentTimestamp(), description : input?.description, grantedBy : input?.grantedBy ?? 'did:jank:bob', grantedTo : input?.grantedTo ?? 'did:jank:alice', @@ -681,10 +679,10 @@ export class TestDataGenerator { * Generates a PermissionsGrant message for testing. */ public static async generatePermissionsGrant(input?: GeneratePermissionsGrantInput): Promise { - const dateExpires = input?.dateExpires ?? Temporal.Now.instant().add({ hours: 24 }).toString({ smallestUnit: 'microseconds' }); + const dateExpires = input?.dateExpires ?? Time.createOffsetTimestamp({ seconds: 60 * 60 * 24 }); const author = input?.author ?? await TestDataGenerator.generatePersona(); const permissionsGrant = await PermissionsGrant.create({ - messageTimestamp : input?.messageTimestamp ?? getCurrentTimeInHighPrecision(), + messageTimestamp : input?.messageTimestamp ?? Time.getCurrentTimestamp(), dateExpires, description : input?.description ?? 'drugs', grantedBy : input?.grantedBy ?? author.did, @@ -836,7 +834,7 @@ export class TestDataGenerator { * @returns random UTC ISO-8601 timestamp */ public static randomTimestamp(): string { - return createTimestamp({ + return Time.createTimestamp({ year : this.randomInt(2000, 2022), month : this.randomInt(1, 12), day : this.randomInt(1, 28), diff --git a/tests/utils/time.spec.ts b/tests/utils/time.spec.ts index 4f721cd81..a2f517343 100644 --- a/tests/utils/time.spec.ts +++ b/tests/utils/time.spec.ts @@ -1,36 +1,36 @@ import { DwnErrorCode } from '../../src/core/dwn-error.js'; import { expect } from 'chai'; import { TestDataGenerator } from '../utils/test-data-generator.js'; -import { createOffsetTimestamp, createTimestamp, validateTimestamp } from '../../src/utils/time.js'; +import { Time } from '../../src/utils/time.js'; describe('time', () => { - describe('validateTimstamp', () => { + describe('validateTimestamp', () => { describe('invalid timestamps', () => { - const invalidTimstamps = [ + const invalidTimestamps = [ '2022-02-31T10:20:30.405060Z', // invalid day '2022-01-36T90:20:30.405060Z', // invalid hour '2022-01-36T25:99:30.405060Z', // invalid minute '2022-14-18T10:30:00.123456Z', // invalid month ]; - invalidTimstamps.forEach((timestamp) => { + invalidTimestamps.forEach((timestamp) => { it(`should throw an exception if an invalid timestamp is passed: ${timestamp}`, () => { - expect(() => validateTimestamp(timestamp)).to.throw(DwnErrorCode.TimestampInvalid); + expect(() => Time.validateTimestamp(timestamp)).to.throw(DwnErrorCode.TimestampInvalid); }); }); }); describe('valid timestamps', () => { it('should pass if a valid timestamp is passed', () => { - expect(() => validateTimestamp('2022-04-29T10:30:00.123456Z')).to.not.throw(); - expect(() => validateTimestamp(TestDataGenerator.randomTimestamp())).to.not.throw(); + expect(() => Time.validateTimestamp('2022-04-29T10:30:00.123456Z')).to.not.throw(); + expect(() => Time.validateTimestamp(TestDataGenerator.randomTimestamp())).to.not.throw(); }); }); }); describe('createTimestamp', () => { it('should create a valid timestamp', () => { - const timestamp = createTimestamp({ + const timestamp = Time.createTimestamp({ year : 2022, month : 4, day : 29, @@ -52,9 +52,10 @@ describe('time', () => { const second = TestDataGenerator.randomInt(0, 59); const millisecond = TestDataGenerator.randomInt(0, 999); const microsecond = TestDataGenerator.randomInt(0, 999); + it(`should create a valid timestamp for random values ${i}`, () => { - const timestamp = createTimestamp({ year, month, day, hour, minute, second, millisecond, microsecond }); - expect(()=> validateTimestamp(timestamp)).to.not.throw(); + const timestamp = Time.createTimestamp({ year, month, day, hour, minute, second, millisecond, microsecond }); + expect(()=> Time.validateTimestamp(timestamp)).to.not.throw(); }); } }); @@ -62,7 +63,7 @@ describe('time', () => { describe('createOffsetTimestamp', () => { it('should use the given timestamp as the base timestamp to compute the offset timestamp', () => { const baseTimestamp = '2000-04-29T10:30:00.123456Z'; - const offsetTimestamp = createOffsetTimestamp({ seconds: 60 * 60 * 24 * 365 }, baseTimestamp); + const offsetTimestamp = Time.createOffsetTimestamp({ seconds: 60 * 60 * 24 * 365 }, baseTimestamp); expect(offsetTimestamp).to.equal('2001-04-29T10:30:00.123456Z'); });