Skip to main content

Self-assessment / Validation

To ensure a high-quality product, a series of tests that cover different aspects of the system has been implemented. The tests have been divided into two main categories: architectural testing and API testing. Moreover, to ensure also a high-quality code production, Code Quality tools have been integrated into the Continuous Integration pipeline.

Architectural Testing​

To ensure that layers’ dependencies are respected, Dependency Cruiser framework has been exploited. Essentially, the configured rules check that:

  • The Domain layer does not access to any other layer.
  • The Application layer can access only the Domain layer.
  • Presentation layer can access only Domain and Application layers.
  • Infrastructure layer can access to the above layers.
Dependency Cruiser rules

forbidden: [
{
name: 'no-unreachable-from-domain',
comment: 'The domain layer should not depend on any other layer',
severity: 'error',
from: {
path: '^(src/domain)'
},
to: {
pathNot: '^(src/domain)'
}
},
{
name: 'no-unreachable-from-application',
comment: 'The application layer should only depend on the domain layer',
severity: 'error',
from: {
path: '^(src/application)'
},
to: {
pathNot: '^(src/domain)|^(src/application)|^(src/utils)'
}
},
{
name: 'no-unreachable-from-presentation',
comment: 'The presentation layer should only depend on the domain and application layers',
severity: 'error',
from: {
path: '^(src/presentation)'
},
to: {
pathNot: '^(src/domain)|^(src/application)|^(src/presentation)|^(src/utils)|^(node_modules/zod)'
}
}
]

API Testing​

API testing has been performed using the Vitest framework.

Example: testing the PUT /devices endpoint

describe('PUT /devices', (): void => {
beforeAll(async (): Promise<void> => {
await connectToMock()
})
describe('PUT /devices', (): void => {
it('responds with a forbidden status if no auth token is provided',
async (): Promise<void> => {
const update: Response = await deviceService.put('/devices')
expect(update.status).toBe(HttpStatusCode.FORBIDDEN)
})
it('should update the device', async (): Promise<void> => {
const newDevice = {
endpoint: {
ipAddress: '192.168.1.1',
port: 1000
},
description: 'updated description',
locationId: 'updated locationId',
isEnabled: false
}
const update: Response = await deviceService
.put('/devices/test-device-id')
.set('Authorization', `Bearer ${TOKEN}`)
.send(newDevice)
expect(update.status).toBe(HttpStatusCode.OK)
expect(update.type).toBe('application/json')
})
})
afterAll(async (): Promise<void> => {
await disconnectFromMock()
})
})

To be able to execute the tests, the database has been mocked using MongoDB Memory Server that allows to run a MongoDB instance in memory. With this configuration, each integration test is isolated from the others, resetting the database after each test.

Example: connecting to the mock database

export const connectToMock = async (): Promise<void> => {
mongoMock = await MongoMemoryServer.create()
await mongoose.connect(mongoMock.getUri(), {
directConnection: true
})
}

export const populateUsers = async (): Promise<void> => {
const userModel: Model<User> = model<User>('User', userSchema, 'user')
await userModel.create(userSample)
}

Code Quality​

Two main tools have been used to ensure the quality of the code produced: Prettier is a code formatter that supports many languages. It enforces a consistent style by parsing code and re-writing it according to the configuration rules. ESLint is a tool that statically analyses code to find suboptimal patterns and errors.

Both tools have been integrated into the Continuous Integration pipeline to keep the high-quality code production.