import { uriToCredentials } from "@prisma/sdk" ; import Inspector from "../inspector" ; import testaway from "testaway" ; import * as p1 from "../prisma1" ; import * as p2 from "../prisma2" ; import * as sql from "../sql" ; import mariadb from "mariadb" ; import yaml from "js-yaml" ; import * as api from "./" ; import chalk from "chalk" ; import execa from "execa" ; import assert from "assert" ; import path from "path" ; import util from "util" ; import url from "url" ; import fs from "fs" ; import pg from "pg" ; import os from "os" ; const tmpdir = path . join ( os . tmpdir ( ) , "prisma-upgrade" ) ; const readFile = util . promisify ( fs . readFile ) ; const userInfo = os . userInfo ( ) ; it ( "import-able" , async function ( ) { this . timeout ( "60s" ) ; await testaway ( tmpdir , path . join ( __dirname , ".." , ".." ) ) ; const result = await execa ( path . join ( tmpdir , "node_modules" , ".bin" , "prisma-upgrade" ) , [ "-h" ] ) ; if ( ! ~ result . stdout . indexOf ( "prisma-upgrade" ) ) { throw new Error ( "module doesn't load" ) ; } } ) ; const dir = path . join ( __dirname , ".." , ".." , "examples" ) ; const allTests = fs . readdirSync ( dir ) ; describe ( "mysql" , function ( ) { this . timeout ( "5s" ) ; const tests = allTests . filter ( ( test ) => test . startsWith ( "mysql" ) ) ; tests . forEach ( ( name ) => { describe ( name , function ( ) { let db ; let engine ; after ( async ( ) => { db && ( await db . end ( ) ) ; engine . close ( ) ; } ) ; it ( "test" , async ( ) => { const abspath = path . join ( dir , name ) ; const expected = await readFile ( path . join ( abspath , "expected.prisma" ) , "utf8" ) ; const expectedSQL = await readFile ( path . join ( abspath , "expected.sql" ) , "utf8" ) ; const prismaYaml = path . join ( abspath , "prisma.yml" ) ; const yml = yaml . safeLoad ( await readFile ( prismaYaml , "utf8" ) ) ; const datamodel = await concatDatamodels ( path . dirname ( prismaYaml ) , yml ) ; const p2schema = new p2 . Schema ( await readFile ( path . join ( abspath , "schema.prisma" ) , "utf8" ) ) ; const p1schema = p1 . parse ( datamodel ) ; p2schema . setURL ( "mysql://root@localhost:3306/prisma_test" ) ; const url = p2schema . url ( ) ; if ( ! url ) { throw new Error ( "Expected url to not be void" ) ; } const credentials = uriToCredentials ( url ) ; db = await mariadb . createConnection ( { host : credentials . host , port : credentials . port , user : credentials . user , password : credentials . password , multipleStatements : true , } ) ; engine = new Inspector ( ) ; try { await db . query ( `drop database prisma_test;` ) ; } catch ( e ) { } await db . query ( `create database prisma_test;` ) ; await db . query ( `use prisma_test;` ) ; await db . query ( await readFile ( path . join ( abspath , "dump.sql" ) , "utf8" ) ) ; await new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( ) , 100 ) ) ; await test ( abspath , db , engine , expected , expectedSQL , url , p1schema , p2schema ) ; } ) ; } ) ; } ) ; } ) ; describe ( "postgres" , ( ) => { const tests = allTests . filter ( ( test ) => test . startsWith ( "postgres" ) ) ; tests . forEach ( ( name ) => { describe ( name , ( ) => { let db ; let engine ; after ( async ( ) => { db && ( await db . end ( ) ) ; engine && engine . close ( ) ; } ) ; it ( "test" , async ( ) => { const abspath = path . join ( dir , name ) ; const expected = await readFile ( path . join ( abspath , "expected.prisma" ) , "utf8" ) ; const expectedSQL = await readFile ( path . join ( abspath , "expected.sql" ) , "utf8" ) ; const prismaYaml = path . join ( abspath , "prisma.yml" ) ; const yml = yaml . safeLoad ( await readFile ( prismaYaml , "utf8" ) ) ; const datamodel = await concatDatamodels ( path . dirname ( prismaYaml ) , yml ) ; const pathname = url . parse ( yml . endpoint ) . pathname || "" ; const pgschema = pathname . slice ( 1 ) ? pathname . slice ( 1 ) . replace ( /\//g , "$" ) : "default$default" ; const p2schema = new p2 . Schema ( await readFile ( path . join ( abspath , "schema.prisma" ) , "utf8" ) ) ; const p1schema = p1 . parse ( datamodel ) ; const pgo = url . parse ( "postgres://localhost:5432/prisma_test" , true ) ; pgo . auth = userInfo . username ; if ( pgschema ) { pgo . query [ "schema" ] = pgschema ; } p2schema . setURL ( url . format ( pgo ) ) ; const credentials = uriToCredentials ( url . format ( pgo ) ) ; const initialDB = new pg . Client ( { host : credentials . host , port : credentials . port , database : "postgres" , password : credentials . password , } ) ; await initialDB . connect ( ) ; try { await initialDB . query ( `drop database prisma_test;` ) ; } catch ( e ) { } await initialDB . query ( `create database prisma_test;` ) ; await initialDB . query ( `drop role if exists root;` ) ; await initialDB . query ( `create role root;` ) ; await initialDB . end ( ) ; db = new pg . Client ( { host : credentials . host , port : credentials . port , database : "prisma_test" , password : credentials . password , } ) ; await db . connect ( ) ; engine = new Inspector ( ) ; await db . query ( await readFile ( path . join ( abspath , "dump.sql" ) , "utf8" ) ) ; await test ( abspath , db , engine , expected , expectedSQL , pgschema , p1schema , p2schema ) ; } ) ; } ) ; } ) ; } ) ; interface DB { query ( query ) : Promise < any > ; } async function test ( _abspath , db , engine , expected , expectedSQL , url , p1schema , p2schema ) { var { ops , breakingOps , idOps , schema } = await api . upgrade ( { url : url , prisma1 : p1schema , prisma2 : p2schema , } ) ; const provider = schema . provider ( ) ; const breakingQueries = sql . translate ( provider , breakingOps ) ; for ( let query of breakingQueries ) { try { await db . query ( query ) ; } catch ( e ) { console . log ( query ) ; throw e ; } } const queries = sql . translate ( schema . provider ( ) , ops ) ; for ( let query of queries ) { try { await db . query ( query ) ; } catch ( e ) { console . log ( query ) ; throw e ; } } const idQueries = sql . translate ( provider , idOps ) ; for ( let query of idQueries ) { try { await db . query ( query ) ; } catch ( e ) { console . log ( query ) ; throw e ; } } const datamodel = await engine . inspect ( schema . toString ( ) ) ; var p2schema = new p2 . Schema ( datamodel ) ; var { ops , breakingOps , schema } = await api . upgrade ( { url : url , prisma1 : p1schema , prisma2 : p2schema , } ) ; if ( ops . length ) { console . log ( schema . toString ( ) ) ; console . log ( ops ) ; assert . equal ( 0 , ops . length , "expected 0 ops the 2nd time around" ) ; } const remainingOps = [ ] ; for ( let op of breakingOps ) { if ( op . type === "MigrateRequiredHasManyOp" && provider === "mysql" ) { continue ; } remainingOps . push ( op ) ; } if ( remainingOps . length ) { console . log ( schema . toString ( ) ) ; console . log ( remainingOps ) ; assert . equal ( 0 , remainingOps . length , "expected 0 remainingOps the 2nd time around" ) ; } const blockSQL = queries . concat ( breakingQueries ) . join ( "\n" ) ; if ( blockSQL !== expectedSQL ) { console . log ( "" ) ; console . log ( "Actual:" ) ; console . log ( "" ) ; console . log ( chalk . dim ( blockSQL ) ) ; console . log ( "" ) ; console . log ( "Expected:" ) ; console . log ( "" ) ; console . log ( chalk . dim ( expectedSQL ) ) ; console . log ( "" ) ; console . log ( assert . equal ( blockSQL , expectedSQL ) ) ; } const actual = schema . toTestString ( ) ; if ( expected . trim ( ) !== actual ) { console . log ( "" ) ; console . log ( "Actual:" ) ; console . log ( "" ) ; console . log ( chalk . dim ( actual ) ) ; console . log ( "" ) ; console . log ( "Expected:" ) ; console . log ( "" ) ; console . log ( chalk . dim ( expected ) ) ; console . log ( "" ) ; console . log ( assert . equal ( actual , expected ) ) ; } } async function concatDatamodels ( wd , yml ) { if ( ! ( "datamodel" in yml ) ) { return "" ; } const datamodels = [ ] . concat ( yml . datamodel ) ; const models = [ ] ; for ( let dm of datamodels ) { models . push ( await readFile ( path . join ( wd , dm ) , "utf8" ) ) ; } return models . join ( "\n\n" ) ; }