Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public void Defaults_WhenNotOverridden_MatchNativeProtocolConventions()
Assert.That(options.DialTimeout, Is.EqualTo(TimeSpan.FromSeconds(30)));
Assert.That(options.ReadTimeout, Is.EqualTo(TimeSpan.FromSeconds(300)));
Assert.That(options.MaxSendBufferBytes, Is.EqualTo(10 * 1024 * 1024));
Assert.That(options.MinPoolSize, Is.Zero);
Assert.That(options.MaxPoolSize, Is.EqualTo(20));
Assert.That(options.PoolTimeout, Is.EqualTo(TimeSpan.FromSeconds(30)));
Assert.That(options.MaxConnectionLifetime, Is.EqualTo(TimeSpan.FromMinutes(30)));
Assert.That(options.IdleTimeout, Is.EqualTo(TimeSpan.FromMinutes(5)));
Assert.That(options.PoolReusePolicy, Is.EqualTo(ClickHouseTcpPoolReusePolicy.Lifo));
});
}

Expand Down Expand Up @@ -49,6 +55,22 @@ public void Validate_EmptyUsername_ThrowsArgumentException()
Assert.Throws<ArgumentException>(() => options.Validate());
}

[Test]
public void Validate_EmptyDatabase_ThrowsArgumentException()
{
var options = new ClickHouseTcpClientOptions { Database = "" };

Assert.Throws<ArgumentException>(() => options.Validate());
}

[Test]
public void Validate_NonPositiveReadTimeout_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { ReadTimeout = TimeSpan.Zero };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[TestCase(0)]
[TestCase(-1)]
[TestCase(65536)]
Expand All @@ -75,6 +97,154 @@ public void Validate_NonPositiveMaxSendBufferBytes_ThrowsArgumentOutOfRangeExcep
Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[Test]
public void Validate_NonPositivePoolTimeout_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { PoolTimeout = TimeSpan.Zero };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[TestCase(0)]
[TestCase(-1)]
public void Validate_MaxPoolSizeBelowOne_ThrowsArgumentOutOfRangeException(int maxPoolSize)
{
var options = new ClickHouseTcpClientOptions { MaxPoolSize = maxPoolSize };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[Test]
public void Validate_NegativeMinPoolSize_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { MinPoolSize = -1 };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[Test]
public void Validate_NoSweepInterval_DoesNotThrow()
{
// Null is not a value out of range but a request to derive the period, so it must pass validation.
var options = new ClickHouseTcpClientOptions { SweepInterval = null };

Assert.Multiple(() =>
{
Assert.DoesNotThrow(() => options.Validate());
Assert.That(new ClickHouseTcpClientOptions().SweepInterval, Is.Null, "deriving the period is the default");
});
}

[TestCase(0)]
[TestCase(-1)]
public void Validate_NonPositiveSweepInterval_ThrowsArgumentOutOfRangeException(int seconds)
{
// Zero does not mean "no sweep" here, unlike the two limits: that is what leaving it null does.
var options = new ClickHouseTcpClientOptions { SweepInterval = TimeSpan.FromSeconds(seconds) };

var thrown = Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());

Assert.That(thrown.Message, Does.Contain("SweepInterval"));
}

[Test]
public void Validate_SweepIntervalTooLargeToArm_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { SweepInterval = TimeSpan.FromDays(30) };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[Test]
public void Validate_MinPoolSizeAboveMaxPoolSize_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { MinPoolSize = 5, MaxPoolSize = 4 };

var thrown = Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());

Assert.That(thrown.Message, Does.Contain("MaxPoolSize (4)"));
}

[Test]
public void Validate_MinPoolSizeEqualToMaxPoolSize_DoesNotThrow()
{
var options = new ClickHouseTcpClientOptions { MinPoolSize = 4, MaxPoolSize = 4 };

Assert.DoesNotThrow(() => options.Validate());
}

[Test]
public void Validate_NegativeMaxConnectionLifetime_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { MaxConnectionLifetime = TimeSpan.FromSeconds(-1) };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[Test]
public void Validate_NegativeIdleTimeout_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { IdleTimeout = TimeSpan.FromSeconds(-1) };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[Test]
public void Validate_LifetimeAndIdleTimeoutDisabled_DoesNotThrow()
{
var options = new ClickHouseTcpClientOptions { MaxConnectionLifetime = TimeSpan.Zero, IdleTimeout = TimeSpan.Zero };

Assert.DoesNotThrow(() => options.Validate());
}

