import XCTest @testable import LChatCore // 🤖 The two-generation memo's contract (#1210): a fill never wipes the hot // set (the removeAll() bug re-parsed whole feeds on thread open), cold hits // promote, and only the stalest generation is ever dropped. final class MemoTests: XCTestCase { func testHitsDoNotRebuild() { var memo = Memo(cap: 4) var builds = 0 XCTAssertEqual(memo.get("a") { builds += 1; return 1 }, 1) XCTAssertEqual(memo.get("a") { builds += 1; return 99 }, 1) XCTAssertEqual(builds, 1) } func testFillKeepsRecentEntriesReachable() { var memo = Memo(cap: 2) var builds = 0 for k in 0..<4 { _ = memo.get(k) { builds += 1; return k } } XCTAssertEqual(builds, 4) // 2 and 3 straddle the last rotation (one hot, one cold) — both must // still be served without rebuilding. THIS is what removeAll() broke. _ = memo.get(3) { builds += 1; return -1 } _ = memo.get(2) { builds += 1; return -1 } XCTAssertEqual(builds, 4) } func testOnlyStalestGenerationDrops() { var memo = Memo(cap: 2) var builds = 0 for k in 0..<2 { _ = memo.get(k) { builds += 1; return k } } // hot = {0,1} _ = memo.get(2) { builds += 1; return 2 } // rotate: cold={0,1}, hot={2} _ = memo.get(0) { builds += 1; return 0 } // cold hit → promoted, no rebuild XCTAssertEqual(builds, 3) // Fill until 1's generation is two rotations back — only THEN does it rebuild. for k in 10..<14 { _ = memo.get(k) { builds += 1; return k } } _ = memo.get(1) { builds += 1; return 1 } XCTAssertEqual(builds, 8) } }