Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions INSTRUCTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ We use Domain Driven Design (DDD) and encourage its application, but all archite
- Ask yourself: how many entities do I have?
- Ask yourself: how many Commands? Queries?
- At this stage, you don't need to persist data anywhere other than **in memory**.
- Here's an indication of the expected number of line of codes for implementations
in **PHP** and **JavaScript**:
- Here's an indication of the expected number of line of codes for implementations in **PHP** and **JavaScript**:

### Step 2

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Fleet Management System - Fulll Technical Test
# Fleet Management System

## Overview

Expand Down
54 changes: 43 additions & 11 deletions features/steps/common_steps.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
import { Given } from "@cucumber/cucumber";
// First group: Testing
import { Before, Given } from "@cucumber/cucumber";
import { World } from "cucumber";
import { registerVehicleInUserFleet } from "./register_vehicle_steps";

// Second group: Domain
import { User } from "../../src/Domain/Models/User";
import { Vehicle } from "../../src/Domain/Models/Vehicle";

// Third group: App
import {
InitializeFleet,
InitializeFleetHandler,
} from "../../src/App/Commands/initializeFleet";

// Fourth group: Infra
import { InMemoryFleetRepository } from "../../src/Infra/Secondary/Repositories/InMemoryFleetRepository";
import { initializeFleetForUser } from "./shared/initializeFleetForUser";
import { registerVehicleInUserFleet } from "./register_vehicle_steps";
import { generateFrenchPlateNumber } from "../../tests/Utils/generateFrenchPlateNumber";

Before(function (): void {
this.context = {};
});

Given("my fleet", async function (): Promise<void> {
const user: User = User.create(crypto.randomUUID());
this.context = { user, repository: new InMemoryFleetRepository() };
this.context.fleetId = await initializeFleetForUser(
this.context.repository,
user,
);
this.context.repository = new InMemoryFleetRepository();
await initializeUser(this.context);
await initializeFleetForUser(this.context);
});

Given("a vehicle", async function (): Promise<void> {
this.context.vehicle = Vehicle.create(generateFrenchPlateNumber());
await initializeVehicle(this.context);
});

Given(
Expand All @@ -25,3 +36,24 @@ Given(
await registerVehicleInUserFleet(this.context);
},
);

async function initializeUser(context: World): Promise<void> {
context.user = User.create(crypto.randomUUID());
}

export async function initializeFleetForUser(context: World): Promise<void> {
const initializeFleet = new InitializeFleet(context.user.id);
const handler = new InitializeFleetHandler(context.repository);
context.fleetId = await handler.handle(initializeFleet);
}

async function initializeVehicle(context: World): Promise<void> {
context.vehicle = Vehicle.create(generateFrenchPlateNumber());
}

function generateFrenchPlateNumber(): string {
const letters = () =>
String.fromCharCode(65 + Math.floor(Math.random() * 26));
const digits = () => Math.floor(100 + Math.random() * 900); // 3 digits from 100 to 999
return `${letters()}${letters()}-${digits()}-${letters()}${letters()}`;
}
60 changes: 34 additions & 26 deletions features/steps/park_vehicle_steps.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
// First group: Testing framework
import { Given, Then, When } from "@cucumber/cucumber";
import { World } from "cucumber";
import { expect } from "chai";

// Second group: Domain
import { Location } from "../../src/Domain/Models/Location";
import { VehicleAlreadyParkedAtThisLocationError } from "../../src/Domain/Errors/VehicleAlreadyParkedAtThisLocationError";

// Third group: Helpers
import { parkVehicleAtLocation } from "./shared/parkVehicleAtLocation";
import { retrieveLocation } from "./shared/retrieveLocation";
// Third group: App
import {
ParkVehicle,
ParkVehicleHandler,
} from "../../src/App/Commands/parkVehicle";
import {
GetLocation,
GetLocationHandler,
} from "../../src/App/Queries/getLocation";

