metadata-injection.test.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { describe, expect, it } from "vitest";
  2. import { injectClaudeMetadataUserId } from "@/app/v1/_lib/proxy/forwarder";
  3. import { ProxySession } from "@/app/v1/_lib/proxy/session";
  4. function createSession(
  5. keyId: number | null | undefined = 123,
  6. sessionId: string | null | undefined = "sess_test"
  7. ): ProxySession {
  8. const session = Object.create(ProxySession.prototype) as ProxySession;
  9. (session as Record<string, unknown>).authState =
  10. keyId === undefined ? undefined : { key: { id: keyId } };
  11. (session as Record<string, unknown>).sessionId = sessionId ?? null;
  12. return session;
  13. }
  14. function extractUserHash(userId: string): string {
  15. const match = userId.match(/^user_([a-f0-9]{64})_account__session_/);
  16. if (!match) {
  17. throw new Error(`Unexpected user_id format: ${userId}`);
  18. }
  19. return match[1];
  20. }
  21. describe("injectClaudeMetadataUserId", () => {
  22. it("无 metadata 时应正确注入 user_id", () => {
  23. const message: Record<string, unknown> = { model: "claude-3-5-sonnet" };
  24. const session = createSession(42, "sess_abc123");
  25. const result = injectClaudeMetadataUserId(message, session);
  26. const metadata = result.metadata as Record<string, unknown>;
  27. expect(result).not.toBe(message);
  28. expect(metadata.user_id).toMatch(/^user_[a-f0-9]{64}_account__session_sess_abc123$/);
  29. });
  30. it("已有 metadata.user_id 时应保持原样不覆盖", () => {
  31. const message: Record<string, unknown> = {
  32. metadata: {
  33. user_id: "existing_user_id",
  34. source: "client",
  35. },
  36. };
  37. const session = createSession(42, "sess_abc123");
  38. const result = injectClaudeMetadataUserId(message, session);
  39. expect(result).toBe(message);
  40. expect((result.metadata as Record<string, unknown>).user_id).toBe("existing_user_id");
  41. });
  42. it("metadata.user_id 为空字符串时应保持原样不注入", () => {
  43. const message: Record<string, unknown> = {
  44. metadata: {
  45. user_id: "",
  46. },
  47. };
  48. const session = createSession(42, "sess_abc123");
  49. const result = injectClaudeMetadataUserId(message, session);
  50. expect(result).toBe(message);
  51. expect((result.metadata as Record<string, unknown>).user_id).toBe("");
  52. });
  53. it("keyId 缺失时应跳过注入并返回原始 message", () => {
  54. const message: Record<string, unknown> = { model: "claude-3" };
  55. const session = createSession(null, "sess_abc123");
  56. const result = injectClaudeMetadataUserId(message, session);
  57. expect(result).toBe(message);
  58. expect(result.metadata).toBeUndefined();
  59. });
  60. it("sessionId 缺失时应跳过注入", () => {
  61. const message: Record<string, unknown> = { model: "claude-3" };
  62. const session = createSession(42, null);
  63. const result = injectClaudeMetadataUserId(message, session);
  64. expect(result).toBe(message);
  65. expect(result.metadata).toBeUndefined();
  66. });
  67. it("相同 keyId 应生成相同 hash", () => {
  68. const messageA: Record<string, unknown> = {};
  69. const messageB: Record<string, unknown> = {};
  70. const sessionA = createSession(7, "sess_one");
  71. const sessionB = createSession(7, "sess_two");
  72. const userIdA = (
  73. injectClaudeMetadataUserId(messageA, sessionA).metadata as Record<string, unknown>
  74. ).user_id as string;
  75. const userIdB = (
  76. injectClaudeMetadataUserId(messageB, sessionB).metadata as Record<string, unknown>
  77. ).user_id as string;
  78. expect(extractUserHash(userIdA)).toBe(extractUserHash(userIdB));
  79. });
  80. it("不同 keyId 应生成不同 hash", () => {
  81. const messageA: Record<string, unknown> = {};
  82. const messageB: Record<string, unknown> = {};
  83. const sessionA = createSession(7, "sess_same");
  84. const sessionB = createSession(8, "sess_same");
  85. const userIdA = (
  86. injectClaudeMetadataUserId(messageA, sessionA).metadata as Record<string, unknown>
  87. ).user_id as string;
  88. const userIdB = (
  89. injectClaudeMetadataUserId(messageB, sessionB).metadata as Record<string, unknown>
  90. ).user_id as string;
  91. expect(extractUserHash(userIdA)).not.toBe(extractUserHash(userIdB));
  92. });
  93. it("metadata 为非对象类型时应安全处理", () => {
  94. const message: Record<string, unknown> = {
  95. metadata: "not-an-object",
  96. };
  97. const session = createSession(42, "sess_abc123");
  98. const result = injectClaudeMetadataUserId(message, session);
  99. const metadata = result.metadata as Record<string, unknown>;
  100. expect(result).not.toBe(message);
  101. expect(typeof metadata).toBe("object");
  102. expect(metadata.user_id).toMatch(/^user_[a-f0-9]{64}_account__session_sess_abc123$/);
  103. });
  104. });