From b3e362105d693b08a54e54c5686c9ff656473a41 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Fri, 13 Feb 2026 12:23:34 -0800 Subject: [PATCH] test: add 3 tests for typeof guard, empty package.json, and learned_skills_path override (round 86) - loadAliases resets to defaults when aliases field is a truthy non-object (string) - detectFromPackageJson returns null for empty (0-byte) package.json - evaluate-session uses learned_skills_path config override with ~ expansion --- tests/hooks/evaluate-session.test.js | 51 ++++++++++++++++++++++++++++ tests/lib/package-manager.test.js | 13 +++++++ tests/lib/session-aliases.test.js | 21 ++++++++++++ 3 files changed, 85 insertions(+) diff --git a/tests/hooks/evaluate-session.test.js b/tests/hooks/evaluate-session.test.js index 41fdc4c8..a5b92e2d 100644 --- a/tests/hooks/evaluate-session.test.js +++ b/tests/hooks/evaluate-session.test.js @@ -360,6 +360,57 @@ function runTests() { } })) passed++; else failed++; + // ── Round 86: config learned_skills_path override with ~ expansion ── + console.log('\nRound 86: config learned_skills_path override:'); + + if (test('uses learned_skills_path from config with ~ expansion', () => { + // evaluate-session.js lines 69-72: + // if (config.learned_skills_path) { + // learnedSkillsPath = config.learned_skills_path.replace(/^~/, require('os').homedir()); + // } + // This branch was never tested — only the parse error (Round 85) and default path. + const configPath = path.join(__dirname, '..', '..', 'skills', 'continuous-learning', 'config.json'); + let originalContent = null; + try { + originalContent = fs.readFileSync(configPath, 'utf8'); + } catch { + // Config file may not exist + } + + try { + // Write config with a custom learned_skills_path using ~ prefix + fs.writeFileSync(configPath, JSON.stringify({ + min_session_length: 10, + learned_skills_path: '~/custom-learned-skills-dir' + })); + + // Create a transcript with 12 user messages (above threshold) + const testDir = createTestDir(); + const transcript = createTranscript(testDir, 12); + const result = runEvaluate({ transcript_path: transcript }); + + assert.strictEqual(result.code, 0, 'Should exit 0'); + // The script logs "Save learned skills to: " where should + // be the expanded home directory, NOT the literal "~" + assert.ok(!result.stderr.includes('~/custom-learned-skills-dir'), + 'Should NOT contain literal ~ in output (should be expanded)'); + assert.ok(result.stderr.includes('custom-learned-skills-dir'), + `Should reference the custom learned skills dir. Got: ${result.stderr}`); + // The ~ should have been replaced with os.homedir() + assert.ok(result.stderr.includes(os.homedir()), + `Should contain expanded home directory. Got: ${result.stderr}`); + + cleanupTestDir(testDir); + } finally { + // Restore original config file + if (originalContent !== null) { + fs.writeFileSync(configPath, originalContent, 'utf8'); + } else { + try { fs.unlinkSync(configPath); } catch { /* best-effort */ } + } + } + })) passed++; else failed++; + // Summary console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0); diff --git a/tests/lib/package-manager.test.js b/tests/lib/package-manager.test.js index 45c347e1..1dc70d5f 100644 --- a/tests/lib/package-manager.test.js +++ b/tests/lib/package-manager.test.js @@ -1350,6 +1350,19 @@ function runTests() { } })) passed++; else failed++; + // ── Round 86: detectFromPackageJson with empty (0-byte) package.json ── + console.log('\nRound 86: detectFromPackageJson (empty package.json):'); + + if (test('detectFromPackageJson returns null for empty (0-byte) package.json', () => { + // package-manager.js line 109-111: readFile returns "" for empty file. + // "" is falsy → if (content) is false → skips JSON.parse → returns null. + const testDir = createTestDir(); + fs.writeFileSync(path.join(testDir, 'package.json'), ''); + const result = pm.detectFromPackageJson(testDir); + assert.strictEqual(result, null, 'Empty package.json should return null (content="" is falsy)'); + cleanupTestDir(testDir); + })) passed++; else failed++; + // Summary console.log('\n=== Test Results ==='); console.log(`Passed: ${passed}`); diff --git a/tests/lib/session-aliases.test.js b/tests/lib/session-aliases.test.js index 98e5060f..d59456c4 100644 --- a/tests/lib/session-aliases.test.js +++ b/tests/lib/session-aliases.test.js @@ -1202,6 +1202,27 @@ function runTests() { 'Entries with invalid/missing dates should sort to the end'); })) passed++; else failed++; + // ── Round 86: loadAliases with truthy non-object aliases field ── + console.log('\nRound 86: loadAliases (truthy non-object aliases field):'); + + if (test('loadAliases resets to defaults when aliases field is a string (typeof !== object)', () => { + // session-aliases.js line 58: if (!data.aliases || typeof data.aliases !== 'object') + // Previous tests covered !data.aliases (undefined) via { noAliasesKey: true }. + // This exercises the SECOND half: aliases is truthy but typeof !== 'object'. + const aliasesPath = aliases.getAliasesPath(); + fs.writeFileSync(aliasesPath, JSON.stringify({ + version: '1.0', + aliases: 'this-is-a-string-not-an-object', + metadata: { totalCount: 0 } + })); + const data = aliases.loadAliases(); + assert.strictEqual(typeof data.aliases, 'object', 'Should reset aliases to object'); + assert.ok(!Array.isArray(data.aliases), 'Should be a plain object, not array'); + assert.strictEqual(Object.keys(data.aliases).length, 0, 'Should have no aliases'); + assert.strictEqual(data.version, '1.0', 'Should have version'); + resetAliases(); + })) passed++; else failed++; + // Summary console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`); process.exit(failed > 0 ? 1 : 0);