Skip to content

Commit

Permalink
refactor(repository-tests): move relation tests to repository-tests p…
Browse files Browse the repository at this point in the history
…ackage

Move these tests to repository-tests package so that they would be tested against real databases such as MongoDB and MySQL.
Introduce a new type MixedIdType to CrudFeatures to solve the different usage of id types in different databases.

Co-authored-by: Nora <[email protected]>
  • Loading branch information
2 people authored and agnes512 committed Sep 3, 2019
1 parent c0abd7c commit fa2ecdc
Show file tree
Hide file tree
Showing 29 changed files with 1,159 additions and 822 deletions.
12 changes: 12 additions & 0 deletions packages/repository-tests/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/repository-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@
"@loopback/build": "^1.7.1",
"@loopback/repository": "^1.12.0",
"@types/debug": "^4.1.5",
"@types/node": "^10.14.17"
"@types/node": "^10.14.17",
"@types/lodash": "^4.14.138",
"lodash": "^4.17.15"
},
"dependencies": {
"@loopback/testlab": "^1.7.4",
"@loopback/context":"1.21.4",
"@types/debug": "^4.1.5",
"debug": "^4.1.1"
},
Expand Down
25 changes: 24 additions & 1 deletion packages/repository-tests/src/crud-test-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,38 @@ export function crudRepositoryTestSuite(
}),
);

let testFiles = [];

const testRoot = path.resolve(__dirname, 'crud');
let testFiles = fs.readdirSync(testRoot);
testFiles = fs.readdirSync(testRoot);
testFiles = testFiles.filter(function(it) {
return (
!!require.extensions[path.extname(it).toLowerCase()] &&
/\.suite\.[^.]+$/.test(it)
);
});

// relations folder tests
const folders = ['acceptance'];

for (const folder of folders) {
const relationsTestRoot = path.resolve(
__dirname,
`crud/relations/${folder}`,
);
let folderTestFiles = fs.readdirSync(relationsTestRoot);
folderTestFiles = folderTestFiles.filter(function(it) {
return (
!!require.extensions[path.extname(it).toLowerCase()] &&
/\.acceptance\.[^.]+$/.test(it)
);
});
folderTestFiles.forEach(file => {
file = `relations/${folder}/` + file;
testFiles.push(file);
});
}

