@@ -14,6 +14,7 @@ describe("workspace contents section", () => {
1414 ] ,
1515 integrations : [ "github" , "snowflake" ] ,
1616 memoryEnabled : true ,
17+ knowledge : null ,
1718 } )
1819 expect ( text ) . toContain ( "Workspace skills (2):" )
1920 expect ( text ) . toContain ( "- revenue-metrics — How revenue is defined" )
@@ -26,30 +27,30 @@ describe("workspace contents section", () => {
2627 } )
2728
2829 test ( "not synced, none, and unknown are three different answers" , ( ) => {
29- const notSynced = Contents . render ( { skills : null , integrations : null , memoryEnabled : null } )
30+ const notSynced = Contents . render ( { skills : null , integrations : null , memoryEnabled : null , knowledge : null } )
3031 expect ( notSynced ) . toContain ( "not synced to this project yet" )
3132 expect ( notSynced ) . not . toContain ( "Integrations:" )
3233 expect ( notSynced ) . not . toContain ( "Workspace memory:" )
3334
34- const none = Contents . render ( { skills : [ ] , integrations : [ ] , memoryEnabled : false } )
35+ const none = Contents . render ( { skills : [ ] , integrations : [ ] , memoryEnabled : false , knowledge : null } )
3536 expect ( none ) . toContain ( "none — this workspace has no custom skills" )
3637 expect ( none ) . toContain ( "Integrations: none attached" )
3738 expect ( none ) . toContain ( "Workspace memory: off." )
3839 } )
3940
4041 test ( "a long list is capped with a count, and an oversize section drops descriptions before anything else" , ( ) => {
4142 const many = Array . from ( { length : 55 } , ( _ , i ) => ( { name : `skill-${ String ( i ) . padStart ( 2 , "0" ) } ` , description : "x" . repeat ( 90 ) } ) )
42- const full = Contents . render ( { skills : many , integrations : [ ] , memoryEnabled : true } , Number . POSITIVE_INFINITY )
43+ const full = Contents . render ( { skills : many , integrations : [ ] , memoryEnabled : true , knowledge : null } , Number . POSITIVE_INFINITY )
4344 expect ( full ) . toContain ( "Workspace skills (55):" )
4445 expect ( full ) . toContain ( "- …and 15 more" )
4546
46- const capped = Contents . render ( { skills : many , integrations : [ ] , memoryEnabled : true } )
47+ const capped = Contents . render ( { skills : many , integrations : [ ] , memoryEnabled : true , knowledge : null } )
4748 expect ( capped . length ) . toBeLessThanOrEqual ( Contents . MAX_CONTENTS_CHARS )
4849 expect ( capped ) . toContain ( "- skill-00\n" )
4950 expect ( capped ) . toContain ( "answer from this section only" )
5051
5152 // Nothing partial: if even the bare list cannot fit, the section is omitted.
52- expect ( Contents . render ( { skills : many , integrations : [ ] , memoryEnabled : true } , 200 ) ) . toBe ( "" )
53+ expect ( Contents . render ( { skills : many , integrations : [ ] , memoryEnabled : true , knowledge : null } , 200 ) ) . toBe ( "" )
5354 } )
5455} )
5556
@@ -61,24 +62,24 @@ describe("workspace summary cache", () => {
6162 integrations : [ { id : "snowflake" } , { id : "github" } ] ,
6263 memory_enabled : true ,
6364 } as any )
64- expect ( await Contents . workspaceSummary ( 35 ) ) . toEqual ( { integrations : [ "github" , "snowflake" ] , memoryEnabled : true } )
65+ expect ( await Contents . workspaceSummary ( 35 ) ) . toEqual ( { integrations : [ "github" , "snowflake" ] , memoryEnabled : true , knowledge : null } )
6566 // Cached: no second request inside the TTL.
6667 await Contents . workspaceSummary ( 35 )
6768 expect ( spy ) . toHaveBeenCalledTimes ( 1 )
6869 spy . mockRestore ( )
6970
7071 Contents . resetForTests ( )
7172 const failing = spyOn ( AltimateApi , "getDatamate" ) . mockRejectedValueOnce ( new Error ( "offline" ) )
72- expect ( await Contents . workspaceSummary ( 36 ) ) . toEqual ( { integrations : null , memoryEnabled : null } )
73+ expect ( await Contents . workspaceSummary ( 36 ) ) . toEqual ( { integrations : null , memoryEnabled : null , knowledge : null } )
7374 failing . mockRestore ( )
7475 } )
7576
7677 test ( "a failed fetch is not cached: the next step asks again" , async ( ) => {
7778 const spy = spyOn ( AltimateApi , "getDatamate" )
7879 . mockRejectedValueOnce ( new Error ( "offline" ) )
7980 . mockResolvedValueOnce ( { id : "38" , name : "w" , integrations : [ { id : "github" } ] , memory_enabled : true } as any )
80- expect ( await Contents . workspaceSummary ( 38 ) ) . toEqual ( { integrations : null , memoryEnabled : null } )
81- expect ( await Contents . workspaceSummary ( 38 ) ) . toEqual ( { integrations : [ "github" ] , memoryEnabled : true } )
81+ expect ( await Contents . workspaceSummary ( 38 ) ) . toEqual ( { integrations : null , memoryEnabled : null , knowledge : null } )
82+ expect ( await Contents . workspaceSummary ( 38 ) ) . toEqual ( { integrations : [ "github" ] , memoryEnabled : true , knowledge : null } )
8283 expect ( spy ) . toHaveBeenCalledTimes ( 2 )
8384 spy . mockRestore ( )
8485 } )
@@ -89,12 +90,75 @@ describe("workspace summary cache", () => {
8990 ( ) => new Promise ( ( r ) => ( release = r ) ) as any ,
9091 )
9192 const started = Date . now ( )
92- expect ( await Contents . workspaceSummary ( 37 ) ) . toEqual ( { integrations : null , memoryEnabled : null } )
93+ expect ( await Contents . workspaceSummary ( 37 ) ) . toEqual ( { integrations : null , memoryEnabled : null , knowledge : null } )
9394 expect ( Date . now ( ) - started ) . toBeLessThan ( 2_000 )
9495 release ( { id : "37" , name : "w" , integrations : [ { id : "github" } ] , memory_enabled : false } )
9596 await new Promise ( ( r ) => setTimeout ( r , 10 ) )
96- expect ( await Contents . workspaceSummary ( 37 ) ) . toEqual ( { integrations : [ "github" ] , memoryEnabled : false } )
97+ expect ( await Contents . workspaceSummary ( 37 ) ) . toEqual ( { integrations : [ "github" ] , memoryEnabled : false , knowledge : null } )
9798 expect ( spy ) . toHaveBeenCalledTimes ( 1 )
9899 spy . mockRestore ( )
99100 } )
100101} )
102+
103+ describe ( "workspace knowledge" , ( ) => {
104+ afterEach ( ( ) => Contents . resetForTests ( ) )
105+
106+ const base = { skills : [ ] , integrations : [ ] , memoryEnabled : true }
107+
108+ test ( "off, all, selected and unknown read differently" , ( ) => {
109+ expect ( Contents . render ( { ...base , knowledge : { kind : "off" } } ) ) . toContain ( "Knowledge: none — the knowledge engine is off" )
110+ expect ( Contents . render ( { ...base , knowledge : { kind : "all" } } ) ) . toContain ( "Knowledge: every document in the organization's knowledge hub" )
111+ expect ( Contents . render ( { ...base , knowledge : { kind : "selected" , count : 2 , names : [ "Metric definitions" , "dbt style guide" ] } } ) ) . toContain (
112+ "Knowledge documents (2): Metric definitions, dbt style guide." ,
113+ )
114+ expect ( Contents . render ( { ...base , knowledge : { kind : "selected" , count : 3 , names : null } } ) ) . toContain (
115+ "Knowledge: 3 selected documents (their names could not be loaded)." ,
116+ )
117+ expect ( Contents . render ( { ...base , knowledge : null } ) ) . not . toContain ( "Knowledge" )
118+ } )
119+
120+ test ( "a long document list is capped with a count" , ( ) => {
121+ const names = Array . from ( { length : 25 } , ( _ , i ) => `doc-${ String ( i ) . padStart ( 2 , "0" ) } ` )
122+ const text = Contents . render ( { ...base , knowledge : { kind : "selected" , count : 25 , names } } )
123+ expect ( text ) . toContain ( "Knowledge documents (25): doc-00" )
124+ expect ( text ) . toContain ( "…and 5 more" )
125+ expect ( text ) . not . toContain ( "doc-24" )
126+ } )
127+
128+ test ( "selected ids resolve to names of documents that still exist, sorted" , async ( ) => {
129+ const summary = spyOn ( AltimateApi , "getDatamate" ) . mockResolvedValueOnce ( {
130+ id : "40" , name : "w" , integrations : [ ] , memory_enabled : true , knowledge_engine_enabled : true , knowledge_bases : [ 7 , 3 , 9 ] ,
131+ } as any )
132+ const docs = spyOn ( AltimateApi , "listKnowledgeDocuments" ) . mockResolvedValueOnce ( [
133+ { id : 3 , name : "Zeta runbook" , deleted : false } ,
134+ { id : 7 , name : "Alpha metrics" , deleted : false } ,
135+ { id : 9 , name : "Removed doc" , deleted : true } ,
136+ { id : 11 , name : "Not selected" , deleted : false } ,
137+ ] )
138+ expect ( ( await Contents . workspaceSummary ( 40 ) ) . knowledge ) . toEqual ( { kind : "selected" , count : 2 , names : [ "Alpha metrics" , "Zeta runbook" ] } )
139+ summary . mockRestore ( )
140+ docs . mockRestore ( )
141+ } )
142+
143+ test ( "engine on with nothing selected is 'all'; engine off is 'off'; the document list is not fetched for either" , async ( ) => {
144+ const docs = spyOn ( AltimateApi , "listKnowledgeDocuments" )
145+ const summary = spyOn ( AltimateApi , "getDatamate" )
146+ . mockResolvedValueOnce ( { id : "41" , name : "w" , knowledge_engine_enabled : true , knowledge_bases : [ ] } as any )
147+ . mockResolvedValueOnce ( { id : "42" , name : "w" , knowledge_engine_enabled : false , knowledge_bases : [ 5 ] } as any )
148+ expect ( ( await Contents . workspaceSummary ( 41 ) ) . knowledge ) . toEqual ( { kind : "all" } )
149+ expect ( ( await Contents . workspaceSummary ( 42 ) ) . knowledge ) . toEqual ( { kind : "off" } )
150+ expect ( docs ) . not . toHaveBeenCalled ( )
151+ summary . mockRestore ( )
152+ docs . mockRestore ( )
153+ } )
154+
155+ test ( "an unreadable document list keeps the count and says the names are unknown" , async ( ) => {
156+ const summary = spyOn ( AltimateApi , "getDatamate" ) . mockResolvedValueOnce ( {
157+ id : "43" , name : "w" , knowledge_engine_enabled : true , knowledge_bases : [ 1 , 2 ] ,
158+ } as any )
159+ const docs = spyOn ( AltimateApi , "listKnowledgeDocuments" ) . mockRejectedValueOnce ( new Error ( "offline" ) )
160+ expect ( ( await Contents . workspaceSummary ( 43 ) ) . knowledge ) . toEqual ( { kind : "selected" , count : 2 , names : null } )
161+ summary . mockRestore ( )
162+ docs . mockRestore ( )
163+ } )
164+ } )
0 commit comments