1+ using Microsoft . Extensions . DependencyInjection ;
2+ using Microsoft . Extensions . Logging ;
3+ using Microsoft . Extensions . Options ;
4+ using NLWebNet . Models ;
5+ using NLWebNet . Services ;
6+
7+ namespace NLWebNet . Tests . Integration ;
8+
9+ [ TestClass ]
10+ public class MultiBackendIntegrationTests
11+ {
12+ [ TestMethod ]
13+ public async Task EndToEnd_MultiBackendSearch_WorksCorrectly ( )
14+ {
15+ // Arrange - Set up a complete multi-backend service configuration
16+ var services = new ServiceCollection ( ) ;
17+
18+ services . AddNLWebNetMultiBackend (
19+ options =>
20+ {
21+ options . DefaultMode = QueryMode . List ;
22+ options . MaxResultsPerQuery = 20 ;
23+ options . EnableDecontextualization = false ; // Simplify for test
24+ } ,
25+ multiBackendOptions =>
26+ {
27+ multiBackendOptions . Enabled = true ;
28+ multiBackendOptions . EnableParallelQuerying = true ;
29+ multiBackendOptions . EnableResultDeduplication = true ;
30+ multiBackendOptions . MaxConcurrentQueries = 2 ;
31+ multiBackendOptions . BackendTimeoutSeconds = 10 ;
32+ } ) ;
33+
34+ var serviceProvider = services . BuildServiceProvider ( ) ;
35+ var nlWebService = serviceProvider . GetRequiredService < INLWebService > ( ) ;
36+ var backendManager = serviceProvider . GetRequiredService < IBackendManager > ( ) ;
37+
38+ // Act - Perform a search using the NLWebService
39+ var request = new NLWebRequest
40+ {
41+ QueryId = "test-001" ,
42+ Query = "millennium falcon" ,
43+ Mode = QueryMode . List ,
44+ Site = null
45+ } ;
46+
47+ var response = await nlWebService . ProcessRequestAsync ( request ) ;
48+
49+ // Assert - Verify the response contains results from multiple backends
50+ Assert . IsNotNull ( response ) ;
51+ Assert . AreEqual ( "test-001" , response . QueryId ) ;
52+ Assert . IsNull ( response . Error , "Response should not have an error" ) ;
53+ Assert . IsNotNull ( response . Results ) ;
54+ Assert . IsTrue ( response . Results . Any ( ) , "Should return search results" ) ;
55+
56+ // Verify backend manager provides information about backends
57+ var backendInfo = backendManager . GetBackendInfo ( ) . ToList ( ) ;
58+ Assert . IsTrue ( backendInfo . Count >= 1 , "Should have at least one backend configured" ) ;
59+ Assert . IsTrue ( backendInfo . Any ( b => b . IsWriteEndpoint ) , "Should have a write endpoint designated" ) ;
60+
61+ // Verify write backend is accessible
62+ var writeBackend = backendManager . GetWriteBackend ( ) ;
63+ Assert . IsNotNull ( writeBackend , "Should have a write backend available" ) ;
64+
65+ var capabilities = writeBackend . GetCapabilities ( ) ;
66+ Assert . IsNotNull ( capabilities , "Write backend should have capabilities" ) ;
67+ }
68+
69+ [ TestMethod ]
70+ public async Task EndToEnd_MultiBackendDisabled_FallsBackToSingleBackend ( )
71+ {
72+ // Arrange - Set up multi-backend service but with multi-backend disabled
73+ var services = new ServiceCollection ( ) ;
74+
75+ services . AddNLWebNetMultiBackend (
76+ options =>
77+ {
78+ options . DefaultMode = QueryMode . List ;
79+ options . MaxResultsPerQuery = 20 ;
80+ options . EnableDecontextualization = false ;
81+ options . MultiBackend . Enabled = false ; // Disabled for backward compatibility
82+ } ) ;
83+
84+ var serviceProvider = services . BuildServiceProvider ( ) ;
85+ var nlWebService = serviceProvider . GetRequiredService < INLWebService > ( ) ;
86+
87+ // Act - Perform a search
88+ var request = new NLWebRequest
89+ {
90+ QueryId = "test-002" ,
91+ Query = "millennium falcon" ,
92+ Mode = QueryMode . List
93+ } ;
94+
95+ var response = await nlWebService . ProcessRequestAsync ( request ) ;
96+
97+ // Assert - Should still work in single-backend mode
98+ Assert . IsNotNull ( response ) ;
99+ Assert . AreEqual ( "test-002" , response . QueryId ) ;
100+ Assert . IsNull ( response . Error , "Response should not have an error" ) ;
101+
102+ // Verify configuration
103+ var options = serviceProvider . GetRequiredService < IOptions < NLWebOptions > > ( ) ;
104+ Assert . IsFalse ( options . Value . MultiBackend . Enabled , "Multi-backend should be disabled" ) ;
105+ }
106+
107+ [ TestMethod ]
108+ public async Task EndToEnd_StreamingResponse_WorksWithMultiBackend ( )
109+ {
110+ // Arrange
111+ var services = new ServiceCollection ( ) ;
112+
113+ services . AddNLWebNetMultiBackend ( options =>
114+ {
115+ options . EnableStreaming = true ;
116+ options . MultiBackend . Enabled = true ;
117+ } ) ;
118+
119+ var serviceProvider = services . BuildServiceProvider ( ) ;
120+ var nlWebService = serviceProvider . GetRequiredService < INLWebService > ( ) ;
121+
122+ // Act - Test streaming response
123+ var request = new NLWebRequest
124+ {
125+ QueryId = "test-003" ,
126+ Query = "millennium falcon" ,
127+ Mode = QueryMode . List ,
128+ Streaming = true
129+ } ;
130+
131+ var responseCount = 0 ;
132+ await foreach ( var response in nlWebService . ProcessRequestStreamAsync ( request ) )
133+ {
134+ responseCount ++ ;
135+ Assert . IsNotNull ( response ) ;
136+ Assert . AreEqual ( "test-003" , response . QueryId ) ;
137+
138+ // Break after a few responses to avoid long test
139+ if ( responseCount >= 3 ) break ;
140+ }
141+
142+ Assert . IsTrue ( responseCount > 0 , "Should receive streaming responses" ) ;
143+ }
144+
145+ [ TestMethod ]
146+ public async Task EndToEnd_DeduplicationAcrossBackends_WorksCorrectly ( )
147+ {
148+ // Arrange
149+ var services = new ServiceCollection ( ) ;
150+
151+ services . AddNLWebNetMultiBackend ( options =>
152+ {
153+ options . MultiBackend . Enabled = true ;
154+ options . MultiBackend . EnableResultDeduplication = true ;
155+ } ) ;
156+
157+ var serviceProvider = services . BuildServiceProvider ( ) ;
158+ var backendManager = serviceProvider . GetRequiredService < IBackendManager > ( ) ;
159+
160+ // Act - Direct test of backend manager deduplication
161+ var results = await backendManager . SearchAsync ( "millennium falcon" , maxResults : 20 ) ;
162+
163+ // Assert
164+ var resultList = results . ToList ( ) ;
165+ var uniqueUrls = resultList . Select ( r => r . Url ) . Distinct ( ) . Count ( ) ;
166+
167+ Assert . AreEqual ( resultList . Count , uniqueUrls ,
168+ "Results should be deduplicated - no duplicate URLs" ) ;
169+
170+ if ( resultList . Count > 1 )
171+ {
172+ // Verify results are sorted by score
173+ var scores = resultList . Select ( r => r . Score ) . ToList ( ) ;
174+ var sortedScores = scores . OrderByDescending ( s => s ) . ToList ( ) ;
175+ CollectionAssert . AreEqual ( sortedScores , scores ,
176+ "Results should be sorted by relevance score" ) ;
177+ }
178+ }
179+ }
0 commit comments