using System.Text.Json; using ClawdDotNet.Core.Tools; using ClawdDotNet.Tools.Database; using ClawdDotNet.Tools.Tests.FileRW; using Microsoft.Extensions.Logging.Abstractions; using Shouldly; namespace ClawdDotNet.Tools.Tests.Database; /// /// Vorpruefung (#579) und Freigabeschluessel (#609) des Database-Werkzeugs - beides ohne /// Datenbankverbindung pruefbar, weil es nur Konfiguration und Statement ansieht. /// public sealed class DatabaseVorpruefungTests { private static readonly DatabaseTool Tool = new(); private static AgentToolContext Kontext(string accessLevel, params string[] tabellen) => new( "agent", "inst", new Dictionary { ["type"] = "mysql", ["connectionString"] = "Server=nirgends", ["accessLevel"] = accessLevel, ["allowedTables"] = JsonSerializer.SerializeToElement(tabellen) }, new NullStateStore(), NullLogger.Instance, CancellationToken.None); private static JsonElement Sql(string sql, string action = "query") => JsonSerializer.SerializeToElement(new { action, sql }); [Fact] public void Ein_Agent_mit_Leserechten_bekommt_sein_INSERT_sofort_abgelehnt_statt_zur_Freigabe() => Tool.Preflight(Sql("INSERT INTO messwerte (ort) VALUES ('Berlin')", "insert"), Kontext("ReadOnly", "messwerte")) .ShouldNotBeNull().ShouldContain("ReadOnly"); [Fact] public void Strukturaenderung_ohne_Admin_wird_vorab_abgelehnt() => Tool.Preflight(Sql("ALTER TABLE messwerte ADD COLUMN x INT"), Kontext("ReadWrite", "messwerte")) .ShouldNotBeNull().ShouldContain("DDL"); [Fact] public void Fremde_Tabelle_wird_vorab_abgelehnt() => Tool.Preflight(Sql("SELECT * FROM protokoll"), Kontext("ReadOnly", "messwerte")) .ShouldNotBeNull().ShouldContain("protokoll"); [Theory] [InlineData("SELECT * FROM messwerte", "ReadOnly")] [InlineData("INSERT INTO messwerte (ort) VALUES ('Berlin')", "ReadWrite")] [InlineData("CREATE TABLE IF NOT EXISTS messwerte (id INT)", "Admin")] [InlineData("DESCRIBE messwerte", "ReadOnly")] public void Erlaubtes_passiert_die_Vorpruefung(string sql, string level) => Tool.Preflight(Sql(sql), Kontext(level, "messwerte")).ShouldBeNull(); [Theory] [InlineData("SELECT * FROM messwerte", "query", "query")] [InlineData("INSERT INTO messwerte (ort) VALUES ('Berlin')", "query", "write")] [InlineData("UPDATE messwerte SET ort = 'X'", "insert", "write")] [InlineData("CREATE TABLE messwerte (id INT)", "query", "schema")] [InlineData("DROP TABLE IF EXISTS messwerte", "query", "schema")] public void Der_Freigabeschluessel_folgt_dem_Statement_nicht_dem_action_Argument(string sql, string action, string erwartet) => Tool.StagingAktion(Sql(sql, action)).ShouldBe(erwartet); [Fact] public void Ohne_sql_gilt_das_action_Argument_wie_bei_MongoDB() => Tool.StagingAktion(JsonSerializer.SerializeToElement(new { action = "insert", collection = "c" })).ShouldBeNull(); }