mirror of
https://github.com/withastro/astro.git
synced 2024-12-23 21:53:55 -05:00
3488be9b59
* feat: no more readable / writable * fix: table typegen * wip: move data seeding * chore: add scripts to basics * feat: data() -> seed file * refactor: ensure precedence of file name * feat: db execute command * fix: test imports * chore: remove old readable error tests * feat: support local db with `db execute` * refactor: remove integrations from test for now * chore: stray comment * chore: remove `table` config object * feat: `db.batch`! * refactor: move migrations/ inside db/ * fix: move ticketing-example to seed file * fix: disable foreign keys when recreating tables * refactor: standardize migrations dir * feat: move to db/config.ts * feat: file watching for db/config.ts dependencies * feat: remove unsafeDisableStudio * chroe: remove bad import * feat: parse config.ts from cli * chore: remove async from localDatabaseClient * fix: update recipes config and seed * chore: update unit tests * chore: update tests to dev server * refactor: collectionToTable -> asDrizzleTable * chore: tidy up collection -> table error states * refactor: regexp -> endsWith * feat: pretty error inserting into table * refactor: try/catch -> catch() * feat: expose utils for integration seed files * fix: add config import to db client modules * fix: just use generic "seeding database" error * chore: remove unused link args * fix: migration queries im,port * chore: remove irrelevant glob/ example * feat: format migration file path * feat: support all config file names * chore: remove db.batch() for now * chore: remove `db` object * core: remove unused integration file * chore: changeset * fix: foreign key empty error message * chore: remove old TODO * fix: bad context reference * refactor: seedDev -> seedLocal * wip: throw some console logs at github * wip: avoid seeding astro:db imported by seed file * wip: use anything in db/ * refactor: only seed when loaded within srcDir * refactor: avoid resolution when not seeding * chore: remove logs * refactor: seed within create local db client * refactor: use normalizePath * wip: logs * wip: logs * refactor: early return * chore: more logs * refactor: no batch * fix: use beforeAll * refactor: move all tests to base block * wip: log dev server starting * chore: remove logs * wip: demo ready * chore: remove duplicate recreateTables() call * Revert "wip: demo ready" This reverts commit37585ce5cb
. * refactor: beforeEach to isolate dev servers * chore: remove useBundledDbUrl * refactor: naming and seed scope * chore: remove stray console logs * wip: fix windows file import * wip: try fileURLToPath * Revert "wip: try fileURLToPath" This reverts commit46fd65d61a
. * Revert "wip: fix windows file import" This reverts commit1a669ea646
. * refactor: dir -> directory * refactor: move execute file to cli * refactor: remove seed.dev convention * wip: attempt fileURLToPath * wip: debug the file exists * fix: use mjs?? * chore: remove duplicate seedLocal * chore: remove log check * refactor: use in memory db for tests * chore: clean up test comment * fix: avoid file writes for db setup on in memory db * chore: bump db changeset to minor --------- Co-authored-by: Nate Moore <nate@astro.build>
179 lines
6.1 KiB
JavaScript
179 lines
6.1 KiB
JavaScript
import { expect } from 'chai';
|
|
import { describe, it } from 'mocha';
|
|
import { getCollectionChangeQueries } from '../../dist/core/cli/migration-queries.js';
|
|
import { column, defineTable } from '../../dist/runtime/config.js';
|
|
import { tablesSchema } from '../../dist/core/types.js';
|
|
|
|
const BaseUser = defineTable({
|
|
columns: {
|
|
id: column.number({ primaryKey: true }),
|
|
name: column.text(),
|
|
age: column.number(),
|
|
email: column.text({ unique: true }),
|
|
mi: column.text({ optional: true }),
|
|
},
|
|
});
|
|
|
|
const BaseSentBox = defineTable({
|
|
columns: {
|
|
to: column.number(),
|
|
toName: column.text(),
|
|
subject: column.text(),
|
|
body: column.text(),
|
|
},
|
|
});
|
|
|
|
const defaultAmbiguityResponses = {
|
|
collectionRenames: {},
|
|
columnRenames: {},
|
|
};
|
|
|
|
/**
|
|
* @typedef {import('../../dist/core/types.js').DBTable} DBTable
|
|
* @param {{ User: DBTable, SentBox: DBTable }} params
|
|
* @returns
|
|
*/
|
|
function resolveReferences(
|
|
{ User = BaseUser, SentBox = BaseSentBox } = {
|
|
User: BaseUser,
|
|
SentBox: BaseSentBox,
|
|
}
|
|
) {
|
|
return tablesSchema.parse({ User, SentBox });
|
|
}
|
|
|
|
function userChangeQueries(
|
|
oldCollection,
|
|
newCollection,
|
|
ambiguityResponses = defaultAmbiguityResponses
|
|
) {
|
|
return getCollectionChangeQueries({
|
|
collectionName: 'User',
|
|
oldCollection,
|
|
newCollection,
|
|
ambiguityResponses,
|
|
});
|
|
}
|
|
|
|
describe('reference queries', () => {
|
|
it('adds references with lossless table recreate', async () => {
|
|
const { SentBox: Initial } = resolveReferences();
|
|
const { SentBox: Final } = resolveReferences({
|
|
SentBox: defineTable({
|
|
columns: {
|
|
...BaseSentBox.columns,
|
|
to: column.number({ references: () => BaseUser.columns.id }),
|
|
},
|
|
}),
|
|
});
|
|
|
|
const { queries } = await userChangeQueries(Initial, Final);
|
|
|
|
expect(queries[0]).to.not.be.undefined;
|
|
const tempTableName = getTempTableName(queries[0]);
|
|
expect(tempTableName).to.not.be.undefined;
|
|
|
|
expect(queries).to.deep.equal([
|
|
`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)`,
|
|
`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 () => {
|
|
const { SentBox: Initial } = resolveReferences({
|
|
SentBox: defineTable({
|
|
columns: {
|
|
...BaseSentBox.columns,
|
|
to: column.number({ references: () => BaseUser.columns.id }),
|
|
},
|
|
}),
|
|
});
|
|
const { SentBox: Final } = resolveReferences();
|
|
|
|
const { queries } = await userChangeQueries(Initial, Final);
|
|
|
|
expect(queries[0]).to.not.be.undefined;
|
|
const tempTableName = getTempTableName(queries[0]);
|
|
expect(tempTableName).to.not.be.undefined;
|
|
|
|
expect(queries).to.deep.equal([
|
|
`CREATE TABLE \"${tempTableName}\" (_id INTEGER PRIMARY KEY, \"to\" integer NOT NULL, \"toName\" text NOT NULL, \"subject\" text NOT NULL, \"body\" text NOT NULL)`,
|
|
`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('does not use ADD COLUMN when adding optional column with reference', async () => {
|
|
const { SentBox: Initial } = resolveReferences();
|
|
const { SentBox: Final } = resolveReferences({
|
|
SentBox: defineTable({
|
|
columns: {
|
|
...BaseSentBox.columns,
|
|
from: column.number({ references: () => BaseUser.columns.id, optional: true }),
|
|
},
|
|
}),
|
|
});
|
|
|
|
const { queries } = await userChangeQueries(Initial, Final);
|
|
expect(queries[0]).to.not.be.undefined;
|
|
const tempTableName = getTempTableName(queries[0]);
|
|
|
|
expect(queries).to.deep.equal([
|
|
`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\"))`,
|
|
`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('adds and updates foreign key with lossless table recreate', async () => {
|
|
const { SentBox: InitialWithoutFK } = resolveReferences();
|
|
const { SentBox: InitialWithDifferentFK } = resolveReferences({
|
|
SentBox: defineTable({
|
|
...BaseSentBox,
|
|
foreignKeys: [{ columns: ['to'], references: () => [BaseUser.columns.id] }],
|
|
}),
|
|
});
|
|
const { SentBox: Final } = resolveReferences({
|
|
SentBox: defineTable({
|
|
...BaseSentBox,
|
|
foreignKeys: [
|
|
{
|
|
columns: ['to', 'toName'],
|
|
references: () => [BaseUser.columns.id, BaseUser.columns.name],
|
|
},
|
|
],
|
|
}),
|
|
});
|
|
|
|
const expected = (tempTableName) => [
|
|
`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\"))`,
|
|
`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\"`,
|
|
];
|
|
|
|
const addedForeignKey = await userChangeQueries(InitialWithoutFK, Final);
|
|
const updatedForeignKey = await userChangeQueries(InitialWithDifferentFK, Final);
|
|
|
|
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]))
|
|
);
|
|
});
|
|
});
|
|
|
|
/** @param {string | undefined} query */
|
|
function getTempTableName(query) {
|
|
// eslint-disable-next-line regexp/no-unused-capturing-group
|
|
return query.match(/User_([a-z\d]+)/)?.[0];
|
|
}
|