diff --git a/src/dependencies.ts b/src/dependencies.ts index 8d1ea9c..3f10440 100644 --- a/src/dependencies.ts +++ b/src/dependencies.ts @@ -1,15 +1,40 @@ +import { Test, TestingModuleBuilder } from '@nestjs/testing'; import { GeocodingProvider } from './clients/geocoding/types'; import { WeatherProvider } from './clients/weather/types'; +import { PackagesService } from './packages.service'; import { PackagesRepository } from './storage/types'; export const packagesRepositoryDependency = Symbol('packages repository'); export const geocodingProviderDependency = Symbol('geocoding provider'); export const weatherProviderDependency = Symbol('weather provider'); +export const packagesServiceDependency = Symbol('packages service'); export type Dependencies = { [packagesRepositoryDependency]: PackagesRepository; [geocodingProviderDependency]: GeocodingProvider; [weatherProviderDependency]: WeatherProvider; + [packagesServiceDependency]: Pick; }; export type Dependency = Dependencies[T]; + +type ProviderDictionary = { + [TKey in keyof Dependencies]: { + provide: TKey; + useValue: Dependencies[TKey]; + }; +}; + +type Provider = ProviderDictionary[keyof ProviderDictionary]; + +type ModuleMetadata = { + // Quick workaround instead of extracting relevant types from @nestjs/types + // (which are not really very different from `any` anyway) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + controllers?: any[]; + providers?: Provider[]; +}; + +export const createTestingModule: ( + metadata: ModuleMetadata, +) => TestingModuleBuilder = Test.createTestingModule.bind(Test); diff --git a/src/packages.controller.spec.ts b/src/packages.controller.spec.ts new file mode 100644 index 0000000..930e6ed --- /dev/null +++ b/src/packages.controller.spec.ts @@ -0,0 +1,69 @@ +import { TestingModule } from '@nestjs/testing'; +import request from 'supertest'; +import { PackagesController } from './packages.controller'; +import { createTestingModule, packagesServiceDependency } from './dependencies'; +import { PackageInfo } from './storage/types'; + +describe('AppController', () => { + it('returns a result from packages service', async () => { + const testTrackingNumber = 'test-tracking-number'; + const testPackageResponse = { + packageData: { + articleName: 'fake name', + articlePrice: 123, + articleQuantity: 456, + senderAddress: 'fake address', + SKU: 'fake sku', + status: 'delivery', + carrier: 'DHL', + trackingNumber: testTrackingNumber, + receiverAddress: 'fake recipient address', + }, + receiverWeather: { + temperature: 'fake temperature', + apparentTemperature: 'fake apparent temperature', + relativeHumidity: 'fake relative humidity', + }, + }; + const moduleFixture: TestingModule = await createTestingModule({ + controllers: [PackagesController], + providers: [ + { + provide: packagesServiceDependency, + useValue: { + getPackageInfoWithWeather: ( + carrier, + trackingNumber, + ) => { + if ( + carrier === 'DHL' && + trackingNumber === testTrackingNumber + ) { + return Promise.resolve(testPackageResponse); + } + + return Promise.resolve(null); + }, + }, + }, + ], + }).compile(); + + const app = moduleFixture.createNestApplication(); + await app.init(); + + try { + const appRequest = request(app.getHttpServer()); + await appRequest + .get(`/packages/DHL/${testTrackingNumber}`) + .expect(200) + .expect(testPackageResponse); + await appRequest + .get(`/packages/UPS/${testTrackingNumber}`) + .expect(404); + await appRequest.get(`/packages/DHL/non-existent`).expect(404); + } finally { + await app.close(); + } + }); +}); diff --git a/src/packages.controller.ts b/src/packages.controller.ts new file mode 100644 index 0000000..6460383 --- /dev/null +++ b/src/packages.controller.ts @@ -0,0 +1,33 @@ +import { + Controller, + Get, + Inject, + NotFoundException, + Param, +} from '@nestjs/common'; +import { Dependencies, packagesServiceDependency } from './dependencies'; +import { CarrierType } from './types'; + +@Controller('packages') +export class PackagesController { + constructor( + @Inject(packagesServiceDependency) + private readonly packagesService: Dependencies[typeof packagesServiceDependency], + ) {} + + @Get(':carrier/:trackingNumber') + async getPackageInfoWithWeather( + @Param('carrier') carrier: string, + @Param('trackingNumber') trackingNumber: string, + ) { + const result = await this.packagesService.getPackageInfoWithWeather( + carrier as CarrierType, + trackingNumber, + ); + if (!result) { + throw new NotFoundException(); + } + + return result; + } +}