Menguji Respons Antarmuka terhadap Kode Status API
Toast email sudah dipakai muncul bahasa Inggris di UI Indonesia. API benar pakai kode status RFC; yang gagal lapisan penerjemahannya.
Ringkasan
Jadi pas test form tambah user, pesan error "email already in use" muncul dalam bahasa Inggris mentah. Ternyata API-nya nggak salah, malah bener banget pakai kode status RFC kayak 409 buat konflik; yang keliru cuma frontend yang nggak nerjemahin ke bahasa Indonesia. Sisanya aman sih, cuma ada satu bug file yatim pas hapus via entitas induk.
Saat menguji form tambah pengguna, saya memasukkan alamat email yang sudah terdaftar. Layar berkedip sebentar, lalu toast muncul di tengah antarmuka berbahasa Indonesia: "email already in use". Pesan itu bahasa Inggris, mentah dari API, tanpa penerjemahan apa pun.
Dugaan awal saya: API-nya mengembalikan kode error yang tidak standar atau rusak. Setelah memeriksa tab network di devtools, faktanya justru sebaliknya. API bicara dengan sangat benar memakai kode status RFC: 201 untuk pembuatan yang berhasil, 400 untuk permintaan rusak, 403 untuk akses terlarang, dan 409 untuk konflik data. StatusConflict memang didefinisikan sebagai 409 di paket net/http Go sesuai RFC 9110 [5] [6]. Yang tidak benar adalah lapisan penerjemahannya: frontend tidak memetakan 409 ke pesan Indonesia yang layak, dan kegagalannya jatuh ke pengguna sebagai bahasa yang salah.
Kerangkanya terbalik kalau dibaca terburu-buru. Sebenarnya bukan backend yang gagal. API seharusnya memang buta bahasa, cuma bicara status protokol; lokalisasi adalah tanggung jawab antarmuka. Dua temuan dari sesi ini persis menggambarkan dua bentuk kegagalan penerjemahan yang sama.
22 Skenario, Dua Temuan yang Sama Sekali Bukan Soal Logika
Sesi ini mencakup 22 skenario di enam area admin: pengguna, media, ulasan, menu, pengaturan, dan profil. Dua puluh lolos tanpa temuan. Dua yang tersisa sama-sama kegagalan penerjemahan di sisi antarmuka, bukan di sisi bisnis.
Temuan R5-1 adalah toast bahasa Inggris tadi — respons 409 benar, pesan yang tampil salah bahasa. Temuan R5-2 satu tingkat lebih serius: file bukan-gambar yang dikirim ke endpoint media memang ditolak server dengan 400, tapi panel upload tidak menampilkan apa pun. Tidak ada toast, tidak ada perubahan; dikonfirmasi dua kali dengan hasil sama. Server sudah jujur mengatakan "permintaan ini buruk", frontend memilih diam total. Dari dua pengguna, yang mana yang lebih menyesatkan? Toast salah bahasa setidaknya memberi tahu ada yang salah; kegagalan senyap membuat orang menunggu proses yang tidak akan pernah selesai.
Detail kecil media juga menarik: upload file palsu, teks belaka yang diganti ekstensi menjadi .png, memang dihentikan oleh validasi tipe di server, bukan lolos diam-diam ke penyimpanan. Yang kurang hanya umpan baliknya. Upload gambar sungguhan sebaliknya berjalan penuh: baris database tercipta, file fisik ada di container, thumbnail tampil. Metadata judul dan alt tersimpan, dan penghapusan menghapus baris sekaligus file fisiknya. Kontras itu penting untuk temuan R4-5 di modul lain: endpoint media sendiri membersihkan diri dengan benar, justru penghapusan lewat induk entitas yang meninggalkan yatim piatu.
Bagian yang paling meyakinkan justru yang tidak menghasilkan temuan. Guard bidang diuji dengan memaksa request memakai bidang "ALL" dari akun editor: server membalas 403 dan database tetap utuh. Penghapusan pengguna memakai konfirmasi native dengan peringatan destruktif yang jelas. Item menu ditambah lalu dihapus sampai navigasi publik kembali persis seperti semula, dan tagline pengaturan diubah lalu dikembalikan setelah nilai lama terverifikasi di database maupun API publik. Keluar sesi membuang token dari localStorage, dan membuka halaman admin tanpa sesi menampilkan formulir masuk di URL yang sama, bukan konten admin yang bocor. Satu sesi, seluruh siklus dibersihkan, tidak ada bekas data uji yang tertinggal.
Sisi verifikasi sesi ini berjalan dengan pola yang sama seperti modul lain: setiap klaim punya bukti artifact. Perubahan kata sandi diverifikasi dengan bcrypt.checkpw [2] — siklus ganti-sandi diuji dua arah: sandi baru diterima dengan 200, sandi lama dibalas 401, lalu dikembalikan sehingga kebalikannya yang terjadi. Sandi lemah ditolak di sisi klien sebelum satu pun request pergi, yang hemat server tapi menuntut aturan validasi frontend dan backend tetap selaras. Enam belas karakter punya unicode pun tidak luput dari cek: em-dash pada catatan ulasan dibuktikan tersimpan sebagai byte E28094 di database, bukan sekadar tampak benar di layar [4].
Desain yang Ternyata Bukan Bug
Satu ekspektasi plan QA runtuh saat pengujian. Rencana lama mengasumsikan ulasan masuk antrean moderasi sebelum tayang, padahal perilaku sebenarnya sudah tayang instan dengan moderasi reaktif: admin menarik ulasan bermasalah dengan alasan wajib, memulihkannya kembali, dan publik langsung melihat perubahan. Perilaku itu sudah jadi desain, dan sesi ini mencatatnya sebagai klarifikasi, bukan temuan. Rencana bisa basi; kode yang menentukan kontrak.
Standar HTTP adalah bahasa universal mesin. Tugas pengembang adalah memastikan mesin itu menerjemahkan ke manusianya dalam bahasa yang mereka pahami, termasuk saat menolak.
Sources
[2] pyca/bcrypt README
[4] Go spec
[5] Go net/http package
[6] golang/go status.go