state = sys_get_temp_dir().'/fna-quiet-'.bin2hex(random_bytes(8)).'.json'; config(['site_mode.state_path'=>$this->state, 'site_mode.quiet'=>false, 'cache.default'=>'array', 'responsecache.enabled'=>false, 'database.default'=>'sqlite', 'database.connections.sqlite.database'=>':memory:', 'scout.driver'=>null, 'editorial.indexnow_enabled'=>false]); DB::purge('sqlite'); Http::preventStrayRequests(); Schema::create('categories', function (Blueprint $t) { $t->id(); $t->string('sSlug'); $t->string('sName'); }); DB::table('categories')->insert([ ['id'=>1,'sSlug'=>'freenode','sName'=>'免费节点'], ['id'=>2,'sSlug'=>'shadowrocketids','sName'=>'免费小火箭ID'], ['id'=>3,'sSlug'=>'client','sName'=>'新手教程'], ]); Schema::create('articles', function (Blueprint $t) { $t->id(); $t->integer('iCategoryId'); $t->string('sSlug'); $t->text('sContent'); $t->integer('iLikeCount')->default(0); $t->timestamps(); }); DB::table('articles')->insert(['id'=>7,'iCategoryId'=>1,'sSlug'=>'m7','sContent'=>'original']); } protected function tearDown(): void { if (isset($this->state) && is_file($this->state)) unlink($this->state); parent::tearDown(); } private function paused(callable $action): void { try { $action(); $this->fail('Expected paused response.'); } catch (HttpResponseException $e) { $this->assertSame(503, $e->getResponse()->getStatusCode()); $this->assertSame('3600', $e->getResponse()->headers->get('Retry-After')); $this->assertSame('FREE_NODE_UPDATES_PAUSED', json_decode($e->getResponse()->getContent(), true)['code']); } } public function test_command_switch_is_persistent_reversible_and_private(): void { $this->assertFalse(SiteMode::quiet()); $this->artisan('site:quiet on')->assertExitCode(0); $this->assertTrue(SiteMode::quiet()); $this->assertSame(0600, fileperms($this->state) & 0777); $this->artisan('site:quiet status')->assertExitCode(0); $this->assertTrue(json_decode(file_get_contents($this->state), true)['quiet']); $this->artisan('site:quiet off')->assertExitCode(0); $this->assertFalse(SiteMode::quiet()); $this->artisan('site:quiet invalid')->assertExitCode(1); $this->assertFalse(SiteMode::quiet()); } public function test_invalid_state_stays_paused_and_environment_fallback_works(): void { config(['site_mode.quiet'=>true]); $this->assertTrue(SiteMode::quiet()); config(['site_mode.quiet'=>false]); file_put_contents($this->state, 'invalid'); $this->assertTrue(SiteMode::quiet()); file_put_contents($this->state, '{"quiet":"false"}'); $this->assertTrue(SiteMode::quiet()); } public function test_receivers_pause_before_images_flows_and_any_database_write(): void { config(['site_mode.quiet'=>true]); $controller = new ArticlesController(); $this->paused(fn () => $controller->receiveFreenode(new Request())); $this->paused(fn () => $controller->receiveFreenode_laji(new Request())); $this->paused(fn () => $controller->receive(new Request(['_raw'=>[ 'id'=>8, 'opt'=>json_encode(['sCategoryName'=>'免费节点']), ]]))); // Moving an existing node article to another category must not bypass the pause. $this->paused(fn () => $controller->receive(new Request(['_raw'=>[ 'id'=>7, 'opt'=>json_encode(['sCategoryName'=>'新手教程']), ]]))); $this->assertSame(1, DB::table('articles')->count()); $this->assertSame('original', DB::table('articles')->where('id',7)->value('sContent')); Http::assertNothingSent(); } public function test_model_rejects_publication_changes_but_allows_metrics_and_other_types(): void { config(['site_mode.quiet'=>true]); $existing = Articles::find(7); $existing->sContent = 'replacement'; $this->paused(fn () => $existing->save()); $this->assertSame('original', DB::table('articles')->where('id',7)->value('sContent')); $new = new Articles(['iCategoryId'=>1,'sSlug'=>'fn-new','sContent'=>'new']); $this->paused(fn () => $new->save()); $metrics = Articles::find(7); $metrics->iLikeCount = 10; $metrics->save(); $this->assertSame(10, DB::table('articles')->where('id',7)->value('iLikeCount')); foreach ([2,3] as $id) SiteMode::guardArticle(new Articles(['iCategoryId'=>$id,'sContent'=>'unchanged scope'])); SiteMode::assertUpdatesAllowed('shadowrocketids'); $this->assertSame(1, DB::table('articles')->count()); config(['site_mode.quiet'=>false]); SiteMode::assertUpdatesAllowed('freenode'); } public function test_promotions_disappear_without_mutating_original_body_or_copy_payload(): void { $category = new Categories(['sSlug'=>'freenode']); $article = new Articles(['sContent'=>'
优惠