2024-02-06 16:53:46 -05:00
import { expect } from 'chai' ;
import { describe , it } from 'mocha' ;
import { getCollectionChangeQueries } from '../../dist/core/cli/migration-queries.js' ;
2024-02-08 16:29:16 -05:00
import { field , defineCollection , collectionsSchema } from '../../dist/core/types.js' ;
2024-02-06 16:53:46 -05:00
2024-02-08 16:29:16 -05:00
const BaseUser = defineCollection ( {
2024-02-06 16:53:46 -05:00
fields : {
id : field . number ( { primaryKey : true } ) ,
name : field . text ( ) ,
age : field . number ( ) ,
email : field . text ( { unique : true } ) ,
mi : field . text ( { optional : true } ) ,
} ,
} ) ;
2024-02-08 16:29:16 -05:00
const BaseSentBox = defineCollection ( {
2024-02-06 16:53:46 -05:00
fields : {
2024-02-08 16:29:16 -05:00
to : field . number ( ) ,
2024-02-06 17:27:50 -05:00
toName : field . text ( ) ,
2024-02-06 16:53:46 -05:00
subject : field . text ( ) ,
body : field . text ( ) ,
} ,
} ) ;
const defaultAmbiguityResponses = {
collectionRenames : { } ,
fieldRenames : { } ,
} ;
2024-02-08 16:29:16 -05:00
/ * *
* @ typedef { import ( '../../dist/core/types.js' ) . DBCollection } DBCollection
* @ param { { User : DBCollection , SentBox : DBCollection } } params
* @ returns
* /
function resolveReferences (
{ User = BaseUser , SentBox = BaseSentBox } = {
User : BaseUser ,
SentBox : BaseSentBox ,
}
) {
return collectionsSchema . parse ( { User , SentBox } ) ;
}
2024-02-06 16:53:46 -05:00
function userChangeQueries (
oldCollection ,
newCollection ,
ambiguityResponses = defaultAmbiguityResponses
) {
return getCollectionChangeQueries ( {
collectionName : 'User' ,
oldCollection ,
newCollection ,
ambiguityResponses ,
} ) ;
}
describe ( 'reference queries' , ( ) => {
it ( 'adds references with lossless table recreate' , async ( ) => {
2024-02-08 16:29:16 -05:00
const { SentBox : Initial } = resolveReferences ( ) ;
const { SentBox : Final } = resolveReferences ( {
SentBox : defineCollection ( {
2024-02-06 17:27:50 -05:00
fields : {
2024-02-08 16:29:16 -05:00
... BaseSentBox . fields ,
to : field . number ( { references : ( ) => BaseUser . fields . id } ) ,
2024-02-06 17:27:50 -05:00
} ,
2024-02-08 16:29:16 -05:00
} ) ,
} ) ;
2024-02-06 17:27:50 -05:00
2024-02-08 16:29:16 -05:00
const { queries } = await userChangeQueries ( Initial , Final ) ;
2024-02-06 17:27:50 -05:00
expect ( queries [ 0 ] ) . to . not . be . undefined ;
const tempTableName = getTempTableName ( queries [ 0 ] ) ;
2024-02-08 16:29:16 -05:00
expect ( tempTableName ) . to . not . be . undefined ;
2024-02-06 17:27:50 -05:00
expect ( queries ) . to . deep . equal ( [
2024-02-08 16:29:16 -05:00
` CREATE TABLE \" ${ tempTableName } \" (_id INTEGER PRIMARY KEY, \" to \" integer NOT NULL REFERENCES \" User \" ( \" id \" ), \" toName \" text NOT NULL, \" subject \" text NOT NULL, \" body \" text NOT NULL) ` ,
2024-02-06 17:27:50 -05:00
` INSERT INTO \" ${ tempTableName } \" ( \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" ) SELECT \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" FROM \" User \" ` ,
'DROP TABLE "User"' ,
` ALTER TABLE \" ${ tempTableName } \" RENAME TO \" User \" ` ,
] ) ;
} ) ;
it ( 'removes references with lossless table recreate' , async ( ) => {
2024-02-08 16:29:16 -05:00
const { SentBox : Initial } = resolveReferences ( {
SentBox : defineCollection ( {
2024-02-06 16:53:46 -05:00
fields : {
2024-02-08 16:29:16 -05:00
... BaseSentBox . fields ,
to : field . number ( { references : ( ) => BaseUser . fields . id } ) ,
2024-02-06 16:53:46 -05:00
} ,
2024-02-08 16:29:16 -05:00
} ) ,
} ) ;
const { SentBox : Final } = resolveReferences ( ) ;
2024-02-06 16:53:46 -05:00
2024-02-08 16:29:16 -05:00
const { queries } = await userChangeQueries ( Initial , Final ) ;
2024-02-06 16:53:46 -05:00
expect ( queries [ 0 ] ) . to . not . be . undefined ;
const tempTableName = getTempTableName ( queries [ 0 ] ) ;
2024-02-08 16:29:16 -05:00
expect ( tempTableName ) . to . not . be . undefined ;
2024-02-06 16:53:46 -05:00
expect ( queries ) . to . deep . equal ( [
2024-02-08 16:29:16 -05:00
` CREATE TABLE \" ${ tempTableName } \" (_id INTEGER PRIMARY KEY, \" to \" integer NOT NULL, \" toName \" text NOT NULL, \" subject \" text NOT NULL, \" body \" text NOT NULL) ` ,
2024-02-06 17:27:50 -05:00
` INSERT INTO \" ${ tempTableName } \" ( \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" ) SELECT \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" FROM \" User \" ` ,
2024-02-06 16:53:46 -05:00
'DROP TABLE "User"' ,
` ALTER TABLE \" ${ tempTableName } \" RENAME TO \" User \" ` ,
] ) ;
} ) ;
2024-02-06 17:27:50 -05:00
it ( 'does not use ADD COLUMN when adding optional column with reference' , async ( ) => {
2024-02-08 16:29:16 -05:00
const { SentBox : Initial } = resolveReferences ( ) ;
const { SentBox : Final } = resolveReferences ( {
SentBox : defineCollection ( {
2024-02-06 16:53:46 -05:00
fields : {
2024-02-08 16:29:16 -05:00
... BaseSentBox . fields ,
from : field . number ( { references : ( ) => BaseUser . fields . id , optional : true } ) ,
2024-02-06 16:53:46 -05:00
} ,
2024-02-08 16:29:16 -05:00
} ) ,
} ) ;
2024-02-06 16:53:46 -05:00
2024-02-08 16:29:16 -05:00
const { queries } = await userChangeQueries ( Initial , Final ) ;
2024-02-06 16:53:46 -05:00
expect ( queries [ 0 ] ) . to . not . be . undefined ;
const tempTableName = getTempTableName ( queries [ 0 ] ) ;
expect ( queries ) . to . deep . equal ( [
2024-02-08 16:29:16 -05:00
` CREATE TABLE \" ${ tempTableName } \" (_id INTEGER PRIMARY KEY, \" to \" integer NOT NULL, \" toName \" text NOT NULL, \" subject \" text NOT NULL, \" body \" text NOT NULL, \" from \" integer REFERENCES \" User \" ( \" id \" )) ` ,
2024-02-06 17:27:50 -05:00
` INSERT INTO \" ${ tempTableName } \" ( \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" ) SELECT \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" FROM \" User \" ` ,
2024-02-06 16:53:46 -05:00
'DROP TABLE "User"' ,
` ALTER TABLE \" ${ tempTableName } \" RENAME TO \" User \" ` ,
] ) ;
} ) ;
2024-02-06 17:27:50 -05:00
it ( 'adds and updates foreign key with lossless table recreate' , async ( ) => {
2024-02-08 16:29:16 -05:00
const { SentBox : InitialWithoutFK } = resolveReferences ( ) ;
const { SentBox : InitialWithDifferentFK } = resolveReferences ( {
SentBox : defineCollection ( {
... BaseSentBox ,
foreignKeys : [ { fields : [ 'to' ] , references : ( ) => [ BaseUser . fields . id ] } ] ,
} ) ,
} ) ;
const { SentBox : Final } = resolveReferences ( {
SentBox : defineCollection ( {
... BaseSentBox ,
2024-02-06 17:27:50 -05:00
foreignKeys : [
2024-02-08 16:29:16 -05:00
{
fields : [ 'to' , 'toName' ] ,
references : ( ) => [ BaseUser . fields . id , BaseUser . fields . name ] ,
} ,
2024-02-06 17:27:50 -05:00
] ,
2024-02-08 16:29:16 -05:00
} ) ,
} ) ;
2024-02-06 17:27:50 -05:00
const expected = ( tempTableName ) => [
2024-02-08 16:29:16 -05:00
` CREATE TABLE \" ${ tempTableName } \" (_id INTEGER PRIMARY KEY, \" to \" integer NOT NULL, \" toName \" text NOT NULL, \" subject \" text NOT NULL, \" body \" text NOT NULL, FOREIGN KEY ( \" to \" , \" toName \" ) REFERENCES \" User \" ( \" id \" , \" name \" )) ` ,
2024-02-06 17:27:50 -05:00
` INSERT INTO \" ${ tempTableName } \" ( \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" ) SELECT \" _id \" , \" to \" , \" toName \" , \" subject \" , \" body \" FROM \" User \" ` ,
'DROP TABLE "User"' ,
` ALTER TABLE \" ${ tempTableName } \" RENAME TO \" User \" ` ,
] ;
2024-02-08 16:29:16 -05:00
const addedForeignKey = await userChangeQueries ( InitialWithoutFK , Final ) ;
const updatedForeignKey = await userChangeQueries ( InitialWithDifferentFK , Final ) ;
2024-02-06 17:27:50 -05:00
expect ( addedForeignKey . queries [ 0 ] ) . to . not . be . undefined ;
expect ( updatedForeignKey . queries [ 0 ] ) . to . not . be . undefined ;
expect ( addedForeignKey . queries ) . to . deep . equal (
expected ( getTempTableName ( addedForeignKey . queries [ 0 ] ) )
) ;
expect ( updatedForeignKey . queries ) . to . deep . equal (
expected ( getTempTableName ( updatedForeignKey . queries [ 0 ] ) )
) ;
} ) ;
2024-02-06 16:53:46 -05:00
} ) ;
2024-02-08 16:29:16 -05:00
/** @param {string | undefined} query */
2024-02-06 16:53:46 -05:00
function getTempTableName ( query ) {
return query . match ( /User_([a-z0-9]+)/ ) ? . [ 0 ] ;
}