[Test]
public void Validate_ShortMaxConnectionLifetime_IsAccepted()
{
// Nothing forbids rotating connections quickly; the pool never interrupts a running operation for age,
// so a short lifetime only means a connection is retired sooner once it comes back.
var options = new ClickHouseTcpClientOptions { MaxConnectionLifetime = TimeSpan.FromSeconds(10) };

Assert.DoesNotThrow(() => options.Validate());
}

[TestCase(nameof(ClickHouseTcpClientOptions.DialTimeout))]
[TestCase(nameof(ClickHouseTcpClientOptions.ReadTimeout))]
[TestCase(nameof(ClickHouseTcpClientOptions.PoolTimeout))]
public void Validate_TimeoutBeyondWhatATimerCanHold_ThrowsAtConstructionNotAtEveryOperation(string property)
{
// These feed CancelAfter / SemaphoreSlim.WaitAsync, which take an int millisecond count. Past ~24.8 days
// the failure would otherwise surface from inside every operation instead of here.
var tooLong = TimeSpan.FromMilliseconds(int.MaxValue) + TimeSpan.FromSeconds(1);
var options = property switch
{
nameof(ClickHouseTcpClientOptions.DialTimeout) => new ClickHouseTcpClientOptions { DialTimeout = tooLong },
nameof(ClickHouseTcpClientOptions.ReadTimeout) => new ClickHouseTcpClientOptions { ReadTimeout = tooLong },
_ => new ClickHouseTcpClientOptions { PoolTimeout = tooLong },
};

var thrown = Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());

Assert.That(thrown.ParamName, Is.EqualTo(property));
}

[Test]
public void Validate_TimeoutAtExactlyWhatATimerCanHold_IsAccepted()
{
// The boundary itself: rejecting it too would be an off-by-one that only shows up at an absurd setting.
var atLimit = TimeSpan.FromMilliseconds(int.MaxValue);
var options = new ClickHouseTcpClientOptions { DialTimeout = atLimit, ReadTimeout = atLimit, PoolTimeout = atLimit };

Assert.DoesNotThrow(() => options.Validate());
}

[Test]
public void Validate_UndefinedPoolReusePolicy_ThrowsArgumentOutOfRangeException()
{
var options = new ClickHouseTcpClientOptions { PoolReusePolicy = (ClickHouseTcpPoolReusePolicy)42 };

Assert.Throws<ArgumentOutOfRangeException>(() => options.Validate());
}

[Test]
public void Validate_CustomSettingWithEmptyName_ThrowsArgumentException()
{
Expand Down Expand Up @@ -142,6 +312,13 @@ public void WithOwnedCustomSettings_CopiesEveryPropertyAndSnapshotsTheSettings()
MaxSendBufferBytes = 4096,
DialTimeout = TimeSpan.FromSeconds(3),
ReadTimeout = TimeSpan.FromSeconds(4),
MinPoolSize = 1,
MaxPoolSize = 7,
PoolTimeout = TimeSpan.FromSeconds(5),
MaxConnectionLifetime = TimeSpan.FromSeconds(6),
IdleTimeout = TimeSpan.FromSeconds(7),
SweepInterval = TimeSpan.FromSeconds(8),
PoolReusePolicy = ClickHouseTcpPoolReusePolicy.Fifo,
};
var defaults = new ClickHouseTcpClientOptions();

Expand Down Expand Up @@ -214,6 +391,12 @@ public void With_ChangingOneProperty_CarriesEveryOtherPropertyAcross()
MaxSendBufferBytes = 4096,
DialTimeout = TimeSpan.FromSeconds(3),
ReadTimeout = TimeSpan.FromSeconds(4),
MinPoolSize = 1,
MaxPoolSize = 7,
PoolTimeout = TimeSpan.FromSeconds(5),
MaxConnectionLifetime = TimeSpan.FromSeconds(6),
IdleTimeout = TimeSpan.FromSeconds(7),
PoolReusePolicy = ClickHouseTcpPoolReusePolicy.Fifo,
Comment thread
cursor[bot] marked this conversation as resolved.
};

var derived = original with { Port = 9440 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,112 @@ public void TypedSetters_ReadBackOnSameInstance_ReturnValuesNotDefaults()
});
}

[Test]
public void ToOptions_PoolKeys_ParsesEachValue()
{
var builder = new ClickHouseTcpConnectionStringBuilder(
"Host=h;MinPoolSize=2;MaxPoolSize=8;PoolTimeout=15;MaxConnectionLifetime=600;IdleTimeout=120;SweepInterval=5;PoolReusePolicy=Fifo");

var options = builder.ToOptions();

Assert.Multiple(() =>
{
Assert.That(options.MinPoolSize, Is.EqualTo(2));
Assert.That(options.MaxPoolSize, Is.EqualTo(8));
Assert.That(options.PoolTimeout, Is.EqualTo(TimeSpan.FromSeconds(15)));
Assert.That(options.MaxConnectionLifetime, Is.EqualTo(TimeSpan.FromMinutes(10)));
Assert.That(options.IdleTimeout, Is.EqualTo(TimeSpan.FromMinutes(2)));
Assert.That(options.SweepInterval, Is.EqualTo(TimeSpan.FromSeconds(5)));
Assert.That(options.PoolReusePolicy, Is.EqualTo(ClickHouseTcpPoolReusePolicy.Fifo));
});
}