for (const ix in testFiles) {
const name = testFiles[ix];
const fullPath = path.resolve(testRoot, name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// License text available at https://opensource.org/licenses/MIT

import {Entity, model, property} from '@loopback/repository';
import {EntityCrudRepository} from '@loopback/repository/src';
import {EntityCrudRepository} from '@loopback/repository';
import {expect, skipIf, toJSON} from '@loopback/testlab';
import {Suite} from 'mocha';
import {
Expand Down
107 changes: 107 additions & 0 deletions packages/repository-tests/src/crud/nested-model-properties.suite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright IBM Corp. 2019. All Rights Reserved.
// Node module: @loopback/repository-tests
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {
DefaultCrudRepository,
Entity,
EntityCrudRepository,
juggler,
model,
property,
} from '@loopback/repository';
import {expect, toJSON} from '@loopback/testlab';
import {
deleteAllModelsInDefaultDataSource,
withCrudCtx,
MixedIdType,
} from '../helpers.repository-tests';
import {
CrudFeatures,
CrudRepositoryCtor,
CrudTestContext,
DataSourceOptions,
} from '../types.repository-tests';

export function nestedModelsPropertiesSuite(
dataSourceOptions: DataSourceOptions,
repositoryClass: CrudRepositoryCtor,
features: CrudFeatures,
) {
describe('Nested models properties', () => {
let db: juggler.DataSource;
let userRepo: EntityCrudRepository<User, typeof User.prototype.id>;

before(deleteAllModelsInDefaultDataSource);

before(
withCrudCtx(async function setupRepository(ctx: CrudTestContext) {
db = ctx.dataSource;
userRepo = new DefaultCrudRepository<User, typeof User.prototype.id>(
User,
db,
);
const models = [User];
await db.automigrate(models.map(m => m.name));
}),
);
beforeEach(async function resetDatabase() {
await userRepo.deleteAll();
});

it('allows models to allow a singel nested model property', async () => {
const user = {
name: 'foo',
roles: [],
address: {street: 'backstreet'},
};
const created = await userRepo.create(user);

const stored = await userRepo.findById(created.id);
expect(toJSON(stored)).to.containDeep(toJSON(user));
});

it('allows models to allow multiple nested model properties in an array', async () => {
const user = {
name: 'foo',
roles: [{name: 'admin'}, {name: 'user'}],
address: {street: 'backstreet'},
};
const created = await userRepo.create(user);

const stored = await userRepo.findById(created.id);
expect(toJSON(stored)).to.containDeep(toJSON(user));
});

@model()
class Role extends Entity {
@property()
name: string;
}

@model()
class Address extends Entity {
@property()
street: string;
}

@model()
class User extends Entity {
@property({
id: true,
generated: true,
})
id: MixedIdType;

@property({type: 'string'})
name: string;

@property.array(Role)
roles: Role[];

@property()
address: Address;
}
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright IBM Corp. 2019. All Rights Reserved.
// Node module: @loopback/repository-tests
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {
BelongsToAccessor,
BelongsToDefinition,
createBelongsToAccessor,
EntityNotFoundError,
Getter,
} from '@loopback/repository';
import {expect, toJSON} from '@loopback/testlab';
import {
deleteAllModelsInDefaultDataSource,
withCrudCtx,
} from '../../../helpers.repository-tests';
import {
CrudFeatures,
CrudRepositoryCtor,
CrudTestContext,
DataSourceOptions,
} from '../../../types.repository-tests';
import {
Customer,
CustomerRepository,
Order,
OrderRepository,
Shipment,
ShipmentRepository,
} from '../fixtures/models';
import {givenBoundCrudRepositories} from '../helpers';

export function belongsToRelationAcceptance(
dataSourceOptions: DataSourceOptions,
repositoryClass: CrudRepositoryCtor,
features: CrudFeatures,
) {
describe('BelongsTo relation (acceptance)', () => {
before(deleteAllModelsInDefaultDataSource);

let findCustomerOfOrder: BelongsToAccessor<
Customer,
typeof Order.prototype.id
>;
let customerRepo: CustomerRepository;
let orderRepo: OrderRepository;
let shipmentRepo: ShipmentRepository;

before(
withCrudCtx(async function setupRepository(ctx: CrudTestContext) {
({customerRepo, orderRepo, shipmentRepo} = givenBoundCrudRepositories(
ctx.dataSource,
repositoryClass,
));
const models = [Customer, Order, Shipment];
await ctx.dataSource.automigrate(models.map(m => m.name));
}),
);
before(givenAccessor);
beforeEach(async () => {
await orderRepo.deleteAll();
});

it('can find customer of order', async () => {
const customer = await customerRepo.create({
name: 'Order McForder',
});
const order = await orderRepo.create({
customerId: customer.id,
description: 'Order from Order McForder, the hoarder of Mordor',
});

const result = await orderRepo.customer(order.id);
// adding parentId to customer so MySQL doesn't complain about null vs
// undefined
expect(toJSON(result)).to.deepEqual(
toJSON({...customer, parentId: features.emptyValue}),
);
});

it('can find shipment of order with a custom foreign key name', async () => {
const shipment = await shipmentRepo.create({
name: 'Tuesday morning shipment',
});
const order = await orderRepo.create({
// eslint-disable-next-line @typescript-eslint/camelcase
shipment_id: shipment.id,
description: 'Order that is shipped Tuesday morning',
});
const result = await orderRepo.shipment(order.id);
expect(result).to.deepEqual(shipment);
});

it('throws EntityNotFound error when the related model does not exist', async () => {
const order = await orderRepo.create({
customerId: 999, // does not exist
description: 'Order of a fictional customer',
});

await expect(findCustomerOfOrder(order.id)).to.be.rejectedWith(
EntityNotFoundError,
);
});
// helpers
function givenAccessor() {
findCustomerOfOrder = createBelongsToAccessor(
Order.definition.relations.customer as BelongsToDefinition,
Getter.fromValue(customerRepo),
orderRepo,
);
}
});
}
Loading

0 comments on commit fa2ecdc

Please sign in to comment.