Bios.Testing 0.2.0

Bios.Testing

Gerçek altyapıyla test için fikstürler. Bugün tek parça var: PostgresContainerFixture.

Satır seviyesi güvenlik ve rol ayrımı güvenlik modelinin kendisiyse, sahte bir veritabanı ya da süper kullanıcı bağlantısı hiçbir şey kanıtlamaz: süper kullanıcı bu korumaların hepsini her koşulda aşar. Bu fikstür Testcontainers ile gerçek bir PostgreSQL kaldırır, ürünün kendi göçlerini sırayla uygular ve ürünün gerçek uygulama rollerine parola verir; testler o rollerle bağlanır.

Göçler bir kopyadan değil çalışma ağacından okunur; yeni bir göç eklendiğinde fikstürün haberi olur, kimsenin bir listeyi elle güncellemesi gerekmez.

Bağlama

Ürün kendi fikstürünü türetir ve kendi değerlerini doldurur. Taban sınıfın yapıcısı protected, PostgresFixtureOptions alanlarının çoğu required; yani aşağıdaki alanların hepsi zorunludur, biri eksikse kod derlenmez.

using Bios.Testing;

public sealed class UrunPostgresFixture : PostgresContainerFixture
{
    public UrunPostgresFixture() : base(new PostgresFixtureOptions
    {
        Image = "postgres:16-alpine",
        Database = "urun",
        Owner = "urun_sahibi",                  // konteynerin süper kullanıcısı
        OwnerPassword = "test-owner",
        Roles = ["urun_api", "urun_worker"],    // göçlerin parolasız yarattığı roller
        RolePassword = "test-role",
        MigrationsDirectory = Path.Combine(
            AppContext.BaseDirectory, "..", "..", "..", "..", "..", "db", "migrations"),
        ResetSql = "truncate table belge, fis restart identity cascade",
    })
    { }
}

public sealed class BelgeTests : IClassFixture<UrunPostgresFixture>
{
    private readonly UrunPostgresFixture _fixture;

    public BelgeTests(UrunPostgresFixture fixture) => _fixture = fixture;

    [Fact]
    public async Task Api_rolu_baskasinin_satirini_goremez()
    {
        await _fixture.ResetAsync();

        await using var connection = new NpgsqlConnection(
            _fixture.ConnectionStringFor("urun_api"));
        await connection.OpenAsync();
        // ... gerçek rolle, gerçek şemaya karşı
    }
}

Sunulan üç üye:

Üye Ne verir
Task<NpgsqlConnection> OpenAsOwnerAsync() Süper kullanıcı bağlantısı; hazırlık ve temizlik için
string ConnectionStringFor(string role) O role ait bağlantı metni, havuzlama kapalı
Task ResetAsync() ResetSql cümlesini çalıştırır

Yapılandırma

Bu paketin appsettings bölümü yoktur; her şey yukarıdaki PostgresFixtureOptions kaydından gelir. İsteğe bağlı tek alan Environment: konteynere fazladan ortam değişkeni geçirir (bir yerel ayar, bir initdb anahtarı).

Environment = new Dictionary<string, string>
{
    ["POSTGRES_INITDB_ARGS"] = "--locale=tr_TR.UTF-8",
},

Dikkat

Göç sırası bayt sırasıdır, sayı sırası değil. MigrationsDirectory içindeki *.sql dosyaları tam yola göre StringComparer.Ordinal ile sıralanır. Bunun anlamı şudur: 10.sql dosyası 2.sql dosyasından önce uygulanır, çünkü '1' karakteri '2' karakterinden önce gelir.

Bu bilerek böyledir ve "akıllı" bir sıralamayla düzeltilmemelidir. Gerçek dağıtım betiği aynı klasörü bir kabuk kalıbıyla (migrations/*.sql) uygular ve kabuk da tam olarak böyle sıralar. Fikstür daha akıllı sıralasaydı, testler dağıtımın hiç kullanmadığı bir sırayı doğrulardı; yani bu testlerin yakalaması gereken sıra hatasını gizlerdi.

Çare sıralamada değil, isimlerde: dosya adlarını sabit genişlikte sıfırla doldurun (001_init.sql, 002_kiracilik.sql, ... 054_x.sql). O zaman bayt sırası ile sayı sırası sonsuza kadar aynı şeyi söyler. Alt klasörler taranmaz.

Roller göçlerde parolasız yaratılır. Fikstür Roles listesindeki her role RolePassword parolasını sonradan verir — gerçek dağıtımda bunu dağıtım betikleri yapar. Listeye yazmadığınız bir rol parolasız kalır ve bağlanamaz.

Havuzlama kapalıdır ve öyle kalmalıdır. Havuzdaki bir bağlantı, test sınıfları arasında konteynerden daha uzun yaşar ve bir sonraki sınıfa ölü bir soket olarak döner.

Türetilmiş fikstür kendi kurulumunu ekleyemez. InitializeAsync ve DisposeAsync sanal (virtual) değildir. Fikstürün kendi hazırlığı varsa yapıcıya konur; temizliği varsa sınıf IDisposable gerçekler, çünkü xunit bir sınıf fikstürünü hem IAsyncLifetime hem IDisposable üzerinden bırakır.

Docker gerekir. Bu paketi kullanan her test takımı çalışan bir Docker ister; paketin kendi testleri de öyle.

Bir ayrıntı: xunit referansı bu projeyi test projesi sanıp paketlemeyi kapatır; csproj'daki IsPackable=true onu bilerek geri açar.

No packages depend on Bios.Testing.

Version Downloads Last updated
0.2.0 56 08/29/2026
0.1.0 2 08/29/2026