mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
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:
parent
7252137ae6
commit
39fbab5684
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
build/
|
||||
node_modules/
|
||||
typings/
|
||||
npm-debug.log
|
||||
npm-debug.log
|
||||
config/parameters.json
|
||||
32
config/parameters.json
Normal file
32
config/parameters.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
32
config/parameters.json.dist
Normal file
32
config/parameters.json.dist
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 => {
|
||||
|
||||
12
src/connection/error/CannotSyncNotConnectedError.ts
Normal file
12
src/connection/error/CannotSyncNotConnectedError.ts
Normal 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.`;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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 => {
|
||||
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
94
test/functional/connection/connection.ts
Normal file
94
test/functional/connection/connection.ts
Normal 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;
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
26
test/functional/connection/entity/Category.ts
Normal file
26
test/functional/connection/entity/Category.ts
Normal 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;
|
||||
|
||||
}
|
||||
14
test/functional/connection/entity/Post.ts
Normal file
14
test/functional/connection/entity/Post.ts
Normal 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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
});
|
||||
})));
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user