Skip to content

Commit cfdb79e

Browse files
📝 Add docstrings to rd_decimal
Docstrings generation was requested by @RaphDal. * #52 (comment) The following files were modified: * `src/dummy-http-server/DummyHttpServer.cs` * `src/net-questdb-client-tests/DecimalTestHelpers.cs` * `src/net-questdb-client-tests/DummyIlpServer.cs` * `src/net-questdb-client-tests/JsonSpecTestRunner.cs` * `src/net-questdb-client/Buffers/Buffer.cs` * `src/net-questdb-client/Buffers/BufferV1.cs` * `src/net-questdb-client/Buffers/BufferV2.cs` * `src/net-questdb-client/Buffers/BufferV3.cs` * `src/net-questdb-client/Buffers/IBuffer.cs` * `src/net-questdb-client/Senders/AbstractSender.cs` * `src/net-questdb-client/Senders/HttpSender.cs` * `src/net-questdb-client/Senders/ISender.cs`
1 parent ea890c8 commit cfdb79e

File tree

12 files changed

+437
-42
lines changed

12 files changed

+437
-42
lines changed

src/dummy-http-server/DummyHttpServer.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ public class DummyHttpServer : IDisposable
4242
private int _port = 29743;
4343
private readonly TimeSpan? _withStartDelay;
4444

45+
/// <summary>
46+
/// Initializes a configurable in-process dummy HTTP server used for testing endpoints.
47+
/// </summary>
48+
/// <param name="withTokenAuth">If true, enable JWT bearer authentication and authorization.</param>
49+
/// <param name="withBasicAuth">If true, enable basic authentication behavior in the test endpoint.</param>
50+
/// <param name="withRetriableError">If true, configure the test endpoint to produce retriable error responses.</param>
51+
/// <param name="withErrorMessage">If true, include error messages in test error responses.</param>
52+
/// <param name="withStartDelay">Optional delay applied when starting the server.</param>
53+
/// <param name="requireClientCert">If true, require client TLS certificates for HTTPS connections.</param>
4554
public DummyHttpServer(bool withTokenAuth = false, bool withBasicAuth = false, bool withRetriableError = false,
4655
bool withErrorMessage = false, TimeSpan? withStartDelay = null, bool requireClientCert = false)
4756
{
@@ -108,6 +117,13 @@ public void Dispose()
108117
_app.StopAsync().Wait();
109118
}
110119

120+
/// <summary>
121+
/// Clears the in-memory receive buffers and resets the endpoint error state and counter.
122+
/// </summary>
123+
/// <remarks>
124+
/// Empties IlpEndpoint.ReceiveBuffer and IlpEndpoint.ReceiveBytes, sets IlpEndpoint.LastError to null,
125+
/// and sets IlpEndpoint.Counter to zero.
126+
/// </remarks>
111127
public void Clear()
112128
{
113129
IlpEndpoint.ReceiveBuffer.Clear();
@@ -116,6 +132,12 @@ public void Clear()
116132
IlpEndpoint.Counter = 0;
117133
}
118134

135+
/// <summary>
136+
/// Starts the HTTP server on the specified port and configures the supported protocol versions.
137+
/// </summary>
138+
/// <param name="port">Port to listen on (defaults to 29743).</param>
139+
/// <param name="versions">Array of supported protocol versions; defaults to {1, 2, 3} when null.</param>
140+
/// <returns>A task that completes after any configured startup delay has elapsed and the server's background run task has been initiated.</returns>
119141
public async Task StartAsync(int port = 29743, int[]? versions = null)
120142
{
121143
if (_withStartDelay.HasValue)
@@ -128,6 +150,9 @@ public async Task StartAsync(int port = 29743, int[]? versions = null)
128150
_ = _app.RunAsync($"http://localhost:{port}");
129151
}
130152

153+
/// <summary>
154+
/// Starts the web application and listens for HTTP requests on http://localhost:{_port}.
155+
/// </summary>
131156
public async Task RunAsync()
132157
{
133158
await _app.RunAsync($"http://localhost:{_port}");
@@ -138,11 +163,19 @@ public async Task StopAsync()
138163
await _app.StopAsync();
139164
}
140165

166+
/// <summary>
167+
/// Gets the server's in-memory text buffer of received data.
168+
/// </summary>
169+
/// <returns>The mutable <see cref="StringBuilder"/> containing the accumulated received text; modifying it updates the server's buffer.</returns>
141170
public StringBuilder GetReceiveBuffer()
142171
{
143172
return IlpEndpoint.ReceiveBuffer;
144173
}
145174

