import java.util.List;

public class ContohPraktikUji {

    // ---- kode produksi yang diuji ----

    static final class Dompet {

        private long saldo;

        long saldo() {
            return saldo;
        }

        void setor(long jumlah) {
            if (jumlah <= 0) {
                throw new IllegalArgumentException("setoran harus positif");
            }
            saldo += jumlah;
        }

        void tarik(long jumlah) {
            if (jumlah > saldo) {
                throw new IllegalStateException("saldo tidak cukup");
            }
            saldo -= jumlah;
        }
    }

    private static void sama(Object diharapkan, Object aktual, String konteks) {
        if (!diharapkan.equals(aktual)) {
            throw new AssertionError(konteks + ": diharapkan "
                + diharapkan + " tetapi aktual " + aktual);
        }
    }

    // ---- gaya lama: satu method, banyak aspek, logika di dalam test ----

    static void gayaLamaSemuaSekaligus() {
        Dompet dompet = new Dompet();
        long[] setoran = {50_000, 30_000};       // data tanpa makna yang jelas
        for (long jumlah : setoran) {
            dompet.setor(jumlah);
        }
        sama(80_000L, dompet.saldo(), "setoran ganda");
        dompet.tarik(20_000);
        sama(60_000L, dompet.saldo(), "setelah tarik");
        boolean ditolak = false;
        try {
            dompet.tarik(999_999);
        } catch (IllegalStateException e) {
            ditolak = true;
        }
        if (!ditolak) {
            throw new AssertionError("tarik melebihi saldo seharusnya ditolak");
        }
    }

    // ---- gaya baik: satu aspek per test, nama menjelaskan perilaku ----

    static void dompetBaruBersaldoNol() {
        Dompet dompet = new Dompet();                     // arrange
        sama(0L, dompet.saldo(), "saldo awal");           // assert
    }

    static void setorMenambahSaldo() {
        Dompet dompet = new Dompet();                     // arrange
        dompet.setor(50_000L);                            // act
        sama(50_000L, dompet.saldo(), "setelah setor");   // assert
    }

    static void tarikMelebihiSaldoDitolak() {
        Dompet dompet = new Dompet();                     // arrange
        try {
            dompet.tarik(1L);                             // act
            throw new AssertionError("seharusnya ditolak");
        } catch (IllegalStateException e) {               // assert
            sama("saldo tidak cukup", e.getMessage(), "pesan penolakan");
        }
    }

    record Uji(String nama, Runnable blok) {}

    public static void main(String[] args) {
        List<Uji> suiteLama = List.of(
            new Uji("testSemuaSekaligus", ContohPraktikUji::gayaLamaSemuaSekaligus));

        List<Uji> suiteBaik = List.of(
            new Uji("dompetBaruBersaldoNol", ContohPraktikUji::dompetBaruBersaldoNol),
            new Uji("setorMenambahSaldo", ContohPraktikUji::setorMenambahSaldo),
            new Uji("tarikMelebihiSaldoDitolak", ContohPraktikUji::tarikMelebihiSaldoDitolak));

        System.out.println("== suite gaya lama ==");
        jalankan(suiteLama);
        System.out.println("== suite gaya baik ==");
        jalankan(suiteBaik);
    }

    static void jalankan(List<Uji> suite) {
        int lulus = 0;
        for (Uji uji : suite) {
            try {
                uji.blok().run();
                lulus++;
                System.out.println("LULUS  " + uji.nama());
            } catch (AssertionError e) {
                System.out.println("GAGAL  " + uji.nama() + " -> " + e.getMessage());
            }
        }
        System.out.println("ringkasan: " + lulus + "/" + suite.size() + " lulus");
    }
}
