id(); $table->string('nama'); }); foreach ([ 'master_kabupaten_kota' => 'provinsi_id', 'master_kecamatan' => 'kabupaten_kota_id', 'master_desa_kelurahan' => 'kecamatan_id', ] as $name => $parent) { Schema::create($name, function (Blueprint $table) use ($parent) { $table->id(); $table->integer($parent); }); } Schema::create('petani', function (Blueprint $table) { $table->id(); $table->integer('komoditas_id')->nullable(); $table->integer('desa_kelurahan_id'); $table->softDeletes(); }); Schema::create('akses_pasar', function (Blueprint $table) { $table->id(); $table->integer('petani_id'); $table->string('praktik_fermentasi')->nullable(); $table->softDeletes(); }); Schema::create('petani_skor', function (Blueprint $table) { $table->id(); $table->integer('petani_id'); $table->float('skor_kesiapan_adopsi'); $table->softDeletes(); }); DB::table('master_komoditas')->insert([ ['id' => 1, 'nama' => 'Kakao'], ['id' => 2, 'nama' => 'Kopi'], ['id' => 3, 'nama' => 'Vanili'], ]); DB::table('master_kabupaten_kota')->insert(['id' => 1, 'provinsi_id' => 1]); DB::table('master_kecamatan')->insert(['id' => 1, 'kabupaten_kota_id' => 1]); DB::table('master_desa_kelurahan')->insert([ ['id' => 1, 'kecamatan_id' => 1], ['id' => 2, 'kecamatan_id' => 1], ]); foreach ([ [1, 1, 1, PraktikFermentasi::RUTIN_SENDIRI->value], [2, 1, 1, PraktikFermentasi::RUTIN_KELOMPOK->value], [3, 2, 2, PraktikFermentasi::PERNAH_SENDIRI->value], [4, 2, 2, PraktikFermentasi::PERNAH_KELOMPOK->value], [5, 3, 2, PraktikFermentasi::BELUM_PERNAH->value], [6, 3, 2, null], [7, 1, 1, ''], [8, 2, 2, PraktikFermentasi::RUTIN_SENDIRI->value], [9, null, 2, PraktikFermentasi::BELUM_PERNAH->value], [10, 1, 1, PraktikFermentasi::RUTIN_SENDIRI->value], [11, 1, 1, 'Tidak dikenal'], ] as [$id, $commodity, $village, $practice]) { DB::table('petani')->insert([ 'id' => $id, 'komoditas_id' => $commodity, 'desa_kelurahan_id' => $village, 'deleted_at' => $id === 10 ? '2026-01-01' : null, ]); DB::table('akses_pasar')->insert([ 'petani_id' => $id, 'praktik_fermentasi' => $practice, 'deleted_at' => $id === 8 ? '2026-01-01' : null, ]); } DB::table('petani_skor')->insert([ ['petani_id' => 1, 'skor_kesiapan_adopsi' => 4.1, 'deleted_at' => null], ['petani_id' => 2, 'skor_kesiapan_adopsi' => 2.6, 'deleted_at' => null], ['petani_id' => 3, 'skor_kesiapan_adopsi' => 4.4, 'deleted_at' => '2026-01-01'], ]); $controller = app(SummaryController::class); $query = new ReflectionMethod($controller, 'filteredAksesPasar'); $chart = new ReflectionMethod($controller, 'fermentationPracticeByCommodityChart'); $this->fermentationChart = fn (array $filters = []) => $chart->invoke( $controller, $query->invoke($controller, new Request($filters)), ); }); test('fermentation groups individual and collective responses by commodity', function () { expect(($this->fermentationChart)())->toBe([ 'labels' => ['Belum ditentukan', 'Kakao', 'Kopi', 'Vanili'], 'series' => [ ['name' => 'Ya, rutin', 'data' => [0, 2, 0, 0]], ['name' => 'Pernah mencoba', 'data' => [0, 0, 2, 0]], ['name' => 'Belum pernah', 'data' => [1, 0, 0, 1]], ], ]); }); test('fermentation respects region and commodity filters', function () { $chart = ($this->fermentationChart)(['desaKelurahan_id' => 2, 'komoditas_id' => 2]); expect($chart['labels'])->toBe(['Kopi']); expect(array_column($chart['series'], 'data'))->toBe([[0], [2], [0]]); expect(($this->fermentationChart)(['desaKelurahan_id' => 1])['labels'])->toBe(['Kakao']); }); test('fermentation respects segment filters and excludes deleted scores', function () { $chart = ($this->fermentationChart)(['segmen' => 'Pemerhati']); expect($chart['labels'])->toBe(['Kakao']); expect(array_column($chart['series'], 'data'))->toBe([[1], [0], [0]]); expect(($this->fermentationChart)(['segmen' => 'Penggerak'])['labels'])->toBe(['Kakao']); }); test('fermentation returns empty grouped series when no matching responses exist', function () { $chart = ($this->fermentationChart)(['provinsi_id' => 99]); expect($chart['labels'])->toBe([]); expect(array_column($chart['series'], 'data'))->toBe([[], [], []]); });