Skip to content

Commit c10eff6

Browse files
authored
feat: align with shared client test cases (#4)
1 parent 7246450 commit c10eff6

File tree

7 files changed

+199
-12
lines changed

7 files changed

+199
-12
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "src/questdb-client-test"]
2+
path = src/questdb-client-test
3+
url = https://github.com/questdb/questdb-client-test.git

src/net-questdb-client/LineTcpSender.cs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class LineTcpSender : IDisposable
4848
private int _currentBufferIndex;
4949
private bool _hasTable;
5050
private bool _noFields = true;
51+
private bool _noSymbols = true;
5152
private int _position;
5253
private bool _quoted;
5354
private byte[] _sendBuffer;
@@ -274,6 +275,7 @@ public LineTcpSender Symbol(ReadOnlySpan<char> symbolName, ReadOnlySpan<char> va
274275
}
275276

276277
Put(',').EncodeUtf8(symbolName).Put('=').EncodeUtf8(value);
278+
_noSymbols = false;
277279
return this;
278280
}
279281

@@ -309,6 +311,18 @@ public LineTcpSender Column(ReadOnlySpan<char> name, long value)
309311
return this;
310312
}
311313

314+
/// <summary>
315+
/// Set value of BOOLEAN column
316+
/// </summary>
317+
/// <param name="name">Column name</param>
318+
/// <param name="value">Column value</param>
319+
/// <returns>Itself</returns>
320+
public LineTcpSender Column(ReadOnlySpan<char> name, bool value)
321+
{
322+
Column(name).Put(value ? 't' : 'f');
323+
return this;
324+
}
325+
312326
/// <summary>
313327
/// Set value of DOUBLE column
314328
/// </summary>
@@ -339,9 +353,12 @@ public LineTcpSender Column(ReadOnlySpan<char> name, DateTime timestamp)
339353
/// </summary>
340354
public void AtNow()
341355
{
342-
Put('\n');
343-
_hasTable = false;
344-
_noFields = true;
356+
if (!_hasTable || (_noFields && _noSymbols))
357+
{
358+
throw new InvalidOperationException("No symbols or column specified.");
359+
}
360+
361+
FinishLine();
345362
}
346363

347364
/// <summary>
@@ -351,7 +368,8 @@ public void AtNow()
351368
public void At(DateTime timestamp)
352369
{
353370
var epoch = timestamp.Ticks - EpochTicks;
354-
Put(' ').Put(epoch).Put('0').Put('0').AtNow();
371+
Put(' ').Put(epoch).Put('0').Put('0');
372+
FinishLine();
355373
}
356374

357375
/// <summary>
@@ -360,7 +378,8 @@ public void At(DateTime timestamp)
360378
/// <param name="epochNano">Nanoseconds since Unix epoch</param>
361379
public void At(long epochNano)
362380
{
363-
Put(' ').Put(epochNano).AtNow();
381+
Put(' ').Put(epochNano);
382+
FinishLine();
364383
}
365384

366385
/// <summary>
@@ -459,6 +478,15 @@ private static bool IsEmpty(ReadOnlySpan<char> name)
459478
return name.Length == 0;
460479
}
461480

