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(1, "Ala", 1000);
	}

	@Test
	void testKonstruktorIGettery() {
		assertEquals(1, konto.getNumer());
		assertEquals("Ala", konto.getWlasciciel());
		assertEquals(1000, konto.getSaldo());
	}
	
	@Test
	void testToString() {
		String napis = konto.toString();
		assertEquals("Konto nr 1, saldo: 1000, wł.: Ala", napis);
	}
	
	@Test
	void testWplata() {
		konto.wplata(400);
		assertEquals(1400, konto.getSaldo());
	}

	@Test
	void testWyplata() throws BrakSrodkow {
		konto.wyplata(400);
		assertEquals(600, 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 testWplataTry() {
		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());
		}
	}
	
	// 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());
	}
}
