Skip to content

Commit 6e2a7fc

Browse files
m-reddingCopilot
andauthored
[Microsoft.ClientModel.TestFramework] Update test-proxy tool logic (Azure#52589)
* update test proxy tool location * Update sdk/core/Microsoft.ClientModel.TestFramework/src/RecordedTests/TestProxy/TestProxyProcess.cs Co-authored-by: Copilot <[email protected]> * fix --------- Co-authored-by: Copilot <[email protected]>
1 parent 7dc4bfc commit 6e2a7fc

File tree

2 files changed

+117
-84
lines changed

2 files changed

+117
-84
lines changed

sdk/core/Microsoft.ClientModel.TestFramework/src/Microsoft.ClientModel.TestFramework.csproj

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,4 @@
2222
<ItemGroup>
2323
<Compile Include="$(MSBuildThisFileDirectory)/../../System.ClientModel/src/Internal/ContentTypeUtilities.cs" LinkBase="Shared\Core" />
2424
</ItemGroup>
25-
26-
<ItemGroup>
27-
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
28-
<_Parameter1>TestProxyPath</_Parameter1>
29-
<_Parameter2>$(NuGetPackageRoot)\azure.sdk.tools.testproxy\$(TestProxyVersion)\tools\net8.0\any\Azure.Sdk.Tools.TestProxy.dll</_Parameter2>
30-
</AssemblyAttribute>
31-
</ItemGroup>
3225
</Project>

sdk/core/Microsoft.ClientModel.TestFramework/src/RecordedTests/TestProxy/TestProxyProcess.cs

Lines changed: 117 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ namespace Microsoft.ClientModel.TestFramework;
2222
public class TestProxyProcess
2323
{
2424
private static readonly string s_dotNetExe;
25+
private readonly int? _proxyPortHttp;
26+
private readonly int? _proxyPortHttps;
27+
private readonly Process? _testProxyProcess;
28+
private readonly StringBuilder _errorBuffer = new();
29+
private static readonly object _lock = new();
30+
private static TestProxyProcess? _shared;
31+
private readonly StringBuilder _output = new();
32+
private static readonly bool s_enableDebugProxyLogging;
33+
34+
internal virtual TestProxyAdminClient AdminClient { get; }
35+
internal virtual TestProxyClient ProxyClient { get; }
2536

2637
/// <summary>
2738
/// The IP address used for the test proxy. Uses 127.0.0.1 instead of localhost to avoid SSL callback slowness.
@@ -39,26 +50,6 @@ public class TestProxyProcess
3950
/// </summary>
4051
public int? ProxyPortHttps => _proxyPortHttps;
4152

42-
private readonly int? _proxyPortHttp;
43-
private readonly int? _proxyPortHttps;
44-
private readonly Process? _testProxyProcess;
45-
46-
/// <summary>
47-
/// Gets the test framework client for interacting with the test proxy.
48-
/// </summary>
49-
internal virtual TestProxyAdminClient AdminClient { get; }
50-
51-
/// <summary>
52-
/// Gets the test proxy client for proxy-specific operations.
53-
/// </summary>
54-
internal virtual TestProxyClient ProxyClient { get; }
55-
56-
private readonly StringBuilder _errorBuffer = new();
57-
private static readonly object _lock = new();
58-
private static TestProxyProcess? _shared;
59-
private readonly StringBuilder _output = new();
60-
private static readonly bool s_enableDebugProxyLogging;
61-
6253
/// <summary>
6354
/// Initializes static members of the <see cref="TestProxyProcess"/> class.
6455
/// Locates the .NET executable and configures debug logging settings.
@@ -102,24 +93,35 @@ private TestProxyProcess(string? proxyPath, bool debugMode = false)
10293

10394
debugMode |= environmentDebugMode;
10495

105-
ProcessStartInfo testProxyProcessInfo = new ProcessStartInfo(
106-
s_dotNetExe,
107-
$"\"{proxyPath}\" start -u --storage-location=\"{TestEnvironment.RepositoryRoot}\"")
96+
ProcessStartInfo testProxyProcessInfo;
97+
98+
if (proxyPath is not null)
10899
{
109-
UseShellExecute = false,
110-
RedirectStandardOutput = true,
111-
RedirectStandardError = true,
112-
EnvironmentVariables =
113-
{
114-
["ASPNETCORE_URLS"] = $"http://{IpAddress}:0;https://{IpAddress}:0",
115-
["Logging__LogLevel__Azure.Sdk.Tools.TestProxy"] = s_enableDebugProxyLogging ? "Debug" : "Error",
116-
["Logging__LogLevel__Default"] = "Error",
117-
["Logging__LogLevel__Microsoft.AspNetCore"] = s_enableDebugProxyLogging ? "Information" : "Error",
118-
["Logging__LogLevel__Microsoft.Hosting.Lifetime"] = "Information",
119-
["ASPNETCORE_Kestrel__Certificates__Default__Path"] = TestEnvironment.DevCertPath,
120-
["ASPNETCORE_Kestrel__Certificates__Default__Password"] = TestEnvironment.DevCertPassword
121-
}
122-
};
100+
testProxyProcessInfo = new ProcessStartInfo(
101+
s_dotNetExe,
102+
$"\"{proxyPath}\" start -u --storage-location=\"{TestEnvironment.RepositoryRoot}\"");
103+
}
104+
else
105+
{
106+
TryRestoreLocalTools();
107+
108+
testProxyProcessInfo = new ProcessStartInfo(
109+
s_dotNetExe,
110+
$"tool run test-proxy start -u --storage-location=\"{TestEnvironment.RepositoryRoot}\"");
111+
}
112+
113+
testProxyProcessInfo.UseShellExecute = false;
114+
testProxyProcessInfo.RedirectStandardOutput = true;
115+
testProxyProcessInfo.RedirectStandardError = true;
116+
117+
// Set environment variables
118+
testProxyProcessInfo.EnvironmentVariables["ASPNETCORE_URLS"] = $"http://{IpAddress}:0;https://{IpAddress}:0";
119+
testProxyProcessInfo.EnvironmentVariables["Logging__LogLevel__Azure.Sdk.Tools.TestProxy"] = s_enableDebugProxyLogging ? "Debug" : "Error";
120+
testProxyProcessInfo.EnvironmentVariables["Logging__LogLevel__Default"] = "Error";
121+
testProxyProcessInfo.EnvironmentVariables["Logging__LogLevel__Microsoft.AspNetCore"] = s_enableDebugProxyLogging ? "Information" : "Error";
122+
testProxyProcessInfo.EnvironmentVariables["Logging__LogLevel__Microsoft.Hosting.Lifetime"] = "Information";
123+
testProxyProcessInfo.EnvironmentVariables["ASPNETCORE_Kestrel__Certificates__Default__Path"] = TestEnvironment.DevCertPath;
124+
testProxyProcessInfo.EnvironmentVariables["ASPNETCORE_Kestrel__Certificates__Default__Password"] = TestEnvironment.DevCertPassword;
123125

124126
_testProxyProcess = Process.Start(testProxyProcessInfo);
125127

@@ -168,7 +170,13 @@ private TestProxyProcess(string? proxyPath, bool debugMode = false)
168170

169171
if (_proxyPortHttp == null || _proxyPortHttps == null)
170172
{
171-
CheckForErrors();
173+
if (_errorBuffer.Length > 0)
174+
{
175+
var error = _errorBuffer.ToString();
176+
_errorBuffer.Clear();
177+
throw new InvalidOperationException($"An error occurred in the test proxy: {error}");
178+
}
179+
172180
// if no errors, fallback to this exception
173181
throw new InvalidOperationException("Failed to start the test proxy. One or both of the ports was not populated." + Environment.NewLine +
174182
$"http: {_proxyPortHttp}" + Environment.NewLine +
@@ -192,6 +200,69 @@ private TestProxyProcess(string? proxyPath, bool debugMode = false)
192200
});
193201
}
194202

