V1CookieRemovalTest.php 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. <?php
  2. use Illuminate\Foundation\Testing\RefreshDatabase;
  3. use Illuminate\Support\Str;
  4. uses(RefreshDatabase::class);
  5. /**
  6. * v1 时代的 cookie 认证绕过已从 v2 控制器里清干净。
  7. *
  8. * 这几条是 characterization test:改动的是冻结中的 v2,必须钉住「未登录时明确
  9. * 被拒」和「带 bearer 时照常工作」两件事,而不是靠 cookie 悄悄放行。
  10. *
  11. * 带 cookie 的用例必须直接操作 `$_COOKIE` 超全局——`withUnencryptedCookies()`
  12. * 只填 Symfony Request 的 cookie 袋子,不碰超全局,用它写出来的测试是空转的。
  13. */
  14. function withV1Cookie(array $cookies, callable $fn): mixed
  15. {
  16. $backup = $_COOKIE;
  17. $_COOKIE = $cookies;
  18. try {
  19. return $fn();
  20. } finally {
  21. $_COOKIE = $backup;
  22. }
  23. }
  24. it('removed the dead DELETE /v2/like route', function () {
  25. // 前端只用 POST /v2/like 与 DELETE /v2/like/{id};这条无 id 的 DELETE 没人调,
  26. // 而且 cookie 是它唯一的认证方式,已整条删除
  27. $this->deleteJson('/api/v2/like')->assertStatus(405);
  28. });
  29. it('keeps the like routes the frontends actually use', function () {
  30. $target = (string) Str::uuid();
  31. $this->getJson("/api/v2/like?view=count&target_id={$target}")
  32. ->assertOk()
  33. ->assertJsonPath('ok', true);
  34. });
  35. it('rejects sentence fulltext without a bearer token', function () {
  36. // 这个 view 以前靠 cookie,且未登录时 $userUid 是未定义变量
  37. // $this->error() 默认 400,这是 v2 既有的失败口径,不是这次改出来的
  38. $json = withV1Cookie(['user_uid' => (string) Str::uuid()], fn () => $this
  39. ->getJson('/api/v2/sentence?view=fulltext&key=abc')
  40. ->assertStatus(400)
  41. ->json());
  42. expect($json['ok'])->toBeFalse()
  43. ->and($json['message'])->toBe(__('auth.failed'));
  44. });
  45. it('serves sentence fulltext to a bearer-authenticated user', function () {
  46. $uid = makeStudio('fulltext-user');
  47. $json = $this->getJson('/api/v2/sentence?view=fulltext&key=abc', authHeader($uid))
  48. ->assertOk()
  49. ->json();
  50. expect($json['ok'])->toBeTrue()
  51. ->and($json['data'])->toHaveKey('rows');
  52. });
  53. it('rejects userdict view=user without a bearer token', function () {
  54. $json = withV1Cookie(['user_id' => '1'], fn () => $this
  55. ->getJson('/api/v2/userdict?view=user')
  56. ->assertStatus(400)
  57. ->json());
  58. expect($json['ok'])->toBeFalse()
  59. ->and($json['message'])->toBe(__('auth.failed'));
  60. });
  61. it('serves userdict view=user to a bearer-authenticated user', function () {
  62. $uid = makeStudio('userdict-user');
  63. $json = $this->getJson('/api/v2/userdict?view=user', authHeader($uid))
  64. ->assertOk()
  65. ->json();
  66. expect($json['ok'])->toBeTrue()
  67. ->and($json['data'])->toHaveKey('rows');
  68. });
  69. it('rejects DELETE /v2/userdict without a bearer token', function () {
  70. $json = withV1Cookie(['user_id' => '1'], fn () => $this
  71. ->deleteJson('/api/v2/userdict', ['id' => json_encode(['1'])])
  72. ->assertStatus(400)
  73. ->json());
  74. expect($json['ok'])->toBeFalse()
  75. ->and($json['message'])->toBe(__('auth.failed'));
  76. });