Skip to content

Commit 9cb7c87

Browse files
committed
bunch of refactoring / code organization and reusability changes
1 parent 2a9cf0b commit 9cb7c87

80 files changed

Lines changed: 957 additions & 1081 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/sthrift-verification/acceptance-api/cucumber.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { isAgent } from 'std-env';
22

33
const terminalFormat = isAgent
4-
? './src/shared/support/formatters/agent-formatter.ts'
4+
? '../verification-shared/src/formatters/agent-formatter.ts'
55
: 'progress-bar';
66

77
export default {
8-
paths: ['../test-support/src/scenarios/feature-files/**/*.feature'],
8+
paths: ['../verification-shared/src/scenarios/**/*.feature'],
99
import: [
1010
'src/world.ts',
1111
'src/step-definitions/index.ts',

packages/sthrift-verification/acceptance-api/package.json

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@
1717
"@serenity-js/core": "^3.37.2",
1818
"@serenity-js/cucumber": "^3.37.2",
1919
"@serenity-js/serenity-bdd": "^3.37.2",
20-
"graphql": "^16.12.0",
2120
"std-env": "^4.0.0"
2221
},
2322
"devDependencies": {
24-
"@apollo/server": "^5.5.0",
2523
"@cellix/service-messaging-base": "workspace:*",
2624
"@cellix/service-mongoose": "workspace:*",
2725
"@cellix/service-payment-base": "workspace:*",
@@ -31,16 +29,10 @@
3129
"@sthrift/application-services": "workspace:*",
3230
"@sthrift/context-spec": "workspace:*",
3331
"@sthrift/domain": "workspace:*",
34-
"@sthrift/graphql": "workspace:*",
3532
"@sthrift/persistence": "workspace:*",
36-
"@sthrift-verification/test-support": "workspace:*",
37-
"@types/graphql-depth-limit": "^1.1.6",
33+
"@sthrift-verification/verification-shared": "workspace:*",
3834
"@types/node": "^24.6.1",
3935
"c8": "^11.0.0",
40-
"graphql-depth-limit": "^1.1.0",
41-
"graphql-middleware": "^6.1.35",
42-
"mongodb": "^6.15.0",
43-
"mongodb-memory-server": "^10.2.0",
4436
"rimraf": "^6.0.1",
4537
"tsx": "^4.20.3",
4638
"typescript": "^5.4.5"

packages/sthrift-verification/acceptance-api/src/contexts/reservation-request/step-definitions/create-reservation-request.steps.ts

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { type DataTable, Given, Then, When } from '@cucumber/cucumber';
22
import { Ensure, equals, includes, isPresent } from '@serenity-js/assertions';
33
import { actorCalled, notes } from '@serenity-js/core';
4+
import {
5+
formatDateForComparison,
6+
parseDateInput,
7+
typedRowsHash,
8+
} from '@sthrift-verification/verification-shared/helpers';
49
import {
510
makeTestUserData,
611
resolveActorName,
@@ -17,21 +22,9 @@ import { CreateReservationRequest as ApiCreateReservationRequest } from '../task
1722

1823
let lastActorName = 'Alice';
1924

20-
function parseDateInput(input: string): Date {
21-
if (input.startsWith('+')) {
22-
const days = Number.parseInt(input.substring(1), 10);
23-
const date = new Date();
24-
date.setDate(date.getDate() + days);
25-
date.setHours(0, 0, 0, 0);
26-
return date;
27-
}
28-
const date = new Date(input);
29-
date.setHours(0, 0, 0, 0);
30-
return date;
31-
}
32-
33-
function formatDateForComparison(date: Date): string {
34-
return date.toISOString().split('T')[0] ?? '';
25+
interface ReservationRequestTableData {
26+
reservationPeriodStart?: string;
27+
reservationPeriodEnd?: string;
3528
}
3629

3730
async function getListingIdFromOwner(ownerName: string): Promise<string> {
@@ -74,7 +67,7 @@ When(
7467
) {
7568
lastActorName = reserver;
7669
const actor = actorCalled(reserver);
77-
const data = dataTable.rowsHash();
70+
const data = typedRowsHash<ReservationRequestTableData>(dataTable);
7871

7972
const listingId = await getListingIdFromOwner(owner);
8073
const startDate = data.reservationPeriodStart;
@@ -104,7 +97,7 @@ When(
10497
) {
10598
lastActorName = actorName;
10699
const actor = actorCalled(actorName);
107-
const data = dataTable.rowsHash();
100+
const data = typedRowsHash<ReservationRequestTableData>(dataTable);
108101

109102
await actor.attemptsTo(
110103
notes<ReservationRequestNotes>().set(
@@ -367,7 +360,7 @@ Given(
367360
) {
368361
lastActorName = reserver;
369362
const actor = actorCalled(reserver);
370-
const data = dataTable.rowsHash();
363+
const data = typedRowsHash<ReservationRequestTableData>(dataTable);
371364

372365
const listingId = await getListingIdFromOwner(owner);
373366
const startDate = data.reservationPeriodStart;
@@ -397,7 +390,7 @@ When(
397390
) {
398391
lastActorName = actorName;
399392
const actor = actorCalled(actorName);
400-
const data = dataTable.rowsHash();
393+
const data = typedRowsHash<ReservationRequestTableData>(dataTable);
401394

402395
await actor.attemptsTo(
403396
notes<{ lastValidationError?: string }>().set(
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { createRealApplicationServicesFactory } from './real-application-services.ts';
1+
export { createMockApplicationServicesFactory } from './mock-application-services.ts';

packages/sthrift-verification/acceptance-api/src/shared/support/application-services/real-application-services.ts renamed to packages/sthrift-verification/acceptance-api/src/shared/support/application-services/mock-application-services.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1+
import type { MessagingService } from '@cellix/service-messaging-base';
12
import type { ServiceMongoose } from '@cellix/service-mongoose';
2-
import { Persistence } from '@sthrift/persistence';
3-
import {
4-
buildApplicationServicesFactory,
5-
type ApplicationServicesFactory,
6-
} from '@sthrift/application-services';
7-
import type { ApiContextSpec } from '@sthrift/context-spec';
3+
import type { PaymentService } from '@cellix/service-payment-base';
84
import type {
95
TokenValidation,
106
TokenValidationResult,
117
} from '@cellix/service-token-validation';
12-
import type { MessagingService } from '@cellix/service-messaging-base';
13-
import type { PaymentService } from '@cellix/service-payment-base';
14-
import { defaultActor } from '@sthrift-verification/test-support/test-data';
8+
import {
9+
type ApplicationServicesFactory,
10+
buildApplicationServicesFactory,
11+
} from '@sthrift/application-services';
12+
import type { ApiContextSpec } from '@sthrift/context-spec';
13+
import { Persistence } from '@sthrift/persistence';
14+
import { defaultActor } from '@sthrift-verification/verification-shared/test-data';
1515

1616
function createMockTokenValidation(): TokenValidation {
1717
return {
@@ -36,7 +36,8 @@ function createNoOpMessagingService(): MessagingService {
3636
throw new Error('MessagingService not implemented in mongodb test session');
3737
};
3838
const service: MessagingService = {
39-
startUp: () => Promise.resolve(service) as ReturnType<MessagingService['startUp']>,
39+
startUp: () =>
40+
Promise.resolve(service) as ReturnType<MessagingService['startUp']>,
4041
shutDown: () => Promise.resolve(),
4142
getConversation: notImplemented,
4243
sendMessage: notImplemented,
@@ -53,7 +54,8 @@ function createNoOpPaymentService(): PaymentService {
5354
throw new Error('PaymentService not implemented in mongodb test session');
5455
};
5556
const service: PaymentService = {
56-
startUp: () => Promise.resolve(service) as ReturnType<PaymentService['startUp']>,
57+
startUp: () =>
58+
Promise.resolve(service) as ReturnType<PaymentService['startUp']>,
5759
shutDown: () => Promise.resolve(),
5860
generatePublicKey: notImplemented,
5961
createCustomerProfile: notImplemented,
@@ -77,7 +79,7 @@ function createNoOpPaymentService(): PaymentService {
7779
return service;
7880
}
7981

80-
export function createRealApplicationServicesFactory(
82+
export function createMockApplicationServicesFactory(
8183
serviceMongoose: ServiceMongoose,
8284
): ApplicationServicesFactory {
8385
const dataSourcesFactory = Persistence(serviceMongoose);
@@ -89,11 +91,15 @@ export function createRealApplicationServicesFactory(
8991
paymentService: createNoOpPaymentService(),
9092
};
9193

92-
const realFactory = buildApplicationServicesFactory(apiContextSpec);
94+
const mockApplicationServicesFactory =
95+
buildApplicationServicesFactory(apiContextSpec);
9396

9497
return {
9598
forRequest: (_rawAuthHeader, hints) => {
96-
return realFactory.forRequest('Bearer test-token', hints);
99+
return mockApplicationServicesFactory.forRequest(
100+
'Bearer test-token',
101+
hints,
102+
);
97103
},
98104
};
99105
}

packages/sthrift-verification/acceptance-api/src/shared/support/domain-test-helpers.ts

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
import type { Domain } from '@sthrift/domain';
22

3-
export const ONE_DAY_MS = 86_400_000;
3+
export type { TestUserData } from '@sthrift-verification/verification-shared/helpers';
4+
export {
5+
makeTestUserData,
6+
ONE_DAY_MS,
7+
resolveActorName,
8+
} from '@sthrift-verification/verification-shared/helpers';
9+
410
export const DEFAULT_SHARING_PERIOD_DAYS = 30;
511

6-
// Resolve Gherkin pronoun references to actor names
7-
export function resolveActorName(actorName: string, defaultName = 'Alice'): string {
8-
return /^(she|he|they)$/i.test(actorName) ? defaultName : actorName;
9-
}
12+
const ONE_DAY = 86_400_000;
1013

1114
type ItemListingProps = Domain.Contexts.Listing.ItemListing.ItemListingProps;
12-
type ItemListingEntityReference = Domain.Contexts.Listing.ItemListing.ItemListingEntityReference;
13-
type ReservationRequestProps = Domain.Contexts.ReservationRequest.ReservationRequest.ReservationRequestProps;
15+
type ItemListingEntityReference =
16+
Domain.Contexts.Listing.ItemListing.ItemListingEntityReference;
17+
type ReservationRequestProps =
18+
Domain.Contexts.ReservationRequest.ReservationRequest.ReservationRequestProps;
1419
type UserEntityReference = Domain.Contexts.User.UserEntityReference;
1520
type Passport = Domain.Passport;
1621

1722
export function makeTestPassport(): Passport {
18-
const alwaysAllow = { determineIf: (fn: (p: Record<string, boolean>) => boolean) => fn(new Proxy({}, { get: () => true })) };
23+
const alwaysAllow = {
24+
determineIf: (fn: (permissions: Record<string, boolean>) => boolean) =>
25+
fn(new Proxy({}, { get: () => true }) as Record<string, boolean>),
26+
};
27+
1928
return {
2029
listing: { forItemListing: () => alwaysAllow },
2130
user: {
@@ -30,28 +39,14 @@ export function makeTestPassport(): Passport {
3039
} as unknown as Passport;
3140
}
3241

33-
interface TestUserData {
34-
id: string;
35-
email: string;
36-
firstName: string;
37-
lastName: string;
38-
}
39-
40-
export function makeTestUserData(actorName: string, overrides?: Partial<TestUserData>): TestUserData {
41-
const defaultId = `test-user-${actorName.toLowerCase()}`;
42-
const defaultEmail = `${actorName.toLowerCase()}@test.com`;
43-
const defaultFirstName = actorName;
44-
const defaultLastName = 'Tester';
45-
46-
return {
47-
id: overrides?.id ?? defaultId,
48-
email: overrides?.email ?? defaultEmail,
49-
firstName: overrides?.firstName ?? defaultFirstName,
50-
lastName: overrides?.lastName ?? defaultLastName,
51-
};
52-
}
53-
54-
export function makeSharerUser(overrides: Partial<{ id: string; email: string; firstName: string; lastName: string }> = {}): UserEntityReference {
42+
export function makeSharerUser(
43+
overrides: Partial<{
44+
id: string;
45+
email: string;
46+
firstName: string;
47+
lastName: string;
48+
}> = {},
49+
): UserEntityReference {
5550
return {
5651
id: overrides.id ?? 'test-sharer-1',
5752
userType: 'personal-user',
@@ -84,9 +79,15 @@ export function makeSharerUser(overrides: Partial<{ id: string; email: string; f
8479
transactions: {
8580
items: [],
8681
getNewItem: () => ({}),
87-
addItem: () => { /* no-op */ },
88-
removeItem: () => { /* no-op */ },
89-
removeAll: () => { /* no-op */ },
82+
addItem: () => {
83+
/* no-op */
84+
},
85+
removeItem: () => {
86+
/* no-op */
87+
},
88+
removeAll: () => {
89+
/* no-op */
90+
},
9091
},
9192
},
9293
},
@@ -97,8 +98,11 @@ export function makeSharerUser(overrides: Partial<{ id: string; email: string; f
9798
} as unknown as UserEntityReference;
9899
}
99100

100-
export function makeItemListingProps(overrides: Partial<ItemListingProps> = {}): ItemListingProps {
101+
export function makeItemListingProps(
102+
overrides: Partial<ItemListingProps> = {},
103+
): ItemListingProps {
101104
const sharer = makeSharerUser();
105+
102106
return {
103107
id: overrides.id ?? `listing-${Date.now()}`,
104108
sharer,
@@ -107,8 +111,10 @@ export function makeItemListingProps(overrides: Partial<ItemListingProps> = {}):
107111
description: 'Default Description',
108112
category: 'Electronics',
109113
location: 'Seattle, WA',
110-
sharingPeriodStart: new Date(Date.now() + 86_400_000),
111-
sharingPeriodEnd: new Date(Date.now() + 86_400_000 * 30),
114+
sharingPeriodStart: new Date(Date.now() + ONE_DAY),
115+
sharingPeriodEnd: new Date(
116+
Date.now() + ONE_DAY * DEFAULT_SHARING_PERIOD_DAYS,
117+
),
112118
state: 'Active',
113119
images: [],
114120
sharingHistory: [],
@@ -121,16 +127,20 @@ export function makeItemListingProps(overrides: Partial<ItemListingProps> = {}):
121127
} as ItemListingProps;
122128
}
123129

124-
export function makeListingReference(overrides: Partial<{ id: string; state: string }> = {}): ItemListingEntityReference {
130+
export function makeListingReference(
131+
overrides: Partial<{ id: string; state: string }> = {},
132+
): ItemListingEntityReference {
125133
return {
126134
id: overrides.id ?? `listing-${Date.now()}`,
127135
sharer: makeSharerUser(),
128136
title: 'Test Listing',
129137
description: 'Test',
130138
category: 'Electronics',
131139
location: 'Seattle',
132-
sharingPeriodStart: new Date(Date.now() + 3_600_000),
133-
sharingPeriodEnd: new Date(Date.now() + 7_200_000),
140+
sharingPeriodStart: new Date(Date.now() + ONE_DAY),
141+
sharingPeriodEnd: new Date(
142+
Date.now() + ONE_DAY * DEFAULT_SHARING_PERIOD_DAYS,
143+
),
134144
state: overrides.state ?? 'Active',
135145
createdAt: new Date('2024-01-01'),
136146
updatedAt: new Date('2024-01-01'),
@@ -139,11 +149,16 @@ export function makeListingReference(overrides: Partial<{ id: string; state: str
139149
} as ItemListingEntityReference;
140150
}
141151

142-
export function makeReservationRequestProps(overrides: Partial<ReservationRequestProps> = {}): ReservationRequestProps {
152+
export function makeReservationRequestProps(
153+
overrides: Partial<ReservationRequestProps> = {},
154+
): ReservationRequestProps {
143155
const listing = makeListingReference();
144156
const reserver = makeSharerUser({ id: 'reserver-1' });
145-
const tomorrow = new Date(Date.now() + 86_400_000);
146-
const nextMonth = new Date(Date.now() + 86_400_000 * 30);
157+
const tomorrow = new Date(Date.now() + ONE_DAY);
158+
const nextMonth = new Date(
159+
Date.now() + ONE_DAY * DEFAULT_SHARING_PERIOD_DAYS,
160+
);
161+
147162
return {
148163
id: overrides.id ?? `rr-${Date.now()}`,
149164
state: 'Requested',

0 commit comments

Comments
 (0)