From 126d9ce18507f8a32a2e480869415dc92dc82758 Mon Sep 17 00:00:00 2001 From: Kononnable Date: Mon, 22 May 2017 22:46:33 +0200 Subject: [PATCH] integration tests column options comparision, added generated column option to creating model from db --- package.json | 2 + src/drivers/MssqlDriver.ts | 6 ++- src/entity.mst | 2 +- src/models/ColumnInfo.ts | 2 + test/drivers/MssqlDriver.test.ts | 6 ++- .../sample1-simple-entity/entity/Post.ts | 2 +- test/integration/integration.test.ts | 11 ++-- test/utils/EntityFileToJson.ts | 53 +++++++++++++++---- 8 files changed, 62 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index aae8988..209a4f2 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "devDependencies": { "@types/chai": "^3.5.2", "@types/chai-as-promised": "0.0.30", + "@types/chai-subset": "^1.3.0", "@types/fs-extra": "^3.0.0", "@types/mocha": "^2.2.41", "@types/mssql": "^3.3.0", @@ -39,6 +40,7 @@ "@types/sinon": "^2.1.3", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", + "chai-subset": "^1.5.0", "codecov": "^2.1.0", "dotenv": "^4.0.0", "fs-extra": "^3.0.1", diff --git a/src/drivers/MssqlDriver.ts b/src/drivers/MssqlDriver.ts index d5f92bd..16220fa 100644 --- a/src/drivers/MssqlDriver.ts +++ b/src/drivers/MssqlDriver.ts @@ -39,9 +39,10 @@ export class MssqlDriver extends AbstractDriver { let request = new MSSQL.Request(this.Connection) let response: { TABLE_NAME: string, COLUMN_NAME: string, COLUMN_DEFAULT: string, IS_NULLABLE: string, DATA_TYPE: string, CHARACTER_MAXIMUM_LENGTH: number, - NUMERIC_PRECISION:number,NUMERIC_SCALE:number }[] + NUMERIC_PRECISION:number,NUMERIC_SCALE:number,IsIdentity:number }[] = await request.query(`SELECT TABLE_NAME,COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE, - DATA_TYPE,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE FROM INFORMATION_SCHEMA.COLUMNS`); + DATA_TYPE,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE, + COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') IsIdentity FROM INFORMATION_SCHEMA.COLUMNS`); entities.forEach((ent) => { response.filter((filterVal) => { return filterVal.TABLE_NAME == ent.EntityName; @@ -49,6 +50,7 @@ export class MssqlDriver extends AbstractDriver { let colInfo: ColumnInfo = new ColumnInfo(); colInfo.name = resp.COLUMN_NAME; colInfo.is_nullable = resp.IS_NULLABLE == 'YES' ? true : false; + colInfo.is_generated = resp.IsIdentity == 1 ? true : false; colInfo.default = resp.COLUMN_DEFAULT; switch (resp.DATA_TYPE) { case "int": diff --git a/src/entity.mst b/src/entity.mst index ac716ef..669bc34 100644 --- a/src/entity.mst +++ b/src/entity.mst @@ -7,7 +7,7 @@ import {Index,Entity, PrimaryColumn, Column, OneToOne, OneToMany, ManyToOne, Joi {{#Columns}} - @Column("{{sql_type}}",{ {{#is_nullable}}nullable:true,{{/is_nullable}}{{#char_max_lenght}}length:{{char_max_lenght}},{{/char_max_lenght}}{{#default}}default:"{{default}}",{{/default}}{{#numericPrecision}}precision:{{numericPrecision}},{{/numericPrecision}}{{#numericScale}}scale:{{numericScale}},{{/numericScale}}{{#isPrimary}}primary:{{isPrimary}},{{/isPrimary}}}){{#relations}} + @Column("{{sql_type}}",{ {{#is_generated}}generated:true,{{/is_generated}}{{#is_nullable}}nullable:true,{{/is_nullable}}{{^is_nullable}}nullable:false,{{/is_nullable}}{{#char_max_lenght}}length:{{char_max_lenght}},{{/char_max_lenght}}{{#default}}default:"{{default}}",{{/default}}{{#numericPrecision}}precision:{{numericPrecision}},{{/numericPrecision}}{{#numericScale}}scale:{{numericScale}},{{/numericScale}}{{#isPrimary}}primary:{{isPrimary}},{{/isPrimary}}}){{#relations}} @{{relationType}}(type=>{{relatedTable}},x=>x.{{relatedColumn}}){{#isOwner}} @JoinTable(){{/isOwner}}{{/relations}} {{name}}:{{ts_type}}; diff --git a/src/models/ColumnInfo.ts b/src/models/ColumnInfo.ts index aa49e79..5f8a952 100644 --- a/src/models/ColumnInfo.ts +++ b/src/models/ColumnInfo.ts @@ -10,10 +10,12 @@ export class ColumnInfo { "float" | "double" | "decimal" | "date" | "time" | "datetime" | "boolean" | "json"; char_max_lenght: number|null=null; isPrimary:boolean=false; + is_generated:boolean=false; numericPrecision:number|null=null; numericScale:number|null=null; relations:RelationInfo[]; + constructor() { this.relations=[]; } diff --git a/test/drivers/MssqlDriver.test.ts b/test/drivers/MssqlDriver.test.ts index 954d4bd..b42f26f 100644 --- a/test/drivers/MssqlDriver.test.ts +++ b/test/drivers/MssqlDriver.test.ts @@ -56,12 +56,13 @@ describe('MssqlDriver', function () { let response = <{ TABLE_NAME: string, COLUMN_NAME: string, COLUMN_DEFAULT: string, IS_NULLABLE: string, DATA_TYPE: string, CHARACTER_MAXIMUM_LENGTH: number, - NUMERIC_PRECISION: number, NUMERIC_SCALE: number + NUMERIC_PRECISION: number, NUMERIC_SCALE: number, IsIdentity:number }[]>[] response.push({ TABLE_NAME: 'name', CHARACTER_MAXIMUM_LENGTH: 0, COLUMN_DEFAULT: 'a', COLUMN_NAME: 'name', DATA_TYPE: 'int', - IS_NULLABLE: 'YES', NUMERIC_PRECISION: 0, NUMERIC_SCALE: 0 + IS_NULLABLE: 'YES', NUMERIC_PRECISION: 0, NUMERIC_SCALE: 0, + IsIdentity:1 }) return response; } @@ -81,6 +82,7 @@ describe('MssqlDriver', function () { default: 'a', is_nullable: true, isPrimary: false, + is_generated:true, name: 'name', numericPrecision: null, numericScale: null, diff --git a/test/integration/examples/sample1-simple-entity/entity/Post.ts b/test/integration/examples/sample1-simple-entity/entity/Post.ts index 87857ac..999192f 100644 --- a/test/integration/examples/sample1-simple-entity/entity/Post.ts +++ b/test/integration/examples/sample1-simple-entity/entity/Post.ts @@ -1,6 +1,6 @@ import {Column, Entity,PrimaryColumn,Index} from "typeorm"; -@Entity() +@Entity("Post") export class Post { @PrimaryColumn("int", { generated: true }) diff --git a/test/integration/integration.test.ts b/test/integration/integration.test.ts index f5666fb..b3031c2 100644 --- a/test/integration/integration.test.ts +++ b/test/integration/integration.test.ts @@ -3,7 +3,6 @@ import "reflect-metadata"; import { createConnection, ConnectionOptions, Connection } from "typeorm"; import fs = require('fs-extra'); import path = require('path') -import { Post } from "./examples/sample1-simple-entity/entity/Post"; import { Engine } from "./../../src/Engine"; import { AbstractDriver } from "./../../src/drivers/AbstractDriver"; import { MssqlDriver } from "./../../src/drivers/MssqlDriver"; @@ -11,7 +10,10 @@ import { DriverType } from "typeorm/driver/DriverOptions"; import { expect } from "chai"; import * as Sinon from 'sinon' import { EntityFileToJson } from "../utils/EntityFileToJson"; +var chai = require('chai'); +var chaiSubset = require('chai-subset'); +chai.use(chaiSubset); describe("integration tests", async function () { let examplesPath = path.resolve(process.cwd(), 'test/integration/examples') @@ -54,7 +56,6 @@ describe("integration tests", async function () { let resultsPath = path.resolve(process.cwd(), `output`) let engine = new Engine( driver, { - //TODO:get data from env host: process.env.MSSQL_Host, port: process.env.MSSQL_Port, databaseName: process.env.MSSQL_Database, @@ -70,8 +71,8 @@ describe("integration tests", async function () { let filesGenPath = path.resolve(resultsPath, 'entities') - let filesOrg = fs.readdirSync(filesOrgPath).map(function (this, val) { return val.toString().toLowerCase(); }).filter(function (this, val, ind, arr) { return val.toString().endsWith('.ts') }) - let filesGen = fs.readdirSync(filesGenPath).map(function (this, val) { return val.toString().toLowerCase(); }).filter(function (this, val, ind, arr) { return val.toString().endsWith('.ts') }) + let filesOrg = fs.readdirSync(filesOrgPath).filter(function (this, val, ind, arr) { return val.toString().endsWith('.ts') }) + let filesGen = fs.readdirSync(filesGenPath).filter(function (this, val, ind, arr) { return val.toString().endsWith('.ts') }) expect(filesOrg).to.be.deep.equal(filesGen) @@ -79,7 +80,7 @@ describe("integration tests", async function () { let entftj = new EntityFileToJson(); let jsonEntityOrg= entftj.convert(fs.readFileSync(path.resolve(filesOrgPath, file))) let jsonEntityGen= entftj.convert(fs.readFileSync(path.resolve(filesGenPath, file))) - expect(jsonEntityGen).to.be.deep.eq(jsonEntityOrg) + expect(jsonEntityGen).to.containSubset(jsonEntityOrg) } }); diff --git a/test/utils/EntityFileToJson.ts b/test/utils/EntityFileToJson.ts index 3256012..4a07685 100644 --- a/test/utils/EntityFileToJson.ts +++ b/test/utils/EntityFileToJson.ts @@ -1,4 +1,28 @@ export class EntityFileToJson { + getColumnOptionsAndType(trimmedLine: string, col: EntityColumn) { + let decoratorParameters = trimmedLine.slice(trimmedLine.indexOf('(') + 1, trimmedLine.lastIndexOf(')')) + + if (decoratorParameters.length > 0) { + if (decoratorParameters.search(',') > 0) { + col.columnType = decoratorParameters.substring(0, decoratorParameters.indexOf(',')).trim() + let badJSON = decoratorParameters.substring(decoratorParameters.indexOf(',') + 1).trim() + if (badJSON.lastIndexOf(',') == badJSON.length - 2) { + badJSON = badJSON.slice(0, badJSON.length - 2) + badJSON[badJSON.length - 1] + } + col.columnOptions = JSON.parse(badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ')) + } else { + if (decoratorParameters[0] == '"' && decoratorParameters.endsWith('"')) { + col.columnType = decoratorParameters + } else { + let badJSON = decoratorParameters.substring(decoratorParameters.indexOf(',') + 1).trim() + if (badJSON.lastIndexOf(',') == badJSON.length - 2) { + badJSON = badJSON.slice(0, badJSON.length - 2) + badJSON[badJSON.length - 1] + } + col.columnOptions = JSON.parse(badJSON.replace(/(['"])?([a-z0-9A-Z_]+)(['"])?:/g, '"$2": ')) + } + } + } + } convert(entityFile: Buffer): EntityJson { let retVal = new EntityJson(); @@ -7,7 +31,7 @@ export class EntityFileToJson { let isMultilineStatement = false; let priorPartOfMultilineStatement = ''; - let lines = entityFile.toString().replace('\r','').split('\n'); + let lines = entityFile.toString().replace('\r', '').split('\n'); for (let line of lines) { let trimmedLine = line.trim(); if (isMultilineStatement) @@ -33,8 +57,9 @@ export class EntityFileToJson { continue; } else { isMultilineStatement = false; - retVal.columns.push(new EntityColumn()) - //TODO:Options, column type if declared + let col = new EntityColumn() + this.getColumnOptionsAndType(trimmedLine, col) + retVal.columns.push(col); continue; } @@ -45,8 +70,10 @@ export class EntityFileToJson { continue; } else { isMultilineStatement = false; - retVal.columns.push(new EntityColumn()) - //TODO:Options, column type if declared + let col = new EntityColumn() + this.getColumnOptionsAndType(trimmedLine, col) + col.columnOptions['primary'] = true + retVal.columns.push(col); continue; } } else if (trimmedLine.startsWith('@PrimaryGeneratedColumn')) { @@ -56,8 +83,11 @@ export class EntityFileToJson { continue; } else { isMultilineStatement = false; - retVal.columns.push(new EntityColumn()) - //TODO:Options, column type if declared + let col = new EntityColumn() + this.getColumnOptionsAndType(trimmedLine, col) + col.columnOptions['primary'] = true + col.columnOptions['generated'] = true + retVal.columns.push(col); continue; } } else if (trimmedLine.startsWith('@ManyToOne')) { @@ -83,11 +113,11 @@ export class EntityFileToJson { continue; } } else if (trimmedLine.split(':').length - 1 > 0) { - retVal.columns[retVal.columns.length-1].columnName=trimmedLine.split(':')[0].trim(); - retVal.columns[retVal.columns.length-1].columnType=trimmedLine.split(':')[1].split(';')[0].trim(); + retVal.columns[retVal.columns.length - 1].columnName = trimmedLine.split(':')[0].trim(); + retVal.columns[retVal.columns.length - 1].columnType = trimmedLine.split(':')[1].split(';')[0].trim(); continue - }else if(trimmedLine='}'){ - isInClassBody=false; + } else if (trimmedLine = '}') { + isInClassBody = false; continue; //class declaration end } } @@ -112,4 +142,5 @@ class EntityJson { class EntityColumn { columnName: string columnType: string + columnOptions: any = {} } \ No newline at end of file