moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,230 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { MindDB } from '../../src/mind/db.js';
import Database from 'better-sqlite3';
import fs from 'fs';
import path from 'path';
import os from 'os';
describe('.mind SQLite Schema', () => {
let db: MindDB;
let dbPath: string;
beforeEach(() => {
dbPath = path.join(os.tmpdir(), `waggle-test-${Date.now()}.mind`);
db = new MindDB(dbPath);
});
afterEach(() => {
db.close();
try {
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
} catch {
// Windows: file may still be locked briefly after close
}
});
it('creates a single portable file on disk', () => {
expect(fs.existsSync(dbPath)).toBe(true);
});
it('creates identity table (Layer 0)', () => {
const cols = getColumns(db, 'identity');
expect(cols).toContainEqual(expect.objectContaining({ name: 'id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'name' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'role' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'department' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'personality' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'capabilities' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'system_prompt' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'created_at' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'updated_at' }));
});
it('creates awareness table (Layer 1)', () => {
const cols = getColumns(db, 'awareness');
expect(cols).toContainEqual(expect.objectContaining({ name: 'id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'category' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'content' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'priority' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'created_at' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'expires_at' }));
});
it('creates sessions table', () => {
const cols = getColumns(db, 'sessions');
expect(cols).toContainEqual(expect.objectContaining({ name: 'id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'gop_id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'project_id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'status' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'started_at' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'ended_at' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'summary' }));
});
it('enforces session status CHECK constraint', () => {
const raw = db.getDatabase();
raw.prepare(`INSERT INTO sessions (gop_id, project_id, status, started_at)
VALUES ('gop:test', NULL, 'active', datetime('now'))`).run();
expect(() => {
raw.prepare(`INSERT INTO sessions (gop_id, project_id, status, started_at)
VALUES ('gop:bad', NULL, 'invalid_status', datetime('now'))`).run();
}).toThrow();
});
it('creates memory_frames table (Layer 2)', () => {
const cols = getColumns(db, 'memory_frames');
expect(cols).toContainEqual(expect.objectContaining({ name: 'id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'frame_type' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'gop_id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 't' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'base_frame_id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'content' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'importance' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'access_count' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'created_at' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'last_accessed' }));
});
it('enforces frame_type CHECK constraint (I, P, B)', () => {
const raw = db.getDatabase();
// Valid types should work
raw.prepare(`INSERT INTO sessions (gop_id, status, started_at) VALUES ('gop:t1', 'active', datetime('now'))`).run();
raw.prepare(`INSERT INTO memory_frames (frame_type, gop_id, t, content, importance)
VALUES ('I', 'gop:t1', 0, 'test', 'normal')`).run();
raw.prepare(`INSERT INTO memory_frames (frame_type, gop_id, t, content, importance)
VALUES ('P', 'gop:t1', 1, 'delta', 'normal')`).run();
raw.prepare(`INSERT INTO memory_frames (frame_type, gop_id, t, content, importance)
VALUES ('B', 'gop:t1', 2, 'xref', 'normal')`).run();
// Invalid type should fail
expect(() => {
raw.prepare(`INSERT INTO memory_frames (frame_type, gop_id, t, content, importance)
VALUES ('X', 'gop:t1', 3, 'bad', 'normal')`).run();
}).toThrow();
});
it('enforces importance CHECK constraint', () => {
const raw = db.getDatabase();
raw.prepare(`INSERT INTO sessions (gop_id, status, started_at) VALUES ('gop:imp', 'active', datetime('now'))`).run();
for (const level of ['critical', 'important', 'normal', 'temporary', 'deprecated']) {
raw.prepare(`INSERT INTO memory_frames (frame_type, gop_id, t, content, importance)
VALUES ('I', 'gop:imp', 0, 'test-${level}', ?)`).run(level);
}
expect(() => {
raw.prepare(`INSERT INTO memory_frames (frame_type, gop_id, t, content, importance)
VALUES ('I', 'gop:imp', 0, 'bad', 'invalid_importance')`).run();
}).toThrow();
});
it('creates FTS5 virtual table for memory search', () => {
const raw = db.getDatabase();
const tables = raw.prepare(
"SELECT name FROM sqlite_master WHERE type='table' AND name='memory_frames_fts'"
).get() as { name: string } | undefined;
expect(tables).toBeDefined();
expect(tables!.name).toBe('memory_frames_fts');
});
it('creates sqlite-vec virtual table for embeddings', () => {
const raw = db.getDatabase();
const tables = raw.prepare(
"SELECT name FROM sqlite_master WHERE type='table' AND name='memory_frames_vec'"
).get() as { name: string } | undefined;
expect(tables).toBeDefined();
expect(tables!.name).toBe('memory_frames_vec');
});
it('creates knowledge_entities table (Layer 3)', () => {
const cols = getColumns(db, 'knowledge_entities');
expect(cols).toContainEqual(expect.objectContaining({ name: 'id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'entity_type' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'name' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'properties' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'valid_from' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'valid_to' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'recorded_at' }));
});
it('creates knowledge_relations table (Layer 3)', () => {
const cols = getColumns(db, 'knowledge_relations');
expect(cols).toContainEqual(expect.objectContaining({ name: 'id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'source_id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'target_id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'relation_type' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'confidence' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'properties' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'valid_from' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'valid_to' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'recorded_at' }));
});
it('creates procedures table (Layer 4)', () => {
const cols = getColumns(db, 'procedures');
expect(cols).toContainEqual(expect.objectContaining({ name: 'id' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'name' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'model' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'template' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'version' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'success_rate' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'avg_cost' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'created_at' }));
expect(cols).toContainEqual(expect.objectContaining({ name: 'updated_at' }));
});
it('creates meta table with schema version', () => {
const raw = db.getDatabase();
const row = raw.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get() as { value: string };
expect(row).toBeDefined();
expect(row.value).toBe('1');
});
it('creates GOP indexes for fast window queries', () => {
const raw = db.getDatabase();
const indexes = raw.prepare(
"SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_frames_%'"
).all() as { name: string }[];
const names = indexes.map(i => i.name);
expect(names).toContain('idx_frames_gop_t');
expect(names).toContain('idx_frames_type');
expect(names).toContain('idx_frames_base');
});
it('creates session index for project queries', () => {
const raw = db.getDatabase();
const idx = raw.prepare(
"SELECT name FROM sqlite_master WHERE type='index' AND name='idx_sessions_project'"
).get() as { name: string } | undefined;
expect(idx).toBeDefined();
});
it('can open an existing .mind file without recreating schema', () => {
const raw = db.getDatabase();
raw.prepare("INSERT INTO meta (key, value) VALUES ('test_key', 'test_value')").run();
db.close();
const db2 = new MindDB(dbPath);
const row = db2.getDatabase().prepare("SELECT value FROM meta WHERE key = 'test_key'").get() as { value: string };
expect(row.value).toBe('test_value');
db2.close();
// Reopen for afterEach cleanup
db = new MindDB(dbPath);
});
it('supports in-memory database for testing', () => {
const memDb = new MindDB(':memory:');
const cols = getColumns(memDb, 'identity');
expect(cols.length).toBeGreaterThan(0);
memDb.close();
});
});
function getColumns(db: MindDB, table: string) {
return db.getDatabase().prepare(`PRAGMA table_info('${table}')`).all() as Array<{
cid: number;
name: string;
type: string;
notnull: number;
dflt_value: string | null;
pk: number;
}>;
}