203+
private static bool TryParsePort(string? output, string scheme, out int? port)
204+
{
205+
if (output == null)
206+
{
207+
TestContext.Progress.WriteLine("output was null");
208+
port = null;
209+
return false;
210+
}
211+
string nowListeningOn = "Now listening on: ";
212+
int nowListeningOnLength = nowListeningOn.Length;
213+
var index = output.IndexOf($"{nowListeningOn}{scheme}:", StringComparison.CurrentCultureIgnoreCase);
214+
if (index > -1)
215+
{
216+
var start = index + nowListeningOnLength;
217+
var uri = output.Substring(start, output.Length - start).Trim();
218+
port = new Uri(uri).Port;
219+
return true;
220+
}
221+
222+
port = null;
223+
return false;
224+
}
225+
226+
private static void TryRestoreLocalTools()
227+
{
228+
try
229+
{
230+
var currentDir = Directory.GetCurrentDirectory();
231+
while (currentDir != null)
232+
{
233+
var toolsJsonPath = Path.Combine(currentDir, ".config", "dotnet-tools.json");
234+
if (File.Exists(toolsJsonPath))
235+
{
236+
// Found a tools manifest, try to restore
237+
var processInfo = new ProcessStartInfo
238+
{
239+
FileName = s_dotNetExe,
240+
Arguments = "tool restore",
241+
WorkingDirectory = currentDir,
242+
UseShellExecute = false,
243+
RedirectStandardOutput = true,
244+
RedirectStandardError = true,
245+
CreateNoWindow = true
246+
};
247+
248+
using var process = Process.Start(processInfo);
249+
if (process != null)
250+
{
251+
process.WaitForExit(30000);
252+
}
253+
break;
254+
}
255+
256+
var parentDir = Directory.GetParent(currentDir);
257+
currentDir = parentDir?.FullName;
258+
}
259+
}
260+
catch
261+
{
262+
// If restore fails, silently continue - the dotnet test-proxy command will handle it
263+
}
264+
}
265+
195266
/// <summary>
196267
/// Starts the test proxy
197268
/// </summary>
@@ -209,12 +280,8 @@ public static TestProxyProcess Start(bool debugMode = false)
209280
var shared = _shared;
210281
if (shared == null)
211282
{
212-
shared = new TestProxyProcess(typeof(TestProxyProcess)
213-
.Assembly
214-
.GetCustomAttributes<AssemblyMetadataAttribute>()
215-
.Single(a => a.Key == "TestProxyPath")
216-
.Value,
217-
debugMode);
283+
var proxyPath = GetTestProxyPath();
284+
shared = new TestProxyProcess(proxyPath, debugMode);
218285

219286
AppDomain.CurrentDomain.DomainUnload += (_, _) =>
220287
{
@@ -228,34 +295,16 @@ public static TestProxyProcess Start(bool debugMode = false)
228295
}
229296
}
230297

