#include "fiko_test.hpp" #include "geri_bildirim_govde.hpp" // saf gövde/URL kurucu (Win32'siz, inline) #include #include using namespace fiko; using namespace fiko::geribildirim; namespace { // Gövdede alanın varlığı (kaba ama sözleşme için yeterli). bool icerir(const std::string& h, const std::string& p) { return h.find(p) != std::string::npos; } // Dizge tam/geçerli UTF-8 mi: her öncü bayt beklenen sayıda devam baytı ister ve // dizge tam sınırda biter. (Not: geçerli bir karakterin SON baytı devam baytıdır — // "son bayt devam olamaz" gibi bir kural YOKTUR.) bool utf8Gecerli(const std::string& s) { for (size_t i = 0; i < s.size();) { const u8 c = (u8)s[i]; size_t n = 0; if (c < 0x80) n = 1; else if ((c & 0xE0) == 0xC0) n = 2; else if ((c & 0xF0) == 0xE0) n = 3; else if ((c & 0xF8) == 0xF0) n = 4; else return false; // devam baytı öncü konumda → bozuk if (i + n > s.size()) return false; // ortadan kesilmiş for (size_t j = 1; j < n; ++j) if (((u8)s[i + j] & 0xC0) != 0x80) return false; i += n; } return true; } } // namespace // ── JSON kaçışı: tırnak/ters-bölü/kontrol karakteri gövdeyi bozmamalı. ────── FIKO_TEST(json_kacis_ozel_karakterler) { FIKO_KONTROL(jsonKacis("a\"b") == "a\\\"b"); FIKO_KONTROL(jsonKacis("a\\b") == "a\\\\b"); FIKO_KONTROL(jsonKacis("a\nb") == "a\\nb"); FIKO_KONTROL(jsonKacis("a\tb") == "a\\tb"); FIKO_KONTROL(jsonKacis(std::string("a\x01" "b")) == "a\\u0001b"); // UTF-8 (Türkçe) baytları olduğu gibi geçmeli — bozulursa mesaj okunmaz olur. FIKO_KONTROL(jsonKacis("ölçüm çöktü") == "ölçüm çöktü"); } // ── base64: RFC 4648 test vektörleri + dolgu kenar durumları. ─────────────── FIKO_TEST(base64_kodla_vektorler) { auto b = [](const char* s) { return std::vector((const u8*)s, (const u8*)s + std::string(s).size()); }; FIKO_KONTROL(base64Kodla(b("")) == ""); FIKO_KONTROL(base64Kodla(b("f")) == "Zg=="); FIKO_KONTROL(base64Kodla(b("fo")) == "Zm8="); FIKO_KONTROL(base64Kodla(b("foo")) == "Zm9v"); FIKO_KONTROL(base64Kodla(b("foob")) == "Zm9vYg=="); FIKO_KONTROL(base64Kodla(b("fooba")) == "Zm9vYmE="); FIKO_KONTROL(base64Kodla(b("foobar")) == "Zm9vYmFy"); // 0x00/0xFF baytları (PNG ikili verisi) doğru kodlanmalı. FIKO_KONTROL(base64Kodla(std::vector{0x00, 0x00, 0x00}) == "AAAA"); FIKO_KONTROL(base64Kodla(std::vector{0xFF, 0xFF, 0xFF}) == "////"); } // ── UTF-8 kısaltma: çok-baytlı karakter ortadan bölünmemeli (olay özeti). ── FIKO_TEST(utf8_kisalt_karakter_bolmez) { FIKO_KONTROL(utf8Kisalt("abc", 10) == "abc"); // sınır altı → dokunma FIKO_KONTROL(utf8Kisalt("abcdef", 3) == "abc"); // saf ASCII → tam kesim // "çç" = 4 bayt (her biri 2). maks=3 tam ortaya düşer → 2'ye geri sarmalı. const std::string cc = "çç"; FIKO_KONTROL(cc.size() == 4); FIKO_KONTROL(utf8Kisalt(cc, 3) == "ç"); FIKO_KONTROL(utf8Kisalt(cc, 2) == "ç"); // Kesilen sonuç HER uzunlukta geçerli UTF-8 olmalı + sınırı aşmamalı. const std::string tr = "ölçüm çöktü"; FIKO_KONTROL(utf8Gecerli(tr)); for (size_t n = 0; n <= tr.size() + 2; ++n) { const std::string k = utf8Kisalt(tr, n); FIKO_KONTROL(utf8Gecerli(k)); FIKO_KONTROL(k.size() <= (n > tr.size() ? tr.size() : n)); } } // ── Sözleşme kuralı 1+2: geçersiz koleksiyon adı gönderimi engellemeli. ───── FIKO_TEST(koleksiyon_gecerlilik_sozlesme_kurallari) { FIKO_KONTROL(koleksiyonGecerliMi("geriBildirimler")); FIKO_KONTROL(koleksiyonGecerliMi("geri_bildirim_2")); // Kural 1: hareketler → kokDB 401 (korunanKoleksiyonlar). FIKO_KONTROL(!koleksiyonGecerliMi("hareketler")); // Kural 2: '.' ve '-' Fiber route parser'ını böler. FIKO_KONTROL(!koleksiyonGecerliMi("geri.bildirim")); FIKO_KONTROL(!koleksiyonGecerliMi("geri-bildirim")); FIKO_KONTROL(!koleksiyonGecerliMi("geri/bildirim")); FIKO_KONTROL(!koleksiyonGecerliMi("")); } // ── URL kurulumu: kokDB generic ekle ucu. ────────────────────────────────── FIKO_TEST(url_kur_kokdb_ekle_ucu) { FIKO_KONTROL(urlKur("https://fikoyt.com:9000", "geriBildirimler") == "https://fikoyt.com:9000/fapiv1/fikoyt.com.FikoYT.sistem.DB.geriBildirimler.ekle"); // Taban sonundaki '/' çift ayraç üretmemeli. FIKO_KONTROL(urlKur("https://fikoyt.com:9000/", "geriBildirimler") == urlKur("https://fikoyt.com:9000", "geriBildirimler")); } // ── Gövde: alanlar + ekran görüntüsü yokluğu/varlığı. ────────────────────── FIKO_TEST(govde_kur_alanlar) { GeriBildirim g; g.mesaj = "Fillet \"donuyor\""; g.kategori = "Hata"; g.surum = "1.13.0"; g.cihaz = "Windows 10"; g.tarih = "2026-07-15T10:00:00Z"; const std::string j = govdeKur(g); FIKO_KONTROL(j.front() == '{' && j.back() == '}'); FIKO_KONTROL(icerir(j, "\"mesaj\":\"Fillet \\\"donuyor\\\"\"")); FIKO_KONTROL(icerir(j, "\"kategori\":\"Hata\"")); FIKO_KONTROL(icerir(j, "\"surum\":\"1.13.0\"")); FIKO_KONTROL(icerir(j, "\"tarih\":\"2026-07-15T10:00:00Z\"")); FIKO_KONTROL(icerir(j, "\"okundu\":false")); // Ekran görüntüsü yoksa alan HİÇ olmamalı (boş string değil). FIKO_KONTROL(!icerir(j, "ekranGoruntusu")); } FIKO_TEST(govde_kur_ekran_goruntusu_base64) { GeriBildirim g; g.mesaj = "x"; g.ekranPng = std::vector{'f', 'o', 'o'}; const std::string j = govdeKur(g); FIKO_KONTROL(icerir(j, "\"ekranGoruntusu\":\"Zm9v\"")); } // ── Gövde her zaman multipart DEĞİL düz JSON: sunucu çökmesini önleyen sözleşme. // (kokDB/routes.go:295 — dosyalı+oturumsuz = nil-deref = sunucu ölür) FIKO_TEST(govde_duz_json_multipart_degil) { GeriBildirim g; g.mesaj = "test"; g.ekranPng = std::vector(64, 0xAB); const std::string j = govdeKur(g); FIKO_KONTROL(!icerir(j, "Content-Disposition")); FIKO_KONTROL(!icerir(j, "multipart")); FIKO_KONTROL(!icerir(j, "boundary")); }