Tests: Verbindungspools nur der eigenen Datenbank leeren statt aller
Die zufaelligen roten Laeufe der vollen Suite (jedes Mal ein anderer Test, zuletzt PromptHistoryTests mit einer reinen Funktion) hatten eine Ursache: Zwanzig Testklassen riefen beim Aufraeumen SqliteConnection.ClearAllPools() auf. Das ist prozessweit und nahm einem parallel laufenden Test die Verbindung mitten im Oeffnen weg - ObjectDisposedException in ApplyPragmas. - SqliteStorage.ReleasePooledConnections(pfad) leert nur die Pools der Datenbanken unter einer Datei oder einem Verzeichnis. - BackupService oeffnet die Quelle fuer VACUUM INTO ohne Pool: eine einmalige Kopie soll die Datei danach nicht offen halten. - PoolReleaseTests: die freigegebene Datenbank laesst sich loeschen, die andere arbeitet weiter. Vier volle Laeufe hintereinander gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f8d0ce94fe
commit
5b8656d969
@@ -23,7 +23,7 @@ public sealed class AuditRepositoryTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class BackupServiceTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_root);
|
||||
try { Directory.Delete(_root, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
@@ -95,7 +95,7 @@ public sealed class BackupServiceTests : IDisposable
|
||||
}, default);
|
||||
|
||||
// Verbindungen schließen, damit VACUUM INTO auf eine ruhige Datei trifft.
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_instanceDir);
|
||||
}
|
||||
|
||||
private string ZipPath => Path.Combine(_root, "sicherung.zip");
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed class LicenseBackupTests : IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
SecretKeyStore.UseDirectory(null);
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_root);
|
||||
try { Directory.Delete(_root, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public sealed class BudgetGuardTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
@@ -220,7 +220,7 @@ public sealed class UsageRepositoryTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public sealed class MemoryRepositoryTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public sealed class StagingRepositoryTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class StagingServiceTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public sealed class WerkzeugrechteTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using ClawdDotNet.Core.Storage;
|
||||
using Shouldly;
|
||||
|
||||
namespace ClawdDotNet.Core.Tests.Storage;
|
||||
|
||||
/// <summary>
|
||||
/// Releasing the pool of one database must leave every other database alone. The tests used
|
||||
/// the process-wide ClearAllPools to clean up, and a test running in parallel lost its
|
||||
/// connection in the middle of opening it — one red run in every few, always another test.
|
||||
/// </summary>
|
||||
public sealed class PoolReleaseTests : IDisposable
|
||||
{
|
||||
private readonly string _root = Path.Combine(Path.GetTempPath(), "clawd-tests", "pool-" + Guid.NewGuid().ToString("N")[..8]);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SqliteStorage.ReleasePooledConnections(_root);
|
||||
try { Directory.Delete(_root, recursive: true); }
|
||||
catch { /* cleaning up is secondary */ }
|
||||
}
|
||||
|
||||
private static async Task TouchAsync(SqliteStorage storage)
|
||||
=> await storage.WriteAsync(async connection =>
|
||||
{
|
||||
await using var cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "INSERT OR REPLACE INTO ToolState (Key, Value) VALUES ('k', 'v')";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}, default);
|
||||
|
||||
[Fact]
|
||||
public async Task Releasing_one_directory_frees_its_files_and_nothing_else()
|
||||
{
|
||||
var first = new SqliteStorage(Path.Combine(_root, "a", "state.db"));
|
||||
var second = new SqliteStorage(Path.Combine(_root, "b", "state.db"));
|
||||
await TouchAsync(first);
|
||||
await TouchAsync(second);
|
||||
|
||||
SqliteStorage.ReleasePooledConnections(Path.Combine(_root, "a"));
|
||||
|
||||
// The released database can go — on Windows an open handle would prevent that.
|
||||
Should.NotThrow(() => Directory.Delete(Path.Combine(_root, "a"), recursive: true));
|
||||
|
||||
// The other one keeps working, and so does its pool.
|
||||
await TouchAsync(second);
|
||||
await using var connection = await second.OpenConnectionAsync(default);
|
||||
await using var cmd = connection.CreateCommand();
|
||||
cmd.CommandText = "SELECT Value FROM ToolState WHERE Key = 'k'";
|
||||
(await cmd.ExecuteScalarAsync()).ShouldBe("v");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_database_file_can_be_named_directly()
|
||||
{
|
||||
var path = Path.Combine(_root, "c", "state.db");
|
||||
await TouchAsync(new SqliteStorage(path));
|
||||
|
||||
SqliteStorage.ReleasePooledConnections(path);
|
||||
|
||||
Should.NotThrow(() => File.Delete(path));
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public sealed class BudgetMissTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* cleanup is secondary */ }
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class CoordinationMigrationTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public sealed class ScannerCapacityTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* cleanup is secondary */ }
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public sealed class TaskScannerTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public sealed class TaskboardServiceTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public sealed class PromptHistoryTests : IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Microsoft.Data.Sqlite.SqliteConnection.ClearAllPools();
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* cleanup is best effort */ }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user