Skip to content

Commit 8db6337

Browse files
Merge pull request #152 from microsoft/dev
chore: merging dev changes to main
2 parents 0123afd + fd39f3c commit 8db6337

File tree

30 files changed

+18609
-14
lines changed

30 files changed

+18609
-14
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
name: .NET Unit Tests with Coverage
2+
3+
on:
4+
pull_request:
5+
branches: [ main, dev ]
6+
paths:
7+
- 'src/api/dotnet/**'
8+
- '.github/workflows/unit-tests-dotnet.yml'
9+
push:
10+
branches: [ main, dev ]
11+
paths:
12+
- 'src/api/dotnet/**'
13+
- '.github/workflows/unit-tests-dotnet.yml'
14+
workflow_dispatch:
15+
16+
env:
17+
DOTNET_VERSION: '10.0.x'
18+
COVERAGE_THRESHOLD: 80
19+
20+
permissions:
21+
contents: read
22+
pull-requests: write
23+
issues: write
24+
checks: write
25+
26+
jobs:
27+
unit-tests:
28+
runs-on: ubuntu-latest
29+
30+
steps:
31+
- name: Checkout repository
32+
uses: actions/checkout@v5
33+
34+
- name: Set up .NET ${{ env.DOTNET_VERSION }}
35+
uses: actions/setup-dotnet@v4
36+
with:
37+
dotnet-version: ${{ env.DOTNET_VERSION }}
38+
dotnet-quality: 'preview'
39+
40+
- name: Restore dependencies
41+
run: |
42+
cd src/api/dotnet
43+
dotnet restore
44+
45+
- name: Build solution
46+
run: |
47+
cd src/api/dotnet
48+
dotnet build --no-restore --configuration Release
49+
50+
- name: Run unit tests with coverage
51+
id: dotnet-test
52+
run: |
53+
cd src/api/dotnet
54+
dotnet test \
55+
--no-build \
56+
--configuration Release \
57+
--settings "tests/CsApi.Tests/coverlet.runsettings" \
58+
--collect:"XPlat Code Coverage" \
59+
--logger "trx;LogFileName=test-results.trx" \
60+
--results-directory ./TestResults \
61+
-v normal
62+
continue-on-error: true
63+
64+
- name: Find coverage file
65+
id: find-coverage
66+
if: always()
67+
run: |
68+
COVERAGE_FILE=$(find src/api/dotnet/TestResults -name "coverage.cobertura.xml" -type f | head -1)
69+
echo "coverage_file=$COVERAGE_FILE" >> $GITHUB_OUTPUT
70+
echo "Found coverage file: $COVERAGE_FILE"
71+
72+
- name: Upload coverage reports
73+
uses: actions/upload-artifact@v4
74+
if: always()
75+
with:
76+
name: coverage-report-dotnet
77+
path: |
78+
src/api/dotnet/TestResults/
79+
${{ steps.find-coverage.outputs.coverage_file }}
80+
81+
- name: Generate test summary
82+
if: always()
83+
run: |
84+
echo "## 🧪 .NET Unit Test Results" >> $GITHUB_STEP_SUMMARY
85+
echo "" >> $GITHUB_STEP_SUMMARY
86+
87+
# Parse test results from trx file
88+
TRX_FILE=$(find src/api/dotnet/TestResults -name "*.trx" -type f | head -1)
89+
if [ -f "$TRX_FILE" ]; then
90+
echo "### 📊 Test Statistics" >> $GITHUB_STEP_SUMMARY
91+
echo "" >> $GITHUB_STEP_SUMMARY
92+
93+
# Extract test counts from trx file
94+
TOTAL=$(grep -oP 'total="\K[0-9]+' "$TRX_FILE" | head -1 || echo "0")
95+
PASSED=$(grep -oP 'passed="\K[0-9]+' "$TRX_FILE" | head -1 || echo "0")
96+
FAILED=$(grep -oP 'failed="\K[0-9]+' "$TRX_FILE" | head -1 || echo "0")
97+
SKIPPED=$(grep -oP 'skipped="\K[0-9]+' "$TRX_FILE" | head -1 || echo "0")
98+
99+
echo "| Metric | Count |" >> $GITHUB_STEP_SUMMARY
100+
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
101+
echo "| Total Tests | $TOTAL |" >> $GITHUB_STEP_SUMMARY
102+
echo "| ✅ Passed | $PASSED |" >> $GITHUB_STEP_SUMMARY
103+
echo "| ❌ Failed | $FAILED |" >> $GITHUB_STEP_SUMMARY
104+
echo "| ⏭️ Skipped | $SKIPPED |" >> $GITHUB_STEP_SUMMARY
105+
echo "" >> $GITHUB_STEP_SUMMARY
106+
fi
107+
108+
# Parse coverage from coverage.xml
109+
COVERAGE_FILE="${{ steps.find-coverage.outputs.coverage_file }}"
110+
if [ -f "$COVERAGE_FILE" ]; then
111+
echo "### 📈 Code Coverage" >> $GITHUB_STEP_SUMMARY
112+
echo "" >> $GITHUB_STEP_SUMMARY
113+
114+
COVERAGE=$(grep -oP 'line-rate="\K[0-9.]+' "$COVERAGE_FILE" | head -1 || echo "0")
115+
BRANCH_COVERAGE=$(grep -oP 'branch-rate="\K[0-9.]+' "$COVERAGE_FILE" | head -1 || echo "0")
116+
COVERAGE_PCT=$(awk "BEGIN {printf \"%.0f\", $COVERAGE * 100}")
117+
BRANCH_PCT=$(awk "BEGIN {printf \"%.0f\", $BRANCH_COVERAGE * 100}")
118+
119+
echo "| Coverage Type | Percentage | Status |" >> $GITHUB_STEP_SUMMARY
120+
echo "|---------------|------------|--------|" >> $GITHUB_STEP_SUMMARY
121+
122+
if [ "$COVERAGE_PCT" -ge "${{ env.COVERAGE_THRESHOLD }}" ]; then
123+
echo "| Line Coverage | **${COVERAGE_PCT}%** | ✅ Passed (Threshold: ${{ env.COVERAGE_THRESHOLD }}%) |" >> $GITHUB_STEP_SUMMARY
124+
else
125+
echo "| Line Coverage | **${COVERAGE_PCT}%** | ❌ Failed (Threshold: ${{ env.COVERAGE_THRESHOLD }}%) |" >> $GITHUB_STEP_SUMMARY
126+
fi
127+
echo "| Branch Coverage | **${BRANCH_PCT}%** | ℹ️ Info |" >> $GITHUB_STEP_SUMMARY
128+
echo "" >> $GITHUB_STEP_SUMMARY
129+
fi
130+
131+
# Overall status
132+
if [ "${{ steps.dotnet-test.outcome }}" == "success" ]; then
133+
echo "### ✅ Overall Status: PASSED" >> $GITHUB_STEP_SUMMARY
134+
echo "All unit tests passed and coverage threshold met." >> $GITHUB_STEP_SUMMARY
135+
else
136+
echo "### ❌ Overall Status: FAILED" >> $GITHUB_STEP_SUMMARY
137+
echo "Unit tests failed or coverage below threshold (${{ env.COVERAGE_THRESHOLD }}%)." >> $GITHUB_STEP_SUMMARY
138+
fi
139+
140+
- name: Check coverage threshold
141+
if: always()
142+
run: |
143+
COVERAGE_FILE="${{ steps.find-coverage.outputs.coverage_file }}"
144+
if [ -f "$COVERAGE_FILE" ]; then
145+
COVERAGE=$(grep -oP 'line-rate="\K[0-9.]+' "$COVERAGE_FILE" | head -1 || echo "0")
146+
COVERAGE_PCT=$(awk "BEGIN {printf \"%.0f\", $COVERAGE * 100}")
147+
148+
if [ "$COVERAGE_PCT" -lt "${{ env.COVERAGE_THRESHOLD }}" ]; then
149+
echo "::error::Code coverage ${COVERAGE_PCT}% is below threshold ${{ env.COVERAGE_THRESHOLD }}%"
150+
exit 1
151+
fi
152+
fi
153+
154+
- name: Fail if tests failed
155+
if: steps.dotnet-test.outcome != 'success'
156+
run: |
157+
echo "::error::Unit tests failed"
158+
exit 1
159+
160+
- name: Publish test results
161+
uses: EnricoMi/publish-unit-test-result-action@v2
162+
if: always()
163+
with:
164+
files: src/api/dotnet/TestResults/**/*.trx
165+
check_name: .NET Unit Test Results

