added configuration for tests; test utils refactoring; functional test refactoring; added some new tests; fixed some bugs discovered by tests:

This commit is contained in:
Umed Khudoiberdiev 2016-06-06 20:34:45 +05:00
parent 7252137ae6
commit 39fbab5684
13 changed files with 440 additions and 232 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
build/
node_modules/
typings/
npm-debug.log
npm-debug.log
config/parameters.json

32
config/parameters.json Normal file
View File

@ -0,0 +1,32 @@
{
"connections": {
"mysql": {
"host": "192.168.99.100",
"port": 3306,
"username": "root",
"password": "admin",
"database": "test"
},
"mysqlSecondary": {
"host": "192.168.99.100",
"port": 3306,
"username": "root",
"password": "admin",
"database": "test2"
},
"postgres": {
"host": "192.168.99.100",
"port": 5432,
"username": "root",
"password": "admin",
"database": "test"
},
"postgresSecondary": {
"host": "192.168.99.100",
"port": 5432,
"username": "root",
"password": "admin",
"database": "test2"
}
}
}

View File

@ -0,0 +1,32 @@
{
"connections": {
"mysql": {
"host": "localhost",
"port": 3306,
"username": "root",
"password": "admin",
"database": "test"
},
"mysqlSecondary": {
"host": "localhost",
"port": 3306,
"username": "root",
"password": "admin",
"database": "test2"
},
"postgres": {
"host": "localhost",
"port": 5432,
"username": "root",
"password": "admin",
"database": "test"
},
"postgresSecondary": {
"host": "localhost",
"port": 5432,
"username": "root",
"password": "admin",
"database": "test2"
}
}
}

View File