[Test]
public void ToOptions_MissingPoolKeys_AppliesDefaults()
{
var options = new ClickHouseTcpConnectionStringBuilder("Host=h").ToOptions();

Assert.Multiple(() =>
{
Assert.That(options.MinPoolSize, Is.Zero);
Assert.That(options.MaxPoolSize, Is.EqualTo(20));
Assert.That(options.PoolTimeout, Is.EqualTo(TimeSpan.FromSeconds(30)));
Assert.That(options.MaxConnectionLifetime, Is.EqualTo(TimeSpan.FromMinutes(30)));
Assert.That(options.IdleTimeout, Is.EqualTo(TimeSpan.FromMinutes(5)));
Assert.That(options.SweepInterval, Is.Null, "an absent key means the period is derived");
Assert.That(options.PoolReusePolicy, Is.EqualTo(ClickHouseTcpPoolReusePolicy.Lifo));
});
}

[Test]
public void ToOptions_PoolReusePolicyInAnyCase_IsAccepted()
{
var options = new ClickHouseTcpConnectionStringBuilder("Host=h;PoolReusePolicy=fifo").ToOptions();

Assert.That(options.PoolReusePolicy, Is.EqualTo(ClickHouseTcpPoolReusePolicy.Fifo));
}

[Test]
public void ToOptions_UnknownPoolReusePolicy_ThrowsNamingTheAcceptedValues()
{
var builder = new ClickHouseTcpConnectionStringBuilder("Host=h;PoolReusePolicy=Random");

var thrown = Assert.Throws<ArgumentException>(() => builder.ToOptions());

Assert.That(thrown.Message, Does.Contain("Lifo").And.Contains("Fifo"));
}

[Test]
public void PoolTypedSetters_ReadBackOnSameInstance_ReturnValuesNotDefaults()
{
var builder = new ClickHouseTcpConnectionStringBuilder
{
MinPoolSize = 3,
MaxPoolSize = 9,
PoolTimeout = TimeSpan.FromSeconds(11),
MaxConnectionLifetime = TimeSpan.FromMinutes(20),
IdleTimeout = TimeSpan.FromMinutes(3),
SweepInterval = TimeSpan.FromSeconds(4),
PoolReusePolicy = ClickHouseTcpPoolReusePolicy.Fifo,
};

Assert.Multiple(() =>
{
Assert.That(builder.MinPoolSize, Is.EqualTo(3));
Assert.That(builder.MaxPoolSize, Is.EqualTo(9));
Assert.That(builder.PoolTimeout, Is.EqualTo(TimeSpan.FromSeconds(11)));
Assert.That(builder.MaxConnectionLifetime, Is.EqualTo(TimeSpan.FromMinutes(20)));
Assert.That(builder.IdleTimeout, Is.EqualTo(TimeSpan.FromMinutes(3)));
Assert.That(builder.SweepInterval, Is.EqualTo(TimeSpan.FromSeconds(4)));
Assert.That(builder.PoolReusePolicy, Is.EqualTo(ClickHouseTcpPoolReusePolicy.Fifo));
});
}

[Test]
public void SweepInterval_SetToNull_RemovesTheKeyRatherThanStoringAValue()
{
// Null is how a caller goes back to deriving the period, so it must leave no key behind: a stored zero
// would fail validation, and any stored number would keep overriding the derivation.
var builder = new ClickHouseTcpConnectionStringBuilder { SweepInterval = TimeSpan.FromSeconds(4) };

builder.SweepInterval = null;

Assert.Multiple(() =>
{
Assert.That(builder.SweepInterval, Is.Null);
Assert.That(builder.ConnectionString, Does.Not.Contain("SweepInterval").IgnoreCase);
Assert.That(builder.ToOptions().SweepInterval, Is.Null);
});
}

[Test]
public void SweepInterval_Unparseable_ReadsAsAbsentSoThePeriodIsDerived()
{
var builder = new ClickHouseTcpConnectionStringBuilder("Host=h;SweepInterval=soon");

Assert.That(builder.SweepInterval, Is.Null);
}

[Test]
public void Setters_RoundTripThroughConnectionString()
{
Expand Down
Loading
Loading