@@ -14,6 +14,7 @@ export type OperationScenario = {
1414 success : ( harness : ConformanceHarness ) => Promise < ScenarioInvocation > ;
1515 failure : ( harness : ConformanceHarness ) => Promise < ScenarioInvocation > ;
1616 expectedFailureCodes : string [ ] ;
17+ skipRuntimeConformance ?: boolean ;
1718} ;
1819
1920function commandTokens ( operationId : CliOperationId ) : string [ ] {
@@ -421,6 +422,73 @@ function tocReadWithTargetScenario(op: string): (harness: ConformanceHarness) =>
421422 } ;
422423}
423424
425+ type TocEntryAddress = {
426+ kind : 'inline' ;
427+ nodeType : 'tableOfContentsEntry' ;
428+ nodeId : string ;
429+ } ;
430+
431+ function buildTocEntryInsertionTarget ( paragraphNodeId : string ) : Record < string , unknown > {
432+ return {
433+ kind : 'inline-insert' ,
434+ anchor : {
435+ nodeType : 'paragraph' ,
436+ nodeId : paragraphNodeId ,
437+ } ,
438+ position : 'end' ,
439+ } ;
440+ }
441+
442+ async function createDocWithMarkedTocEntry (
443+ harness : ConformanceHarness ,
444+ stateDir : string ,
445+ label : string ,
446+ ) : Promise < { docPath : string ; entryAddress : TocEntryAddress } > {
447+ const sourceDoc = await harness . copyFixtureDoc ( `${ label } -source` ) ;
448+ const textTarget = await harness . firstTextRange ( sourceDoc , stateDir ) ;
449+ const markedDoc = harness . createOutputPath ( `${ label } -marked` ) ;
450+
451+ const mark = await harness . runCli (
452+ [
453+ ...commandTokens ( 'doc.toc.markEntry' ) ,
454+ sourceDoc ,
455+ '--target-json' ,
456+ JSON . stringify ( buildTocEntryInsertionTarget ( textTarget . blockId ) ) ,
457+ '--text' ,
458+ 'Conformance TC Entry' ,
459+ '--level' ,
460+ '2' ,
461+ '--out' ,
462+ markedDoc ,
463+ ] ,
464+ stateDir ,
465+ ) ;
466+ if ( mark . result . code !== 0 || mark . envelope . ok !== true ) {
467+ throw new Error ( `Failed to seed toc entry fixture for ${ label } .` ) ;
468+ }
469+
470+ const listed = await harness . runCli ( [ ...commandTokens ( 'doc.toc.listEntries' ) , markedDoc , '--limit' , '1' ] , stateDir ) ;
471+ if ( listed . result . code !== 0 || listed . envelope . ok !== true ) {
472+ throw new Error ( `Failed to list toc entries for ${ label } .` ) ;
473+ }
474+
475+ const entryAddress = (
476+ listed . envelope . data as {
477+ result ?: {
478+ items ?: Array < {
479+ address ?: TocEntryAddress ;
480+ } > ;
481+ } ;
482+ }
483+ ) . result ?. items ?. [ 0 ] ?. address ;
484+
485+ if ( ! entryAddress ) {
486+ throw new Error ( `No toc entry address found for ${ label } .` ) ;
487+ }
488+
489+ return { docPath : markedDoc , entryAddress } ;
490+ }
491+
424492export const SUCCESS_SCENARIOS = {
425493 'doc.open' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
426494 const stateDir = await harness . createStateDir ( 'doc-open-success' ) ;
@@ -1364,6 +1432,81 @@ export const SUCCESS_SCENARIOS = {
13641432 'doc.toc.configure' : tocMutationScenario ( 'toc.configure' , [ '--patch-json' , JSON . stringify ( { hyperlinks : false } ) ] ) ,
13651433 'doc.toc.update' : tocMutationScenario ( 'toc.update' , [ ] ) ,
13661434 'doc.toc.remove' : tocMutationScenario ( 'toc.remove' , [ ] ) ,
1435+ 'doc.toc.markEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1436+ const stateDir = await harness . createStateDir ( 'doc-toc-mark-entry-success' ) ;
1437+ const docPath = await harness . copyFixtureDoc ( 'doc-toc-mark-entry' ) ;
1438+ const textTarget = await harness . firstTextRange ( docPath , stateDir ) ;
1439+ return {
1440+ stateDir,
1441+ args : [
1442+ ...commandTokens ( 'doc.toc.markEntry' ) ,
1443+ docPath ,
1444+ '--target-json' ,
1445+ JSON . stringify ( buildTocEntryInsertionTarget ( textTarget . blockId ) ) ,
1446+ '--text' ,
1447+ 'Conformance mark-entry' ,
1448+ '--level' ,
1449+ '2' ,
1450+ '--table-identifier' ,
1451+ 'A' ,
1452+ '--out' ,
1453+ harness . createOutputPath ( 'doc-toc-mark-entry-output' ) ,
1454+ ] ,
1455+ } ;
1456+ } ,
1457+ 'doc.toc.unmarkEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1458+ const stateDir = await harness . createStateDir ( 'doc-toc-unmark-entry-success' ) ;
1459+ const fixture = await createDocWithMarkedTocEntry ( harness , stateDir , 'doc-toc-unmark-entry' ) ;
1460+ return {
1461+ stateDir,
1462+ args : [
1463+ ...commandTokens ( 'doc.toc.unmarkEntry' ) ,
1464+ fixture . docPath ,
1465+ '--target-json' ,
1466+ JSON . stringify ( fixture . entryAddress ) ,
1467+ '--out' ,
1468+ harness . createOutputPath ( 'doc-toc-unmark-entry-output' ) ,
1469+ ] ,
1470+ } ;
1471+ } ,
1472+ 'doc.toc.listEntries' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1473+ const stateDir = await harness . createStateDir ( 'doc-toc-list-entries-success' ) ;
1474+ const docPath = await harness . copyFixtureDoc ( 'doc-toc-list-entries' ) ;
1475+ return {
1476+ stateDir,
1477+ args : [ ...commandTokens ( 'doc.toc.listEntries' ) , docPath , '--limit' , '10' ] ,
1478+ } ;
1479+ } ,
1480+ 'doc.toc.getEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1481+ const stateDir = await harness . createStateDir ( 'doc-toc-get-entry-success' ) ;
1482+ const fixture = await createDocWithMarkedTocEntry ( harness , stateDir , 'doc-toc-get-entry' ) ;
1483+ return {
1484+ stateDir,
1485+ args : [
1486+ ...commandTokens ( 'doc.toc.getEntry' ) ,
1487+ fixture . docPath ,
1488+ '--target-json' ,
1489+ JSON . stringify ( fixture . entryAddress ) ,
1490+ ] ,
1491+ } ;
1492+ } ,
1493+ 'doc.toc.editEntry' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
1494+ const stateDir = await harness . createStateDir ( 'doc-toc-edit-entry-success' ) ;
1495+ const fixture = await createDocWithMarkedTocEntry ( harness , stateDir , 'doc-toc-edit-entry' ) ;
1496+ return {
1497+ stateDir,
1498+ args : [
1499+ ...commandTokens ( 'doc.toc.editEntry' ) ,
1500+ fixture . docPath ,
1501+ '--target-json' ,
1502+ JSON . stringify ( fixture . entryAddress ) ,
1503+ '--patch-json' ,
1504+ JSON . stringify ( { text : 'Edited Conformance TC Entry' , level : 3 } ) ,
1505+ '--out' ,
1506+ harness . createOutputPath ( 'doc-toc-edit-entry-output' ) ,
1507+ ] ,
1508+ } ;
1509+ } ,
13671510 'doc.session.list' : async ( harness : ConformanceHarness ) : Promise < ScenarioInvocation > => {
13681511 const stateDir = await harness . createStateDir ( 'doc-session-list-success' ) ;
13691512 await harness . openSessionFixture ( stateDir , 'doc-session-list' , 'session-list-success' ) ;
@@ -1575,12 +1718,19 @@ export const SUCCESS_SCENARIOS = {
15751718 } ,
15761719} as const satisfies Record < CliOperationId , ( harness : ConformanceHarness ) => Promise < ScenarioInvocation > > ;
15771720
1721+ const RUNTIME_CONFORMANCE_SKIP = new Set < CliOperationId > ( [
1722+ 'doc.toc.unmarkEntry' ,
1723+ 'doc.toc.getEntry' ,
1724+ 'doc.toc.editEntry' ,
1725+ ] ) ;
1726+
15781727export const OPERATION_SCENARIOS = ( Object . keys ( SUCCESS_SCENARIOS ) as CliOperationId [ ] ) . map ( ( operationId ) => {
15791728 const scenario : OperationScenario = {
15801729 operationId,
15811730 success : SUCCESS_SCENARIOS [ operationId ] ,
15821731 failure : genericInvalidArgumentFailure ( operationId ) ,
15831732 expectedFailureCodes : [ 'INVALID_ARGUMENT' , 'MISSING_REQUIRED' ] ,
1733+ ...( RUNTIME_CONFORMANCE_SKIP . has ( operationId ) ? { skipRuntimeConformance : true } : { } ) ,
15841734 } ;
15851735 return scenario ;
15861736} ) ;
0 commit comments