@ -29,6 +29,7 @@ import {SchemaCreatorFactory} from "../schema-creator/SchemaCreatorFactory";
import {ReactiveRepositoryNotFoundError} from "./error/ReactiveRepositoryNotFoundError";
import {RepositoryNotTreeError} from "./error/RepositoryNotTreeError";
import {EntitySchema} from "../metadata/entity-schema/EntitySchema";
import {CannotSyncNotConnectedError} from "./error/CannotSyncNotConnectedError";
/**
* A single connection instance to the database. Each connection has its own repositories, subscribers and metadatas.
@ -168,12 +169,13 @@ export class Connection {
// build all metadatas registered in the current connection
this.buildMetadatas();
// set connected status for the current connection
this._isConnected = true;
// second build schema
if (this.options.autoSchemaCreate === true)
await this.syncSchema();
// set connected status for the current connection
this._isConnected = true;
return this;
}
@ -184,13 +186,17 @@ export class Connection {
if (!this.isConnected)
throw new CannotCloseNotConnectedError(this.name);
return this.driver.disconnect();
await this.driver.disconnect();
this._isConnected = false;
}
/**
* Creates database schema for all entities registered in this connection.
*/
async syncSchema(dropBeforeSync: boolean = false): Promise<void> {
if (!this.isConnected)
throw new CannotSyncNotConnectedError(this.name);
if (dropBeforeSync)
await this.driver.clearDatabase();
@ -203,7 +209,7 @@ export class Connection {
/**
* Imports entities from the given paths (directories) for the current connection.
*/
importEntitiesFromDirectories(paths: string[]): this {
async importEntitiesFromDirectories(paths: string[]): Promise<this> {
this.importEntities(importClassesFromDirectories(paths));
return this;
}
@ -211,7 +217,7 @@ export class Connection {
/**
* Imports entity schemas from the given paths (directories) for the current connection.
*/
importEntitySchemaFromDirectories(paths: string[]): this {
async importEntitySchemaFromDirectories(paths: string[]): Promise<this> {
this.importSchemas(importClassesFromDirectories(paths) as any[]); // todo: check it.
return this;
}
@ -219,7 +225,7 @@ export class Connection {
/**
* Imports subscribers from the given paths (directories) for the current connection.
*/
importSubscribersFromDirectories(paths: string[]): this {
async importSubscribersFromDirectories(paths: string[]): Promise<this> {
this.importSubscribers(importClassesFromDirectories(paths));
return this;
}
@ -227,7 +233,7 @@ export class Connection {
/**
* Imports naming strategies from the given paths (directories) for the current connection.
*/
importNamingStrategiesFromDirectories(paths: string[]): this {
async importNamingStrategiesFromDirectories(paths: string[]): Promise<this> {
this.importEntities(importClassesFromDirectories(paths));
return this;
}
@ -235,7 +241,7 @@ export class Connection {
/**
* Imports entities for the current connection.
*/
importEntities(entities: Function[]): this {
async importEntities(entities: Function[]): Promise<this> {
if (this.isConnected)
throw new CannotImportAlreadyConnectedError("entities", this.name);
@ -246,7 +252,7 @@ export class Connection {
/**
* Imports schemas for the current connection.
*/
importSchemas(schemas: EntitySchema[]): this {
async importSchemas(schemas: EntitySchema[]): Promise<this> {
if (this.isConnected)
throw new CannotImportAlreadyConnectedError("schemas", this.name);
@ -257,7 +263,7 @@ export class Connection {
/**
* Imports entities for the given connection. If connection name is not given then default connection is used.
*/
importSubscribers(subscriberClasses: Function[]): this {
async importSubscribers(subscriberClasses: Function[]): Promise<this> {
if (this.isConnected)
throw new CannotImportAlreadyConnectedError("entity subscribers", this.name);
@ -268,7 +274,7 @@ export class Connection {
/**
* Imports entities for the current connection.
*/
importNamingStrategies(strategies: Function[]): this {
async importNamingStrategies(strategies: Function[]): Promise<this> {
if (this.isConnected)
throw new CannotImportAlreadyConnectedError("naming strategies", this.name);
@ -375,26 +381,32 @@ export class Connection {
private buildMetadatas() {
// take imported naming strategy metadatas
getMetadataArgsStorage()
.namingStrategies
.filterByTargets(this.namingStrategyClasses)
.forEach(metadata => this.namingStrategyMetadatas.push(new NamingStrategyMetadata(metadata)));
if (this.namingStrategyClasses && this.namingStrategyClasses.length) {
getMetadataArgsStorage()
.namingStrategies
.filterByTargets(this.namingStrategyClasses)
.forEach(metadata => this.namingStrategyMetadatas.push(new NamingStrategyMetadata(metadata)));
}
// take imported event subscribers
getMetadataArgsStorage()
.entitySubscribers
.filterByTargets(this.subscriberClasses)
.map(metadata => getFromContainer(metadata.target))
.forEach(subscriber => this.entitySubscribers.push(subscriber));
if (this.subscriberClasses && this.subscriberClasses.length) {
getMetadataArgsStorage()
.entitySubscribers
.filterByTargets(this.subscriberClasses)
.map(metadata => getFromContainer(metadata.target))
.forEach(subscriber => this.entitySubscribers.push(subscriber));
}
// take imported entity listeners
getMetadataArgsStorage()
.entityListeners
.filterByTargets(this.entityClasses)
.forEach(metadata => this.entityListeners.push(new EntityListenerMetadata(metadata)));
if (this.entityClasses && this.entityClasses.length) {
getMetadataArgsStorage()
.entityListeners
.filterByTargets(this.entityClasses)
.forEach(metadata => this.entityListeners.push(new EntityListenerMetadata(metadata)));
}
// build entity metadatas from metadata args storage (collected from decorators)
if (this.entityClasses) {
if (this.entityClasses && this.entityClasses.length) {
getFromContainer(EntityMetadataBuilder)
.buildFromMetadataArgsStorage(this.createNamingStrategy(), this.entityClasses)
.forEach(metadata => {
@ -404,7 +416,7 @@ export class Connection {
}
// build entity metadatas from given entity schemas
if (this.entitySchemas) {
if (this.entitySchemas && this.entitySchemas.length) {
getFromContainer(EntityMetadataBuilder)
.buildFromSchemas(this.createNamingStrategy(), this.entitySchemas)
.forEach(metadata => {

View File

@ -0,0 +1,12 @@
/**
* @internal
*/
export class CannotSyncNotConnectedError extends Error {
name = "CannotSyncNotConnectedError";
constructor(connectionName: string) {
super();
this.message = `Cannot sync schema of the "${connectionName}" connection because connection is not yet established.`;
}
}

View File

@ -20,7 +20,6 @@ import {ColumnMetadataArgs} from "../metadata-args/ColumnMetadataArgs";
import {RelationMetadataArgs} from "../metadata-args/RelationMetadataArgs";
import {JoinColumnMetadataArgs} from "../metadata-args/JoinColumnMetadataArgs";
import {JoinTableMetadataArgs} from "../metadata-args/JoinTableMetadataArgs";
import {JoinColumnOptions} from "../decorator/options/JoinColumnOptions";
/**
* Aggregates all metadata: table, column, relation into one collection grouped by tables for a given set of classes.
@ -76,7 +75,6 @@ export class EntityMetadataBuilder {
const column: ColumnMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
mode: mode,
propertyName: columnName,
// todo: what to do with it?: propertyType:
@ -104,7 +102,6 @@ export class EntityMetadataBuilder {
const relationSchema = schema.relations[relationName];
const relation: RelationMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName,
// todo: what to do with it?: propertyType:
relationType: relationSchema.type,
@ -130,14 +127,12 @@ export class EntityMetadataBuilder {
if (typeof relationSchema.joinColumn === "boolean") {
const joinColumn: JoinColumnMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName
};
metadataArgsStorage.joinColumns.push(joinColumn);
} else {
const joinColumn: JoinColumnMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName,
name: relationSchema.joinColumn.name,
referencedColumnName: relationSchema.joinColumn.referencedColumnName
@ -151,14 +146,12 @@ export class EntityMetadataBuilder {
if (typeof relationSchema.joinTable === "boolean") {
const joinTable: JoinTableMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName
};
metadataArgsStorage.joinTables.push(joinTable);
} else {
const joinTable: JoinTableMetadataArgs = {
target: schema.target || schema.name,
// targetId: schema.name,
propertyName: relationName,
name: relationSchema.joinTable.name,
joinColumn: relationSchema.joinTable.joinColumn,
@ -170,17 +163,17 @@ export class EntityMetadataBuilder {
});
});
return this.buildFromAnyMetadataArgsStorage(metadataArgsStorage, namingStrategy);
return this.build(metadataArgsStorage, namingStrategy);
}
/**
* Builds a complete metadata aggregations for the given entity classes.
*/
buildFromMetadataArgsStorage(namingStrategy: NamingStrategyInterface, entityClasses?: Function[]): EntityMetadata[] {
return this.buildFromAnyMetadataArgsStorage(getMetadataArgsStorage(), namingStrategy, entityClasses);
return this.build(getMetadataArgsStorage(), namingStrategy, entityClasses);
}
buildFromAnyMetadataArgsStorage(metadataArgsStorage: MetadataArgsStorage, namingStrategy: NamingStrategyInterface, entityClasses?: Function[]): EntityMetadata[] {
private build(metadataArgsStorage: MetadataArgsStorage, namingStrategy: NamingStrategyInterface, entityClasses?: Function[]): EntityMetadata[] {
const embeddableMergedArgs = metadataArgsStorage.getMergedEmbeddableTableMetadatas(entityClasses);
const entityMetadatas = metadataArgsStorage.getMergedTableMetadatas(entityClasses).map(mergedArgs => {

View File

@ -66,7 +66,7 @@ export class TableMetadata extends TargetMetadata {
*/
get name() {
if (this.isClosure && this._name)
if (this.isClosureJunction && this._name)
return this.entityMetadata.namingStrategy.closureJunctionTableName(this._name);
// if custom name is given then use it
@ -104,6 +104,13 @@ export class TableMetadata extends TargetMetadata {
return this.tableType === "closure";
}
/**
* Checks if this table is a junction table of the closure table.
*/
get isClosureJunction() {
return this.tableType === "closureJunction";
}
/**
* Checks if this table is an embeddable table.
*/

View File

@ -46,6 +46,14 @@ export class Repository<Entity> {
// Public Methods
// -------------------------------------------------------------------------
/**
* Repository's target is an object (or unique string in the case if entity is loaded from a schema) that is managed
* by this repository.
*/
get target(): Function|string {
return this.metadata.target;
}
/**
* Checks if entity has an id.
*/

View File

@ -0,0 +1,94 @@
import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
import {setupTestingConnections, closeConnections, reloadDatabases} from "../../utils/utils";
import {Connection} from "../../../src/connection/Connection";
import {CannotConnectAlreadyConnectedError} from "../../../src/connection/error/CannotConnectAlreadyConnectedError";
import {CannotCloseNotConnectedError} from "../../../src/connection/error/CannotCloseNotConnectedError";
import {CannotImportAlreadyConnectedError} from "../../../src/connection/error/CannotImportAlreadyConnectedError";
import {Repository} from "../../../src/repository/Repository";
import {TreeRepository} from "../../../src/repository/TreeRepository";
chai.should();
chai.use(require("sinon-chai"));
chai.use(require("chai-as-promised"));
describe("Connection", () => {
describe("after connection is established successfully", function() {
let connections: Connection[];
before(() => setupTestingConnections({ entities: [Post, Category] }).then(all => connections = all));
beforeEach(() => reloadDatabases(connections));
it("connection.isConnected should be true", () => connections.forEach(connection => {
connection.isConnected.should.be.true;
}));
it("import entities, entity schemas, subscribers and naming strategies should not be possible once connection is done", () => connections.forEach(connection => {
connection.importEntities([Post]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
connection.importSchemas([]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
connection.importSubscribers([]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
connection.importNamingStrategies([]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
connection.importEntitiesFromDirectories([]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
connection.importEntitySchemaFromDirectories([]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
connection.importSubscribersFromDirectories([]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
connection.importNamingStrategiesFromDirectories([]).should.be.rejectedWith(CannotImportAlreadyConnectedError);
}));
it("should not be able to connect again", () => connections.forEach(connection => {
connection.connect().should.be.rejectedWith(CannotConnectAlreadyConnectedError);
}));
it("should be able to get entity repository", () => connections.forEach(connection => {
connection.getRepository(Post).should.be.instanceOf(Repository);
connection.getRepository(Post).target.should.be.eql(Post);
}));
it("should be able to close a connection", () => connections.forEach(connection => {
connection.close().should.be.fulfilled;
}));
});
describe("working with repositories after connection is established successfully", function() {
let connections: Connection[];
before(() => setupTestingConnections({ entities: [Post, Category] }).then(all => connections = all));
after(() => closeConnections(connections));
beforeEach(() => reloadDatabases(connections));
it("should be able to get entity repository", () => connections.forEach(connection => {
connection.getRepository(Post).should.be.instanceOf(Repository);
connection.getRepository(Post).target.should.be.eql(Post);
}));
it("should be able to get tree entity repository", () => connections.forEach(connection => {
connection.getTreeRepository(Category).should.be.instanceOf(TreeRepository);
connection.getTreeRepository(Category).target.should.be.eql(Category);
}));
});
describe("after connection is closed successfully", function() {
// open a close connections
let connections: Connection[] = [];
before(() => setupTestingConnections({ entities: [Post] }).then(all => {
connections = all;
return Promise.all(connections.map(connection => connection.close()));
}));
it("should not be able to close already closed connection", () => connections.forEach(connection => {
connection.close().should.be.rejectedWith(CannotCloseNotConnectedError);
}));
it("connection.isConnected should be false", () => connections.forEach(connection => {
connection.isConnected.should.be.false;
}));
});
});

View File

@ -0,0 +1,26 @@
import {ClosureTable} from "../../../../src/decorator/tables/ClosureTable";
import {PrimaryColumn} from "../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../src/decorator/columns/Column";
import {TreeParent} from "../../../../src/decorator/tree/TreeParent";
import {TreeChildren} from "../../../../src/decorator/tree/TreeChildren";
import {TreeLevelColumn} from "../../../../src/decorator/tree/TreeLevelColumn";
@ClosureTable("category")
export class Category {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
name: string;
@TreeParent()
parentCategory: Category;
@TreeChildren({ cascadeAll: true })
childCategories: Category[];
@TreeLevelColumn()
level: number;
}

View File

@ -0,0 +1,14 @@
import {Table} from "../../../../src/decorator/tables/Table";
import {PrimaryColumn} from "../../../../src/decorator/columns/PrimaryColumn";
import {Column} from "../../../../src/decorator/columns/Column";
@Table()
export class Post {
@PrimaryColumn("int", { generated: true })
id: number;
@Column()
title: string;
}

View File

@ -2,11 +2,10 @@ import "reflect-metadata";
import * as chai from "chai";
import {expect} from "chai";
import {Connection} from "../../../../src/connection/Connection";
import {Repository} from "../../../../src/repository/Repository";
import {Post} from "./entity/Post";
import {Category} from "./entity/Category";
import {CreateConnectionOptions} from "../../../../src/connection-manager/CreateConnectionOptions";
import {createConnection} from "../../../../src/index";
import {FindOptions} from "../../../../src/repository/FindOptions";
import {closeConnections, reloadDatabases, setupTestingConnections} from "../../../utils/utils";
chai.should();
chai.use(require("sinon-chai"));
@ -15,287 +14,169 @@ chai.use(require("chai-as-promised"));
describe("persistence > one-to-many", function() {
// -------------------------------------------------------------------------
// Configuration
// Setup
// -------------------------------------------------------------------------
const parameters: CreateConnectionOptions = {
driver: "mysql",
connection: {
host: "192.168.99.100",
port: 3306,
username: "root",
password: "admin",
database: "test",
autoSchemaCreate: true
},
entities: [Post, Category]
};
// connect to db
let connection: Connection;
before(function() {
return createConnection(parameters)
.then(con => connection = con)
.catch(e => console.log("Error during connection to db: " + e));
});
after(function() {
connection.close();
});
// clean up database before each test
function reloadDatabase() {
return connection.syncSchema(true)
.catch(e => console.log("Error during schema re-creation: ", e));
}
let postRepository: Repository<Post>;
let categoryRepository: Repository<Category>;
before(function() {
postRepository = connection.getRepository(Post);
categoryRepository = connection.getRepository(Category);
});
let connections: Connection[];
before(() => setupTestingConnections({ entities: [Post, Category] }).then(all => connections = all));
after(() => closeConnections(connections));
beforeEach(() => reloadDatabases(connections));
// -------------------------------------------------------------------------
// Specifications
// -------------------------------------------------------------------------
describe("add exist element to exist object with empty one-to-many relation and save it", function() {
let newPost: Post, newCategory: Category, loadedPost: Post;
before(reloadDatabase);
it("should contain a new category", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
// save a new category
before(function () {
newCategory = categoryRepository.create();
let newCategory = categoryRepository.create();
newCategory.name = "Animals";
return categoryRepository.persist(newCategory);
});
newCategory = await categoryRepository.persist(newCategory);
// save a new post
before(function() {
newPost = postRepository.create();
let newPost = postRepository.create();
newPost.title = "All about animals";
return postRepository.persist(newPost);
});
newPost = await postRepository.persist(newPost);
// add category to post and save it
before(function() {
newPost.categories = [newCategory];
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// load a post and join its category
before(function() {
return postRepository
.findOneById(1, { alias: "post", innerJoinAndSelect: { categories: "post.categories" } })
.then(post => loadedPost = post);
});
it("should contain a new category", function () {
const findOptions: FindOptions = { alias: "post", innerJoinAndSelect: { categories: "post.categories" } };
const loadedPost = await postRepository.findOneById(1, findOptions);
expect(loadedPost).not.to.be.empty;
expect(loadedPost.categories).not.to.be.empty;
if (loadedPost.categories) {
expect(loadedPost.categories[0]).not.to.be.empty;
}
});
})));
});
describe("add exist element to new object with empty one-to-many relation and save it", function() {
let newPost: Post, newCategory: Category, loadedPost: Post;
before(reloadDatabase);
it("should contain a new element", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
// save a new category
before(function () {
newCategory = categoryRepository.create();
let newCategory = categoryRepository.create();
newCategory.name = "Animals";
return categoryRepository.persist(newCategory);
});
newCategory = await categoryRepository.persist(newCategory);
// save a new post
before(function() {
newPost = postRepository.create();
let newPost = postRepository.create();
newPost.title = "All about animals";
newPost.categories = [newCategory];
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// load a post and join its category
before(function() {
return postRepository
.findOneById(1, { alias: "post", innerJoinAndSelect: { categories: "post.categories" } })
.then(post => loadedPost = post);
});
it("should contain a new element", function () {
const findOptions: FindOptions = { alias: "post", innerJoinAndSelect: { categories: "post.categories" } };
const loadedPost = await postRepository.findOneById(1, findOptions);
expect(loadedPost).not.to.be.empty;
expect(loadedPost.categories).not.to.be.empty;
if (loadedPost.categories) {
expect(loadedPost.categories[0]).not.to.be.empty;
}
});
})));
});
describe("remove exist element from one-to-many relation and save it", function() {
let newPost: Post, firstNewCategory: Category, secondNewCategory: Category, loadedPost: Post;
before(reloadDatabase);
it("should have only one category", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
// save a new category
before(function () {
firstNewCategory = categoryRepository.create();
let firstNewCategory = categoryRepository.create();
firstNewCategory.name = "Animals";
return categoryRepository.persist(firstNewCategory);
});
firstNewCategory = await categoryRepository.persist(firstNewCategory);
// save a second category
before(function () {
secondNewCategory = categoryRepository.create();
let secondNewCategory = categoryRepository.create();
secondNewCategory.name = "Insects";
return categoryRepository.persist(secondNewCategory);
});
secondNewCategory = await categoryRepository.persist(secondNewCategory);
// save a new post
before(function() {
newPost = postRepository.create();
let newPost = postRepository.create();
newPost.title = "All about animals";
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// add categories to post and save it
before(function() {
newPost.categories = [firstNewCategory, secondNewCategory];
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// remove one of the categories and save it
before(function() {
newPost.categories = [firstNewCategory];
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// load a post and join its category
before(function() {
return postRepository
.findOneById(1, { alias: "post", innerJoinAndSelect: { categories: "post.categories" } })
.then(post => loadedPost = post);
});
it("should have only one category", function () {
const findOptions: FindOptions = { alias: "post", innerJoinAndSelect: { categories: "post.categories" } };
const loadedPost = await postRepository.findOneById(1, findOptions);
expect(loadedPost).not.to.be.empty;
expect(loadedPost.categories).not.to.be.empty;
if (loadedPost.categories) {
expect(loadedPost.categories[0]).not.to.be.empty;
expect(loadedPost.categories[1]).to.be.empty;
}
});
})));
});
describe("remove all elements from one-to-many relation and save it", function() {
let newPost: Post, firstNewCategory: Category, secondNewCategory: Category, loadedPost: Post;
before(reloadDatabase);
it("should not have categories since they all are removed", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
// save a new category
before(function () {
firstNewCategory = categoryRepository.create();
let firstNewCategory = categoryRepository.create();
firstNewCategory.name = "Animals";
return categoryRepository.persist(firstNewCategory);
});
firstNewCategory = await categoryRepository.persist(firstNewCategory);
// save a second category
before(function () {
secondNewCategory = categoryRepository.create();
let secondNewCategory = categoryRepository.create();
secondNewCategory.name = "Insects";
return categoryRepository.persist(secondNewCategory);
});
secondNewCategory = await categoryRepository.persist(secondNewCategory);
// save a new post
before(function() {
newPost = postRepository.create();
let newPost = postRepository.create();
newPost.title = "All about animals";
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// add categories to post and save it
before(function() {
newPost.categories = [firstNewCategory, secondNewCategory];
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// remove one of the categories and save it
before(function() {
newPost.categories = [];
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// load a post and join its category
before(function() {
return postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { categories: "post.categories" } })
.then(post => loadedPost = post);
});
it("should not have categories since they all are removed", function () {
const findOptions: FindOptions = { alias: "post", leftJoinAndSelect: { categories: "post.categories" } };
const loadedPost = await postRepository.findOneById(1, findOptions);
expect(loadedPost).not.to.be.empty;
expect(loadedPost.categories).to.be.empty;
});
})));
});
describe("set relation to null (elements exist there) from one-to-many relation and save it", function() {
let newPost: Post, firstNewCategory: Category, secondNewCategory: Category, loadedPost: Post;
before(reloadDatabase);
it("should not have categories since they all are removed", () => Promise.all(connections.map(async connection => {
const postRepository = connection.getRepository(Post);
const categoryRepository = connection.getRepository(Category);
// save a new category
before(function () {
firstNewCategory = categoryRepository.create();
let firstNewCategory = categoryRepository.create();
firstNewCategory.name = "Animals";
return categoryRepository.persist(firstNewCategory);
});
firstNewCategory = await categoryRepository.persist(firstNewCategory);
// save a second category
before(function () {
secondNewCategory = categoryRepository.create();
let secondNewCategory = categoryRepository.create();
secondNewCategory.name = "Insects";
return categoryRepository.persist(secondNewCategory);
});
secondNewCategory = await categoryRepository.persist(secondNewCategory);
// save a new post
before(function() {
newPost = postRepository.create();
let newPost = postRepository.create();
newPost.title = "All about animals";
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// add categories to post and save it
before(function() {
newPost.categories = [firstNewCategory, secondNewCategory];
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// remove one of the categories and save it
before(function() {
newPost.categories = null; // todo: what to do with undefined?
return postRepository.persist(newPost);
});
await postRepository.persist(newPost);
// load a post and join its category
before(function() {
return postRepository
.findOneById(1, { alias: "post", leftJoinAndSelect: { categories: "post.categories" } })
.then(post => loadedPost = post);
});
it("should not have categories since they all are removed", function () {
const findOptions: FindOptions = { alias: "post", leftJoinAndSelect: { categories: "post.categories" } };
const loadedPost = await postRepository.findOneById(1, findOptions);
expect(loadedPost).not.to.be.empty;
expect(loadedPost.categories).to.be.empty;
});
})));
});

View File

@ -2,6 +2,98 @@ import {CreateConnectionOptions} from "../../src/connection-manager/CreateConnec
import {createConnection} from "../../src/index";
import {Connection} from "../../src/connection/Connection";
export interface TestingConnectionOptions {
entities?: Function[];
entityDirectories?: string[];
secondaryConnections?: boolean;
}
export function closeConnections(connections: Connection[]) {
return Promise.all(connections.map(connection => connection.close()));
}
export async function setupTestingConnections(options?: TestingConnectionOptions): Promise<Connection[]> {
// const parameters = require(__dirname + "/../../../../config/parameters.json"); // path is relative to compile directory
const parameters = require(__dirname + "/../../config/parameters.json"); // path is relative to compile directory
const mysqlParameters: CreateConnectionOptions = {
driver: "mysql",
connection: {
host: parameters.connections.mysql.host,
port: parameters.connections.mysql.port,
username: parameters.connections.mysql.username,
password: parameters.connections.mysql.password,
database: parameters.connections.mysql.database,
autoSchemaCreate: true,
logging: {
logFailedQueryError: true
}
},
entities: options && options.entities ? options.entities : [],
entityDirectories: options && options.entityDirectories ? options.entityDirectories : [],
};
const mysqlSecondaryParameters: CreateConnectionOptions = {
driver: "mysql",
connection: {
host: parameters.connections.mysqlSecondary.host,
port: parameters.connections.mysqlSecondary.port,
username: parameters.connections.mysqlSecondary.username,
password: parameters.connections.mysqlSecondary.password,
database: parameters.connections.mysqlSecondary.database,
autoSchemaCreate: true,
logging: {
logFailedQueryError: true
}
},
entities: options && options.entities ? options.entities : [],
entityDirectories: options && options.entityDirectories ? options.entityDirectories : [],
};
const postgresParameters: CreateConnectionOptions = {
driver: "postgres",
connection: {
host: parameters.connections.postgres.host,
port: parameters.connections.postgres.port,
username: parameters.connections.postgres.username,
password: parameters.connections.postgres.password,
database: parameters.connections.postgres.database,
autoSchemaCreate: true,
logging: {
logFailedQueryError: true
}
},
entities: options && options.entities ? options.entities : [],
entityDirectories: options && options.entityDirectories ? options.entityDirectories : [],
};
const postgresSecondaryParameters: CreateConnectionOptions = {
driver: "postgres",
connection: {
host: parameters.connections.postgresSecondary.host,
port: parameters.connections.postgresSecondary.port,
username: parameters.connections.postgresSecondary.username,
password: parameters.connections.postgresSecondary.password,
database: parameters.connections.postgresSecondary.database,
autoSchemaCreate: true,
logging: {
logFailedQueryError: true
}
},
entities: options && options.entities ? options.entities : [],
entityDirectories: options && options.entityDirectories ? options.entityDirectories : [],
};
const allParameters: CreateConnectionOptions[] = [mysqlParameters, /*, postgresParameters*/];
if (options && options.secondaryConnections)
allParameters.push(mysqlSecondaryParameters/*, postgresSecondaryParameters*/);
return Promise.all(allParameters.map(parameters => createConnection(parameters)));
}
/**
* @deprecated
*/
export function setupConnection(callback: (connection: Connection) => any, entities: Function[]) {
const parameters: CreateConnectionOptions = {
@ -18,10 +110,8 @@ export function setupConnection(callback: (connection: Connection) => any, entit
};
return function() {
console.log("creating connection");
return createConnection(parameters)
.then(connection => {
console.log("connection");
if (callback)
callback(connection);
return connection;
@ -30,6 +120,12 @@ export function setupConnection(callback: (connection: Connection) => any, entit
};
}
export function reloadDatabases(connections: Connection[]) {
return Promise.all(connections.map(connection => connection.syncSchema(true)));
}
/**
*/
export function reloadDatabase(connection: Connection) {
return function () {
return connection.syncSchema(true)