@elysiumjs/mnemosyne-drizzle 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.omc/state/agent-replay-3920b579-6d8e-4f5f-924a-319202936685.jsonl +7 -0
- package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/mission-state.json +93 -0
- package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/3920b579-6d8e-4f5f-924a-319202936685/subagent-tracking-state.json +61 -0
- package/.turbo/turbo-build.log +3 -0
- package/CHANGELOG.md +40 -0
- package/README.md +51 -0
- package/dist/commands/migration_generate.command.d.ts +11 -0
- package/dist/commands/migration_generate.command.js +1 -0
- package/dist/commands/migration_run.command.d.ts +13 -0
- package/dist/commands/migration_run.command.js +15 -0
- package/dist/database.d.ts +79 -0
- package/dist/database.js +1 -0
- package/dist/drizzle.module.d.ts +40 -0
- package/dist/drizzle.module.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -0
- package/dist/migrations.d.ts +33 -0
- package/dist/migrations.js +1 -0
- package/dist/model.d.ts +61 -0
- package/dist/model.js +1 -0
- package/dist/repository.d.ts +35 -0
- package/dist/repository.js +1 -0
- package/dist/table.d.ts +69 -0
- package/dist/table.js +1 -0
- package/dist/tenancy.d.ts +88 -0
- package/dist/tenancy.js +1 -0
- package/package.json +11 -9
- package/scripts/minify.ts +10 -0
- package/src/commands/migration_generate.command.ts +101 -0
- package/src/commands/migration_run.command.ts +160 -0
- package/src/database.ts +37 -7
- package/src/drizzle.module.ts +97 -0
- package/src/index.ts +8 -30
- package/src/migrations.ts +100 -0
- package/src/model.ts +18 -11
- package/src/table.ts +4 -4
- package/src/tenancy.ts +5 -5
- package/tests/drizzle.module.test.ts +201 -0
- package/tests/migrations.test.ts +123 -0
- package/tests/setup.ts +0 -10
- package/tsconfig.json +2 -2
- package/bunfig.toml +0 -11
- package/tests/database.test.ts +0 -198
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it } from 'bun:test';
|
|
16
|
+
|
|
17
|
+
import { getMigrationName, planMigrations } from '../src/migrations';
|
|
18
|
+
|
|
19
|
+
const dir = '/app/src/database/migrations';
|
|
20
|
+
|
|
21
|
+
// Records applied after every migration in these tests was generated.
|
|
22
|
+
const applied = (...names: string[]) =>
|
|
23
|
+
names.map((name) => ({ name, appliedAt: new Date('2027-01-01T00:00:00Z') }));
|
|
24
|
+
|
|
25
|
+
describe('getMigrationName', () => {
|
|
26
|
+
it('should use the file name of a flat migration', () => {
|
|
27
|
+
expect(getMigrationName(`${dir}/0000_users.sql`)).toBe('0000_users.sql');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should use the folder name of a Drizzle Kit 1.0 migration', () => {
|
|
31
|
+
expect(getMigrationName(`${dir}/20260101120000_users/migration.sql`)).toBe(
|
|
32
|
+
'20260101120000_users'
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('planMigrations', () => {
|
|
38
|
+
it('should run every migration on an empty database, in order', () => {
|
|
39
|
+
const files = [
|
|
40
|
+
`${dir}/20260102000000_posts/migration.sql`,
|
|
41
|
+
`${dir}/20260101000000_users/migration.sql`
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
expect(planMigrations(files, applied())).toEqual({
|
|
45
|
+
pending: [
|
|
46
|
+
`${dir}/20260101000000_users/migration.sql`,
|
|
47
|
+
`${dir}/20260102000000_posts/migration.sql`
|
|
48
|
+
],
|
|
49
|
+
renames: []
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should skip migrations already applied under their current name', () => {
|
|
54
|
+
const files = [`${dir}/0000_users.sql`, `${dir}/20260101000000_posts/migration.sql`];
|
|
55
|
+
|
|
56
|
+
expect(planMigrations(files, applied('0000_users.sql', '20260101000000_posts'))).toEqual({
|
|
57
|
+
pending: [],
|
|
58
|
+
renames: []
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should recognise flat migrations that drizzle-kit up moved into folders', () => {
|
|
63
|
+
const files = [
|
|
64
|
+
`${dir}/20250301101500_users/migration.sql`,
|
|
65
|
+
`${dir}/20250302090000_posts/migration.sql`,
|
|
66
|
+
`${dir}/20260101000000_comments/migration.sql`
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
expect(planMigrations(files, applied('0000_users.sql', '0001_posts.sql'))).toEqual({
|
|
70
|
+
pending: [`${dir}/20260101000000_comments/migration.sql`],
|
|
71
|
+
renames: [
|
|
72
|
+
{ from: '0000_users.sql', to: '20250301101500_users' },
|
|
73
|
+
{ from: '0001_posts.sql', to: '20250302090000_posts' }
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should match timestamp-prefixed flat migrations whose prefix differs from the folder', () => {
|
|
79
|
+
const files = [`${dir}/20250301101501_add_users_table/migration.sql`];
|
|
80
|
+
|
|
81
|
+
expect(planMigrations(files, applied('20250301101459_add_users_table.sql'))).toEqual({
|
|
82
|
+
pending: [],
|
|
83
|
+
renames: [
|
|
84
|
+
{ from: '20250301101459_add_users_table.sql', to: '20250301101501_add_users_table' }
|
|
85
|
+
]
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should pair repeated names in order', () => {
|
|
90
|
+
const files = [
|
|
91
|
+
`${dir}/20250301000000_update/migration.sql`,
|
|
92
|
+
`${dir}/20250302000000_update/migration.sql`,
|
|
93
|
+
`${dir}/20250303000000_update/migration.sql`
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
expect(planMigrations(files, applied('0000_update.sql', '0001_update.sql'))).toEqual({
|
|
97
|
+
pending: [`${dir}/20250303000000_update/migration.sql`],
|
|
98
|
+
renames: [
|
|
99
|
+
{ from: '0000_update.sql', to: '20250301000000_update' },
|
|
100
|
+
{ from: '0001_update.sql', to: '20250302000000_update' }
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should not move a record whose flat file is still there', () => {
|
|
106
|
+
const files = [`${dir}/0000_users.sql`, `${dir}/20260101000000_users/migration.sql`];
|
|
107
|
+
|
|
108
|
+
expect(planMigrations(files, applied('0000_users.sql'))).toEqual({
|
|
109
|
+
pending: [`${dir}/20260101000000_users/migration.sql`],
|
|
110
|
+
renames: []
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should run a new migration that reuses the name of a deleted one', () => {
|
|
115
|
+
const files = [`${dir}/20260101000000_update/migration.sql`];
|
|
116
|
+
const records = [{ name: '0003_update.sql', appliedAt: new Date('2025-06-01T00:00:00Z') }];
|
|
117
|
+
|
|
118
|
+
expect(planMigrations(files, records)).toEqual({
|
|
119
|
+
pending: [`${dir}/20260101000000_update/migration.sql`],
|
|
120
|
+
renames: []
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
});
|
package/tests/setup.ts
CHANGED
package/tsconfig.json
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"outDir": "dist",
|
|
5
5
|
"declaration": true,
|
|
6
|
-
"emitDeclarationOnly": true,
|
|
7
6
|
"declarationMap": false,
|
|
8
7
|
"declarationDir": "dist",
|
|
9
|
-
"rootDir": "src"
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"allowImportingTsExtensions": false
|
|
10
10
|
},
|
|
11
11
|
"include": ["src"]
|
|
12
12
|
}
|
package/bunfig.toml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
[test]
|
|
2
|
-
root = "./tests"
|
|
3
|
-
preload = ["./tests/setup.ts"]
|
|
4
|
-
coverage = true
|
|
5
|
-
coverageThreshold = { line = 0.9, function = 0.7, statement = 0.8 }
|
|
6
|
-
coverageSkipTestFiles = true
|
|
7
|
-
coverageReporter = ["lcov", "text"]
|
|
8
|
-
coverageDir = "./coverage"
|
|
9
|
-
|
|
10
|
-
[run]
|
|
11
|
-
bun = true
|
package/tests/database.test.ts
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, jest, mock, spyOn } from 'bun:test';
|
|
16
|
-
|
|
17
|
-
import { Application, Service } from '@elysiumjs/core';
|
|
18
|
-
|
|
19
|
-
import { Database } from '../src/database';
|
|
20
|
-
|
|
21
|
-
// Mock drizzle function
|
|
22
|
-
mock.module('drizzle-orm/bun-sql', () => ({
|
|
23
|
-
drizzle: mock(() => ({ mockDrizzleInstance: true }))
|
|
24
|
-
}));
|
|
25
|
-
|
|
26
|
-
describe('DrizzleDatabase', () => {
|
|
27
|
-
// Mock console.error and process.exit to prevent actual console output and process exit
|
|
28
|
-
const originalConsoleError = console.error;
|
|
29
|
-
const originalProcessExit = process.exit;
|
|
30
|
-
|
|
31
|
-
beforeAll(() => {
|
|
32
|
-
// Mock Application.context so that getConnection('default') works
|
|
33
|
-
const mockContext = {
|
|
34
|
-
getStore: mock(() => new Map())
|
|
35
|
-
};
|
|
36
|
-
Object.defineProperty(Application, 'context', {
|
|
37
|
-
get: () => mockContext,
|
|
38
|
-
configurable: true
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
console.error = mock();
|
|
44
|
-
process.exit = mock() as any;
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
afterEach(() => {
|
|
48
|
-
console.error = originalConsoleError;
|
|
49
|
-
process.exit = originalProcessExit;
|
|
50
|
-
jest.clearAllMocks();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
afterAll(() => {
|
|
54
|
-
mock.restore();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('registerConnection', () => {
|
|
58
|
-
it('should register a new connection', () => {
|
|
59
|
-
// Mock Service.exists to return false (connection doesn't exist)
|
|
60
|
-
const existsSpy = spyOn(Service, 'exists').mockReturnValueOnce(false);
|
|
61
|
-
const instanceSpy = spyOn(Service, 'instance');
|
|
62
|
-
|
|
63
|
-
// Call the function
|
|
64
|
-
const result = Database.registerConnection('test', { connection: 'sqlite:test.db' });
|
|
65
|
-
|
|
66
|
-
// Check if Service.exists was called with the correct connection name
|
|
67
|
-
expect(existsSpy).toHaveBeenCalledWith('db.connection.test');
|
|
68
|
-
|
|
69
|
-
// Check if Service.instance was called with the correct parameters
|
|
70
|
-
expect(instanceSpy).toHaveBeenCalledWith('db.connection.test', {
|
|
71
|
-
mockDrizzleInstance: true
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// Check if the result is correct
|
|
75
|
-
expect(result as any).toEqual({ mockDrizzleInstance: true });
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should exit if the connection already exists', () => {
|
|
79
|
-
// Mock Service.exists to return true (connection exists)
|
|
80
|
-
spyOn(Service, 'exists').mockReturnValueOnce(true);
|
|
81
|
-
|
|
82
|
-
// Call the function
|
|
83
|
-
Database.registerConnection('test', { connection: 'sqlite:test.db' });
|
|
84
|
-
|
|
85
|
-
// Check if console.error and process.exit were called
|
|
86
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
87
|
-
expect.stringContaining('A connection with the name test has already been registered')
|
|
88
|
-
);
|
|
89
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('getConnection', () => {
|
|
94
|
-
it('should retrieve an existing connection', () => {
|
|
95
|
-
// Mock Service.exists to return true (connection exists)
|
|
96
|
-
const existsSpy = spyOn(Service, 'exists').mockReturnValueOnce(true);
|
|
97
|
-
|
|
98
|
-
// Mock Service.get to return a mock connection
|
|
99
|
-
const mockConnection = { mockConnection: true };
|
|
100
|
-
const getSpy = spyOn(Service, 'get').mockReturnValue(mockConnection);
|
|
101
|
-
|
|
102
|
-
// Call the function
|
|
103
|
-
const result = Database.getConnection('test');
|
|
104
|
-
|
|
105
|
-
// Check if Service.exists was called with the correct connection name
|
|
106
|
-
expect(existsSpy).toHaveBeenCalledWith('db.connection.test');
|
|
107
|
-
|
|
108
|
-
// Check if Service.get was called with the correct connection name
|
|
109
|
-
expect(getSpy).toHaveBeenCalledWith('db.connection.test');
|
|
110
|
-
|
|
111
|
-
// Check if the result is correct
|
|
112
|
-
expect(result as any).toEqual(mockConnection);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should exit if the connection does not exist', () => {
|
|
116
|
-
// Mock Service.exists to return false (connection doesn't exist)
|
|
117
|
-
spyOn(Service, 'exists').mockReturnValueOnce(false);
|
|
118
|
-
|
|
119
|
-
// Call the function
|
|
120
|
-
Database.getConnection('test');
|
|
121
|
-
|
|
122
|
-
// Check if console.error and process.exit were called
|
|
123
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
124
|
-
expect.stringContaining('No connection with name test found')
|
|
125
|
-
);
|
|
126
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe('connectionExists', () => {
|
|
131
|
-
it('should return true if the connection exists', () => {
|
|
132
|
-
// Mock Service.exists to return true
|
|
133
|
-
const existsSpy = spyOn(Service, 'exists').mockReturnValueOnce(true);
|
|
134
|
-
|
|
135
|
-
// Call the function
|
|
136
|
-
const result = Database.connectionExists('test');
|
|
137
|
-
|
|
138
|
-
// Check if Service.exists was called with the correct connection name
|
|
139
|
-
expect(existsSpy).toHaveBeenCalledWith('db.connection.test');
|
|
140
|
-
|
|
141
|
-
// Check if the result is correct
|
|
142
|
-
expect(result).toBe(true);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('should return false if the connection does not exist', () => {
|
|
146
|
-
// Mock Service.exists to return false
|
|
147
|
-
const existsSpy = spyOn(Service, 'exists').mockReturnValueOnce(false);
|
|
148
|
-
|
|
149
|
-
// Call the function
|
|
150
|
-
const result = Database.connectionExists('test');
|
|
151
|
-
|
|
152
|
-
// Check if Service.exists was called with the correct connection name
|
|
153
|
-
expect(existsSpy).toHaveBeenCalledWith('db.connection.test');
|
|
154
|
-
|
|
155
|
-
// Check if the result is correct
|
|
156
|
-
expect(result).toBe(false);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('getDefaultConnection', () => {
|
|
161
|
-
it('should retrieve the default connection', () => {
|
|
162
|
-
// Mock Service to return the mock connection
|
|
163
|
-
const mockConnection = { mockConnection: true };
|
|
164
|
-
spyOn(Service, 'exists').mockReturnValueOnce(true);
|
|
165
|
-
spyOn(Service, 'get').mockReturnValue(mockConnection);
|
|
166
|
-
|
|
167
|
-
// Call the function
|
|
168
|
-
const result = Database.getDefaultConnection();
|
|
169
|
-
|
|
170
|
-
// Check if the result is correct
|
|
171
|
-
expect(result as any).toEqual(mockConnection);
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe('setDefaultConnection', () => {
|
|
176
|
-
it('should set the default connection', () => {
|
|
177
|
-
const instanceSpy = spyOn(Service, 'instance');
|
|
178
|
-
const removeSpy = spyOn(Service, 'remove');
|
|
179
|
-
spyOn(Service, 'exists').mockReturnValueOnce(true);
|
|
180
|
-
|
|
181
|
-
// Mock Service.get to return a mock connection
|
|
182
|
-
const mockConnection = { mockConnection: true };
|
|
183
|
-
spyOn(Service, 'get').mockReturnValue(mockConnection);
|
|
184
|
-
|
|
185
|
-
// Call the function
|
|
186
|
-
const result = Database.setDefaultConnection('test');
|
|
187
|
-
|
|
188
|
-
// Check if Service.remove was called with the correct connection name
|
|
189
|
-
expect(removeSpy).toHaveBeenCalledWith('db.connection.default');
|
|
190
|
-
|
|
191
|
-
// Check if Service.instance was called with the correct parameters
|
|
192
|
-
expect(instanceSpy).toHaveBeenCalledWith('db.connection.default', mockConnection);
|
|
193
|
-
|
|
194
|
-
// Check if the result is correct
|
|
195
|
-
expect(result as any).toEqual(mockConnection);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
});
|