1- import { describe , it } from "node:test" ;
1+ import { describe , it , beforeEach , afterEach } from "node:test" ;
22import assert from "node:assert/strict" ;
3+ import { mkdirSync , rmSync , writeFileSync } from "node:fs" ;
4+ import { join } from "node:path" ;
35import { paginateSections } from "../src/utils/pagination.js" ;
46import { getOracleSections } from "../src/storage/oracle.js" ;
57import { getDecisionSections } from "../src/storage/decisions.js" ;
6- import { getMemorySections } from "../src/storage/memory.js" ;
78import { getFullContextSections } from "../src/tools/context.js" ;
89
9- const WORKSPACE = "/home/georgeb/axme-workspace" ;
10- const AXME_CODE = "/home/georgeb/axme-workspace/axme-code" ;
11- const CONTROL_PLANE = "/home/georgeb/axme-workspace/axme-control-plane" ;
10+ const TEST_ROOT = "/tmp/axme-pagination-integration-test" ;
11+ const PROJECT = join ( TEST_ROOT , "project" ) ;
12+
13+ function setupProject ( ) {
14+ rmSync ( TEST_ROOT , { recursive : true , force : true } ) ;
15+ const axme = join ( PROJECT , ".axme-code" ) ;
16+ mkdirSync ( join ( axme , "oracle" ) , { recursive : true } ) ;
17+ mkdirSync ( join ( axme , "decisions" ) , { recursive : true } ) ;
18+ mkdirSync ( join ( axme , "memory" , "feedback" ) , { recursive : true } ) ;
19+ mkdirSync ( join ( axme , "memory" , "patterns" ) , { recursive : true } ) ;
20+ mkdirSync ( join ( axme , "safety" ) , { recursive : true } ) ;
21+ mkdirSync ( join ( axme , "sessions" ) , { recursive : true } ) ;
22+
23+ writeFileSync ( join ( axme , "oracle" , "stack.md" ) , "# Stack\n\nNode.js 20, TypeScript 5.9, ESM modules.\n\nesbuild for bundling.\n" ) ;
24+ writeFileSync ( join ( axme , "oracle" , "structure.md" ) , "# Structure\n\nsrc/ — main source\ntest/ — tests\ndist/ — build output\n" ) ;
25+ writeFileSync ( join ( axme , "oracle" , "patterns.md" ) , "# Patterns\n\nCamelCase functions, PascalCase types.\n" ) ;
26+ writeFileSync ( join ( axme , "oracle" , "glossary.md" ) , "# Glossary\n\nMCP = Model Context Protocol\nOracle = Project knowledge base\n" ) ;
27+ writeFileSync ( join ( axme , "config.yaml" ) , "model: claude-sonnet-4-6\npresets:\n - essential-safety\n" ) ;
28+ writeFileSync ( join ( axme , "safety" , "rules.yaml" ) , `git:\n protectedBranches: [main]\n allowDirectPushToMain: false\n allowForcePush: false\nbash:\n deniedPrefixes:\n - "rm -rf /"\n allowedPrefixes: []\nfilesystem:\n deniedPaths: []\n readOnlyPaths: []\n` ) ;
29+
30+ // Create enough decisions for pagination (>30KB total)
31+ writeFileSync ( join ( axme , "decisions" , "index.md" ) , "# Decisions\n" ) ;
32+ for ( let i = 1 ; i <= 30 ; i ++ ) {
33+ const id = `D-${ String ( i ) . padStart ( 3 , "0" ) } ` ;
34+ const slug = `test-decision-${ i } ` ;
35+ const body = `This is decision ${ i } . ` . repeat ( 50 ) ; // ~1KB each
36+ writeFileSync ( join ( axme , "decisions" , `${ id } -${ slug } .md` ) , `---\nid: ${ id } \nslug: ${ slug } \ntitle: Test decision ${ i } \nenforce: required\nstatus: active\ndate: "2026-04-01"\nsource: manual\n---\n${ body } \n` ) ;
37+ }
38+ }
39+
40+ beforeEach ( ( ) => setupProject ( ) ) ;
41+ afterEach ( ( ) => rmSync ( TEST_ROOT , { recursive : true , force : true } ) ) ;
1242
1343describe ( "pagination integration with real data" , ( ) => {
14- it ( "workspace oracle paginates correctly" , ( ) => {
15- const sections = getOracleSections ( WORKSPACE ) ;
44+ it ( "oracle paginates correctly" , ( ) => {
45+ const sections = getOracleSections ( PROJECT ) ;
1646 assert . ok ( sections . length > 0 , "should have oracle sections" ) ;
17- const totalChars = sections . reduce ( ( s , sec ) => s + sec . length , 0 ) ;
18- console . log ( ` workspace oracle: ${ sections . length } sections, ${ totalChars } chars` ) ;
1947
2048 const p1 = paginateSections ( sections , 1 , "axme_oracle" , { } ) ;
21- console . log ( ` page 1/${ p1 . totalPages } : ${ p1 . text . length } chars` ) ;
22- if ( p1 . totalPages > 1 ) {
23- assert . ok ( p1 . text . includes ( "page: 2" ) , "should have next-page instruction" ) ;
24- const p2 = paginateSections ( sections , 2 , "axme_oracle" , { } ) ;
25- console . log ( ` page 2/${ p2 . totalPages } : ${ p2 . text . length } chars` ) ;
26- }
49+ assert . ok ( p1 . text . length > 0 , "page 1 should have content" ) ;
2750 } ) ;
2851
29- it ( "axme-code decisions paginates correctly (largest: 114KB)" , ( ) => {
30- const sections = getDecisionSections ( AXME_CODE ) ;
31- assert . ok ( sections . length > 0 , "should have decisions" ) ;
32- const totalChars = sections . reduce ( ( s , sec ) => s + sec . length , 0 ) ;
33- console . log ( ` axme-code decisions: ${ sections . length } items, ${ totalChars } chars` ) ;
52+ it ( "decisions paginate correctly with many items" , ( ) => {
53+ const sections = getDecisionSections ( PROJECT ) ;
54+ assert . ok ( sections . length >= 30 , "should have 30+ decisions" ) ;
3455
3556 // Walk all pages
3657 let page = 1 ;
3758 let allContent = "" ;
3859 while ( true ) {
39- const result = paginateSections ( sections , page , "axme_decisions" , { project_path : AXME_CODE } ) ;
40- console . log ( ` page ${ result . page } /${ result . totalPages } : ${ result . text . length } chars` ) ;
60+ const result = paginateSections ( sections , page , "axme_decisions" , { project_path : PROJECT } ) ;
4161 assert . ok ( result . text . length <= 30_000 , `page ${ page } too large: ${ result . text . length } ` ) ;
4262 allContent += result . text ;
4363 if ( result . page >= result . totalPages ) break ;
@@ -50,35 +70,22 @@ describe("pagination integration with real data", () => {
5070 }
5171 } ) ;
5272
53- it ( "workspace context paginates correctly" , ( ) => {
54- const sections = getFullContextSections ( WORKSPACE ) ;
55- const totalChars = sections . reduce ( ( s , sec ) => s + sec . length , 0 ) ;
56- console . log ( ` workspace context: ${ sections . length } sections, ${ totalChars } chars` ) ;
73+ it ( "context includes safety in first page" , ( ) => {
74+ const sections = getFullContextSections ( PROJECT ) ;
75+ assert . ok ( sections . length > 0 ) ;
5776
58- const p1 = paginateSections ( sections , 1 , "axme_context" , { workspace_path : WORKSPACE } ) ;
59- console . log ( ` page 1/${ p1 . totalPages } : ${ p1 . text . length } chars` ) ;
60- // First page should always have storage root header
77+ const p1 = paginateSections ( sections , 1 , "axme_context" , { } ) ;
6178 assert . ok ( p1 . text . includes ( "AXME Storage Root" ) , "first page must include storage root" ) ;
62- // First page should always have safety rules
63- assert . ok ( p1 . text . includes ( "Safety Rules" ) || p1 . text . includes ( "Safety" ) , "first page should include safety" ) ;
64- } ) ;
65-
66- it ( "control-plane oracle paginates correctly" , ( ) => {
67- const sections = getOracleSections ( CONTROL_PLANE ) ;
68- const totalChars = sections . reduce ( ( s , sec ) => s + sec . length , 0 ) ;
69- console . log ( ` control-plane oracle: ${ sections . length } sections, ${ totalChars } chars` ) ;
70-
71- const p1 = paginateSections ( sections , 1 , "axme_oracle" , { project_path : CONTROL_PLANE } ) ;
72- console . log ( ` page 1/${ p1 . totalPages } : ${ p1 . text . length } chars` ) ;
73- assert . ok ( p1 . text . length <= 30_000 , `page 1 too large: ${ p1 . text . length } ` ) ;
79+ assert . ok (
80+ p1 . text . includes ( "Safety" ) || p1 . text . includes ( "Protected branches" ) || p1 . text . includes ( "Load Full Knowledge Base" ) ,
81+ "first page should include safety or KB load instruction"
82+ ) ;
7483 } ) ;
7584
7685 it ( "no data loss across pages" , ( ) => {
77- const sections = getDecisionSections ( WORKSPACE ) ;
78- const totalChars = sections . reduce ( ( s , sec ) => s + sec . length , 0 ) ;
79- console . log ( ` workspace decisions: ${ sections . length } items, ${ totalChars } chars` ) ;
86+ const sections = getDecisionSections ( PROJECT ) ;
87+ assert . ok ( sections . length > 0 ) ;
8088
81- // Collect all pages
8289 let page = 1 ;
8390 const pageTexts : string [ ] = [ ] ;
8491 while ( true ) {
@@ -87,9 +94,7 @@ describe("pagination integration with real data", () => {
8794 if ( result . page >= result . totalPages ) break ;
8895 page ++ ;
8996 }
90- console . log ( ` ${ pageTexts . length } total pages` ) ;
9197
92- // Every section should appear in exactly one page
9398 for ( const sec of sections ) {
9499 const found = pageTexts . filter ( pt => pt . includes ( sec ) ) ;
95100 assert . equal ( found . length , 1 , `section should appear in exactly 1 page: ${ sec . slice ( 0 , 60 ) } ` ) ;
0 commit comments