constants_cli_version_test.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. package claude
  2. import (
  3. "strconv"
  4. "strings"
  5. "testing"
  6. "github.com/stretchr/testify/require"
  7. )
  8. // fable51MinCLIVersion 是上游对 claude-fable-5-1 的客户端版本闸门下限。
  9. // 低于该版本时上游直接 400:
  10. //
  11. // claude_code_version_too_old
  12. // "Claude Code <ver> does not support this model; version 2.1.251 or newer is required."
  13. const fable51MinCLIVersion = "2.1.251"
  14. // TestCLICurrentVersionMatchesDefaultUserAgent 锁定"常量与 UA 同源"这条约束。
  15. // 两者不一致会被 Anthropic 判为第三方客户端,且只改一处是历史上的常见疏漏,
  16. // 所以这里从 UA 里反解版本号来比对,而不是并列两个字面量。
  17. func TestCLICurrentVersionMatchesDefaultUserAgent(t *testing.T) {
  18. t.Parallel()
  19. ua := DefaultHeaders["User-Agent"]
  20. const prefix = "claude-cli/"
  21. require.True(t, strings.HasPrefix(ua, prefix), "unexpected User-Agent: %q", ua)
  22. rest := strings.TrimPrefix(ua, prefix)
  23. version := rest
  24. if idx := strings.IndexByte(rest, ' '); idx >= 0 {
  25. version = rest[:idx]
  26. }
  27. require.Equal(t, CLICurrentVersion, version,
  28. "DefaultHeaders User-Agent version must match CLICurrentVersion")
  29. }
  30. // TestCLICurrentVersionSatisfiesFable51Gate 锁定伪装版本不低于 claude-fable-5-1
  31. // 的闸门下限。DefaultModels 里登记了该模型却发着更旧的版本号,等于模型清单可见
  32. // 但一调就 400。
  33. func TestCLICurrentVersionSatisfiesFable51Gate(t *testing.T) {
  34. t.Parallel()
  35. require.GreaterOrEqual(t, compareSemver(t, CLICurrentVersion, fable51MinCLIVersion), 0,
  36. "CLICurrentVersion %s is below the claude-fable-5-1 gate %s",
  37. CLICurrentVersion, fable51MinCLIVersion)
  38. }
  39. // compareSemver 比较两个三段 semver,返回 -1 / 0 / 1。
  40. func compareSemver(t *testing.T, a, b string) int {
  41. t.Helper()
  42. as, bs := strings.Split(a, "."), strings.Split(b, ".")
  43. require.Len(t, as, 3, "not a three-part semver: %q", a)
  44. require.Len(t, bs, 3, "not a three-part semver: %q", b)
  45. for i := range as {
  46. ai, err := strconv.Atoi(as[i])
  47. require.NoError(t, err, "non-numeric segment in %q", a)
  48. bi, err := strconv.Atoi(bs[i])
  49. require.NoError(t, err, "non-numeric segment in %q", b)
  50. switch {
  51. case ai > bi:
  52. return 1
  53. case ai < bi:
  54. return -1
  55. }
  56. }
  57. return 0
  58. }