godcrm/pes-core/memory/test-store.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

283 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Quick integration test for MemoryStore
import { MemoryStore } from './store.js';
import { unlinkSync, existsSync } from 'node:fs';
const DB_PATH = '/tmp/pes-test-store.db';
if (existsSync(DB_PATH)) unlinkSync(DB_PATH);
const store = new MemoryStore(DB_PATH).init();
let passed = 0;
let failed = 0;
function assert(condition, msg) {
if (condition) { passed++; console.log(`${msg}`); }
else { failed++; console.error(`${msg}`); }
}
// --- STATS ---
console.log('\n📊 STATS');
const stats = store.getStats();
assert(stats.id === 1, 'Singleton stats exists');
assert(stats.xp === 0, 'Initial XP = 0');
assert(stats.phase === 'puppy', 'Initial phase = puppy');
assert(stats.courage === 0.3, 'Initial courage = 0.3');
assert(typeof stats.trait_phases === 'object', 'trait_phases is parsed JSON');
assert(stats.trait_phases.courage === 'resistance', 'courage phase = resistance');
// --- COMMANDS ---
console.log('\n🎾 COMMANDS');
const cmd1 = store.learnCommand('sit', { category: 'standard' });
assert(cmd1.input === 'sit', 'Command created: sit');
assert(cmd1.category === 'standard', 'Category = standard');
assert(cmd1.understood === 0, 'Not yet understood');
const result = store.markCommandLearned(cmd1.id);
assert(result.xp === 8, 'Learned command → +8 XP');
assert(result.command.understood === 1, 'Now understood');
const cmd2 = store.learnCommand('sit'); // reinforce
assert(cmd2.attempts === 2, 'Attempts incremented to 2');
const cmd3 = store.learnCommand('scan_api', { category: 'custom' });
store.markCommandLearned(cmd3.id);
const allCmds = store.listCommands({ understood: true });
assert(allCmds.length === 2, '2 understood commands');
store.useCommand(cmd1.id, true);
const used = store.getCommand('sit');
assert(used.attempts === 3, 'Attempts = 3 after use');
// --- XP ---
console.log('\n⭐ XP');
const statsAfterXP = store.getStats();
assert(statsAfterXP.xp === 16, 'Total XP = 16 (8+8 for two commands)');
assert(statsAfterXP.level > 0, `Level > 0 (actual: ${statsAfterXP.level})`);
const xpLog = store.getXPLog();
assert(xpLog.length === 2, '2 XP log entries');
const xpByReason = store.getXPByReason();
assert(xpByReason[0].reason === 'command_learned', 'XP by reason works');
// addXP directly
store.addXP(20, 'solved_bug', JSON.stringify({ file: 'api.js', line: 42 }));
assert(store.getStats().xp === 36, 'XP after solved_bug = 36');
try {
store.addXP(100, 'just_because');
assert(false, 'Should reject invalid XP reason');
} catch (e) {
assert(e.message.includes('Invalid XP reason'), 'Rejects invalid XP reason');
}
// --- INTERACTIONS ---
console.log('\n📝 INTERACTIONS');
const intId = store.logInteraction({
actor: 'owner',
action_type: 'praise',
action_detail: 'Good boy!',
emotion_before: 'playful',
emotion_after: 'happy',
context: { text: 'молодец' }
});
assert(typeof intId === 'number', 'Interaction logged');
store.logInteraction({ actor: 'agent:ralph', action_type: 'error', action_detail: '500 in /api' });
store.logInteraction({ actor: 'owner', action_type: 'scold', action_detail: 'No!' });
const history = store.getHistory({ limit: 10 });
assert(history.length === 3, '3 interactions in history');
const praiseCount = store.countByType('praise');
assert(praiseCount === 1, '1 praise interaction');
const ownerHistory = store.getHistory({ actor: 'owner' });
assert(ownerHistory.length === 2, '2 owner interactions');
// --- RELATIONSHIPS ---
console.log('\n🤝 RELATIONSHIPS');
const rel = store.addOrUpdateRelationship('owner', { trust: 0.3, affection: 0.5 });
assert(rel.entity === 'owner', 'Owner relationship created');
assert(rel.trust === 0.3, 'Trust = 0.3');
store.updateTrust('owner', 0.2);
const updatedRel = store.getRelationship('owner');
assert(updatedRel.trust === 0.5, 'Trust updated to 0.5');
store.addOrUpdateRelationship('agent:ralph', { trust: 0.1 });
const allRels = store.listRelationships();
assert(allRels.length === 2, '2 relationships');
// Phase auto-advance: owner has 2 interactions now, need 5 for acquaintance
for (let i = 0; i < 4; i++) store.addOrUpdateRelationship('owner', { trust: 0.5 });
store.updatePhase('owner');
const ownerRel = store.getRelationship('owner');
assert(ownerRel.relationship_phase === 'acquaintance', `Owner phase: ${ownerRel.relationship_phase} (expected acquaintance)`);
// --- FETCH LOG ---
console.log('\n🎾 FETCH LOG');
const fetchId = store.logFetch('file', JSON.stringify({ path: 'config.js', finding: 'hardcoded port' }));
assert(typeof fetchId === 'number', 'Fetch logged');
store.rateFetch(fetchId, 0.9);
const fetch = store.db.prepare('SELECT * FROM fetch_log WHERE id = ?').get(fetchId);
assert(fetch.usefulness_rating === 0.9, 'Fetch rated 0.9');
store.logFetch('junk', JSON.stringify({ content: 'nothing useful' }));
const fetchLog = store.getFetchLog();
assert(fetchLog.length === 2, '2 fetch entries');
const fileFetches = store.getFetchLog({ fetchType: 'file' });
assert(fileFetches.length === 1, '1 file fetch');
// --- LETTERS ---
console.log('\n📜 LETTERS');
const letter = store.createLetter('milestone', 'owner', { message: 'PES reached level 10!', xp: 36 });
assert(letter.letter_type === 'milestone', 'Milestone letter created');
assert(letter.read === 0, 'Letter unread');
const unread = store.getUnreadLetters('owner');
assert(unread.length === 1, '1 unread letter');
store.markLetterRead(letter.id);
const read = store.getUnreadLetters('owner');
assert(read.length === 0, '0 unread after marking read');
// Snapshot
const snapshot = store.createSnapshot();
assert(snapshot.stats.xp === 36, 'Snapshot has correct XP');
assert(snapshot.commands_learned.length === 2, 'Snapshot has 2 commands');
assert(snapshot.total_interactions >= 3, 'Snapshot has interactions');
// --- TRAIT CHANGES ---
console.log('\n🐺 TRAIT CHANGES (White Fang)');
// Set critical period (7 days from now)
store.setCriticalPeriodEnd(new Date(Date.now() + 7 * 86400000).toISOString());
const traitResult = store.updateTrait('courage', 1, 'owner_praise');
assert(traitResult.trait === 'courage', 'Trait update returned');
assert(traitResult.delta > 0, `Positive delta: ${traitResult.delta}`);
assert(traitResult.phase === 'resistance', 'Phase = resistance (first attempt)');
assert(traitResult.newValue > 0.3, `Courage increased: ${traitResult.newValue}`);
// Multiple updates
for (let i = 0; i < 5; i++) store.updateTrait('courage', 1, 'praise');
const courageLog = store.getTraitChangeLog('courage');
assert(courageLog.length === 6, '6 courage change entries');
// --- SESSIONS ---
console.log('\n🔌 SESSIONS');
const sessionId = store.startSession('playful');
assert(typeof sessionId === 'number', 'Session started');
const active = store.getActiveSession();
assert(active !== null, 'Active session exists');
assert(active.mood_start === 'playful', 'Mood start = playful');
const ended = store.endSession(sessionId, 'happy');
assert(ended.moodEnd === 'happy', 'Session ended with mood');
assert(ended.duration >= 0, `Duration: ${ended.duration}s`);
const noActive = store.getActiveSession();
assert(noActive === null, 'No active session after end');
// --- REACTIONS ---
console.log('\n👍 REACTIONS');
store.logReaction({
pes_action: 'fetch',
pes_expression: '🐾→🎾→📂config.js',
reactor: 'owner',
reaction_type: 'emoji',
reaction_value: '👍',
reaction_speed: 5,
weight: 1.0,
pattern_tags: ['config_file'],
platform: 'telegram'
});
store.logReaction({
pes_action: 'fetch',
reactor: 'owner',
reaction_type: 'text',
reaction_value: 'good find!',
weight: 0.8,
platform: 'web'
});
store.logReaction({
pes_action: 'fetch',
reactor: 'owner',
reaction_type: 'emoji',
reaction_value: '👍',
weight: 1.0
});
store.logReaction({
pes_action: 'bark',
reactor: 'owner',
reaction_type: 'emoji',
reaction_value: '👎',
weight: -1.0
});
const fetchWeight = store.getPatternWeight('fetch');
assert(fetchWeight.count === 3, '3 fetch reactions');
assert(fetchWeight.avgWeight > 0.5, `Positive avg weight: ${fetchWeight.avgWeight}`);
// Recalculate preference
const fetchPref = store.recalculatePreference('fetch');
assert(fetchPref !== null, 'Preference recalculated');
assert(fetchPref.positive > 0, `Positive count: ${fetchPref.positive}`);
console.log(` 📊 fetch behavior: ${fetchPref.behavior}, confidence: ${fetchPref.confidence}`);
const barkPref = store.recalculatePreference('bark');
assert(barkPref.behavior === 'avoid' || barkPref.behavior === 'decrease', `Bark behavior: ${barkPref.behavior}`);
// shouldDo
const shouldFetch = store.shouldDo('fetch');
assert(shouldFetch !== 'avoid' && shouldFetch !== 'never', `Should fetch: ${shouldFetch}`);
const shouldBark = store.shouldDo('bark');
console.log(` 📊 shouldDo bark: ${shouldBark}`);
const shouldUnknown = store.shouldDo('dance');
assert(shouldUnknown === 'neutral', 'Unknown action = neutral');
// --- PREFERENCES ---
console.log('\n⚙ PREFERENCES');
const pref = store.getPreference('fetch');
assert(pref !== null, 'Fetch preference exists');
const avoided = store.getByBehavior('avoid');
console.log(` 📊 Avoided patterns: ${avoided.length}`);
// --- SUMMARY ---
console.log('\n📋 SUMMARY');
const summary = store.getSummary();
assert(summary.xp === 36, `Summary XP: ${summary.xp}`);
assert(summary.commands_understood === 2, `Commands understood: ${summary.commands_understood}`);
assert(summary.known_entities === 2, `Known entities: ${summary.known_entities}`);
assert(summary.unread_letters === 0, 'No unread letters');
console.log(` 📊 Level: ${summary.level}, Phase: ${summary.phase}`);
console.log(` 📊 Interactions 24h: ${summary.interactions_last_24h}`);
console.log(` 📊 Preferred: ${summary.preferred_patterns}, Avoided: ${summary.avoided_patterns}`);
// --- WILLINGNESS DECAY ---
console.log('\n⏳ WILLINGNESS DECAY');
const beforeDecay = store.getCommand('sit');
console.log(` Before: willingness = ${beforeDecay.willingness}`);
store.decayWillingness(0); // 0 days = decay everything
const afterDecay = store.getCommand('sit');
assert(afterDecay.willingness < beforeDecay.willingness || afterDecay.willingness === 0.1,
`After decay: ${afterDecay.willingness}`);
// Cleanup
store.close();
if (existsSync(DB_PATH)) unlinkSync(DB_PATH);
console.log(`\n${'='.repeat(40)}`);
console.log(`✅ Passed: ${passed}`);
console.log(`❌ Failed: ${failed}`);
console.log(`${'='.repeat(40)}`);
process.exit(failed > 0 ? 1 : 0);