@@ -54,7 +54,7 @@ public async Task BuildContextAsync_IncludesBoardName()
5454 }
5555
5656 [ Fact ]
57- public async Task BuildContextAsync_IncludesColumnNamesAndPositions ( )
57+ public async Task BuildContextAsync_IncludesColumnFlowLine ( )
5858 {
5959 var board = new Board ( "Dev Board" , ownerId : Guid . NewGuid ( ) ) ;
6060 var boardId = board . Id ;
@@ -65,23 +65,16 @@ public async Task BuildContextAsync_IncludesColumnNamesAndPositions()
6565
6666 _boardRepoMock . Setup ( r => r . GetByIdAsync ( boardId , default ) ) . ReturnsAsync ( board ) ;
6767 _columnRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { col1 , col2 , col3 } ) ;
68- _cardRepoMock . Setup ( r => r . GetByColumnIdAsync ( col1 . Id , default ) ) . ReturnsAsync ( Array . Empty < Card > ( ) ) ;
69- _cardRepoMock . Setup ( r => r . GetByColumnIdAsync ( col2 . Id , default ) ) . ReturnsAsync ( Array . Empty < Card > ( ) ) ;
70- _cardRepoMock . Setup ( r => r . GetByColumnIdAsync ( col3 . Id , default ) ) . ReturnsAsync ( Array . Empty < Card > ( ) ) ;
68+ _cardRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( Array . Empty < Card > ( ) ) ;
7169 _labelRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( Array . Empty < Label > ( ) ) ;
7270
7371 var result = await _builder . BuildContextAsync ( boardId ) ;
7472
75- result . Should ( ) . Contain ( "To Do" ) ;
76- result . Should ( ) . Contain ( "In Progress" ) ;
77- result . Should ( ) . Contain ( "Done" ) ;
78- result . Should ( ) . Contain ( "position 0" ) ;
79- result . Should ( ) . Contain ( "position 1" ) ;
80- result . Should ( ) . Contain ( "position 2" ) ;
73+ result . Should ( ) . Contain ( "Columns: To Do → In Progress → Done" ) ;
8174 }
8275
8376 [ Fact ]
84- public async Task BuildContextAsync_IncludesCardTitlesUnderColumns ( )
77+ public async Task BuildContextAsync_IncludesCardIdsAndTitlesUnderColumns ( )
8578 {
8679 var board = new Board ( "Dev Board" , ownerId : Guid . NewGuid ( ) ) ;
8780 var boardId = board . Id ;
@@ -94,13 +87,22 @@ public async Task BuildContextAsync_IncludesCardTitlesUnderColumns()
9487
9588 _boardRepoMock . Setup ( r => r . GetByIdAsync ( boardId , default ) ) . ReturnsAsync ( board ) ;
9689 _columnRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { col } ) ;
97- _cardRepoMock . Setup ( r => r . GetByColumnIdAsync ( colId , default ) ) . ReturnsAsync ( new [ ] { card1 , card2 } ) ;
90+ _cardRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { card1 , card2 } ) ;
9891 _labelRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( Array . Empty < Label > ( ) ) ;
9992
10093 var result = await _builder . BuildContextAsync ( boardId ) ;
10194
10295 result . Should ( ) . Contain ( "Fix login bug" ) ;
10396 result . Should ( ) . Contain ( "Update README" ) ;
97+
98+ // Card IDs should appear as short hex prefixes in brackets
99+ var shortId1 = BoardContextBuilder . FormatShortId ( card1 . Id ) ;
100+ var shortId2 = BoardContextBuilder . FormatShortId ( card2 . Id ) ;
101+ result . Should ( ) . Contain ( $ "[{ shortId1 } ]") ;
102+ result . Should ( ) . Contain ( $ "[{ shortId2 } ]") ;
103+
104+ // Cards should appear under column heading
105+ result . Should ( ) . Contain ( "Cards in \" To Do\" :" ) ;
104106 }
105107
106108 [ Fact ]
@@ -137,7 +139,7 @@ public async Task BuildContextAsync_LimitsCardsPerColumn()
137139
138140 _boardRepoMock . Setup ( r => r . GetByIdAsync ( boardId , default ) ) . ReturnsAsync ( board ) ;
139141 _columnRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { col } ) ;
140- _cardRepoMock . Setup ( r => r . GetByColumnIdAsync ( colId , default ) ) . ReturnsAsync ( cards ) ;
142+ _cardRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( cards ) ;
141143 _labelRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( Array . Empty < Label > ( ) ) ;
142144
143145 var result = await _builder . BuildContextAsync ( boardId ) ;
@@ -164,15 +166,14 @@ public async Task BuildContextAsync_RespectsTokenBudget()
164166 . Select ( i => new Label ( boardId , $ "Label-{ i } ", "#FF0000" ) )
165167 . ToList ( ) ;
166168
169+ var allCards = columns . SelectMany ( col =>
170+ Enumerable . Range ( 0 , 10 )
171+ . Select ( j => new Card ( boardId , col . Id , $ "A card with a fairly long title in column { col . Name } number { j } ") )
172+ ) . ToList ( ) ;
173+
167174 _boardRepoMock . Setup ( r => r . GetByIdAsync ( boardId , default ) ) . ReturnsAsync ( board ) ;
168175 _columnRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( columns ) ;
169- foreach ( var col in columns )
170- {
171- var colCards = Enumerable . Range ( 0 , 10 )
172- . Select ( j => new Card ( boardId , col . Id , $ "A card with a fairly long title in column { col . Name } number { j } ") )
173- . ToList ( ) ;
174- _cardRepoMock . Setup ( r => r . GetByColumnIdAsync ( col . Id , default ) ) . ReturnsAsync ( colCards ) ;
175- }
176+ _cardRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( allCards ) ;
176177 _labelRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( labels ) ;
177178
178179 var result = await _builder . BuildContextAsync ( boardId ) ;
@@ -208,6 +209,66 @@ public async Task BuildContextAsync_OmitsColumnsSection_WhenNoColumns()
208209
209210 var result = await _builder . BuildContextAsync ( boardId ) ;
210211
211- result . Should ( ) . NotContain ( "Columns (in order):" ) ;
212+ result . Should ( ) . NotContain ( "Columns:" ) ;
213+ result . Should ( ) . NotContain ( "Cards in" ) ;
214+ }
215+
216+ [ Fact ]
217+ public async Task BuildContextAsync_SkipsEmptyColumns ( )
218+ {
219+ var board = new Board ( "Dev Board" , ownerId : Guid . NewGuid ( ) ) ;
220+ var boardId = board . Id ;
221+
222+ var col1 = new Column ( boardId , "Empty Column" , 0 ) ;
223+ var col2 = new Column ( boardId , "Has Cards" , 1 ) ;
224+ var card = new Card ( boardId , col2 . Id , "A card" ) ;
225+
226+ _boardRepoMock . Setup ( r => r . GetByIdAsync ( boardId , default ) ) . ReturnsAsync ( board ) ;
227+ _columnRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { col1 , col2 } ) ;
228+ _cardRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { card } ) ;
229+ _labelRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( Array . Empty < Label > ( ) ) ;
230+
231+ var result = await _builder . BuildContextAsync ( boardId ) ;
232+
233+ result . Should ( ) . NotContain ( "Cards in \" Empty Column\" " ) ;
234+ result . Should ( ) . Contain ( "Cards in \" Has Cards\" " ) ;
235+ }
236+
237+ [ Fact ]
238+ public void FormatShortId_ReturnsFirst8HexChars ( )
239+ {
240+ var id = Guid . Parse ( "abcdef12-3456-7890-abcd-ef1234567890" ) ;
241+ var shortId = BoardContextBuilder . FormatShortId ( id ) ;
242+
243+ shortId . Should ( ) . Be ( "abcdef12" ) ;
244+ shortId . Should ( ) . HaveLength ( BoardContextBuilder . ShortIdLength ) ;
245+ }
246+
247+ [ Fact ]
248+ public async Task BuildContextAsync_IncludesCardLabels ( )
249+ {
250+ var board = new Board ( "Dev Board" , ownerId : Guid . NewGuid ( ) ) ;
251+ var boardId = board . Id ;
252+
253+ var label = new Label ( boardId , "Bug" , "#FF0000" ) ;
254+ var col = new Column ( boardId , "To Do" , 0 ) ;
255+ var card = new Card ( boardId , col . Id , "Fix crash" ) ;
256+ card . AddLabel ( new CardLabel ( card . Id , label . Id ) ) ;
257+
258+ _boardRepoMock . Setup ( r => r . GetByIdAsync ( boardId , default ) ) . ReturnsAsync ( board ) ;
259+ _columnRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { col } ) ;
260+ _cardRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { card } ) ;
261+ _labelRepoMock . Setup ( r => r . GetByBoardIdAsync ( boardId , default ) ) . ReturnsAsync ( new [ ] { label } ) ;
262+
263+ var result = await _builder . BuildContextAsync ( boardId ) ;
264+
265+ result . Should ( ) . Contain ( "Fix crash [Bug]" ) ;
266+ }
267+
268+ [ Fact ]
269+ public async Task BuildContextAsync_BudgetIs4000 ( )
270+ {
271+ // Verify the budget constant is 4000 chars
272+ BoardContextBuilder . MaxContextCharacters . Should ( ) . Be ( 4000 ) ;
212273 }
213274}
0 commit comments