src/App/src/components/Chat/Chat.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ const Chat: React.FC<ChatProps> = ({
9191
if (!convId || !newMessages.length) {
9292
return;
9393
}
94-
const isNewConversation = reqType !== 'graph' ? !selectedConversationId : false;
94+
const isNewConversation = !selectedConversationId;
9595

9696
if (false) { // Disabled: chart display default
9797
setIsChartLoading(true);
@@ -508,12 +508,15 @@ const Chat: React.FC<ChatProps> = ({
508508
} catch {
509509
// Error parsing chart response
510510
}
511-
} else if (!isChartResponseReceived) {
511+
}
512+
513+
// If no messages have been added yet but we have streamed content, save it
514+
if (updatedMessages.length === 0 && streamMessage.content) {
512515
updatedMessages = [newMessage, streamMessage];
513516
}
514517
}
515518

516-
if (updatedMessages.length > 0 && updatedMessages[updatedMessages.length - 1]?.role !== ERROR) {
519+
if (updatedMessages.length > 0) {
517520
saveToDB(updatedMessages, conversationId, isChatReq);
518521
}
519522
} catch (e) {

src/App/src/components/ChatHistoryListItemCell/ChatHistoryListItemCell.module.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
overflow: hidden;
4141
white-space: nowrap;
4242
text-overflow: ellipsis;
43-
padding-right: 8px;
4443
}
4544

4645
.chatHistoryItem {
@@ -51,6 +50,6 @@
5150

5251
.chatHistoryItemsButtonsContainer {
5352
flex-shrink: 0;
54-
margin-left: auto;
53+
margin-left: 8px;
5554
}
5655

src/App/src/components/ChatHistoryListItemCell/ChatHistoryListItemCell.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,7 @@ export const ChatHistoryListItemCell: React.FC<
119119
}
120120
};
121121

122-
const truncatedTitle =
123-
item?.title?.length > 28
124-
? `${item.title.substring(0, 28)} ...`
125-
: item.title;
122+
const displayTitle = item?.title;
126123

127124
const handleSaveEdit = async (e: any) => {
128125
e.preventDefault();
@@ -295,7 +292,7 @@ export const ChatHistoryListItemCell: React.FC<
295292
>
296293
<Stack horizontal verticalAlign={"center"} style={{ flex: 1, minWidth: 0 }}>
297294
<div className={styles.chatTitle}>
298-
{truncatedTitle}
295+
{displayTitle}
299296
</div>
300297
{deleteLoading && (
301298
<Spinner
@@ -305,11 +302,14 @@ export const ChatHistoryListItemCell: React.FC<
305302
/>
306303
)}
307304
</Stack>
308-
{!deleteLoading && (isHovered || isSelected) && (
305+
{!deleteLoading && (
309306
<Stack
310307
horizontal
311308
horizontalAlign="end"
312309
className={styles.chatHistoryItemsButtonsContainer}
310+
style={{
311+
visibility: isHovered || isSelected ? 'visible' : 'hidden'
312+
}}
313313
>
314314
<IconButton
315315
className={styles.itemButton}

src/api/dotnet/Controllers/HistoryFabController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public async Task<IActionResult> Read(
7979
{
8080
var nextMessage = validMessages[j];
8181

82-
if (nextMessage.Role == "assistant")
82+
if (nextMessage.Role == "assistant" || nextMessage.Role == "error")
8383
{
8484
pairedAssistant = nextMessage;
8585
break;

src/api/dotnet/CsApi.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
<NoWarn>1591;SKEXP0110;CA2252</NoWarn>
88
<EnablePreviewFeatures>true</EnablePreviewFeatures>
99
</PropertyGroup>
10+
11+
<!-- Exclude test project files from compilation -->
12+
<ItemGroup>
13+
<Compile Remove="tests\**" />
14+
<Content Remove="tests\**" />
15+
<None Remove="tests\**" />
16+
</ItemGroup>
17+
1018
<ItemGroup>
1119
<PackageReference Include="Azure.AI.Projects.OpenAI" Version="1.0.0-beta.4" />
1220
<PackageReference Include="Azure.AI.Projects" Version="1.2.0-beta.3" />

src/api/dotnet/CsApi.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.14.36408.4 d17.14
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsApi", "CsApi.csproj", "{52BFFFD8-2994-82EF-37A8-5E6F7286921D}"
77
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsApi.Tests", "tests\CsApi.Tests\CsApi.Tests.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
9+
EndProject
810
Global
911
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1012
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +17,10 @@ Global
1517
{52BFFFD8-2994-82EF-37A8-5E6F7286921D}.Debug|Any CPU.Build.0 = Debug|Any CPU
1618
{52BFFFD8-2994-82EF-37A8-5E6F7286921D}.Release|Any CPU.ActiveCfg = Release|Any CPU
1719
{52BFFFD8-2994-82EF-37A8-5E6F7286921D}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
1824
EndGlobalSection
1925
GlobalSection(SolutionProperties) = preSolution
2026
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)