175+
/// <summary>
176+
/// Gets the in-memory list of bytes received by the ILP endpoint.
177+
/// </summary>
178+
/// <returns>The mutable list of bytes received by the endpoint.</returns>
146179
public List<byte> GetReceivedBytes()
147180
{
148181
return IlpEndpoint.ReceiveBytes;
@@ -160,6 +193,10 @@ public async Task<bool> Healthcheck()
160193
}
161194

162195

196+
/// <summary>
197+
/// Generates a JWT for the test server when the provided credentials match the server's static username and password.
198+
/// </summary>
199+
/// <returns>The JWT string when credentials are valid; <c>null</c> otherwise. The issued token is valid for one day.</returns>
163200
public string? GetJwtToken(string username, string password)
164201
{
165202
if (username == Username && password == Password)
@@ -180,6 +217,11 @@ public int GetCounter()
180217
return IlpEndpoint.Counter;
181218
}
182219

220+
/// <summary>
221+
/// Produces a human-readable string representation of the server's received-bytes buffer, interpreting embedded markers and formatting arrays and numeric values.
222+
/// </summary>
223+
/// <returns>The formatted textual representation of the received bytes buffer.</returns>
224+
/// <exception cref="NotImplementedException">Thrown when the buffer contains an unsupported type code.</exception>
183225
public string PrintBuffer()
184226
{
185227
var bytes = GetReceivedBytes().ToArray();
@@ -263,4 +305,4 @@ public string PrintBuffer()
263305
sb.Append(Encoding.UTF8.GetString(bytes, lastAppend, i - lastAppend));
264306
return sb.ToString();
265307
}
266-
}
308+
}

src/net-questdb-client-tests/DecimalTestHelpers.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ namespace net_questdb_client_tests;
3030

