codesPath = tempnam(sys_get_temp_dir(), 'codes_'); file_put_contents($this->codesPath, "1_AAAA\n2_BBBB\n3_CCCC\n"); } protected function tearDown(): void { if (is_file($this->codesPath)) { unlink($this->codesPath); } parent::tearDown(); } public function test_all_codes_loads_from_file(): void { $pool = new CouponCodePool($this->codesPath); $this->assertSame(['1_AAAA', '2_BBBB', '3_CCCC'], $pool->allCodes()); } public function test_available_codes_excludes_used_coupons(): void { Coupon::factory()->create(['code' => '1_AAAA', 'phone' => '61111111']); $pool = new CouponCodePool($this->codesPath); $this->assertEqualsCanonicalizing(['2_BBBB', '3_CCCC'], $pool->availableCodes()); } public function test_pick_random_returns_only_unused_code(): void { Coupon::factory()->create(['code' => '2_BBBB', 'phone' => '61111111']); $pool = new CouponCodePool($this->codesPath); for ($i = 0; $i < 20; $i++) { $picked = $pool->pickRandom(); $this->assertContains($picked, ['1_AAAA', '3_CCCC']); } } public function test_has_available_is_false_when_all_codes_used(): void { Coupon::factory()->create(['code' => '1_AAAA', 'phone' => '61111111']); Coupon::factory()->create(['code' => '2_BBBB', 'phone' => '62222222']); Coupon::factory()->create(['code' => '3_CCCC', 'phone' => '63333333']); $pool = new CouponCodePool($this->codesPath); $this->assertFalse($pool->hasAvailable()); $this->assertNull($pool->pickRandom()); $this->assertSame([], $pool->availableCodes()); } }