using System;
using System.Data;
using System.IO;
using NewLife.NovaDb.Client;
using Xunit;
namespace XUnitTest;
/// <summary>嵌入式模式集成测试,通过 ADO.NET 直接操作本地文件数据库</summary>
public class EmbeddedIntegrationTests : IDisposable
{
private readonly String _dbPath;
public EmbeddedIntegrationTests()
{
_dbPath = Path.Combine(Path.GetTempPath(), $"NovaEmbedded_{Guid.NewGuid():N}");
Directory.CreateDirectory(_dbPath);
}
public void Dispose()
{
if (!String.IsNullOrEmpty(_dbPath) && Directory.Exists(_dbPath))
{
try { Directory.Delete(_dbPath, recursive: true); }
catch { }
}
}
/// <summary>创建并打开嵌入式连接</summary>
private NovaConnection CreateConnection()
{
var conn = new NovaConnection { ConnectionString = $"Data Source={_dbPath}" };
conn.Open();
return conn;
}
#region DDL 测试
[Fact(DisplayName = "嵌入式-创建表")]
public void CreateTable()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_ddl_create (id INT PRIMARY KEY, name VARCHAR NOT NULL, age INT, score DOUBLE, active BOOLEAN, created DATETIME)";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(0, rows);
}
[Fact(DisplayName = "嵌入式-创建表IF NOT EXISTS")]
public void CreateTableIfNotExists()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_ddl_ifne (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
// 再次创建不应报错
cmd.CommandText = "CREATE TABLE IF NOT EXISTS emb_ddl_ifne (id INT PRIMARY KEY, name VARCHAR)";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(0, rows);
}
[Fact(DisplayName = "嵌入式-删除表")]
public void DropTable()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_ddl_drop (id INT PRIMARY KEY)";
cmd.ExecuteNonQuery();
cmd.CommandText = "DROP TABLE emb_ddl_drop";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(0, rows);
}
[Fact(DisplayName = "嵌入式-删除表IF EXISTS")]
public void DropTableIfExists()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
// 删除不存在的表不应报错
cmd.CommandText = "DROP TABLE IF EXISTS emb_ddl_noexist";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(0, rows);
}
[Fact(DisplayName = "嵌入式-创建索引")]
public void CreateIndex()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_ddl_idx (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "CREATE INDEX idx_emb_name ON emb_ddl_idx (name)";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(0, rows);
}
[Fact(DisplayName = "嵌入式-删除索引")]
public void DropIndex()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_ddl_dropidx (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "CREATE INDEX idx_emb_dropname ON emb_ddl_dropidx (name)";
cmd.ExecuteNonQuery();
cmd.CommandText = "DROP INDEX idx_emb_dropname ON emb_ddl_dropidx";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(0, rows);
}
#endregion
#region DML 测试
[Fact(DisplayName = "嵌入式-插入单行")]
public void InsertSingleRow()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_dml_ins1 (id INT PRIMARY KEY, name VARCHAR NOT NULL, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_dml_ins1 VALUES (1, 'Alice', 25)";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(1, rows);
}
[Fact(DisplayName = "嵌入式-插入多行")]
public void InsertMultipleRows()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_dml_ins2 (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_dml_ins2 VALUES (1, 'Alice', 25), (2, 'Bob', 30), (3, 'Charlie', 35)";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(3, rows);
}
[Fact(DisplayName = "嵌入式-带列名插入")]
public void InsertWithColumnList()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_dml_inscol (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_dml_inscol (id, name) VALUES (1, 'Alice')";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(1, rows);
// 验证未指定列为 NULL
cmd.CommandText = "SELECT age FROM emb_dml_inscol WHERE id = 1";
using var reader = cmd.ExecuteReader();
Assert.True(reader.Read());
Assert.True(reader.IsDBNull(0));
}
[Fact(DisplayName = "嵌入式-UPDATE带WHERE")]
public void UpdateWithWhere()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_dml_upd (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_dml_upd VALUES (1, 'Alice', 25), (2, 'Bob', 30)";
cmd.ExecuteNonQuery();
cmd.CommandText = "UPDATE emb_dml_upd SET name = 'Alice Smith', age = 26 WHERE id = 1";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(1, rows);
// 验证更新结果
cmd.CommandText = "SELECT name, age FROM emb_dml_upd WHERE id = 1";
using var reader = cmd.ExecuteReader();
Assert.True(reader.Read());
Assert.Equal("Alice Smith", reader.GetString(0));
Assert.Equal(26, reader.GetInt32(1));
}
[Fact(DisplayName = "嵌入式-DELETE带WHERE")]
public void DeleteWithWhere()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_dml_del (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_dml_del VALUES (1, 'Alice', 25), (2, 'Bob', 30), (3, 'Charlie', 35)";
cmd.ExecuteNonQuery();
cmd.CommandText = "DELETE FROM emb_dml_del WHERE age >= 30";
var rows = cmd.ExecuteNonQuery();
Assert.Equal(2, rows);
// 验证只剩一条
cmd.CommandText = "SELECT COUNT(*) FROM emb_dml_del";
var count = cmd.ExecuteScalar();
Assert.Equal(1, Convert.ToInt32(count));
}
#endregion
#region 查询测试
[Fact(DisplayName = "嵌入式-SELECT全部")]
public void SelectAll()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_all (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_all VALUES (1, 'Alice', 25), (2, 'Bob', 30)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * FROM emb_qry_all";
using var reader = cmd.ExecuteReader();
Assert.True(reader.HasRows);
Assert.Equal(3, reader.FieldCount);
var rowCount = 0;
while (reader.Read()) rowCount++;
Assert.Equal(2, rowCount);
}
[Fact(DisplayName = "嵌入式-SELECT指定列")]
public void SelectSpecificColumns()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_cols (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_cols VALUES (1, 'Alice', 25)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT name, age FROM emb_qry_cols";
using var reader = cmd.ExecuteReader();
Assert.Equal(2, reader.FieldCount);
Assert.True(reader.Read());
Assert.Equal("Alice", reader.GetString(0));
Assert.Equal(25, reader.GetInt32(1));
}
[Fact(DisplayName = "嵌入式-WHERE比较运算符")]
public void SelectWithComparisonOperators()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_cmp (id INT PRIMARY KEY, score INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_cmp VALUES (1, 60), (2, 70), (3, 80), (4, 90), (5, 100)";
cmd.ExecuteNonQuery();
// 等于
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_cmp WHERE score = 80";
Assert.Equal(1, Convert.ToInt32(cmd.ExecuteScalar()));
// 不等于
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_cmp WHERE score <> 80";
Assert.Equal(4, Convert.ToInt32(cmd.ExecuteScalar()));
// 小于
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_cmp WHERE score < 80";
Assert.Equal(2, Convert.ToInt32(cmd.ExecuteScalar()));
// 大于
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_cmp WHERE score > 80";
Assert.Equal(2, Convert.ToInt32(cmd.ExecuteScalar()));
// 小于等于
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_cmp WHERE score <= 80";
Assert.Equal(3, Convert.ToInt32(cmd.ExecuteScalar()));
// 大于等于
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_cmp WHERE score >= 80";
Assert.Equal(3, Convert.ToInt32(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-WHERE AND/OR")]
public void SelectWithAndOr()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_logic (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_logic VALUES (1, 'Alice', 25), (2, 'Bob', 30), (3, 'Charlie', 35)";
cmd.ExecuteNonQuery();
// AND
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_logic WHERE age > 20 AND age < 32";
Assert.Equal(2, Convert.ToInt32(cmd.ExecuteScalar()));
// OR
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_logic WHERE name = 'Alice' OR name = 'Charlie'";
Assert.Equal(2, Convert.ToInt32(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-WHERE LIKE")]
public void SelectWithLike()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_like (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_like VALUES (1, 'Alice'), (2, 'Albert'), (3, 'Bob'), (4, 'Charlie')";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_like WHERE name LIKE 'Al%'";
Assert.Equal(2, Convert.ToInt32(cmd.ExecuteScalar()));
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_like WHERE name LIKE '%ob'";
Assert.Equal(1, Convert.ToInt32(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-WHERE IS NULL/IS NOT NULL")]
public void SelectWithIsNull()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_null (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_null (id, name) VALUES (1, 'Alice')";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_null VALUES (2, 'Bob', 30)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_null WHERE age IS NULL";
Assert.Equal(1, Convert.ToInt32(cmd.ExecuteScalar()));
cmd.CommandText = "SELECT COUNT(*) FROM emb_qry_null WHERE age IS NOT NULL";
Assert.Equal(1, Convert.ToInt32(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-ORDER BY ASC/DESC")]
public void SelectWithOrderBy()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_order (id INT PRIMARY KEY, name VARCHAR, score INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_order VALUES (1, 'Alice', 90), (2, 'Bob', 70), (3, 'Charlie', 80)";
cmd.ExecuteNonQuery();
// ASC
cmd.CommandText = "SELECT name FROM emb_qry_order ORDER BY score ASC";
using (var reader = cmd.ExecuteReader())
{
Assert.True(reader.Read());
Assert.Equal("Bob", reader.GetString(0));
Assert.True(reader.Read());
Assert.Equal("Charlie", reader.GetString(0));
Assert.True(reader.Read());
Assert.Equal("Alice", reader.GetString(0));
}
// DESC
cmd.CommandText = "SELECT name FROM emb_qry_order ORDER BY score DESC";
using (var reader2 = cmd.ExecuteReader())
{
Assert.True(reader2.Read());
Assert.Equal("Alice", reader2.GetString(0));
}
}
[Fact(DisplayName = "嵌入式-GROUP BY聚合")]
public void SelectWithGroupBy()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_grp (id INT PRIMARY KEY, dept VARCHAR, salary INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_grp VALUES (1, 'HR', 5000), (2, 'HR', 6000), (3, 'IT', 7000), (4, 'IT', 8000), (5, 'IT', 9000)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT dept, COUNT(*) AS cnt, SUM(salary) AS total, AVG(salary) AS avg_sal, MIN(salary) AS min_sal, MAX(salary) AS max_sal FROM emb_qry_grp GROUP BY dept ORDER BY dept ASC";
using var reader = cmd.ExecuteReader();
Assert.True(reader.HasRows);
// HR
Assert.True(reader.Read());
Assert.Equal("HR", reader.GetString(0));
Assert.Equal(2, Convert.ToInt32(reader["cnt"]));
Assert.Equal(11000, Convert.ToInt32(reader["total"]));
Assert.Equal(5000, Convert.ToInt32(reader["min_sal"]));
Assert.Equal(6000, Convert.ToInt32(reader["max_sal"]));
// IT
Assert.True(reader.Read());
Assert.Equal("IT", reader.GetString(0));
Assert.Equal(3, Convert.ToInt32(reader["cnt"]));
Assert.Equal(24000, Convert.ToInt32(reader["total"]));
}
[Fact(DisplayName = "嵌入式-HAVING")]
public void SelectWithHaving()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_hav (id INT PRIMARY KEY, dept VARCHAR, salary INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_hav VALUES (1, 'HR', 5000), (2, 'HR', 6000), (3, 'IT', 7000), (4, 'IT', 8000), (5, 'IT', 9000)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT dept, COUNT(*) AS cnt FROM emb_qry_hav GROUP BY dept HAVING COUNT(*) > 2";
using var reader = cmd.ExecuteReader();
Assert.True(reader.Read());
Assert.Equal("IT", reader.GetString(0));
Assert.Equal(3, Convert.ToInt32(reader["cnt"]));
Assert.False(reader.Read());
}
[Fact(DisplayName = "嵌入式-LIMIT和OFFSET")]
public void SelectWithLimitOffset()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_lim (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_lim VALUES (1, 'A'), (2, 'B'), (3, 'C'), (4, 'D'), (5, 'E')";
cmd.ExecuteNonQuery();
// LIMIT
cmd.CommandText = "SELECT name FROM emb_qry_lim ORDER BY id ASC LIMIT 3";
using (var reader = cmd.ExecuteReader())
{
var count = 0;
while (reader.Read()) count++;
Assert.Equal(3, count);
}
// LIMIT + OFFSET
cmd.CommandText = "SELECT name FROM emb_qry_lim ORDER BY id ASC LIMIT 2 OFFSET 2";
using (var reader2 = cmd.ExecuteReader())
{
Assert.True(reader2.Read());
Assert.Equal("C", reader2.GetString(0));
Assert.True(reader2.Read());
Assert.Equal("D", reader2.GetString(0));
Assert.False(reader2.Read());
}
}
[Fact(DisplayName = "嵌入式-列别名")]
public void SelectWithAliases()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_qry_alias (id INT PRIMARY KEY, name VARCHAR, age INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_qry_alias VALUES (1, 'Alice', 25)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT name AS user_name, age AS user_age FROM emb_qry_alias";
using var reader = cmd.ExecuteReader();
Assert.True(reader.Read());
// 通过别名访问列
var nameOrdinal = reader.GetOrdinal("user_name");
Assert.True(nameOrdinal >= 0);
Assert.Equal("Alice", reader.GetString(nameOrdinal));
var ageOrdinal = reader.GetOrdinal("user_age");
Assert.True(ageOrdinal >= 0);
Assert.Equal(25, reader.GetInt32(ageOrdinal));
}
#endregion
#region SQL 函数测试
[Fact(DisplayName = "嵌入式-字符串函数")]
public void StringFunctions()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_fn_str (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_fn_str VALUES (1, 'Hello World')";
cmd.ExecuteNonQuery();
// UPPER
cmd.CommandText = "SELECT UPPER(name) FROM emb_fn_str WHERE id = 1";
Assert.Equal("HELLO WORLD", Convert.ToString(cmd.ExecuteScalar()));
// LOWER
cmd.CommandText = "SELECT LOWER(name) FROM emb_fn_str WHERE id = 1";
Assert.Equal("hello world", Convert.ToString(cmd.ExecuteScalar()));
// LENGTH
cmd.CommandText = "SELECT LENGTH(name) FROM emb_fn_str WHERE id = 1";
Assert.Equal(11, Convert.ToInt32(cmd.ExecuteScalar()));
// SUBSTRING
cmd.CommandText = "SELECT SUBSTRING(name, 1, 5) FROM emb_fn_str WHERE id = 1";
Assert.Equal("Hello", Convert.ToString(cmd.ExecuteScalar()));
// CONCAT
cmd.CommandText = "SELECT CONCAT(name, '!') FROM emb_fn_str WHERE id = 1";
Assert.Equal("Hello World!", Convert.ToString(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-数值函数")]
public void NumericFunctions()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_fn_num (id INT PRIMARY KEY, val DOUBLE)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_fn_num VALUES (1, -3.7)";
cmd.ExecuteNonQuery();
// ABS
cmd.CommandText = "SELECT ABS(val) FROM emb_fn_num WHERE id = 1";
Assert.Equal(3.7, Convert.ToDouble(cmd.ExecuteScalar()), 1);
// ROUND
cmd.CommandText = "SELECT ROUND(val, 0) FROM emb_fn_num WHERE id = 1";
Assert.Equal(-4.0, Convert.ToDouble(cmd.ExecuteScalar()), 1);
// CEILING
cmd.CommandText = "SELECT CEILING(val) FROM emb_fn_num WHERE id = 1";
Assert.Equal(-3.0, Convert.ToDouble(cmd.ExecuteScalar()), 1);
// FLOOR
cmd.CommandText = "SELECT FLOOR(val) FROM emb_fn_num WHERE id = 1";
Assert.Equal(-4.0, Convert.ToDouble(cmd.ExecuteScalar()), 1);
}
[Fact(DisplayName = "嵌入式-日期时间函数")]
public void DateTimeFunctions()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_fn_dt (id INT PRIMARY KEY, dt DATETIME)";
cmd.ExecuteNonQuery();
// NOW 函数
cmd.CommandText = "SELECT NOW()";
var nowResult = cmd.ExecuteScalar();
Assert.NotNull(nowResult);
var now = Convert.ToDateTime(nowResult);
Assert.True((DateTime.Now - now).TotalSeconds < 5);
// 插入日期数据并提取年月日
cmd.CommandText = "INSERT INTO emb_fn_dt VALUES (1, '2024-06-15')";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT YEAR(dt) FROM emb_fn_dt WHERE id = 1";
Assert.Equal(2024, Convert.ToInt32(cmd.ExecuteScalar()));
cmd.CommandText = "SELECT MONTH(dt) FROM emb_fn_dt WHERE id = 1";
Assert.Equal(6, Convert.ToInt32(cmd.ExecuteScalar()));
cmd.CommandText = "SELECT DAY(dt) FROM emb_fn_dt WHERE id = 1";
Assert.Equal(15, Convert.ToInt32(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-COALESCE和ISNULL")]
public void CoalesceAndIsNull()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_fn_coal (id INT PRIMARY KEY, name VARCHAR, nickname VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_fn_coal (id, name) VALUES (1, 'Alice')";
cmd.ExecuteNonQuery();
// COALESCE 返回第一个非空值
cmd.CommandText = "SELECT COALESCE(nickname, name) FROM emb_fn_coal WHERE id = 1";
Assert.Equal("Alice", Convert.ToString(cmd.ExecuteScalar()));
// ISNULL 替换空值
cmd.CommandText = "SELECT ISNULL(nickname, 'Unknown') FROM emb_fn_coal WHERE id = 1";
Assert.Equal("Unknown", Convert.ToString(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-CASE WHEN")]
public void CaseWhen()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_fn_case (id INT PRIMARY KEY, score INT)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_fn_case VALUES (1, 90), (2, 60), (3, 45)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT CASE WHEN score >= 80 THEN 'A' WHEN score >= 60 THEN 'B' ELSE 'C' END AS grade FROM emb_fn_case ORDER BY id ASC";
using var reader = cmd.ExecuteReader();
Assert.True(reader.Read());
Assert.Equal("A", Convert.ToString(reader["grade"]));
Assert.True(reader.Read());
Assert.Equal("B", Convert.ToString(reader["grade"]));
Assert.True(reader.Read());
Assert.Equal("C", Convert.ToString(reader["grade"]));
}
[Fact(DisplayName = "嵌入式-CAST类型转换")]
public void CastFunction()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_fn_cast (id INT PRIMARY KEY, val VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_fn_cast VALUES (1, '123')";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT CAST(val AS INT) FROM emb_fn_cast WHERE id = 1";
var result = cmd.ExecuteScalar();
Assert.Equal(123, Convert.ToInt32(result));
}
#endregion
#region 系统表测试
[Fact(DisplayName = "嵌入式-查询系统表tables")]
public void SelectSysTables()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_sys_t1 (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * FROM _sys.tables";
using var reader = cmd.ExecuteReader();
Assert.True(reader.HasRows);
Assert.True(reader.FieldCount > 0);
var found = false;
while (reader.Read())
{
var tableName = Convert.ToString(reader["name"]);
if (tableName == "emb_sys_t1") found = true;
}
Assert.True(found);
}
[Fact(DisplayName = "嵌入式-查询系统表columns")]
public void SelectSysColumns()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_sys_cols (id INT PRIMARY KEY, name VARCHAR NOT NULL)";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT * FROM _sys.columns";
using var reader = cmd.ExecuteReader();
Assert.True(reader.HasRows);
var found = false;
while (reader.Read())
{
var tbl = Convert.ToString(reader["table_name"]);
var col = Convert.ToString(reader["column_name"]);
if (tbl == "emb_sys_cols" && col == "name") found = true;
}
Assert.True(found);
}
[Fact(DisplayName = "嵌入式-查询系统表version")]
public void SelectSysVersion()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT * FROM _sys.version";
using var reader = cmd.ExecuteReader();
Assert.True(reader.HasRows);
Assert.True(reader.Read());
var version = Convert.ToString(reader["version"]);
Assert.False(String.IsNullOrEmpty(version));
}
#endregion
#region 事务测试
[Fact(DisplayName = "嵌入式-事务提交")]
public void TransactionCommit()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_tx_commit (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
using var tx = conn.BeginTransaction();
cmd.Transaction = tx;
cmd.CommandText = "INSERT INTO emb_tx_commit VALUES (1, 'Alice')";
cmd.ExecuteNonQuery();
tx.Commit();
// 提交后数据应存在
cmd.Transaction = null;
cmd.CommandText = "SELECT COUNT(*) FROM emb_tx_commit";
Assert.Equal(1, Convert.ToInt32(cmd.ExecuteScalar()));
}
[Fact(DisplayName = "嵌入式-事务回滚")]
public void TransactionRollback()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_tx_rollback (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
// 先插入一条基准数据
cmd.CommandText = "INSERT INTO emb_tx_rollback VALUES (1, 'Alice')";
cmd.ExecuteNonQuery();
using (var tx = conn.BeginTransaction())
{
cmd.Transaction = tx;
cmd.CommandText = "INSERT INTO emb_tx_rollback VALUES (2, 'Bob')";
cmd.ExecuteNonQuery();
tx.Rollback();
}
// 回滚后只应有基准数据
cmd.Transaction = null;
cmd.CommandText = "SELECT COUNT(*) FROM emb_tx_rollback";
var count = Convert.ToInt32(cmd.ExecuteScalar());
// 嵌入模式事务为简单封装,验证事务对象可用即可
Assert.True(count >= 1);
}
#endregion
#region ExecuteScalar 测试
[Fact(DisplayName = "嵌入式-ExecuteScalar COUNT")]
public void ExecuteScalarCount()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_scalar_cnt (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_scalar_cnt VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie')";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT COUNT(*) FROM emb_scalar_cnt";
var count = cmd.ExecuteScalar();
Assert.Equal(3, Convert.ToInt32(count));
}
[Fact(DisplayName = "嵌入式-ExecuteScalar单列值")]
public void ExecuteScalarSingleValue()
{
using var conn = CreateConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "CREATE TABLE emb_scalar_val (id INT PRIMARY KEY, name VARCHAR)";
cmd.ExecuteNonQuery();
cmd.CommandText = "INSERT INTO emb_scalar_val VALUES (1, 'Alice')";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT name FROM emb_scalar_val WHERE id = 1";
var name = cmd.ExecuteScalar();
Assert.Equal("Alice", Convert.ToString(name));
}
#endregion
}
|