231-
/// <summary>
232-
/// Attempts to parse a port number from test proxy output for the specified scheme.
233-
/// </summary>
234-
/// <param name="output">The output line from the test proxy.</param>
235-
/// <param name="scheme">The URI scheme (http or https) to parse.</param>
236-
/// <param name="port">When this method returns, contains the parsed port number if successful; otherwise, null.</param>
237-
/// <returns>true if the port was successfully parsed; otherwise, false.</returns>
238-
private static bool TryParsePort(string? output, string scheme, out int? port)
298+
private static string? GetTestProxyPath()
239299
{
240-
if (output == null)
241-
{
242-
TestContext.Progress.WriteLine("output was null");
243-
port = null;
244-
return false;
245-
}
246-
string nowListeningOn = "Now listening on: ";
247-
int nowListeningOnLength = nowListeningOn.Length;
248-
var index = output.IndexOf($"{nowListeningOn}{scheme}:", StringComparison.CurrentCultureIgnoreCase);
249-
if (index > -1)
300+
// Look for environment variable override
301+
var envPath = Environment.GetEnvironmentVariable("TEST_PROXY_EXE_PATH");
302+
if (!string.IsNullOrEmpty(envPath))
250303
{
251-
var start = index + nowListeningOnLength;
252-
var uri = output.Substring(start, output.Length - start).Trim();
253-
port = new Uri(uri).Port;
254-
return true;
304+
return envPath;
255305
}
256306

257-
port = null;
258-
return false;
307+
return null;
259308
}
260309

261310
/// <summary>
@@ -278,15 +327,6 @@ public virtual async Task CheckProxyOutputAsync()
278327
}
279328
}
280329

281-
CheckForErrors();
282-
}
283-
284-
/// <summary>
285-
/// Checks for any errors in the error buffer and throws an exception if errors are found.
286-
/// </summary>
287-
/// <exception cref="InvalidOperationException">Thrown when errors are found in the test proxy.</exception>
288-
private void CheckForErrors()
289-
{
290330
if (_errorBuffer.Length > 0)
291331
{
292332
var error = _errorBuffer.ToString();

0 commit comments

Comments
 (0)