package konto;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class KontoTest {
	private Konto konto;

	@BeforeEach
	void setUp() {
		// metoda wykona się bezpośrednio przed każdy testem
		// istnieje też @AfterEach tearDown() - wykonuje się po każdym teście
		// Zazwyczaj w setUp przygotowuje się wspólne dane potrzebne przed każdym testem, taki stan początkowy (w literaturze: "fixture").
		konto = new Konto(123, "Ala Kowalska", 1000);
	}

	@Test
	void testKonstruktorIGettery() {
		assertEquals(123, konto.getNumer());
		assertEquals("Ala Kowalska", konto.getWlasciciel());
		assertEquals(1000, konto.getSaldo());
	}
	
	@Test
	void testToString() {
		assertEquals("Konto nr 123, wł. Ala Kowalska, saldo 1000", konto.toString());
	}
	
	@Test
	void testWplata() {
		konto.wplata(300);
		assertEquals(1300, konto.getSaldo());
	}

	@Test
	void testWyplata() throws BrakSrodkow {
		konto.wyplata(200);
		assertEquals(800, konto.getSaldo());
	}
	// Temat sprawdzania wyjątków w testach.
	
	// Aby stwierdzić, że w pewnej sytuacji metoda POWINNA skończyć się wyjątkiem,
	// możemy użyć odpowiednio try-catch (testWplataTry)
	// albo użyć dedykowanych rozwiązań JUnit - dalsze przykłady
	
	@Test
	void testWplataUjemna_Try() {
		try {
			konto.wplata(-100);
			// Gdyby nie było wyjątku i program doszedłby do tego miejsca,
			// test powinien skończyć się porażką
			fail("Powinien być wyjątek, a nie ma.");
		} catch(IllegalArgumentException e) {
			// Jeśli wyłapiemy wyjątek, możemy sprawdzić w teście jego szczegóły,
			// np. jaki jest message (bardzo rzadko takie rzeczy sprawdza się w testach, ale można):
			assertEquals("Niedodatnia kwota w metodzie wplata", e.getMessage());
		} finally {
			// Możemy też upewnić się, że w przypadku wystąpienia wyjątku stan konta się nie zmienił.
			assertEquals(1000, konto.getSaldo());
		}
	}
	
	/* JUnit 4 pozwalał pisać tak. Było to proste w zapisie, ale mało precyzyjne w weryfikacji,
	 * bo tylko sprawdzał sam fakt wystapienia wyjątku, ale nie jego szczegóły.
	
	@Test(expected=IllegalArgumentException.class)
	void testWplataUjemna_v0() {
		konto.wplata(-100);
	}
	*/
	
	// Analogiczny test można też napisać wykorzystując nową funkcjonalność JUnit 5:
	@Test
	void testWplataUjemna_v1() {
		// assertThrows wykonuje podany fragment kodu (w praktyce podaje się wyrażenie lambda)
		// i sprawdza czy ten kończy się podanym rodzajem wyjątku.
		// Jeśli jest wyjątek - dobrze
		// Jeśli nie ma wyjątku - test failuje
		
		assertThrows(IllegalArgumentException.class, () -> {
			konto.wplata(-100);			
		});

		// Dodatkowo po wykonaniu assertThrows możemy sprawdzić jaki jest stan końcowy,
		// np. czy saldo się nie zmieniło.
		assertEquals(1000, konto.getSaldo());
	}
	
	// Aby sprawdzić jaki jest message w wyjątku itp, możemy odebrać obiekt wyjątku i sprawdzić bezpośrednio
	@Test
	void testWplataUjemna_v2() {
		IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
			konto.wplata(-100);			
		});

		assertEquals("Niedodatnia kwota w metodzie wplata", exception.getMessage());
		assertEquals(1000, konto.getSaldo());
	}
	
	
	@Test
	void testWyplataUjemna() {
		assertThrows(IllegalArgumentException.class, () -> {
			konto.wyplata(-100);			
		});
		assertEquals(1000, konto.getSaldo());
	}
	
	@Test
	void testBrakSrodkow() {
		assertThrows(BrakSrodkow.class, () -> {
			konto.wyplata(1300);			
		});
		assertEquals(1000, konto.getSaldo());
	}
}
