Skip to content

Conversation

mdaigle
Copy link
Contributor

@mdaigle mdaigle commented Jul 17, 2025

Description

The TDS.Servers project provides mocked server implementations for a variety of use cases. The construction and endpoint initialization flows for these mock servers was complicated and resulted in duplicated code across TDS.Servers, FunctionalTests, and ManualTests. This PR consolidates endpoint initialization and makes test server constructor parameters more explicit (no more defaults buried a few layers deep).

One downside of this approach is that it's now slightly harder to get the connection string for a test server. Before, some servers had a ConnectionString property you could access. The issue with these connection strings is they had a lot of non-standard default behavior coded into them at the time they were constructed. This makes it hard to understand which properties you're actually using when connecting to a test server. With my changes, you now need to manually construct a connection string using SqlConnectionStringBuilder and explicitly set connection parameters. Any parameters you don't set use the stock connection string defaults.

Testing

I added some testing around pool invalidation during failover and routing behavior during transient errors.

@mdaigle mdaigle added the Area\Tests Issues that are targeted to tests or test projects label Jul 17, 2025
Copy link
Contributor

@paulmedynski paulmedynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, with a few suggestions.

public TdsServer() : this(new TdsServerArguments())
{
}
/// <summary>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a blank line here.

Copy link
Contributor

@paulmedynski paulmedynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few tidying-up suggestions.

Copy link
Contributor

@paulmedynski paulmedynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of suggestions.

if (Arguments.IsEnabledPermanentTimeout ||
(Arguments.IsEnabledTransientTimeout && RequestCounter < 1)) // Fail first time, then connect
(Arguments.IsEnabledTransientTimeout && RequestCounter < 1))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestCounter < Arguments.RepeatCount ?

@paulmedynski paulmedynski self-assigned this Sep 4, 2025
Copy link
Contributor

@paulmedynski paulmedynski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still some outstanding requests from earlier PRs as well. I will go back through them and see if they have been addressed.

@paulmedynski
Copy link
Contributor

Two small comments remain. Should this convereted from a draft now?

@mdaigle mdaigle marked this pull request as ready for review September 5, 2025 16:20
@Copilot Copilot AI review requested due to automatic review settings September 5, 2025 16:20
@mdaigle mdaigle requested a review from a team as a code owner September 5, 2025 16:20
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the Test TDS Servers infrastructure by consolidating endpoint initialization and improving the construction flow for mock servers. The changes eliminate duplicated code across TDS.Servers, FunctionalTests, and ManualTests while making constructor parameters more explicit rather than hiding defaults in nested calls.

Key changes include:

  • Standardized naming from "TDS" to "Tds" across server classes for consistency
  • Replaced properties with field initializers for server arguments to make defaults explicit
  • Simplified endpoint initialization by removing auto-generated connection strings
  • Added new transient delay and error servers for improved testing scenarios
  • Consolidated connection string building using SqlConnectionStringBuilder

Reviewed Changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
TransientTdsErrorTdsServerArguments.cs Renamed class and replaced properties with field initializers for clearer defaults
TransientTdsErrorTdsServer.cs New server class for testing transient error scenarios with explicit error behavior
TransientDelayTdsServerArguments.cs New arguments class for delay simulation testing
TransientDelayTdsServer.cs New server class for simulating network delays during connections
TdsServerArguments.cs Renamed class and converted properties to field initializers
TdsServer.cs New simplified TDS server implementation
GenericTdsServer.cs Refactored to use generics and added endpoint management
Connection test files Updated to use new server construction patterns and explicit connection strings

Comment on lines +20 to +22
public void SetErrorBehavior(bool isEnabledTransientFault, uint errorNumber, int repeatCount = 1, string message = null)
{
Arguments.IsEnabledTransientError = isEnabledTransientFault;
Copy link
Preview

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter name 'isEnabledTransientFault' is inconsistent with the field name 'IsEnabledTransientError'. Consider renaming the parameter to 'isEnabledTransientError' for consistency.

Suggested change
public void SetErrorBehavior(bool isEnabledTransientFault, uint errorNumber, int repeatCount = 1, string message = null)
{
Arguments.IsEnabledTransientError = isEnabledTransientFault;
public void SetErrorBehavior(bool isEnabledTransientError, uint errorNumber, int repeatCount = 1, string message = null)
{
Arguments.IsEnabledTransientError = isEnabledTransientError;

Copilot uses AI. Check for mistakes.

Comment on lines +122 to +128
public void Start([CallerMemberName] string methodName = "")
{
_endpoint = new TDSServerEndPoint(this) { ServerEndPoint = new IPEndPoint(IPAddress.Any, 0) };
_endpoint.EndpointName = methodName;
_endpoint.EventLog = Arguments.Log;
_endpoint.Start();
}
Copy link
Preview

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Start method creates a new endpoint instance but doesn't check if one already exists. This could lead to resource leaks if Start is called multiple times. Consider adding a check for existing endpoint or making the method idempotent.

Copilot uses AI. Check for mistakes.

@@ -880,5 +910,7 @@ private bool AreEqual(byte[] left, byte[] right)

return left.SequenceEqual<byte>(right);
}

public virtual void Dispose() => _endpoint?.Dispose();
Copy link
Preview

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Dispose method only disposes the endpoint but doesn't set it to null or prevent further usage. Consider implementing the full IDisposable pattern with a disposed flag to prevent usage after disposal.

Copilot uses AI. Check for mistakes.

{
/// <summary>
/// Worker thread
/// </summary>
protected Thread ProcessorThread { get; set; }
protected Task ProcessorTask { get; set; }
Copy link
Preview

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ProcessorTask property should be disposed in the Dispose method to prevent potential resource leaks from unfinished tasks.

Copilot uses AI. Check for mistakes.

using RoutingTdsServer router = new RoutingTdsServer(
new RoutingTdsServerArguments()
{
//RoutingTCPHost = server.EndPoint.Address.ToString() == IPAddress.Any.ToString() ? IPAddress.Loopback.ToString() : server.EndPoint.Address.ToString(),
Copy link
Preview

Copilot AI Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented-out code. If this logic might be needed later, consider documenting the reason or implementing it properly.

Suggested change
//RoutingTCPHost = server.EndPoint.Address.ToString() == IPAddress.Any.ToString() ? IPAddress.Loopback.ToString() : server.EndPoint.Address.ToString(),

Copilot uses AI. Check for mistakes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area\Tests Issues that are targeted to tests or test projects
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants