Skip to content

Commit 935da53

Browse files
committed
增加 ObjectQL 类的 close 方法,允许在测试中直接传递 app 实例以便于资源管理和连接关闭
1 parent 3200c80 commit 935da53

4 files changed

Lines changed: 54 additions & 35 deletions

File tree

packages/foundation/core/src/app.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,15 @@ export class ObjectQL implements IObjectQL {
197197
return objects;
198198
}
199199

200+
async close() {
201+
for (const [name, driver] of Object.entries(this.datasources)) {
202+
if (driver.disconnect) {
203+
console.log(`Closing driver '${name}'...`);
204+
await driver.disconnect();
205+
}
206+
}
207+
}
208+
200209
async init() {
201210
// 0. Init Plugins (This allows plugins to register custom loaders)
202211
for (const plugin of this.pluginsList) {

packages/foundation/types/src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface IObjectQL {
1010
getConfigs(): Record<string, ObjectConfig>;
1111
datasource(name: string): Driver;
1212
init(): Promise<void>;
13+
close?(): Promise<void>;
1314
removePackage(name: string): void;
1415
metadata: MetadataRegistry;
1516

packages/tools/cli/__tests__/commands.test.ts

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,15 @@ describe('CLI Commands', () => {
162162

163163
beforeEach(async () => {
164164
// Create a test SQLite database with sample schema
165+
const dbPath = path.join(testDir, `test_${Date.now()}.db`);
165166
const driver = new SqlDriver({
166167
client: 'sqlite3',
167-
connection: { filename: path.join(testDir, 'test.db') },
168-
useNullAsDefault: true
168+
connection: { filename: dbPath },
169+
useNullAsDefault: true,
170+
pool: {
171+
min: 1,
172+
max: 1 // Single connection for test
173+
}
169174
});
170175

171176
app = new ObjectQL({
@@ -195,42 +200,21 @@ describe('CLI Commands', () => {
195200
});
196201

197202
await app.init();
198-
199-
// Create a config file for the sync command to use
200-
configPath = path.join(testDir, 'objectql.config.js');
201-
const configContent = `
202-
const { ObjectQL } = require('@objectql/core');
203-
const { SqlDriver } = require('@objectql/driver-sql');
204-
205-
const driver = new SqlDriver({
206-
client: 'sqlite3',
207-
connection: { filename: '${path.join(testDir, 'test.db').replace(/\\/g, '\\\\')}' },
208-
useNullAsDefault: true
209-
});
210-
211-
const app = new ObjectQL({
212-
datasources: { default: driver }
213-
});
214-
215-
module.exports = { default: app };
216-
`;
217-
fs.writeFileSync(configPath, configContent, 'utf-8');
218203
});
219204

220205
afterEach(async () => {
221-
if (app && app.datasources && app.datasources.default) {
222-
const driver = app.datasources.default as any;
223-
if (driver.disconnect) {
224-
await driver.disconnect();
225-
}
206+
try {
207+
if (app) await app.close();
208+
} catch (e) {
209+
// Ignore if already closed
226210
}
227211
});
228212

229213
it('should introspect database and generate .object.yml files', async () => {
230214
const outputDir = path.join(testDir, 'objects');
231215

232216
await syncDatabase({
233-
config: configPath,
217+
app: app,
234218
output: outputDir
235219
});
236220

@@ -269,7 +253,7 @@ describe('CLI Commands', () => {
269253
const outputDir = path.join(testDir, 'objects_selective');
270254

271255
await syncDatabase({
272-
config: configPath,
256+
app: app,
273257
output: outputDir,
274258
tables: ['users']
275259
});
@@ -284,7 +268,7 @@ describe('CLI Commands', () => {
284268

285269
// First sync
286270
await syncDatabase({
287-
config: configPath,
271+
app: app,
288272
output: outputDir
289273
});
290274

@@ -295,7 +279,7 @@ describe('CLI Commands', () => {
295279

296280
// Second sync without force - should skip
297281
await syncDatabase({
298-
config: configPath,
282+
app: app,
299283
output: outputDir
300284
});
301285

@@ -308,7 +292,7 @@ describe('CLI Commands', () => {
308292

309293
// First sync
310294
await syncDatabase({
311-
config: configPath,
295+
app: app,
312296
output: outputDir
313297
});
314298

@@ -318,7 +302,7 @@ describe('CLI Commands', () => {
318302

319303
// Second sync with force - should overwrite
320304
await syncDatabase({
321-
config: configPath,
305+
app: app,
322306
output: outputDir,
323307
force: true
324308
});

packages/tools/cli/src/commands/sync.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface SyncOptions {
99
output?: string;
1010
tables?: string[];
1111
force?: boolean;
12+
app?: any; // Allow passing app instance directly for testing
1213
}
1314

1415
/**
@@ -20,10 +21,15 @@ export async function syncDatabase(options: SyncOptions) {
2021

2122
console.log(chalk.blue('🔄 Syncing database schema to ObjectQL...'));
2223
console.log(chalk.gray(`Output directory: ${outputDir}\n`));
24+
25+
let app: any = options.app;
26+
const shouldClose = !options.app; // Only close if we loaded it ourselves
2327

2428
try {
25-
// Load ObjectQL instance from config
26-
const app = await loadObjectQLInstance(options.config);
29+
// Load ObjectQL instance from config if not provided
30+
if (!app) {
31+
app = await loadObjectQLInstance(options.config);
32+
}
2733

2834
// Check if driver supports introspection
2935
const driver = app.datasources?.default;
@@ -109,6 +115,16 @@ export async function syncDatabase(options: SyncOptions) {
109115
console.error(chalk.gray(error.stack));
110116
}
111117
throw error;
118+
} finally {
119+
// Ensure connection is closed if we opened it
120+
if (shouldClose) {
121+
if (app && app.close) {
122+
await app.close();
123+
} else if (app && app.datasources && app.datasources.default && (app.datasources.default as any).disconnect) {
124+
// Fallback for older versions if close isn't available
125+
await (app.datasources.default as any).disconnect();
126+
}
127+
}
112128
}
113129
}
114130

@@ -287,6 +303,15 @@ async function loadObjectQLInstance(configPath?: string): Promise<any> {
287303
}
288304

289305
const configModule = require(configFile);
306+
307+
// Clear cache to support multiple runs in same process (e.g. tests)
308+
try {
309+
const resolvedPath = require.resolve(configFile);
310+
delete require.cache[resolvedPath];
311+
} catch (e) {
312+
// Ignore resolution errors
313+
}
314+
290315
// Support multiple export patterns: default, app, objectql, or db (in order of precedence)
291316
const app = configModule.default || configModule.app || configModule.objectql || configModule.db;
292317

0 commit comments

Comments
 (0)