user = User::factory()->create(['role' => 'apoteker']); $this->actingAs($this->user); $kategori = Kategori::create(['nama' => 'Antibiotik']); $satuan = Satuan::create(['nama' => 'Tablet']); $this->obatMasuk = ObatMasuk::create([ 'nama_obat' => 'Amoxicillin', 'kategori_id' => $kategori->id, 'satuan_id' => $satuan->id, 'kode_batch' => 'BTH-001', 'stok' => 100, 'tanggal_penerimaan' => now()->toDateString(), 'tanggal_kadaluarsa' => now()->addYear()->toDateString(), 'user_id' => $this->user->id, ]); } private function validData(array $overrides = []): array { return array_merge([ 'obat_masuk_id' => $this->obatMasuk->id, 'kode_batch' => 'BTH-001', 'jumlah' => 10, 'tujuan_pemakaian' => 'Penggunaan internal', 'tanggal_kadaluarsa' => now()->addYear()->toDateString(), 'tanggal_pengeluaran' => now()->toDateString(), 'nama_petugas' => 'Petugas A', 'nama_penerima' => 'Penerima B', ], $overrides); } // ───────────────────────────────────────── // index // ───────────────────────────────────────── public function test_index_returns_200(): void { $response = $this->get(route('obat-keluar.index')); $response->assertStatus(200); } public function test_index_guest_redirects_to_login(): void { auth()->logout(); $response = $this->get(route('obat-keluar.index')); $response->assertRedirect(route('login')); } public function test_index_filter_by_status(): void { ObatKeluar::create(array_merge($this->validData(['jumlah' => 5]), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $response = $this->get(route('obat-keluar.index', ['status' => 'proses'])); $response->assertStatus(200); } public function test_index_search(): void { ObatKeluar::create(array_merge($this->validData(), [ 'nama_obat' => 'Amoxicillin', 'kode_batch' => 'BTH-001', 'user_id' => $this->user->id, ])); ObatKeluar::create(array_merge($this->validData(), [ 'nama_obat' => 'Paracetamol', 'kode_batch' => 'BTH-002', 'user_id' => $this->user->id, ])); $response = $this->get(route('obat-keluar.index', ['search' => 'Paracetamol'])); $data = $response->viewData('obatKeluars'); $names = $data->pluck('nama_obat')->toArray(); $this->assertContains('Paracetamol', $names); $this->assertNotContains('Amoxicillin', $names); } // ───────────────────────────────────────── // create, show, edit // ───────────────────────────────────────── public function test_create_returns_200(): void { $response = $this->get(route('obat-keluar.create')); $response->assertStatus(200); } public function test_show_returns_200(): void { $keluar = ObatKeluar::create(array_merge($this->validData(), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $response = $this->get(route('obat-keluar.show', $keluar)); $response->assertStatus(200); } public function test_edit_returns_200(): void { $keluar = ObatKeluar::create(array_merge($this->validData(), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $response = $this->get(route('obat-keluar.edit', $keluar)); $response->assertStatus(200); } // ───────────────────────────────────────── // store — berhasil // ───────────────────────────────────────── public function test_store_creates_obat_keluar_and_decrements_stok(): void { $initialStok = $this->obatMasuk->stok; // 100 $response = $this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 10])); $response->assertRedirect(route('obat-keluar.index')); $response->assertSessionHas('success'); $this->assertDatabaseHas('obat_keluars', [ 'obat_masuk_id' => $this->obatMasuk->id, 'jumlah' => 10, 'nama_obat' => 'Amoxicillin', ]); // Stok berkurang dari 100 menjadi 90 $this->assertEquals($initialStok - 10, $this->obatMasuk->fresh()->stok); } public function test_store_sets_user_id_and_nama_obat_automatically(): void { $this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 5])); $this->assertDatabaseHas('obat_keluars', [ 'user_id' => $this->user->id, 'nama_obat' => 'Amoxicillin', ]); } // ───────────────────────────────────────── // store — gagal karena stok // ───────────────────────────────────────── public function test_store_fails_when_jumlah_exceeds_stok(): void { $response = $this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 200])); // stok cuma 100 // Harus kembali ke form dengan error jumlah $response->assertSessionHasErrors('jumlah'); } public function test_store_stok_unchanged_when_validation_fails(): void { $this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 200])); $this->assertEquals(100, $this->obatMasuk->fresh()->stok); } // ───────────────────────────────────────── // store — validasi field wajib // ───────────────────────────────────────── public function test_store_fails_without_required_fields(): void { $response = $this->post(route('obat-keluar.store'), []); $response->assertSessionHasErrors([ 'obat_masuk_id', 'kode_batch', 'jumlah', 'tujuan_pemakaian', 'tanggal_kadaluarsa', 'tanggal_pengeluaran', 'nama_petugas', 'nama_penerima', ]); } // ───────────────────────────────────────── // update — perubahan jumlah // ───────────────────────────────────────── public function test_update_adjusts_stok_when_jumlah_increases(): void { // Buat obat keluar awal dengan jumlah 10 → stok jadi 90 $keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 10]), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $this->obatMasuk->decrement('stok', 10); // stok = 90 // Update jumlah menjadi 30 (selisih +20, stok dari 90 → 70) $response = $this->put( route('obat-keluar.update', $keluar), $this->validData(['jumlah' => 30]) ); $response->assertRedirect(route('obat-keluar.index')); $this->assertEquals(70, $this->obatMasuk->fresh()->stok); } public function test_update_adjusts_stok_when_jumlah_decreases(): void { // Buat obat keluar awal dengan jumlah 20 → stok jadi 80 $keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 20]), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $this->obatMasuk->decrement('stok', 20); // stok = 80 // Update jumlah menjadi 10 (selisih −10, stok dari 80 → 90) $response = $this->put( route('obat-keluar.update', $keluar), $this->validData(['jumlah' => 10]) ); $response->assertRedirect(route('obat-keluar.index')); $this->assertEquals(90, $this->obatMasuk->fresh()->stok); } public function test_update_fails_when_increased_jumlah_exceeds_available_stok(): void { // Stok awal 100, keluar 10 → stok = 90 $keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 10]), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $this->obatMasuk->decrement('stok', 10); // Coba update ke 200 (selisih = +190, stok hanya 90) $response = $this->put( route('obat-keluar.update', $keluar), $this->validData(['jumlah' => 200]) ); $response->assertSessionHasErrors('jumlah'); } public function test_update_fails_without_required_fields(): void { $keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 10]), [ 'nama_obat' => 'Amoxicillin', 'user_id' => $this->user->id, ])); $response = $this->put(route('obat-keluar.update', $keluar), []); $response->assertSessionHasErrors([ 'obat_masuk_id', 'kode_batch', 'jumlah', 'tujuan_pemakaian', 'tanggal_kadaluarsa', 'tanggal_pengeluaran', 'nama_petugas', 'nama_penerima', ]); } // ───────────────────────────────────────── // destroy — stok dikembalikan // ───────────────────────────────────────── public function test_destroy_deletes_obat_keluar_and_restores_stok(): void { // Buat keluar jumlah 15 → stok jadi 85 $keluar = ObatKeluar::create(array_merge($this->validData(['jumlah' => 15]), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $this->obatMasuk->decrement('stok', 15); $response = $this->delete(route('obat-keluar.destroy', $keluar)); $response->assertRedirect(route('obat-keluar.index')); $response->assertSessionHas('success'); // Stok dikembalikan → 100 $this->assertEquals(100, $this->obatMasuk->fresh()->stok); $this->assertDatabaseMissing('obat_keluars', ['id' => $keluar->id]); } // ───────────────────────────────────────── // superadmin access // ───────────────────────────────────────── public function test_superadmin_can_access_index(): void { $superadmin = User::factory()->create(['role' => 'superadmin']); $this->actingAs($superadmin); $response = $this->get(route('obat-keluar.index')); $response->assertStatus(200); } public function test_superadmin_can_access_show(): void { $superadmin = User::factory()->create(['role' => 'superadmin']); $this->actingAs($superadmin); $keluar = ObatKeluar::create(array_merge($this->validData(), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $response = $this->get(route('obat-keluar.show', $keluar)); $response->assertStatus(200); } public function test_superadmin_can_access_create_store_edit_update_destroy(): void { $superadmin = User::factory()->create(['role' => 'superadmin']); $this->actingAs($superadmin); $keluar = ObatKeluar::create(array_merge($this->validData(), [ 'nama_obat' => 'Amoxicillin', 'status' => 'proses', 'user_id' => $this->user->id, ])); $this->get(route('obat-keluar.create'))->assertStatus(200); $this->post(route('obat-keluar.store'), $this->validData(['jumlah' => 10])) ->assertRedirect(route('obat-keluar.index')); $this->assertDatabaseHas('obat_keluars', ['user_id' => $superadmin->id, 'jumlah' => 10]); $this->get(route('obat-keluar.edit', $keluar))->assertStatus(200); $this->put(route('obat-keluar.update', $keluar), $this->validData(['jumlah' => 8])) ->assertRedirect(route('obat-keluar.index')); $this->assertDatabaseHas('obat_keluars', ['id' => $keluar->id, 'jumlah' => 8]); $this->delete(route('obat-keluar.destroy', $keluar))->assertRedirect(route('obat-keluar.index')); $this->assertDatabaseMissing('obat_keluars', ['id' => $keluar->id]); } }