gateway_messages_cache_json_test.go 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. //go:build unit
  2. package service
  3. import (
  4. "encoding/json"
  5. "strings"
  6. "testing"
  7. "github.com/stretchr/testify/require"
  8. "github.com/tidwall/gjson"
  9. )
  10. func TestAddMessageCacheBreakpoints_JSONStringEscaping(t *testing.T) {
  11. var controls strings.Builder
  12. for r := rune(0); r <= 0x9f; r++ {
  13. controls.WriteRune(r)
  14. }
  15. tests := []struct{ name, text string }{
  16. {"delete_character", "before\x7fafter"},
  17. {"bell_and_vertical_tab", "before\a\vafter"},
  18. {"nul_and_escape", "before\x00\x1bafter"},
  19. {"all_ascii_and_c1_controls", controls.String()},
  20. {"supplementary_nonprinting_rune", "before\U000e0067after"},
  21. {"unicode_and_json_metacharacters", "中文 😀 <>& \"quote\" \\slash\n\r\t\b\f"},
  22. {"literal_backslash_sequences", `literal \x7f \a \v \U000e0067`},
  23. }
  24. for _, tt := range tests {
  25. t.Run(tt.name, func(t *testing.T) {
  26. // Both the final message and the second-to-last user turn are promoted
  27. // from strings to text blocks by the cache rewrite.
  28. body, err := json.Marshal(map[string]any{
  29. "messages": []map[string]any{
  30. {"role": "user", "content": tt.text},
  31. {"role": "assistant", "content": "ack"},
  32. {"role": "user", "content": "next"},
  33. {"role": "assistant", "content": tt.text},
  34. },
  35. })
  36. require.NoError(t, err)
  37. out := addMessageCacheBreakpoints(body)
  38. // gjson accepts some malformed input, so validate with encoding/json
  39. // before inspecting the rewritten content and cache metadata.
  40. var decoded any
  41. require.NoError(t, json.Unmarshal(out, &decoded))
  42. for _, path := range []string{"messages.0.content", "messages.3.content"} {
  43. content := gjson.GetBytes(out, path)
  44. require.True(t, content.IsArray())
  45. require.Len(t, content.Array(), 1)
  46. block := content.Array()[0]
  47. require.Equal(t, "text", block.Get("type").String())
  48. require.Equal(t, tt.text, block.Get("text").String())
  49. require.Equal(t, "ephemeral", block.Get("cache_control.type").String())
  50. require.Equal(t, "5m", block.Get("cache_control.ttl").String())
  51. }
  52. require.JSONEq(t, string(out), string(addMessageCacheBreakpoints(out)))
  53. })
  54. }
  55. }