Skip to content

Commit 8945d3c

Browse files
committed
added more unit tests
1 parent 7e9d178 commit 8945d3c

File tree

6 files changed

+408
-3
lines changed

6 files changed

+408
-3
lines changed

src/SciSharp.MySQL.Replication/Events/LogEvent.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ static LogEvent()
6868
DataTypes[(int)ColumnType.VARCHAR] = new VarCharType();
6969
DataTypes[(int)ColumnType.DATETIME] = new DateTimeType();
7070
DataTypes[(int)ColumnType.DATETIME_V2] = new DateTimeV2Type();
71+
//DataTypes[(int)ColumnType.TIMESTAMP] = new TimestampType();
72+
//DataTypes[(int)ColumnType.ENUM] = new EnumType();
7173
}
7274

7375
/// <summary>

src/SciSharp.MySQL.Replication/Types/TinyType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class TinyType : IMySQLDataType
2020
public object ReadValue(ref SequenceReader<byte> reader, int meta)
2121
{
2222
reader.TryRead(out byte x);
23-
return x;
23+
return (SByte)x;
2424
}
2525
}
2626
}

tests/Test/DataTypesTest.cs

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
using System;
2+
using System.Linq;
3+
using System.Collections.Generic;
4+
using System.Threading.Tasks;
5+
using System.Text.Json;
6+
using MySql.Data.MySqlClient;
7+
using SciSharp.MySQL.Replication;
8+
using SciSharp.MySQL.Replication.Events;
9+
using Xunit;
10+
using Xunit.Abstractions;
11+
using Microsoft.Extensions.Logging;
12+
using Microsoft.Extensions.Logging.Console;
13+
using System.Collections;
14+
using System.Data;
15+
using System.Globalization;
16+
17+
namespace Test
18+
{
19+
[Trait("Category", "DataTypes")]
20+
public class DataTypesTest : IClassFixture<MySQLFixture>
21+
{
22+
protected readonly MySQLFixture _mysqlFixture;
23+
24+
public DataTypesTest(MySQLFixture mysqlFixture)
25+
{
26+
_mysqlFixture = mysqlFixture;
27+
}
28+
29+
[Fact]
30+
public async Task TestDateTimeType()
31+
{
32+
var currentValue = DateTime.Now;
33+
currentValue = new DateTime(currentValue.Year, currentValue.Month, currentValue.Day, currentValue.Hour, currentValue.Minute, currentValue.Second);
34+
35+
await TestDataType<DateTime>("datetime_table", currentValue, currentValue.AddDays(1), (reader, index) =>
36+
{
37+
return reader.GetDateTime(index);
38+
});
39+
}
40+
41+
[Fact]
42+
public async Task TestIntType()
43+
{
44+
var currentValue = 42;
45+
await TestDataType<int>("int_table", currentValue, currentValue + 10, (reader, index) =>
46+
{
47+
return reader.GetInt32(index);
48+
});
49+
}
50+
51+
//[Fact]
52+
public async Task TestBigIntType()
53+
{
54+
var currentValue = 9223372036854775800L;
55+
await TestDataType<long>("bigint_table", currentValue, currentValue - 10, (reader, index) =>
56+
{
57+
return reader.GetInt64(index);
58+
});
59+
}
60+
61+
[Fact]
62+
public async Task TestTinyIntType()
63+
{
64+
var currentValue = (sbyte)42;
65+
await TestDataType<sbyte>("tinyint_table", currentValue, (sbyte)(currentValue + 10), (reader, index) =>
66+
{
67+
return (sbyte)reader.GetByte(index);
68+
});
69+
}
70+
71+
[Fact]
72+
public async Task TestSmallIntType()
73+
{
74+
var currentValue = (short)1000;
75+
await TestDataType<short>("smallint_table", currentValue, (short)(currentValue + 10), (reader, index) =>
76+
{
77+
return reader.GetInt16(index);
78+
});
79+
}
80+
81+
[Fact]
82+
public async Task TestMediumIntType()
83+
{
84+
var currentValue = 8388000; // Close to 2^23 limit for MEDIUMINT
85+
await TestDataType<int>("mediumint_table", currentValue, currentValue + 10, (reader, index) =>
86+
{
87+
return reader.GetInt32(index);
88+
});
89+
}
90+
91+
//[Fact]
92+
public async Task TestVarCharType()
93+
{
94+
var currentValue = "Hello World";
95+
await TestDataType<string>("varchar_table", currentValue, currentValue + " Updated", (reader, index) =>
96+
{
97+
return reader.GetString(index);
98+
});
99+
}
100+
101+
//[Fact]
102+
public async Task TestDecimalType()
103+
{
104+
var currentValue = 123.45m;
105+
await TestDataType<decimal>("decimal_table", currentValue, currentValue + 10.55m, (reader, index) =>
106+
{
107+
return reader.GetDecimal(index);
108+
});
109+
}
110+
111+
[Fact]
112+
public async Task TestFloatType()
113+
{
114+
var currentValue = 123.45f;
115+
await TestDataType<float>("float_table", currentValue, currentValue + 10.55f, (reader, index) =>
116+
{
117+
return reader.GetFloat(index);
118+
});
119+
}
120+
121+
//[Fact]
122+
public async Task TestDoubleType()
123+
{
124+
var currentValue = 123456.789012;
125+
await TestDataType<double>("double_table", currentValue, currentValue + 100.123, (reader, index) =>
126+
{
127+
return reader.GetDouble(index);
128+
});
129+
}
130+
131+
[Fact]
132+
public async Task TestDateType()
133+
{
134+
var currentValue = DateTime.Today;
135+
await TestDataType<DateTime>("date_table", currentValue, currentValue.AddDays(5), (reader, index) =>
136+
{
137+
return reader.GetDateTime(index).Date;
138+
});
139+
}
140+
141+
//[Fact]
142+
public async Task TestTimeType()
143+
{
144+
var currentValue = new TimeSpan(10, 30, 45);
145+
await TestDataType<TimeSpan>("time_table", currentValue, currentValue.Add(new TimeSpan(1, 15, 30)), (reader, index) =>
146+
{
147+
return reader.GetTimeSpan(index);
148+
});
149+
}
150+
151+
//[Fact]
152+
public async Task TestTimestampType()
153+
{
154+
var currentValue = DateTime.UtcNow;
155+
currentValue = new DateTime(currentValue.Year, currentValue.Month, currentValue.Day, currentValue.Hour, currentValue.Minute, currentValue.Second, DateTimeKind.Utc);
156+
157+
await TestDataType<DateTime>("timestamp_table", currentValue, currentValue.AddHours(1), (reader, index) =>
158+
{
159+
return DateTime.SpecifyKind(reader.GetDateTime(index), DateTimeKind.Utc);
160+
});
161+
}
162+
163+
//[Fact]
164+
public async Task TestBlobType()
165+
{
166+
var currentValue = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
167+
var updateValue = new byte[] { 0x06, 0x07, 0x08, 0x09, 0x0A };
168+
169+
await TestDataType<byte[]>("blob_table", currentValue, updateValue, (reader, index) =>
170+
{
171+
var length = (int)reader.GetBytes(index, 0, null, 0, 0);
172+
var buffer = new byte[length];
173+
reader.GetBytes(index, 0, buffer, 0, length);
174+
return buffer;
175+
});
176+
}
177+
178+
//[Fact]
179+
public async Task TestEnumType()
180+
{
181+
var currentValue = "SMALL";
182+
await TestDataType<string>("enum_table", currentValue, "MEDIUM", (reader, index) =>
183+
{
184+
return reader.GetString(index);
185+
});
186+
}
187+
188+
//[Fact]
189+
public async Task TestSetType()
190+
{
191+
var currentValue = "RED,GREEN";
192+
await TestDataType<string>("set_table", currentValue, "RED,BLUE", (reader, index) =>
193+
{
194+
return reader.GetString(index);
195+
});
196+
}
197+
198+
//[Fact]
199+
public async Task TestJsonType()
200+
{
201+
var currentValue = @"{""name"": ""John"", ""age"": 30}";
202+
var updateValue = @"{""name"": ""Jane"", ""age"": 25, ""city"": ""New York""}";
203+
204+
await TestDataType<string>("json_table", currentValue, updateValue, (reader, index) =>
205+
{
206+
return reader.GetString(index);
207+
});
208+
}
209+
210+
//[Fact]
211+
public async Task TestYearType()
212+
{
213+
var currentValue = 2023;
214+
await TestDataType<int>("year_table", currentValue, 2024, (reader, index) =>
215+
{
216+
return reader.GetInt16(index);
217+
});
218+
}
219+
220+
private async Task TestDataType<TDateType>(string tableName, TDateType currentValue, TDateType updateValue, Func<MySqlDataReader, int, TDateType> dataReader)
221+
{
222+
// Insert a new row into the table
223+
var command = _mysqlFixture.CreateCommand();
224+
command.CommandText = $"insert into {tableName} (value) values (@value);SELECT LAST_INSERT_ID();";
225+
command.Parameters.AddWithValue("@value", currentValue);
226+
var id = (Int32)(UInt64)await command.ExecuteScalarAsync();
227+
228+
// Validate the WriteRowsEvent
229+
var writeRowsEvent = await _mysqlFixture.ReceiveAsync<WriteRowsEvent>();
230+
231+
Assert.Equal(1, writeRowsEvent.RowSet.Rows.Count);
232+
Assert.Equal("id", writeRowsEvent.RowSet.ColumnNames[0]);
233+
Assert.Equal("value", writeRowsEvent.RowSet.ColumnNames[1]);
234+
var idFromClient = writeRowsEvent.RowSet.Rows[0][0];
235+
var valueFromClient = writeRowsEvent.RowSet.Rows[0][1];
236+
Assert.Equal(id, (Int32)idFromClient);
237+
Assert.NotNull(valueFromClient);
238+
Assert.Equal(currentValue, (TDateType)valueFromClient);
239+
240+
// Validate the data in the database with query
241+
command = _mysqlFixture.CreateCommand();
242+
command.CommandText = $"select value from {tableName} where id = @id";
243+
command.Parameters.AddWithValue("@id", id);
244+
245+
MySqlDataReader reader = await command.ExecuteReaderAsync() as MySqlDataReader;
246+
247+
Assert.True(await reader.ReadAsync());
248+
249+
var savedValue = dataReader(reader, 0);
250+
await reader.CloseAsync();
251+
252+
Assert.Equal(currentValue, savedValue);
253+
254+
// Update the row
255+
command = _mysqlFixture.CreateCommand();
256+
command.CommandText = $"update {tableName} set value=@value where id = @id";
257+
command.Parameters.AddWithValue("@id", id);
258+
command.Parameters.AddWithValue("@value", updateValue);
259+
260+
Assert.Equal(1, await command.ExecuteNonQueryAsync());
261+
262+
// Validate the UpdateRowsEvent
263+
var updateRowsEvent = await _mysqlFixture.ReceiveAsync<UpdateRowsEvent>();
264+
Assert.Equal(1, updateRowsEvent.RowSet.Rows.Count);
265+
Assert.Equal("id", updateRowsEvent.RowSet.ColumnNames[0]);
266+
Assert.Equal("value", updateRowsEvent.RowSet.ColumnNames[1]);
267+
var idCellValue = updateRowsEvent.RowSet.Rows[0][0] as CellValue;
268+
var valueCellValue = updateRowsEvent.RowSet.Rows[0][1] as CellValue;
269+
Assert.NotNull(idCellValue);
270+
Assert.Equal(id, (Int32)idCellValue.NewValue);
271+
Assert.Equal(id, (Int32)idCellValue.OldValue);
272+
Assert.NotNull(valueCellValue);
273+
Assert.Equal(currentValue, valueCellValue.OldValue);
274+
Assert.Equal(updateValue, valueCellValue.NewValue);
275+
276+
// Delete the row
277+
command = _mysqlFixture.CreateCommand();
278+
command.CommandText = $"delete from {tableName} where id = @id";
279+
command.Parameters.AddWithValue("@id", id);
280+
281+
await command.ExecuteNonQueryAsync();
282+
283+
// Validate the DeleteRowsEvent
284+
var deleteRowsEvent = await _mysqlFixture.ReceiveAsync<DeleteRowsEvent>();
285+
Assert.Equal(1, deleteRowsEvent.RowSet.Rows.Count);
286+
Assert.Equal("id", deleteRowsEvent.RowSet.ColumnNames[0]);
287+
Assert.Equal("value", deleteRowsEvent.RowSet.ColumnNames[1]);
288+
idFromClient = deleteRowsEvent.RowSet.Rows[0][0];
289+
valueFromClient = deleteRowsEvent.RowSet.Rows[0][1];
290+
Assert.Equal(id, (Int32)idFromClient);
291+
Assert.NotNull(valueFromClient);
292+
Assert.Equal(updateValue, (TDateType)valueFromClient);
293+
}
294+
}
295+
}

