Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- **Metadata plugin tests** — Updated `MetadataPlugin` test suite in `packages/metadata/src/metadata.test.ts` to match the refactored `start()` implementation. Tests now verify `setDataEngine` (via `getService('objectql')`) instead of the removed `setDatabaseDriver` (via `getServices()`) pattern. Also added `setDataEngine` and `setRealtimeService` to the `NodeMetadataManager` mock class.

### Added
- **Claude Code integration (`CLAUDE.md`)** — Added root `CLAUDE.md` file so that [Claude Code](https://docs.anthropic.com/en/docs/claude-code) automatically loads the project's system prompt when launched in the repository. Content is synced with `.github/copilot-instructions.md` and includes build/test quick-reference commands, all prime directives, monorepo structure, protocol domains, coding patterns, and domain-specific prompt references. This complements the existing GitHub Copilot instructions and `skills/` directory.

Expand Down
50 changes: 27 additions & 23 deletions packages/metadata/src/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ describe('MetadataPlugin', () => {
stopWatching = vi.fn();
setTypeRegistry = vi.fn();
setDatabaseDriver = vi.fn();
setDataEngine = vi.fn();
setRealtimeService = vi.fn();
register = vi.fn();
};
return { NodeMetadataManager: MockNodeMetadataManager };
Expand Down Expand Up @@ -533,80 +535,82 @@ describe('MetadataPlugin', () => {
expect(ctx.logger.info).toHaveBeenCalled();
});

it('should bridge driver service to MetadataManager in start()', async () => {
it('should bridge ObjectQL engine to MetadataManager in start()', async () => {
const { MetadataPlugin } = await import('./plugin.js');
const plugin = new MetadataPlugin({ rootDir: '/tmp/test', watch: false });

const mockDriver = { name: 'mock-driver', find: vi.fn(), create: vi.fn() };
const services = new Map<string, any>();
services.set('driver.mock-driver', mockDriver);
const mockObjectQL = { find: vi.fn(), create: vi.fn(), update: vi.fn() };

const ctx = createMockPluginContext();
ctx.getServices = vi.fn().mockReturnValue(services);
ctx.getService = vi.fn().mockImplementation((name: string) => {
if (name === 'objectql') return mockObjectQL;
return null;
});

await plugin.init(ctx);
await plugin.start(ctx);

// Verify setDatabaseDriver was called on the manager with the driver
// Verify setDataEngine was called on the manager with the ObjectQL engine
const manager = (plugin as any).manager;
expect(manager.setDatabaseDriver).toHaveBeenCalledWith(mockDriver);
expect(manager.setDataEngine).toHaveBeenCalledWith(mockObjectQL);
});

it('should bridge driver AFTER filesystem metadata loading', async () => {
it('should bridge ObjectQL engine AFTER filesystem metadata loading', async () => {
const { MetadataPlugin } = await import('./plugin.js');
const plugin = new MetadataPlugin({ rootDir: '/tmp/test', watch: false });

const callOrder: string[] = [];
const mockDriver = { name: 'mock-driver', find: vi.fn(), create: vi.fn() };
const services = new Map<string, any>();
services.set('driver.mock-driver', mockDriver);
const mockObjectQL = { find: vi.fn(), create: vi.fn(), update: vi.fn() };

const manager = (plugin as any).manager;
manager.loadMany = vi.fn().mockImplementation(async () => {
callOrder.push('loadMany');
return [];
});
manager.setDatabaseDriver = vi.fn().mockImplementation(() => {
callOrder.push('setDatabaseDriver');
manager.setDataEngine = vi.fn().mockImplementation(() => {
callOrder.push('setDataEngine');
});

const ctx = createMockPluginContext();
ctx.getServices = vi.fn().mockReturnValue(services);
ctx.getService = vi.fn().mockImplementation((name: string) => {
if (name === 'objectql') return mockObjectQL;
return null;
});

await plugin.init(ctx);
await plugin.start(ctx);

// setDatabaseDriver must be called after all loadMany calls
// setDataEngine must be called after all loadMany calls
const lastLoad = callOrder.lastIndexOf('loadMany');
const driverIdx = callOrder.indexOf('setDatabaseDriver');
const driverIdx = callOrder.indexOf('setDataEngine');
expect(driverIdx).toBeGreaterThan(lastLoad);
});

it('should not fail when no driver service is available', async () => {
it('should not fail when no ObjectQL service is available', async () => {
const { MetadataPlugin } = await import('./plugin.js');
const plugin = new MetadataPlugin({ rootDir: '/tmp/test', watch: false });

const ctx = createMockPluginContext();
ctx.getServices = vi.fn().mockReturnValue(new Map());
// getService returns null by default — no objectql available

await plugin.init(ctx);
// Should not throw
await expect(plugin.start(ctx)).resolves.not.toThrow();

// setDatabaseDriver should not have been called
// setDataEngine should not have been called
const manager = (plugin as any).manager;
expect(manager.setDatabaseDriver).not.toHaveBeenCalled();
expect(manager.setDataEngine).not.toHaveBeenCalled();
});

it('should gracefully handle getServices errors', async () => {
it('should gracefully handle getService errors', async () => {
const { MetadataPlugin } = await import('./plugin.js');
const plugin = new MetadataPlugin({ rootDir: '/tmp/test', watch: false });

const ctx = createMockPluginContext();
ctx.getServices = vi.fn().mockImplementation(() => { throw new Error('services unavailable'); });
ctx.getService = vi.fn().mockImplementation(() => { throw new Error('service unavailable'); });

await plugin.init(ctx);
// Should not throw even when getServices fails
// Should not throw even when getService fails
await expect(plugin.start(ctx)).resolves.not.toThrow();
});
});
Expand Down