Given("a location", async function (): Promise<void> {
this.context.location = Location.create(48.8566, 2.3522);
Expand All @@ -17,34 +24,20 @@ Given("a location", async function (): Promise<void> {
Given(
"my vehicle has been parked in this location",
async function (): Promise<void> {
await parkVehicleAtLocation(
this.context.repository,
this.context.fleetId,
this.context.vehicle.plateNumber,
this.context.location,
);
await parkVehicleInFleetAtThisLocation(this.context);
},
);

When("I park my vehicle at this location", async function (): Promise<void> {
await parkVehicleAtLocation(
this.context.repository,
this.context.fleetId,
this.context.vehicle.plateNumber,
this.context.location,
);
await parkVehicleInFleetAtThisLocation(this.context);
});

When(
"I try to park my vehicle at this location",
async function (): Promise<void> {
try {
await parkVehicleAtLocation(
this.context.repository,
this.context.fleetId,
this.context.vehicle.plateNumber,
this.context.location,
);
await parkVehicleInFleetAtThisLocation(this.context);

this.context.parkingAttemptError = null;
} catch (error) {
this.context.parkingAttemptError = error;
Expand All @@ -55,11 +48,7 @@ When(
Then(
"the known location of my vehicle should verify this location",
async function (): Promise<void> {
const actualLocation = await retrieveLocation(
this.context.repository,
this.context.fleetId,
this.context.vehicle.plateNumber,
);
const actualLocation = await getLocation(this.context);

expect(actualLocation).to.deep.equal(this.context.location);
},
Expand All @@ -77,3 +66,22 @@ Then(
expect(this.context.parkingAttemptError).to.deep.equal(expected);
},
);

async function parkVehicleInFleetAtThisLocation(context: World) {
const parkVehicleCommand = new ParkVehicle(
context.fleetId,
context.vehicle.plateNumber,
context.location,
);
const handler = new ParkVehicleHandler(context.repository);
await handler.handle(parkVehicleCommand);
}

async function getLocation(context: World): Promise<Location> {
const getLocationQuery = new GetLocation(
context.fleetId,
context.vehicle.plateNumber,
);
const handler = new GetLocationHandler(context.repository);
return await handler.handle(getLocationQuery);
}
46 changes: 31 additions & 15 deletions features/steps/register_vehicle_steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ import { World } from "cucumber";
import { expect } from "chai";

// Second group: Domain
import { Fleet } from "../../src/Domain/Models/Fleet";
import { User } from "../../src/Domain/Models/User";
import { Vehicle } from "../../src/Domain/Models/Vehicle";
import { VehicleAlreadyRegisteredError } from "../../src/Domain/Errors/VehicleAlreadyRegisteredError";

// Third group: Helpers
import { initializeFleetForUser } from "./shared/initializeFleetForUser";
import { retrieveFleet } from "./shared/retrieveFleet";
// Third group: App
import {
InitializeFleet,
InitializeFleetHandler,
} from "../../src/App/Commands/initializeFleet";
import {
RegisterVehicle,
RegisterVehicleHandler,
} from "../../src/App/Commands/registerVehicle";
import { GetFleet, GetFleetHandler } from "../../src/App/Queries/getFleet";

Given("the fleet of another user", async function (): Promise<void> {
this.context.otherUser = User.create(crypto.randomUUID());
this.context.otherFleetId = await initializeFleetForUser(
this.context.repository,
this.context.otherUser,
);
await initializeOtherUser(this.context);
await initializeFleetForOtherUser(this.context);
});

Given(
Expand Down Expand Up @@ -51,14 +52,9 @@ When(
Then(
"this vehicle should be part of my vehicle fleet",
async function (): Promise<void> {
const fleet = await retrieveFleet(
this.context.repository,
this.context.fleetId,
);
const fleet = await getFleet(this.context);

const vehiclePlateNumber: string = fleet.vehicles
.map((v: Vehicle): string => v.plateNumber)
.toString();
const vehiclePlateNumber = getVehiclePlateNumber(fleet);

expect(vehiclePlateNumber).to.deep.include(
this.context.vehicle.plateNumber,
Expand All @@ -77,6 +73,16 @@ Then(
},
);

async function initializeOtherUser(context: World): Promise<void> {
context.otherUser = User.create(crypto.randomUUID());
}

async function initializeFleetForOtherUser(context: World): Promise<void> {
const initializeFleet = new InitializeFleet(context.otherUser.id);
const handler = new InitializeFleetHandler(context.repository);
context.otherFleetId = await handler.handle(initializeFleet);
}

export async function registerVehicleInUserFleet(
context: World,
): Promise<void> {
Expand All @@ -98,3 +104,13 @@ async function registerVehicleInOtherUserFleet(context: World): Promise<void> {
const handler = new RegisterVehicleHandler(context.repository);
await handler.handle(registerVehicleCommand);
}

async function getFleet(context: World): Promise<Fleet> {
const getFleetQuery = new GetFleet(context.fleetId);
const handler = new GetFleetHandler(context.repository);
return await handler.handle(getFleetQuery);
}

function getVehiclePlateNumber(fleet: Fleet): string {
return fleet.vehicles.map((v: Vehicle): string => v.plateNumber).toString();
}
15 changes: 0 additions & 15 deletions features/steps/shared/initializeFleetForUser.ts

This file was deleted.

21 changes: 0 additions & 21 deletions features/steps/shared/parkVehicleAtLocation.ts

This file was deleted.

12 changes: 0 additions & 12 deletions features/steps/shared/retrieveFleet.ts

This file was deleted.

16 changes: 0 additions & 16 deletions features/steps/shared/retrieveLocation.ts

This file was deleted.

Loading