481+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
482+
private void FinishLine()
483+
{
484+
Put('\n');
485+
_hasTable = false;
486+
_noFields = true;
487+
_noSymbols = true;
488+
}
489+
462490
private LineTcpSender Column(ReadOnlySpan<char> columnName)
463491
{
464492
if (_hasTable)
@@ -570,6 +598,7 @@ private bool IsValidTableName(ReadOnlySpan<char> tableName)
570598
case '\u000B': // New line allowed for compatibility, there are tests to make sure it works
571599
case '\u000c':
572600
case '\r':
601+
case '\n':
573602
case '\u000e':
574603
case '\u000f':
575604
case '\u007f':
@@ -618,6 +647,7 @@ private bool IsValidColumnName(ReadOnlySpan<char> tableName)
618647
case '\u000B':
619648
case '\u000c':
620649
case '\r':
650+
case '\n':
621651
case '\u000e':
622652
case '\u000f':
623653
case '\u007f':

src/questdb-client-test

Submodule questdb-client-test added at 42a3083

src/tcp-client-test/DummyIlpServer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ private async Task SaveData(Stream connection, Socket socket)
174174
_received.Write(_buffer, 0, received);
175175
_totalReceived += received;
176176
}
177+
else
178+
{
179+
return;
180+
}
177181
}
178182
}
179183

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
using System;
2+
using System.IO;
3+
using System.Net;
4+
using System.Text;
5+
using System.Text.Json;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using NUnit.Framework;
9+
using QuestDB;
10+
11+
namespace tcp_client_test;
12+
13+
[TestFixture]
14+
public class JsonSpecTestRunner
15+
{
16+
private const int Port = 29472;
17+
private static readonly TestCase[]? TestCases = ReadTestCases();
18+
19+
[TestCaseSource(nameof(TestCases))]
20+
public async Task Run(TestCase testCase)
21+
{
22+
using var srv = CreateTcpListener(Port);
23+
srv.AcceptAsync();
24+
25+
using var ls = await LineTcpSender.ConnectAsync(IPAddress.Loopback.ToString(), Port, tlsMode: TlsMode.Disable);
26+
Exception? exception = null;
27+
28+
try
29+
{
30+
ls.Table(testCase.table);
31+
foreach (var symbol in testCase.symbols)
32+
{
33+
ls.Symbol(symbol.name, symbol.value);
34+
}
35+
36+
foreach (var column in testCase.columns)
37+
{
38+
switch (column.type)
39+
{
40+
case "STRING":
41+
ls.Column(column.name, ((JsonElement)column.value).GetString());
42+
break;
43+
44+
case "DOUBLE":
45+
ls.Column(column.name, ((JsonElement)column.value).GetDouble());
46+
break;
47+
48+
case "BOOLEAN":
49+
ls.Column(column.name, ((JsonElement)column.value).GetBoolean());
50+
break;
51+
52+
case "LONG":
53+
ls.Column(column.name, (long)((JsonElement)column.value).GetDouble());
54+
break;
55+
56+
default:
57+
throw new NotSupportedException("Column type not supported: " + column.type);
58+
}
59+
}
60+
61+
ls.AtNow();
62+
ls.Send();
63+
}
64+
catch (Exception? ex)
65+
{
66+
if (testCase.result.status == "SUCCESS")
67+
{
68+
throw;
69+
}
70+
exception = ex;
71+
}
72+
ls.Dispose();
73+
74+
if (testCase.result.status == "SUCCESS")
75+
{
76+
WaitAssert(srv, testCase.result.line + "\n");
77+
}
78+
else if (testCase.result.status == "ERROR")
79+
{
80+
Assert.NotNull(exception, "Exception should be thrown");
81+
if (exception is NotSupportedException)
82+
{
83+
throw exception;
84+
}
85+
}
86+
else
87+
{
88+
Assert.Fail("Unsupported test case result status: " + testCase.result.status);
89+
}
90+
}
91+
92+
private static void WaitAssert(DummyIlpServer srv, string expected)
93+
{
94+
var expectedLen = Encoding.UTF8.GetBytes(expected).Length;
95+
for (var i = 0; i < 500 && srv.TotalReceived < expectedLen; i++) Thread.Sleep(10);
96+
Assert.AreEqual(expected, srv.GetTextReceived());
97+
}
98+
99+
private DummyIlpServer CreateTcpListener(int port, bool tls = false)
100+
{
101+
return new DummyIlpServer(port, tls);
102+
}
103+
104+
private static TestCase[]? ReadTestCases()
105+
{
106+
using var jsonFile = File.OpenRead("ilp-client-interop-test.json");
107+
return JsonSerializer.Deserialize<TestCase[]>(jsonFile);
108+
}
109+
110+
public class TestCase
111+
{
112+
public string testName { get; set; }
113+
public string table { get; set; }
114+
public TestCaseSymbol[] symbols { get; set; }
115+
public TestCaseColumn[] columns { get; set; }
116+
public TestCaseResult result { get; set; }
117+
118+
public override string ToString()
119+
{
120+
return testName;
121+
}
122+
}
123+
124+
public class TestCaseSymbol
125+
{
126+
public string name { get; set; }
127+
public string value { get; set; }
128+
}
129+
130+
public class TestCaseColumn
131+
{
132+
public string type { get; set; }
133+
public string name { get; set; }
134+
public object value { get; set; }
135+
}
136+
137+
public class TestCaseResult
138+
{
139+
public string status { get; set; }
140+
public string line { get; set; }
141+
}
142+
}

src/tcp-client-test/LineTcpSenderTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ await LineTcpSender.ConnectAsync(
324324
}
325325
catch (IOException ex)
326326
{
327-
Assert.That(ex.Message, Is.EqualTo("Unable to write data to the transport connection: Broken pipe."));
327+
Assert.That(ex.Message.StartsWith("Unable to write data to the transport connection:"), Is.True);
328328
}
329329
if (i == 1)
330330
{

src/tcp-client-test/tcp-client-test.csproj

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="BouncyCastle.NetCore" Version="1.9.0"/>
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/>
14-
<PackageReference Include="NUnit" Version="3.13.2"/>
15-
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0"/>
16-
<PackageReference Include="coverlet.collector" Version="3.1.0"/>
12+
<PackageReference Include="BouncyCastle.NetCore" Version="1.9.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
14+
<PackageReference Include="NUnit" Version="3.13.2" />
15+
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
16+
<PackageReference Include="coverlet.collector" Version="3.1.0" />
1717
</ItemGroup>
1818

1919
<ItemGroup>
20-
<ProjectReference Include="..\net-questdb-client\net-questdb-client.csproj"/>
20+
<ProjectReference Include="..\net-questdb-client\net-questdb-client.csproj" />
2121
</ItemGroup>
2222

2323
<ItemGroup>
@@ -26,4 +26,11 @@
2626
</None>
2727
</ItemGroup>
2828

29+
<ItemGroup>
30+
<None Include="..\questdb-client-test\ilp-client-interop-test.json">
31+
<Link>ilp-client-interop-test.json</Link>
32+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
33+
</None>
34+
</ItemGroup>
35+
2936
</Project>

0 commit comments

Comments
 (0)