provider-endpoint-regression-742.test.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { describe, expect, test } from "vitest";
  2. import { createProvider, deleteProvider, updateProvider } from "@/repository/provider";
  3. import {
  4. createProviderEndpoint,
  5. findProviderEndpointsByVendorAndType,
  6. softDeleteProviderEndpoint,
  7. tryDeleteProviderVendorIfEmpty,
  8. } from "@/repository/provider-endpoints";
  9. const run = process.env.DSN ? describe : describe.skip;
  10. run("Provider endpoint regression #742", () => {
  11. test("sibling disappearance before fix: provider edit should keep non-target sibling endpoint visible", async () => {
  12. const suffix = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
  13. const oldUrl = `https://742-${suffix}.example.com/v1/messages`;
  14. const siblingUrl = `https://742-${suffix}.example.com/v2/messages`;
  15. let providerId: number | null = null;
  16. let vendorId: number | null = null;
  17. let providerType:
  18. | "claude"
  19. | "claude-auth"
  20. | "codex"
  21. | "gemini-cli"
  22. | "gemini"
  23. | "openai-compatible"
  24. | null = null;
  25. try {
  26. const created = await createProvider({
  27. name: `Regression 742 ${suffix}`,
  28. url: oldUrl,
  29. key: `sk-742-${suffix}`,
  30. provider_type: "claude",
  31. website_url: `https://vendor-${suffix}.example.com`,
  32. favicon_url: null,
  33. tpm: null,
  34. rpm: null,
  35. rpd: null,
  36. cc: null,
  37. });
  38. providerId = created.id;
  39. vendorId = created.providerVendorId;
  40. providerType = created.providerType;
  41. expect(vendorId).not.toBeNull();
  42. await createProviderEndpoint({
  43. vendorId: vendorId!,
  44. providerType: providerType!,
  45. url: siblingUrl,
  46. label: "sibling",
  47. });
  48. const updated = await updateProvider(providerId, { url: siblingUrl });
  49. expect(updated?.url).toBe(siblingUrl);
  50. const activeEndpoints = await findProviderEndpointsByVendorAndType(vendorId!, providerType!);
  51. const activeUrls = activeEndpoints.map((endpoint) => endpoint.url).sort();
  52. expect(activeUrls).toEqual([oldUrl, siblingUrl].sort());
  53. } finally {
  54. if (providerId != null) {
  55. await deleteProvider(providerId).catch(() => false);
  56. }
  57. if (vendorId != null && providerType != null) {
  58. const activeEndpoints = await findProviderEndpointsByVendorAndType(
  59. vendorId,
  60. providerType
  61. ).catch(() => []);
  62. await Promise.all(
  63. activeEndpoints.map((endpoint) =>
  64. softDeleteProviderEndpoint(endpoint.id).catch(() => false)
  65. )
  66. );
  67. await tryDeleteProviderVendorIfEmpty(vendorId).catch(() => false);
  68. }
  69. }
  70. });
  71. });