tests/Test/MySQLFixture.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using MySql.Data.MySqlClient;
5+
using SciSharp.MySQL.Replication;
6+
using SciSharp.MySQL.Replication.Events;
7+
8+
namespace Test
9+
{
10+
public class MySQLFixture : IDisposable
11+
{
12+
private const string _host = "localhost";
13+
private const string _username = "root";
14+
private const string _password = "root";
15+
16+
private readonly MySqlConnection _connection;
17+
18+
public IReplicationClient Client { get; private set; }
19+
20+
public MySQLFixture()
21+
{
22+
_connection = new MySqlConnection($"Server={_host};Database=garden;Uid={_username};Pwd={_password};");
23+
Client = new ReplicationClient();
24+
ConnectAsync().Wait();
25+
}
26+
27+
private async Task ConnectAsync()
28+
{
29+
await _connection.OpenAsync();
30+
await Client.ConnectAsync(_host, _username, _password, 1);
31+
}
32+
33+
public MySqlCommand CreateCommand()
34+
{
35+
return _connection.CreateCommand();
36+
}
37+
38+
public async Task<TLogEvent> ReceiveAsync<TLogEvent>(CancellationToken cancellationToken = default)
39+
where TLogEvent : LogEvent
40+
{
41+
while (!cancellationToken.IsCancellationRequested)
42+
{
43+
var logEvent = await Client.ReceiveAsync();
44+
45+
if (logEvent is TLogEvent requiredLogEvent)
46+
{
47+
return requiredLogEvent;
48+
}
49+
}
50+
51+
return default;
52+
}
53+
54+
public void Dispose()
55+
{
56+
Client?.CloseAsync().AsTask().Wait();
57+
_connection?.CloseAsync().Wait();
58+
_connection?.Dispose();
59+
}
60+
}
61+
}

tests/Test/docker-compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ services:
55
db:
66
image: mysql:latest
77
container_name: mysql-rep
8-
command: --default-authentication-plugin=mysql_native_password
98
restart: always
109
environment:
1110
MYSQL_ROOT_PASSWORD: root

0 commit comments

Comments
 (0)