1+ /**
2+ * Keyman is copyright (C) SIL Global. MIT License.
3+ *
4+ * Created by jahorton on 2026-03-09
5+ *
6+ * This file adds unit tests for verifying core mechanics of the
7+ * SearchQuotientNode set of types.
8+ */
9+
10+ import { assert } from 'chai' ;
11+
12+ import { LexicalModelTypes } from '@keymanapp/common-types' ;
13+ import { jsonFixture } from '@keymanapp/common-test-resources/model-helpers.mjs' ;
14+ import { generateSpaceSeed , InputSegment , models , PathResult , SearchNode , SearchQuotientNode , SearchQuotientRoot , TokenResultMapping } from '@keymanapp/lm-worker/test-index' ;
15+
16+ import LexicalModel = LexicalModelTypes . LexicalModel ;
17+ import TrieModel = models . TrieModel ;
18+
19+ const testModel = new TrieModel ( jsonFixture ( 'models/tries/english-1000' ) ) ;
20+
21+ export class MockQuotientNode extends SearchQuotientNode {
22+ /* This section is the part relevant for SearchQuotientNode-specific testing. */
23+ public readonly receivedResults : TokenResultMapping [ ] = [ ] ;
24+ private _spaceId : number ;
25+
26+ constructor ( parent : SearchQuotientNode ) {
27+ super ( ) ;
28+ if ( parent ) {
29+ this . linkAndQueueFromParent ( parent , this . receivedResults ) ;
30+ }
31+ }
32+
33+ mockResult ( resultNode : SearchNode ) {
34+ // Temporarily set ._spaceId so that TokenResultMapping construction may proceed.
35+ this . _spaceId = resultNode . spaceId ;
36+ this . saveResult ( new TokenResultMapping ( this , resultNode ) ) ;
37+ this . _spaceId = undefined ;
38+ }
39+
40+ get spaceId ( ) : number {
41+ return this . _spaceId ;
42+ }
43+
44+ /* End relevant section */
45+
46+ // The rest of this is simply just... implementing the abstract methods so
47+ // that the type signature is satisfied.
48+ get model ( ) : LexicalModel {
49+ throw new Error ( 'Method not implemented.' ) ;
50+ }
51+ get parents ( ) : SearchQuotientNode [ ] {
52+ throw new Error ( 'Method not implemented.' ) ;
53+ }
54+ handleNextNode ( ) : PathResult < TokenResultMapping > {
55+ throw new Error ( 'Method not implemented.' ) ;
56+ }
57+ increaseMaxEditDistance ( ) : void {
58+ throw new Error ( 'Method not implemented.' ) ;
59+ }
60+ get currentCost ( ) : number {
61+ throw new Error ( 'Method not implemented.' ) ;
62+ }
63+ lowestPossibleSingleCost : number ;
64+ correctionsEnabled : boolean ;
65+ inputCount : number ;
66+ codepointLength : number ;
67+ bestExample : { text : string ; p : number ; } ;
68+ inputSegments : InputSegment [ ] ;
69+ get sourceRangeKey ( ) : string {
70+ throw new Error ( 'Method not implemented.' ) ;
71+ }
72+ merge ( space : SearchQuotientNode ) : SearchQuotientNode {
73+ throw new Error ( 'Method not implemented.' ) ;
74+ }
75+ split ( charIndex : number ) : [ SearchQuotientNode , SearchQuotientNode ] [ ] {
76+ throw new Error ( 'Method not implemented.' ) ;
77+ }
78+ isSameNode ( node : SearchQuotientNode ) : boolean {
79+ throw new Error ( 'Method not implemented.' ) ;
80+ }
81+ }
82+
83+ describe ( 'SearchQuotientNode' , ( ) => {
84+ it ( 'propagates node search results to linked descendants' , ( ) => {
85+ const root = new SearchQuotientRoot ( testModel ) ;
86+
87+ const baseNode = new MockQuotientNode ( root ) ;
88+ const descendants = [
89+ new MockQuotientNode ( baseNode ) ,
90+ new MockQuotientNode ( baseNode ) ,
91+ new MockQuotientNode ( baseNode )
92+ ] ;
93+
94+ const rootPath = new SearchNode ( testModel . traverseFromRoot ( ) , generateSpaceSeed ( ) ) ;
95+ const path1 = rootPath . buildSubstitutionEdges (
96+ [ {
97+ sample : {
98+ insert : 't' ,
99+ deleteLeft : 0 ,
100+ deleteRight : 0
101+ } ,
102+ p : 1.0
103+ } ] ,
104+ 13
105+ ) . flatMap ( ( n ) => n . processSubsetEdge ( ) ) [ 0 ] ;
106+ const path2 = rootPath . buildSubstitutionEdges (
107+ [ {
108+ sample : {
109+ insert : 'a' ,
110+ deleteLeft : 0 ,
111+ deleteRight : 0
112+ } ,
113+ p : 1.0
114+ } ] ,
115+ 13
116+ ) . flatMap ( ( n ) => n . processSubsetEdge ( ) ) [ 0 ] ;
117+ baseNode . mockResult ( path1 ) ;
118+ baseNode . mockResult ( path2 ) ;
119+
120+ // Check that each descendant received the node.
121+ descendants . forEach ( ( qn ) => assert . sameMembers ( qn . receivedResults . map ( ( r ) => r . spaceId ) , [ path1 . spaceId , path2 . spaceId ] ) ) ;
122+ } ) ;
123+ } ) ;
0 commit comments