277 lines
8.5 KiB
C#
277 lines
8.5 KiB
C#
using Omika.HfSqlProbe;
|
|
|
|
namespace Omika.Tests.Tools;
|
|
|
|
/// <summary>
|
|
/// Tests unitaires du probe HFSQL ODBC — appelle les VRAIES classes
|
|
/// du projet <c>Omika.HfSqlProbe</c>. Aucune connexion réelle.
|
|
/// </summary>
|
|
public class HfSqlProbeTests
|
|
{
|
|
// =========================================================================
|
|
// SqlIdentifierValidator
|
|
// =========================================================================
|
|
|
|
[Theory]
|
|
[InlineData("Client")]
|
|
[InlineData("ProbeTest")]
|
|
[InlineData("CL_PKID")]
|
|
[InlineData("Marker")]
|
|
[InlineData("a")]
|
|
[InlineData("A")]
|
|
[InlineData("_")]
|
|
[InlineData("_test")]
|
|
[InlineData("Client123")]
|
|
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_____")]
|
|
public void ValidateSqlIdentifier_ValidNames_Accepted(string name)
|
|
{
|
|
Assert.True(SqlIdentifierValidator.IsValid(name),
|
|
$"Valid identifier rejected: '{name}'");
|
|
_ = SqlIdentifierValidator.Validate(name);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
[InlineData("123Client")] // starts with digit
|
|
[InlineData("Client;DROP TABLE")]
|
|
[InlineData("Client--")]
|
|
[InlineData("Client/*x*/")]
|
|
[InlineData("Client x")]
|
|
[InlineData("Client\t")]
|
|
[InlineData("Client\n")]
|
|
[InlineData("Client\r")]
|
|
[InlineData("Client\r\n")]
|
|
[InlineData("Client\nDROP")]
|
|
[InlineData("Client'")]
|
|
[InlineData("Client\"")]
|
|
[InlineData("[Client]")]
|
|
[InlineData("`Client`")]
|
|
[InlineData("Client.Name")]
|
|
[InlineData("Client\\Name")]
|
|
[InlineData("C lien")]
|
|
[InlineData("C;DROP")]
|
|
public void ValidateSqlIdentifier_InvalidNames_Rejected(string name)
|
|
{
|
|
Assert.False(SqlIdentifierValidator.IsValid(name),
|
|
$"Invalid identifier accepted: '{name}'");
|
|
Assert.Throws<ArgumentException>(() => SqlIdentifierValidator.Validate(name));
|
|
}
|
|
|
|
// =========================================================================
|
|
// ConnectionStringRedactor
|
|
// =========================================================================
|
|
|
|
[Fact]
|
|
public void Redact_RemovesPWD()
|
|
{
|
|
string input = "DSN=Test;PWD=MySecret123;UID=admin;Database=TestDB";
|
|
string result = ConnectionStringRedactor.Redact(input);
|
|
Assert.DoesNotContain("MySecret123", result);
|
|
Assert.Contains("PWD=***", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Redact_RemovesUID()
|
|
{
|
|
string input = "DSN=Test;UID=admin123;PWD=pass";
|
|
string result = ConnectionStringRedactor.Redact(input);
|
|
Assert.DoesNotContain("admin123", result);
|
|
Assert.Contains("UID=***", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Redact_RemovesPasswordKey()
|
|
{
|
|
string input = "DSN=Test;Password=SuperSecret;Server=localhost";
|
|
string result = ConnectionStringRedactor.Redact(input);
|
|
Assert.DoesNotContain("SuperSecret", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Redact_PreservesNonCredentials()
|
|
{
|
|
string input = "DSN=HFSQL_Test;Server=localhost;Database=TestDB";
|
|
string result = ConnectionStringRedactor.Redact(input);
|
|
Assert.Contains("DSN=HFSQL_Test", result);
|
|
Assert.Contains("Server=localhost", result);
|
|
Assert.Contains("Database=TestDB", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Redact_CaseInsensitive()
|
|
{
|
|
string input = "DSN=X;pwd=secret123";
|
|
string result = ConnectionStringRedactor.Redact(input);
|
|
Assert.DoesNotContain("secret123", result);
|
|
Assert.Contains("PWD=***", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Redact_SpecialCharsInPassword()
|
|
{
|
|
string input = "DSN=X;Password=aB12!@#$";
|
|
string result = ConnectionStringRedactor.Redact(input);
|
|
Assert.DoesNotContain("aB12!@#$", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Redact_EmptyPassword_Works()
|
|
{
|
|
string input = "DSN=X;PWD=;Server=Y";
|
|
string result = ConnectionStringRedactor.Redact(input);
|
|
Assert.Contains("PWD=***", result);
|
|
}
|
|
|
|
// =========================================================================
|
|
// ProbeArgumentParser
|
|
// =========================================================================
|
|
|
|
[Theory]
|
|
[InlineData("capabilities")]
|
|
[InlineData("CAPABILITIES")]
|
|
[InlineData("Capabilities")]
|
|
public void NormalizeCommand_RecognizesCapabilities(string cmd)
|
|
{
|
|
Assert.Equal("capabilities", ProbeArgumentParser.NormalizeCommand([cmd]));
|
|
}
|
|
|
|
[Fact]
|
|
public void NormalizeCommand_EmptyArgs_ReturnsEmpty()
|
|
{
|
|
Assert.Equal("", ProbeArgumentParser.NormalizeCommand([]));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("")]
|
|
[InlineData("unknown")]
|
|
public void IsValidCommand_RejectsUnknown(string cmd)
|
|
{
|
|
Assert.False(ProbeArgumentParser.IsValidCommand(cmd));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("capabilities")]
|
|
[InlineData("transaction")]
|
|
[InlineData("returning")]
|
|
[InlineData("omika-guid")]
|
|
[InlineData("--help")]
|
|
public void IsValidCommand_AcceptsKnown(string cmd)
|
|
{
|
|
Assert.True(ProbeArgumentParser.IsValidCommand(cmd));
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_ParsesConnectionStringOption()
|
|
{
|
|
var options = ProbeArgumentParser.Parse(
|
|
["--connection-string", "DSN=Test;UID=u;PWD=p"]);
|
|
Assert.Equal("DSN=Test;UID=u;PWD=p", options.ConnectionString);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_ParsesAllowWriteProbeFlag()
|
|
{
|
|
var options = ProbeArgumentParser.Parse(["--allow-write-probe"]);
|
|
Assert.True(options.AllowWriteProbe);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_ParsesTableAndIdColumn()
|
|
{
|
|
var options = ProbeArgumentParser.Parse(
|
|
["--table", "MyTable", "--id-column", "PKID"]);
|
|
Assert.Equal("MyTable", options.Table);
|
|
Assert.Equal("PKID", options.IdColumn);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_ParsesSchema()
|
|
{
|
|
var options = ProbeArgumentParser.Parse(["--schema", "batik"]);
|
|
Assert.Equal("batik", options.Schema);
|
|
}
|
|
|
|
[Fact]
|
|
public void Parse_DoesNotSetAllowWriteProbeByDefault()
|
|
{
|
|
var options = ProbeArgumentParser.Parse([]);
|
|
Assert.False(options.AllowWriteProbe);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Write gate
|
|
// =========================================================================
|
|
|
|
[Theory]
|
|
[InlineData("transaction")]
|
|
[InlineData("returning")]
|
|
public void IsWriteCommand_WriteCommandsRequireGate(string cmd)
|
|
{
|
|
Assert.True(ProbeArgumentParser.IsWriteCommand(cmd),
|
|
$"'{cmd}' must require --allow-write-probe.");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("capabilities")]
|
|
[InlineData("")]
|
|
[InlineData("unknown")]
|
|
public void IsWriteCommand_ReadCommandsDoNotRequireGate(string cmd)
|
|
{
|
|
Assert.False(ProbeArgumentParser.IsWriteCommand(cmd));
|
|
}
|
|
|
|
// =========================================================================
|
|
// ProbeSqlBuilder
|
|
// =========================================================================
|
|
|
|
[Fact]
|
|
public void SqlBuilder_InsertReturning_ValidatesAllIdentifiers()
|
|
{
|
|
string sql = ProbeSqlBuilder.BuildInsertReturning("TestTable", "TestPKID", "Marker");
|
|
Assert.Contains("INSERT INTO TestTable", sql);
|
|
Assert.Contains("VALUES (?)", sql);
|
|
Assert.Contains("RETURNING TestPKID", sql);
|
|
Assert.DoesNotContain("'", sql);
|
|
}
|
|
|
|
[Fact]
|
|
public void SqlBuilder_RejectsInvalidTable()
|
|
{
|
|
var ex = Assert.Throws<ArgumentException>(
|
|
() => ProbeSqlBuilder.BuildInsertReturning("Bad;DROP", "PKID", "Marker"));
|
|
Assert.Contains("Bad;DROP", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void SqlBuilder_RejectsInvalidMarkerColumn()
|
|
{
|
|
Assert.Throws<ArgumentException>(
|
|
() => ProbeSqlBuilder.BuildInsertMarker("Table", "x y"));
|
|
}
|
|
|
|
[Fact]
|
|
public void SqlBuilder_RejectsInvalidIdColumn()
|
|
{
|
|
Assert.Throws<ArgumentException>(
|
|
() => ProbeSqlBuilder.BuildInsertReturning("Table", "PKID--", "M"));
|
|
}
|
|
|
|
[Fact]
|
|
public void SqlBuilder_CountByMarker_Parameterized()
|
|
{
|
|
string sql = ProbeSqlBuilder.BuildCountByMarker("TestTable", "Marker");
|
|
Assert.Contains("SELECT COUNT(*)", sql);
|
|
Assert.Contains("WHERE Marker = ?", sql);
|
|
Assert.DoesNotContain("'", sql);
|
|
}
|
|
|
|
[Fact]
|
|
public void SqlBuilder_InsertMarker_Parameterized()
|
|
{
|
|
string sql = ProbeSqlBuilder.BuildInsertMarker("T", "Col");
|
|
Assert.Contains("INSERT INTO T (Col)", sql);
|
|
Assert.Contains("VALUES (?)", sql);
|
|
}
|
|
}
|