3131
internal static class DecimalTestHelpers
3232
{
33+
/// <summary>
34+
/// Asserts that the buffer contains a decimal field for the specified column with the given scale and mantissa bytes.
35+
/// </summary>
36+
/// <param name="buffer">The encoded row payload to search for the column's decimal field.</param>
37+
/// <param name="columnName">The name of the column whose decimal payload is expected in the buffer.</param>
38+
/// <param name="expectedScale">The expected scale byte of the decimal field.</param>
39+
/// <param name="expectedMantissa">The expected mantissa bytes of the decimal field.</param>
3340
internal static void AssertDecimalField(ReadOnlySpan<byte> buffer,
3441
string columnName,
3542
byte expectedScale,
@@ -49,6 +56,14 @@ internal static void AssertDecimalField(ReadOnlySpan<byte> buffer,
4956
$"Mantissa bytes for `{columnName}` did not match expectation.");
5057
}
5158

59+
/// <summary>
60+
/// Asserts that the buffer contains a null decimal field payload for the specified column.
61+
/// </summary>
62+
/// <param name="buffer">Buffer containing the encoded record(s) to inspect.</param>
63+
/// <param name="columnName">Name of the column whose decimal payload should be null.</param>
64+
/// <remarks>
65+
/// Verifies the payload starts with '=' then the DECIMAL type marker, and that both scale and mantissa length are zero.
66+
/// </remarks>
5267
internal static void AssertDecimalNullField(ReadOnlySpan<byte> buffer, string columnName)
5368
{
5469
var payload = ExtractDecimalPayload(buffer, columnName);
@@ -61,6 +76,12 @@ internal static void AssertDecimalNullField(ReadOnlySpan<byte> buffer, string co
6176
Assert.That(payload[3], Is.EqualTo(0), $"Unexpected mantissa length for `{columnName}`.");
6277
}
6378

79+
/// <summary>
80+
/// Locate and return the payload bytes for a decimal column identified by name.
81+
/// </summary>
82+
/// <param name="buffer">The byte span containing the encoded record payload to search.</param>
83+
/// <param name="columnName">The column name whose payload prefix ("columnName=") will be located.</param>
84+
/// <returns>The slice of <paramref name="buffer"/> immediately after the found "columnName=" prefix.</returns>
6485
private static ReadOnlySpan<byte> ExtractDecimalPayload(ReadOnlySpan<byte> buffer, string columnName)
6586
{
6687
var prefix = Encoding.ASCII.GetBytes($"{columnName}=");

src/net-questdb-client-tests/DummyIlpServer.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ public class DummyIlpServer : IDisposable
4949
private string? _publicKeyY;
5050
private volatile int _totalReceived;
5151

52+
/// <summary>
53+
/// Initializes the dummy ILP server and starts a TCP listener bound to the loopback interface.
54+
/// </summary>
55+
/// <param name="port">TCP port to listen on.</param>
56+
/// <param name="tls">If true, enables TLS for incoming connections.</param>
5257
public DummyIlpServer(int port, bool tls)
5358
{
5459
_tls = tls;
@@ -69,6 +74,12 @@ public void AcceptAsync()
6974
Task.Run(AcceptConnections);
7075
}
7176

77+
/// <summary>
78+
/// Accepts a single incoming connection, optionally negotiates TLS and performs server authentication, then reads and saves data from the client.
79+
/// </summary>
80+
/// <remarks>
81+
/// Handles one client socket from the listener, wraps the connection with TLS if configured, invokes server-auth when credentials are set, and delegates continuous data receipt to the save routine. Socket errors are caught and the client socket is disposed on exit.
82+
/// </remarks>
7283
private async Task AcceptConnections()
7384
{
7485
Socket? clientSocket = null;
@@ -107,6 +118,11 @@ private X509Certificate GetCertificate()
107118
return X509Certificate.CreateFromCertFile("certificate.pfx");
108119
}
109120

121+
/// <summary>
122+
/// Performs the server-side authentication handshake over the given stream using a challenge-response ECDSA verification.
123+
/// </summary>
124+
/// <param name="connection">Stream used for the authentication handshake; the method may write to it and will close it if the requested key id mismatches or the signature verification fails.</param>
125+
/// <exception cref="InvalidOperationException">Thrown when the configured public key coordinates are not set.</exception>
110126
private async Task RunServerAuth(Stream connection)
111127
{
112128
var receivedLen = await ReceiveUntilEol(connection);
@@ -165,6 +181,11 @@ private static string Pad(string text)
165181
return text + new string('=', padding);
166182
}
167183

184+
/// <summary>
185+
/// Decode a Base64 string that may use URL-safe characters and missing padding into its raw byte representation.
186+
/// </summary>
187+
/// <param name="encodedPrivateKey">A Base64-encoded string which may use '-' and '_' instead of '+' and '/' and may omit padding.</param>
188+
/// <returns>The decoded bytes represented by the normalized Base64 input.</returns>
168189
public static byte[] FromBase64String(string encodedPrivateKey)
169190
{
170191
var replace = encodedPrivateKey
@@ -173,6 +194,11 @@ public static byte[] FromBase64String(string encodedPrivateKey)
173194
return Convert.FromBase64String(Pad(replace));
174195
}
175196

197+
/// <summary>
198+
/// Reads bytes from the provided stream until a newline ('\n') byte is encountered, storing any bytes that follow the newline from the final read into the server's receive buffer.
199+
/// </summary>
200+
/// <param name="connection">The stream to read incoming bytes from.</param>
201+
/// <returns>The index position of the newline byte within the internal read buffer.</returns>
176202
private async Task<int> ReceiveUntilEol(Stream connection)
177203
{
178204
var len = 0;
@@ -223,16 +249,35 @@ private async Task SaveData(Stream connection, Socket socket)
223249
}
224250
}
225251

252+
/// <summary>
253+
/// Produces a human-readable representation of the data received from the connected client.
254+
/// </summary>
255+
/// <returns>A formatted string containing the contents of the server's received buffer.</returns>
226256
public string GetTextReceived()
227257
{
228258
return PrintBuffer();
229259
}
230260

261+
/// <summary>
262+
/// Gets a copy of all bytes received so far.
263+
/// </summary>
264+
/// <returns>A byte array containing the raw bytes received up to this point.</returns>
231265
public byte[] GetReceivedBytes()
232266
{
233267
return _received.ToArray();
234268
}
235269

270+
/// <summary>
271+
/// Converts the server's accumulated receive buffer into a human-readable string by decoding UTF-8 text and expanding embedded binary markers into readable representations.
272+
/// </summary>
273+
/// <remarks>
274+
/// The method scans the internal receive buffer for the marker sequence "==". After the marker a type byte determines how the following bytes are interpreted:
275+
/// - type 14: formats a multi-dimensional array of doubles as "ARRAY&lt;dim1,dim2,...&gt;[v1,v2,...]".
276+
/// - type 16: formats a single double value.
277+
/// All bytes outside these marked sections are decoded as UTF-8 text and included verbatim.
278+
/// </remarks>
279+
/// <returns>A formatted string containing the decoded UTF-8 text and expanded representations of any detected binary markers.</returns>
280+
/// <exception cref="NotImplementedException">Thrown when an unknown type marker is encountered after the marker sequence.</exception>
236281
public string PrintBuffer()
237282
{
238283
var bytes = _received.ToArray();
@@ -317,6 +362,12 @@ public string PrintBuffer()
317362
return sb.ToString();
318363
}
319364

365+
/// <summary>
366+
/// Enables server-side authentication by configuring the expected key identifier and the ECDSA public key coordinates.
367+
/// </summary>
368+
/// <param name="keyId">The key identifier expected from the client during authentication.</param>
369+
/// <param name="publicKeyX">Base64-encoded X coordinate of the ECDSA public key (secp256r1).</param>
370+
/// <param name="publicKeyY">Base64-encoded Y coordinate of the ECDSA public key (secp256r1).</param>
320371
public void WithAuth(string keyId, string publicKeyX, string publicKeyY)
321372
{
322373
_keyId = keyId;

src/net-questdb-client-tests/JsonSpecTestRunner.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ public class JsonSpecTestRunner
4343
private const int HttpPort = 29473;
4444
private static readonly TestCase[]? TestCases = ReadTestCases();
4545

46+
/// <summary>
47+
/// Populate the provided sender with the test case's table, symbols, and columns, then send the prepared row.
48+
/// </summary>
49+
/// <param name="sender">The ISender to configure and use for sending the test case row.</param>
50+
/// <param name="testCase">The test case containing table name, symbols, and typed columns to write.</param>
51+
/// <returns>A task that completes when the prepared row has been sent.</returns>
4652
private static async Task ExecuteTestCase(ISender sender, TestCase testCase)
4753
{
4854
sender.Table(testCase.Table);
@@ -87,6 +93,10 @@ private static async Task ExecuteTestCase(ISender sender, TestCase testCase)
8793
await sender.SendAsync();
8894
}
8995

96+
/// <summary>
97+
/// Executes the provided test case by sending its configured table, symbols, and columns to a local TCP listener and asserting the listener's received output against the test case's expected result.
98+
/// </summary>
99+
/// <param name="testCase">The test case to run; provides table, symbols, columns to send and a Result describing the expected validation (Status, Line, AnyLines, or BinaryBase64).</param>
90100
[TestCaseSource(nameof(TestCases))]
91101
public async Task RunTcp(TestCase testCase)
92102
{
@@ -143,6 +153,10 @@ public async Task RunTcp(TestCase testCase)
143153
}
144154
}
145155

156+
/// <summary>
157+
/// Executes the provided test case by sending data over HTTP to a dummy server using a QuestDB sender and validates the server's response according to the test case result.
158+
/// </summary>
159+
/// <param name="testCase">The test case describing table, symbols, columns, and expected result (status, line(s), or base64 binary) to execute and validate.</param>
146160
[TestCaseSource(nameof(TestCases))]
147161
public async Task RunHttp(TestCase testCase)
148162
{
@@ -259,6 +273,10 @@ public class TestCase
259273
[JsonPropertyName("columns")] public TestCaseColumn[] Columns { get; set; } = null!;
260274
[JsonPropertyName("result")] public TestCaseResult Result { get; set; } = null!;
261275

276+
/// <summary>
277+
/// Provides the test case name for display and logging.
278+
/// </summary>
279+
/// <returns>The TestName of the test case.</returns>
262280
public override string ToString()
263281
{
264282
return TestName;

src/net-questdb-client/Buffers/Buffer.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,15 @@ public static class Buffer
3939
/// <param name="maxBufSize"></param>
4040
/// <param name="version"></param>
4141
/// <returns></returns>
42-
/// <exception cref="NotImplementedException"></exception>
42+
/// <summary>
43+
/// Creates a concrete IBuffer implementation configured for the specified protocol version.
44+
/// </summary>
45+
/// <param name="bufferSize">Size in bytes of each buffer segment.</param>
46+
/// <param name="maxNameLen">Maximum allowed length for names stored in the buffer.</param>
47+
/// <param name="maxBufSize">Maximum total buffer capacity.</param>
48+
/// <param name="version">Protocol version that determines which concrete buffer implementation to create.</param>
49+
/// <returns>An <see cref="IBuffer"/> instance corresponding to the specified protocol version.</returns>
50+
/// <exception cref="NotImplementedException">Thrown when an unsupported protocol version is provided.</exception>
4351
public static IBuffer Create(int bufferSize, int maxNameLen, int maxBufSize, ProtocolVersion version)
4452
{
4553
return version switch
@@ -51,4 +59,4 @@ public static IBuffer Create(int bufferSize, int maxNameLen, int maxBufSize, Pro
5159
_ => throw new NotImplementedException(),
5260
};
5361
}
54-
}
62+
}

0 commit comments

Comments
 (0)