Skip to content
This repository was archived by the owner on Sep 4, 2024. It is now read-only.

Commit 976a7d5

Browse files
committed
test(repository): add repository integration tests
1 parent 18200f8 commit 976a7d5

File tree

12 files changed

+2681
-83
lines changed

12 files changed

+2681
-83
lines changed

package-lock.json

Lines changed: 2338 additions & 67 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,20 @@
4444
"!*/__tests__"
4545
],
4646
"peerDependencies": {
47-
"@loopback/core": "^4.0.3"
47+
"@loopback/core": "^4.0.3",
48+
"@loopback/repository": "^5.0.4",
49+
"@loopback/rest": "^12.0.4"
4850
},
4951
"dependencies": {
50-
"@loopback/repository": "^5.0.4",
5152
"sequelize": "^6.25.2",
5253
"tslib": "^2.0.0"
5354
},
5455
"devDependencies": {
56+
"@loopback/boot": "^5.0.4",
5557
"@loopback/build": "^9.0.3",
5658
"@loopback/core": "^4.0.3",
5759
"@loopback/eslint-config": "^13.0.3",
60+
"@loopback/rest": "^12.0.4",
5861
"@loopback/testlab": "^5.0.3",
5962
"@semantic-release/changelog": "^6.0.1",
6063
"@semantic-release/commit-analyzer": "^9.0.2",
@@ -66,6 +69,7 @@
6669
"eslint": "^8.22.0",
6770
"semantic-release": "^19.0.5",
6871
"source-map-support": "^0.5.21",
72+
"sqlite3": "^5.1.2",
6973
"typescript": "~4.7.4"
7074
},
7175
"config": {

src/__tests__/acceptance/README.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/__tests__/fixtures/application.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {BootMixin} from '@loopback/boot';
2+
import {ApplicationConfig} from '@loopback/core';
3+
import {RepositoryMixin} from '@loopback/repository';
4+
import {RestApplication} from '@loopback/rest';
5+
6+
export class SequelizeSandboxApplication extends BootMixin(
7+
RepositoryMixin(RestApplication),
8+
) {
9+
constructor(options: ApplicationConfig = {}) {
10+
super(options);
11+
this.projectRoot = __dirname;
12+
}
13+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import {
2+
Count,
3+
CountSchema,
4+
FilterExcludingWhere,
5+
repository,
6+
Where,
7+
} from '@loopback/repository';
8+
import {
9+
get,
10+
getModelSchemaRef,
11+
param,
12+
post,
13+
requestBody,
14+
response,
15+
} from '@loopback/rest';
16+
import {User} from '../models/user.model';
17+
import {UserRepository} from '../repositories/user.repository';
18+
19+
export class UserController {
20+
constructor(
21+
@repository(UserRepository)
22+
public userRepository: UserRepository,
23+
) {}
24+
25+
@post('/users')
26+
@response(200, {
27+
description: 'User model instance',
28+
content: {'application/json': {schema: getModelSchemaRef(User)}},
29+
})
30+
async create(
31+
@requestBody({
32+
content: {
33+
'application/json': {
34+
schema: getModelSchemaRef(User, {
35+
title: 'NewUser',
36+
exclude: ['id'],
37+
}),
38+
},
39+
},
40+
})
41+
user: Omit<User, 'id'>,
42+
): Promise<User> {
43+
/**
44+
* This `syncSequelizeModel` call below is only for testing purposes,
45+
* in real project you are supposed to run migrations instead
46+
* to sync model definitions to the target database.
47+
*/
48+
await this.userRepository.syncSequelizeModel({
49+
force: false,
50+
});
51+
return this.userRepository.create(user);
52+
}
53+
54+
@get('/users/count')
55+
@response(200, {
56+
description: 'User model count',
57+
content: {'application/json': {schema: CountSchema}},
58+
})
59+
async count(@param.where(User) where?: Where<User>): Promise<Count> {
60+
/**
61+
* This `syncSequelizeModel` call below is only for testing purposes,
62+
* in real project you are supposed to run migrations instead
63+
* to sync model definitions to the target database.
64+
*/
65+
await this.userRepository.syncSequelizeModel({
66+
force: false,
67+
});
68+
return this.userRepository.count(where);
69+
}
70+
71+
@get('/users/{id}')
72+
@response(200, {
73+
description: 'User model instance',
74+
content: {
75+
'application/json': {
76+
schema: getModelSchemaRef(User, {includeRelations: true}),
77+
},
78+
},
79+
})
80+
async findById(
81+
@param.path.number('id') id: number,
82+
@param.filter(User, {exclude: 'where'}) filter?: FilterExcludingWhere<User>,
83+
): Promise<User> {
84+
/**
85+
* This `syncSequelizeModel` call below is only for testing purposes,
86+
* in real project you are supposed to run migrations instead
87+
* to sync model definitions to the target database.
88+
*/
89+
await this.userRepository.syncSequelizeModel({
90+
force: false,
91+
});
92+
return this.userRepository.findById(id, filter);
93+
}
94+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core';
2+
import {
3+
SequelizeDataSource,
4+
SequelizeDataSourceConfig,
5+
} from '../../../sequelize';
6+
7+
const config: SequelizeDataSourceConfig = {
8+
name: 'db',
9+
host: '0.0.0.0',
10+
connector: 'sqlite3',
11+
database: 'database',
12+
file: ':memory:',
13+
};
14+
15+
// Observe application's life cycle to disconnect the datasource when
16+
// application is stopped. This allows the application to be shut down
17+
// gracefully. The `stop()` method is inherited from `juggler.DataSource`.
18+
// Learn more at https://loopback.io/doc/en/lb4/Life-cycle.html
19+
@lifeCycleObserver('datasource')
20+
export class DbDataSource
21+
extends SequelizeDataSource
22+
implements LifeCycleObserver
23+
{
24+
static dataSourceName = 'db';
25+
static readonly defaultConfig = config;
26+
27+
constructor(
28+
@inject('datasources.config.db', {optional: true})
29+
dsConfig: SequelizeDataSourceConfig = config,
30+
) {
31+
super(dsConfig);
32+
}
33+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {Entity, model, property} from '@loopback/repository';
2+
3+
@model()
4+
export class User extends Entity {
5+
@property({
6+
type: 'number',
7+
id: true,
8+
generated: true,
9+
})
10+
id?: number;
11+
12+
@property({
13+
type: 'string',
14+
})
15+
name?: string;
16+
17+
@property({
18+
type: 'string',
19+
required: true,
20+
})
21+
email: string;
22+
23+
@property({
24+
type: 'number',
25+
})
26+
age?: number;
27+
28+
@property()
29+
active?: boolean;
30+
31+
constructor(data?: Partial<User>) {
32+
super(data);
33+
}
34+
}
35+
36+
export interface UserRelations {
37+
// describe navigational properties here
38+
}
39+
40+
export type UserWithRelations = User & UserRelations;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {inject} from '@loopback/core';
2+
import {SequelizeRepository} from '../../../sequelize';
3+
import {DbDataSource} from '../datasources/db.datasource';
4+
import {User, UserRelations} from '../models/user.model';
5+
6+
export class UserRepository extends SequelizeRepository<
7+
User,
8+
typeof User.prototype.id,
9+
UserRelations
10+
> {
11+
constructor(@inject('datasources.db') dataSource: DbDataSource) {
12+
super(User, dataSource);
13+
}
14+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import {RestApplication} from '@loopback/rest';
2+
import {
3+
Client,
4+
createRestAppClient,
5+
expect,
6+
givenHttpServerConfig,
7+
TestSandbox,
8+
} from '@loopback/testlab';
9+
import {resolve} from 'path';
10+
import {SequelizeSandboxApplication} from '../fixtures/application';
11+
12+
describe('Sequelize Repository (integration)', () => {
13+
const sandbox = new TestSandbox(resolve(__dirname, '../../.sandbox'));
14+
15+
let app: SequelizeSandboxApplication;
16+
let client: Client;
17+
18+
beforeEach('reset sandbox', () => sandbox.reset());
19+
beforeEach(getAppAndClient);
20+
afterEach(async () => {
21+
if (app) await app.stop();
22+
(app as unknown) = undefined;
23+
});
24+
25+
it('creates an entity', async () => {
26+
const user = {
27+
name: 'shubham',
28+
age: 10,
29+
email: 'test@lb4.com',
30+
};
31+
const res = await client.post('/users').send(user);
32+
33+
expect(res.body).to.have.property('name', user.name);
34+
expect(res.body).to.have.property('age', user.age);
35+
expect(res.body).to.have.property('email', user.email);
36+
});
37+
38+
it('counts created entity', async () => {
39+
const user = {
40+
name: 'shubham',
41+
age: 10,
42+
email: 'test@lb4.com',
43+
};
44+
await client.post('/users').send(user);
45+
46+
const res = await client.get('/users/count').send();
47+
expect(res.body).to.have.property('count', 1);
48+
});
49+
50+
it('fetches an entity', async () => {
51+
const user = {
52+
name: 'shubham',
53+
age: 10,
54+
email: 'test@lb4.com',
55+
};
56+
const create = await client.post('/users').send(user);
57+
const res = await client.get(`/users/${create.body.id}`);
58+
expect(res.body).to.have.property('name', user.name);
59+
expect(res.body).to.have.property('age', user.age);
60+
expect(res.body).to.have.property('email', user.email);
61+
});
62+
63+
async function getAppAndClient() {
64+
await sandbox.copyFile(resolve(__dirname, '../fixtures/application.js'));
65+
await sandbox.copyFile(
66+
resolve(__dirname, '../fixtures/datasources/db.datasource.js'),
67+
'datasources/db.datasource.js',
68+
);
69+
await sandbox.copyFile(
70+
resolve(__dirname, '../fixtures/models/user.model.js'),
71+
'models/user.model.js',
72+
);
73+
await sandbox.copyFile(
74+
resolve(__dirname, '../fixtures/controllers/user.controller.js'),
75+
'controllers/user.controller.js',
76+
);
77+
await sandbox.copyFile(
78+
resolve(__dirname, '../fixtures/repositories/user.repository.js'),
79+
'repositories/user.repository.js',
80+
);
81+
82+
const MyApp = require(resolve(
83+
sandbox.path,
84+
'application.js',
85+
)).SequelizeSandboxApplication;
86+
app = new MyApp({
87+
rest: givenHttpServerConfig(),
88+
});
89+
90+
await app.boot();
91+
await app.start();
92+
93+
client = createRestAppClient(app as RestApplication);
94+
}
95+
});

src/__tests__/unit/sequelize.datasource.unit.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ describe('Sequelize DataSource', () => {
66
it('throws error when nosql connectors are supplied', () => {
77
try {
88
new SequelizeDataSource({
9+
name: 'db',
910
user: 'test',
1011
password: 'secret',
1112
connector: 'memory' as SupportedLoopbackConnectors,

0 commit comments

Comments
 (0)