// Uçtan uca: kral + iki istemci, sahte taşıma üstünde (senkron, deterministik). #include "fiko/istakip/kanal.hpp" #include "fiko_test.hpp" using namespace fiko::istakip; using fiko::orkestra::NodeAdres; using fiko::orkestra::SahteAg; using fiko::orkestra::TasimaSahte; namespace { constexpr uint64_t KRAL = 1, NODE_A = 2, NODE_B = 3; struct Duzenek { SahteAg ag; TasimaSahte kralTasima{ag, KRAL}, aTasima{ag, NODE_A}, bTasima{ag, NODE_B}; DepoBellek depo; IsMerkez merkez{depo}; IsKralKanal kral; IsIstemci istA{NODE_A}, istB{NODE_B}; IsIstemciKanal kanalA{istA, aTasima, NodeAdres{KRAL, 0}}; IsIstemciKanal kanalB{istB, bTasima, NodeAdres{KRAL, 0}}; explicit Duzenek(size_t parcaBoyu = 512) : kral{merkez, kralTasima, parcaBoyu} {} }; IsKayit teklif(const char* ad) { IsKayit k; k.tur = "teklif"; k.alanlar = {{"ad", ad}}; return k; } } // namespace FIKO_TEST(kanal_yaz_delta_yayilir) { Duzenek d; d.kanalA.baglan(); d.kanalB.baglan(); FIKO_KONTROL(d.kanalA.bagli() && d.kanalB.bagli()); d.kanalA.yaz(teklif("t1")); FIKO_KONTROL(d.istA.bekleyenler().empty()); // kendi deltası bekleyeni düşürdü FIKO_KONTROL(d.istA.bul(1) && d.istB.bul(1)); // iki istemci de kaydı gördü FIKO_KONTROL(d.istB.turListe("teklif").size() == 1); } FIKO_TEST(kanal_gec_baglanan_snapshot_ile_yetisir) { Duzenek d; d.kanalA.baglan(); d.kanalA.yaz(teklif("t1")); d.kanalA.yaz(teklif("t2")); FIKO_KONTROL(!d.istB.bul(1)); // B henüz bağlanmadı d.kanalB.baglan(); FIKO_KONTROL(d.istB.bul(1) && d.istB.bul(2) && d.istB.sonSurum() == 2); } FIKO_TEST(kanal_kopuk_istemci_delta_kacirir_snapshot_toplar) { Duzenek d; d.kanalA.baglan(); d.kanalB.baglan(); d.ag.kopar(NODE_B); d.kanalB.koptu(); d.kanalA.yaz(teklif("t1")); // B kopukken değişiklik FIKO_KONTROL(!d.istB.bul(1)); d.ag.bagla(NODE_B); d.kanalB.baglan(); // dönünce delta imleci: yalnız kaçırılan gelir FIKO_KONTROL(d.istB.bul(1) && d.istB.sonSurum() == 1); } FIKO_TEST(kanal_cevrimdisi_yazma_donuste_akar) { Duzenek d; d.kanalA.baglan(); d.ag.kopar(NODE_A); d.kanalA.koptu(); d.kanalA.yaz(teklif("cevrimdisi")); FIKO_KONTROL(d.istA.bekleyenler().size() == 1 && !d.merkez.sonSurum()); d.ag.bagla(NODE_A); d.kanalA.baglan(); // snapshot → bekleyenler akar → delta döner FIKO_KONTROL(d.istA.bekleyenler().empty()); FIKO_KONTROL(d.merkez.sonSurum() == 1 && d.istA.bul(1)); } FIKO_TEST(kanal_tekrar_gonderim_cift_kayit_uretmez) { Duzenek d; d.kanalA.baglan(); const uint64_t sira = d.kanalA.yaz(teklif("t1")); // Ağ tekrar oynatması: aynı yazma çerçevesi ikinci kez gelir (idempotens). fiko::SurecCerceve c; c.tip = fiko::SurecMesajTip::IsYaz; c.korelasyon = sira; c.govde = istakipproto::yazSerialize(NODE_A, sira, 0, teklif("t1")); d.aTasima.gonder(NodeAdres{KRAL, 0}, c); FIKO_KONTROL(d.merkez.surumdenBeri(0).size() == 1); // hâlâ tek kayıt FIKO_KONTROL(d.istA.bekleyenler().empty()); // "tekrar" onayı bekleyeni düşürür } FIKO_TEST(kanal_rol_kapisi_yazmayi_reddeder) { Duzenek d; d.kral.yazIzinAyarla([](const NodeAdres&, uint64_t no, const IsKayit&) { return no != NODE_B; // B = İzleyici (salt-okur) }); std::string sebep; d.kanalB.yazHataAyarla([&](uint64_t, const std::string& s) { sebep = s; }); d.kanalA.baglan(); d.kanalB.baglan(); d.kanalB.yaz(teklif("yetkisiz")); FIKO_KONTROL(d.merkez.sonSurum() == 0); // kayıt oluşmadı FIKO_KONTROL(d.istB.bekleyenler().empty()); // kalıcı red: bekleyen düşer FIKO_KONTROL(sebep == "yetki" && d.kral.redYetki() == 1); d.kanalA.yaz(teklif("yetkili")); FIKO_KONTROL(d.merkez.sonSurum() == 1 && d.istB.bul(1)); // B okumaya devam eder } FIKO_TEST(kanal_cakisma_iki_istemci_ayni_kayit) { Duzenek d; std::string sebep; d.kanalB.yazHataAyarla([&](uint64_t, const std::string& s) { sebep = s; }); d.kanalA.baglan(); d.kanalB.baglan(); d.kanalA.yaz(teklif("t1")); // kimlik 1, sürüm 1 — ikisi de gördü // A önce günceller (kilitli, sürüm 1 → 2 olur)... auto ga = *d.istA.bul(1); alanAta(ga, "durum", "A-guncelledi"); d.kanalA.yaz(ga, ga.surum); // ...B hâlâ elindeki ESKİ sürüm 1'e dayanarak yazmaya kalkar → çakışma reddi. auto gb = teklif("t1"); gb.kimlik = 1; alanAta(gb, "durum", "B-ezmeye-calisti"); d.kanalB.yaz(gb, 1); FIKO_KONTROL(sebep == "cakisma" && d.kral.redCakisma() == 1); FIKO_KONTROL(d.istB.bekleyenler().empty()); // sonsuz yeniden deneme yok FIKO_KONTROL(std::string(alanBul(*d.istB.bul(1), "durum")) == "A-guncelledi"); // veri korundu // B taze sürümü görüp yeniden yazar → geçer. auto tekrar = *d.istB.bul(1); alanAta(tekrar, "durum", "B-taze"); d.kanalB.yaz(tekrar, tekrar.surum); FIKO_KONTROL(std::string(alanBul(*d.istA.bul(1), "durum")) == "B-taze"); } FIKO_TEST(kanal_parcali_snapshot_buyuk_dokum) { Duzenek d(2); // parça boyu 2 kayıt d.kanalA.baglan(); for (int i = 0; i < 5; ++i) d.kanalA.yaz(teklif("t")); d.kanalB.baglan(); // 5 kayıt → 3 parça (2+2+1) FIKO_KONTROL(d.istB.sonSurum() == 5 && d.istB.turListe("teklif").size() == 5); FIKO_KONTROL(d.kanalB.bagli()); d.kanalA.yaz(teklif("t6")); // parçalı katılım sonrası delta akışı çalışır FIKO_KONTROL(d.istB.turListe("teklif").size() == 6); } FIKO_TEST(kanal_kimlik_burunme_reddi) { Duzenek d; d.kanalA.baglan(); // kanal A'nın kimliği NODE_A olarak bağlandı d.kanalB.baglan(); // A kanalından B'nin kimliğiyle yazma çerçevesi (spoof): izin sorulmadan red. fiko::SurecCerceve c; c.tip = fiko::SurecMesajTip::IsYaz; c.korelasyon = 1; c.govde = istakipproto::yazSerialize(NODE_B, 1, 0, teklif("sahte")); d.aTasima.gonder(NodeAdres{KRAL, 0}, c); FIKO_KONTROL(d.merkez.sonSurum() == 0 && d.kral.redYetki() == 1); // Kendi kimliğiyle yazma normal akar; bağlı kimlik listesi iki ucu görür. d.kanalA.yaz(teklif("gercek")); FIKO_KONTROL(d.merkez.sonSurum() == 1); const auto kimlikler = d.kral.istemciKimlikleri(); FIKO_KONTROL(kimlikler.size() == 2); } FIKO_TEST(kanal_dosya_cekme_whitelist_ve_parcali) { Duzenek d; // Kral dosya servisi: yalnız "kayitli.stp" izinli; içerik 100 bayt, parça 16 bayt // (7 parçalı gerçek birleştirme + hash doğrulama). const std::string icerik(100, 'F'); d.kral.dosyaServisAyarla( [](const std::string& yol) { return yol == "kayitli.stp"; }, [&](const std::string&, std::string& out) { out = icerik; return true; }, 16); bool geldi = false, basarili = false; std::string gelen; d.kanalA.dosyaSonucAyarla([&](bool ok, const std::string&, const std::string& ic) { geldi = true; basarili = ok; gelen = ic; }); d.kanalA.baglan(); FIKO_KONTROL(!d.kanalA.dosyaIste("")); // boş yol reddi FIKO_KONTROL(d.kanalA.dosyaIste("kayitli.stp")); FIKO_KONTROL(geldi && basarili && gelen == icerik); // sahte ağ senkron: anında tamam // Whitelist dışı yol: kral "dosya" reddi döner (path-traversal kapalı). geldi = false; FIKO_KONTROL(d.kanalA.dosyaIste("C:/gizli/sifreler.txt")); FIKO_KONTROL(geldi && !basarili); // Servis bağlanmamış ikinci kanal (B): istek reddedilir. bool bGeldi = false, bBasarili = true; d.kanalB.dosyaSonucAyarla([&](bool ok, const std::string&, const std::string&) { bGeldi = true; bBasarili = ok; }); d.kanalB.baglan(); FIKO_KONTROL(d.kanalB.dosyaIste("kayitli.stp")); FIKO_KONTROL(bGeldi && bBasarili); // izinli yol B'ye de gelir } FIKO_TEST(kanal_kimlik_dogrulama_tofu) { Duzenek d; // TOFU anahtarları: A kralla eşleşmiş (aynı anahtar iki uçta), B eşleşmemiş. std::array anahtarA{}; anahtarA.fill(7); uint8_t sayac = 0; // deterministik "rastgele" (nonce) d.kral.kimlikAyarla( KRAL, [&](uint64_t no) -> const std::array* { return no == NODE_A ? &anahtarA : nullptr; }, [&](uint8_t* p, size_t n) { for (size_t i = 0; i < n; ++i) p[i] = ++sayac; }, /*zorunlu*/ true); d.kanalA.kimlikAyarla( [&](uint64_t no) -> const std::array* { return no == KRAL ? &anahtarA : nullptr; }); std::string bHata; d.kanalB.yazHataAyarla([&](uint64_t, const std::string& s) { bHata = s; }); d.kanalA.baglan(); // sınama → otomatik kanıt → bekleyen snapshot işlenir (senkron zincir) FIKO_KONTROL(d.kanalA.bagli()); d.kanalA.yaz(teklif("kimlikli")); FIKO_KONTROL(d.merkez.sonSurum() == 1); d.kanalB.baglan(); // eşleşmemiş makine: "eslesme" reddi, kanal senkron OLMAZ FIKO_KONTROL(!d.kanalB.bagli() && bHata == "eslesme"); FIKO_KONTROL(!d.istB.bul(1)); // veri sızmadı } FIKO_TEST(kanal_kimlik_yanlis_anahtar_reddi) { Duzenek d; std::array kralinKaydi{}, sahtekar{}; kralinKaydi.fill(9); sahtekar.fill(1); // istemci elindeki anahtar KRALDAKİYLE UYUŞMUYOR d.kral.kimlikAyarla( KRAL, [&](uint64_t) -> const std::array* { return &kralinKaydi; }, [](uint8_t* p, size_t n) { for (size_t i = 0; i < n; ++i) p[i] = (uint8_t)i; }, true); d.kanalA.kimlikAyarla( [&](uint64_t) -> const std::array* { return &sahtekar; }); std::string hata; d.kanalA.yazHataAyarla([&](uint64_t, const std::string& s) { hata = s; }); d.kanalA.baglan(); FIKO_KONTROL(!d.kanalA.bagli() && hata == "kimlik"); // kanıt uyuşmadı // Doğrulanmamış kanaldan dosya isteği de kapalı. d.kral.dosyaServisAyarla([](const std::string&) { return true; }, [](const std::string&, std::string& ic) { ic = "x"; return true; }, 16); bool dosyaOk = true; d.kanalA.dosyaSonucAyarla([&](bool ok, const std::string&, const std::string&) { dosyaOk = ok; }); // bagli değilken dosyaIste zaten false döner — zorla çerçeve gönderip kapıyı sınıyoruz. fiko::SurecCerceve c; c.tip = fiko::SurecMesajTip::IsDosyaIste; fiko::surecproto::yazStr(c.govde, "kayitli.stp"); d.aTasima.gonder(NodeAdres{KRAL, 0}, c); FIKO_KONTROL(!d.kanalA.bagli()); // hâlâ dışarıda (dosya da gelmedi) } FIKO_TEST(kanal_guncelleme_ve_silme_akisi) { Duzenek d; d.kanalA.baglan(); d.kanalB.baglan(); d.kanalA.yaz(teklif("t1")); auto g = *d.istA.bul(1); alanAta(g, "durum", "onaylandi"); d.kanalA.yaz(g); FIKO_KONTROL(std::string(alanBul(*d.istB.bul(1), "durum")) == "onaylandi"); auto sil = *d.istA.bul(1); sil.silindi = true; d.kanalA.yaz(sil); FIKO_KONTROL(d.istB.turListe("teklif").empty()); // silme B'ye de yayıldı }