withoutMiddleware([ VerifyCsrfToken::class, \Illuminate\Session\Middleware\AuthenticateSession::class, ]); } // ─── Comment component ──────────────────────────── public function test_comment_header_uses_singular_count_for_one_comment(): void { [$user, $video] = $this->makeUserAndVideo(); Comment::create([ 'user_id' => $user->id, 'video_id' => $video->id, 'body' => 'the only comment', ]); $this->actingAs($user); $html = view('components.video-comments', ['video' => $video])->render(); $this->assertStringContainsString('1 Comment', $html); $this->assertStringNotContainsString('1 Comments', $html); } public function test_comment_header_breaks_out_replies_from_top_level(): void { [$user, $video] = $this->makeUserAndVideo(); $parent = Comment::create([ 'user_id' => $user->id, 'video_id' => $video->id, 'body' => 'top', ]); Comment::create([ 'user_id' => $user->id, 'video_id' => $video->id, 'parent_id' => $parent->id, 'body' => 'reply 1', ]); Comment::create([ 'user_id' => $user->id, 'video_id' => $video->id, 'parent_id' => $parent->id, 'body' => 'reply 2', ]); $this->actingAs($user); $html = view('components.video-comments', ['video' => $video])->render(); $this->assertStringContainsString('1 Comment', $html, 'Top-level count is 1.'); $this->assertStringContainsString('2 replies', $html, 'Replies are counted separately.'); } public function test_each_comment_timestamp_is_a_lc_permalink(): void { [$user, $video] = $this->makeUserAndVideo(); $c = Comment::create([ 'user_id' => $user->id, 'video_id' => $video->id, 'body' => 'hi', ]); $this->actingAs($user); $html = view('components.video-comments', ['video' => $video])->render(); $this->assertStringContainsString('?lc=' . $c->id . '#comment-' . $c->id, $html); $this->assertStringContainsString('ytc-permalink', $html); } // ─── Description markdown ──────────────────────── public function test_leaked_markdown_headers_render_as_html(): void { $out = HtmlSanitizer::render("Intro line\n## Letra\nla la\n### Chorus"); $this->assertStringContainsString('

Letra

', $out); $this->assertStringContainsString('

Chorus

', $out); $this->assertStringNotContainsString('## Letra', $out); $this->assertStringNotContainsString('### Chorus', $out); } public function test_normal_plain_text_description_passes_through(): void { $out = HtmlSanitizer::render("hello world\nhow are you"); $this->assertStringContainsString('hello world', $out); $this->assertStringContainsString('how are you', $out); $this->assertStringNotContainsString('

', $out); } // ─── Download access gate ──────────────────────── public function test_disabled_download_returns_403_for_mp4(): void { [$user, $video] = $this->makeUserAndVideo(['download_access' => 'disabled']); $this->get(route('videos.download', $video))->assertStatus(403); } public function test_disabled_download_returns_403_for_mp3(): void { [$user, $video] = $this->makeUserAndVideo(['download_access' => 'disabled']); $this->get(route('videos.downloadMp3', $video))->assertStatus(403); } public function test_registered_download_denies_guests(): void { [$user, $video] = $this->makeUserAndVideo(['download_access' => 'registered']); $this->get(route('videos.download', $video))->assertStatus(403); } // ─── Up Next anchor cards ───────────────────────── public function test_up_next_sidebar_renders_anchor_cards_with_hrefs(): void { [$owner, $target] = $this->makeUserAndVideo(); [, $rec] = $this->makeUserAndVideo(); $html = $this->get(route('videos.show', $target))->getContent(); $this->assertStringContainsString('assertStringContainsString('data-rec-url=', $html, 'data-rec-url hooks keep the SPA transition working for left-clicks.'); } // ─── Helpers ────────────────────────────────────── private function makeUserAndVideo(array $attrs = []): array { $user = User::factory()->create([ 'username' => 'u'.substr(bin2hex(random_bytes(4)), 0, 8), ]); $video = Video::create(array_merge([ 'user_id' => $user->id, 'title' => 'T '.substr(bin2hex(random_bytes(4)), 0, 6), 'description'=> '', 'filename' => 'users/x/videos/'.substr(bin2hex(random_bytes(4)), 0, 8).'/video.mp4', 'path' => 'users/x/videos/'.substr(bin2hex(random_bytes(4)), 0, 8).'/video.mp4', 'type' => 'generic', 'visibility' => 'public', 'status' => 'ready', 'share_token'=> bin2hex(random_bytes(8)), ], $attrs)); return [$user, $video]; } }