allowed-model-rules.test.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import { describe, expect, it } from "vitest";
  2. import {
  3. findMatchingAllowedModelRule,
  4. isAllowedModelRule,
  5. matchesAllowedModelRules,
  6. normalizeAllowedModelRules,
  7. } from "@/lib/allowed-model-rules";
  8. import type { AllowedModelRule } from "@/types/provider";
  9. describe("allowed-model-rules", () => {
  10. it("normalizes legacy string arrays into exact-match rules", () => {
  11. expect(normalizeAllowedModelRules(["claude-opus", "gpt-4o"])).toEqual([
  12. { matchType: "exact", pattern: "claude-opus" },
  13. { matchType: "exact", pattern: "gpt-4o" },
  14. ]);
  15. });
  16. it("preserves rule objects and trims mixed inputs", () => {
  17. const input = [
  18. " claude-sonnet ",
  19. { matchType: "prefix", pattern: " claude-opus-" },
  20. ] satisfies Array<string | AllowedModelRule>;
  21. expect(normalizeAllowedModelRules(input)).toEqual([
  22. { matchType: "exact", pattern: "claude-sonnet" },
  23. { matchType: "prefix", pattern: "claude-opus-" },
  24. ]);
  25. });
  26. it("treats null and empty rules as allow-all", () => {
  27. expect(matchesAllowedModelRules("claude-opus", null)).toBe(true);
  28. expect(matchesAllowedModelRules("claude-opus", [])).toBe(true);
  29. expect(matchesAllowedModelRules("claude-opus", ["", " "])).toBe(true);
  30. expect(findMatchingAllowedModelRule("claude-opus", null)).toBeNull();
  31. expect(findMatchingAllowedModelRule("claude-opus", [])).toBeNull();
  32. });
  33. it("matches advanced rule types in order and returns the first hit", () => {
  34. const rules: AllowedModelRule[] = [
  35. { matchType: "contains", pattern: "opus" },
  36. { matchType: "prefix", pattern: "claude-opus-" },
  37. { matchType: "regex", pattern: "^claude-opus-4" },
  38. ];
  39. expect(matchesAllowedModelRules("claude-opus-4-1", rules)).toBe(true);
  40. expect(findMatchingAllowedModelRule("claude-opus-4-1", rules)).toEqual(rules[0]);
  41. });
  42. it("returns false when no rule matches or regex is invalid", () => {
  43. expect(
  44. matchesAllowedModelRules("claude-opus-4-1", [{ matchType: "regex", pattern: "[" }])
  45. ).toBe(false);
  46. expect(
  47. matchesAllowedModelRules("claude-opus-4-1", [{ matchType: "suffix", pattern: "-haiku" }])
  48. ).toBe(false);
  49. });
  50. it("detects allowed model rule objects safely", () => {
  51. expect(isAllowedModelRule({ matchType: "prefix", pattern: "claude-" })).toBe(true);
  52. expect(isAllowedModelRule({ matchType: "prefix", source: "claude-" })).toBe(false);
  53. expect(isAllowedModelRule("claude-opus")).toBe(false);
  54. });
  55. });