import { describe, test } from 'node:test'; import assert from 'node:assert/strict'; import { updatePostTracking, pruneContentHistory, updateFollowerCount } from '../state-updater.mjs'; const SAMPLE_STATE = `--- last_post_date: "2026-04-05" first_post_date: "2026-01-15" last_post_topic: "AI strategy" posts_this_week: 2 weekly_goal: 3 current_streak: 5 longest_streak: 12 current_week: "2026-W14" last_import_date: "2026-04-01" last_import_week: "2026-W14" follower_count: 850 follower_target: 10000 target_date: "2026-12-31" monthly_growth: [] projected_10k_date: "" growth_rate_needed: 0 --- # LinkedIn Session State ## Recent Posts - [2026-04-05] "AI governance is not about..." (1450) - AI strategy - [2026-04-03] "Three things I learned..." (1200) - leadership - [2026-03-28] "Why most teams fail at..." (1350) - team building ## Session Notes ## Planned Content ## Milestone Log `; describe('updatePostTracking', () => { test('sets last_post_date to provided date', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-07', postTopic: 'AI governance', hookText: 'The real problem with AI governance...', charCount: 1500, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^last_post_date: "2026-04-07"$/m); }); test('sets last_post_topic', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-07', postTopic: 'AI governance', hookText: 'The real problem...', charCount: 1500, format: 'post' }); assert.match(result.content, /^last_post_topic: "AI governance"$/m); }); test('increments posts_this_week when same week', () => { // 2026-04-06 is a Monday, ISO W15. current_week is W14. // Use a date that stays in W14: 2026-04-05 is Sunday W14 — but last_post_date is already 04-05. // Let's use a state with current_week matching the post date week. const w15State = SAMPLE_STATE.replace('current_week: "2026-W14"', 'current_week: "2026-W15"'); const result = updatePostTracking(w15State, { postDate: '2026-04-07', // Tuesday W15 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^posts_this_week: 3$/m); // was 2, incremented }); test('increments streak when gap <= 2 days', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', // 1 day after last_post_date 2026-04-05 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 6$/m); // was 5, incremented }); test('resets streak to 1 when gap > 2 days', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-09', // 4 days after 2026-04-05 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 1$/m); }); test('sets first_post_date when null', () => { const nullFirstPost = SAMPLE_STATE.replace( 'first_post_date: "2026-01-15"', 'first_post_date: null' ); const result = updatePostTracking(nullFirstPost, { postDate: '2026-04-07', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^first_post_date: "2026-04-07"$/m); }); test('does NOT overwrite existing first_post_date', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-07', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^first_post_date: "2026-01-15"$/m); }); test('triggers week rollover when ISO week changes', () => { // 2026-04-14 is W16, current_week is W14 const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-14', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); // After rollover, posts_this_week resets to 0 then increments to 1 assert.match(result.content, /^posts_this_week: 1$/m); assert.match(result.content, /^current_week: "2026-W16"$/m); }); test('appends to Recent Posts section', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', postTopic: 'AI governance', hookText: 'The real problem with AI governance today...', charCount: 1500, format: 'post' }); assert.notEqual(result, null); assert.ok(result.content.includes('- [2026-04-06] "The real problem with AI governance today..." (1500) - AI governance')); // Existing entries should still be there assert.ok(result.content.includes('- [2026-04-05] "AI governance is not about..."')); }); test('updates longest_streak when current exceeds it', () => { const highStreak = SAMPLE_STATE.replace('current_streak: 5', 'current_streak: 12'); const result = updatePostTracking(highStreak, { postDate: '2026-04-06', // 1 day gap, streak increments to 13 postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 13$/m); assert.match(result.content, /^longest_streak: 13$/m); }); test('does not update longest_streak when current is lower', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', postTopic: 'test', hookText: 'Hook', charCount: 1000, format: 'post' }); assert.notEqual(result, null); assert.match(result.content, /^current_streak: 6$/m); assert.match(result.content, /^longest_streak: 12$/m); // unchanged }); test('returns changes array describing what changed', () => { const result = updatePostTracking(SAMPLE_STATE, { postDate: '2026-04-06', postTopic: 'AI governance', hookText: 'Hook', charCount: 1500, format: 'post' }); assert.notEqual(result, null); assert.ok(Array.isArray(result.changes)); assert.ok(result.changes.length > 0); }); }); describe('pruneContentHistory', () => { test('removes entries older than 90 days', () => { const today = new Date(); const old = new Date(today); old.setDate(old.getDate() - 100); const oldDate = old.toISOString().slice(0, 10); const recent = new Date(today); recent.setDate(recent.getDate() - 10); const recentDate = recent.toISOString().slice(0, 10); const stateWithOld = SAMPLE_STATE.replace( '## Recent Posts\n\n', `## Recent Posts\n\n- [${oldDate}] "Old post..." (1000) - old topic\n- [${recentDate}] "Recent post..." (1200) - recent topic\n` ); const result = pruneContentHistory(stateWithOld, 90); assert.notEqual(result, null); assert.equal(result.pruned, 1); assert.ok(!result.content.includes(oldDate)); assert.ok(result.content.includes(recentDate)); }); test('preserves entries within 90 days', () => { const today = new Date(); const recent = new Date(today); recent.setDate(recent.getDate() - 30); const recentDate = recent.toISOString().slice(0, 10); const stateWithRecent = SAMPLE_STATE.replace( '## Recent Posts\n\n', `## Recent Posts\n\n- [${recentDate}] "Recent post..." (1200) - topic\n` ); const result = pruneContentHistory(stateWithRecent, 90); assert.equal(result, null); // nothing to prune }); test('returns null when no entries exist', () => { const emptyRecent = SAMPLE_STATE.replace( /## Recent Posts\n\n[\s\S]*?(?=## Session Notes)/, '## Recent Posts\n\n' ); const result = pruneContentHistory(emptyRecent, 90); assert.equal(result, null); }); test('handles custom maxAgeDays', () => { const today = new Date(); const old = new Date(today); old.setDate(old.getDate() - 40); const oldDate = old.toISOString().slice(0, 10); const stateWithOld = SAMPLE_STATE.replace( '## Recent Posts\n\n', `## Recent Posts\n\n- [${oldDate}] "Somewhat old..." (1000) - topic\n` ); const result = pruneContentHistory(stateWithOld, 30); assert.notEqual(result, null); assert.equal(result.pruned, 1); }); }); describe('updateFollowerCount', () => { test('updates follower_count', () => { const result = updateFollowerCount(SAMPLE_STATE, { count: 920, month: '2026-04' }); assert.notEqual(result, null); assert.match(result.content, /^follower_count: 920$/m); }); test('recalculates growth_rate_needed', () => { const result = updateFollowerCount(SAMPLE_STATE, { count: 920, month: '2026-04' }); assert.notEqual(result, null); const match = result.content.match(/^growth_rate_needed: (\d+)$/m); assert.ok(match, 'growth_rate_needed should be present'); const rate = parseInt(match[1], 10); assert.ok(rate > 0, 'growth_rate_needed should be positive'); }); test('appends to Milestone Log section', () => { const result = updateFollowerCount(SAMPLE_STATE, { count: 920, month: '2026-04' }); assert.notEqual(result, null); assert.ok(result.content.includes('[2026-04] 920 (+70)')); }); });