| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- package service
- import (
- "encoding/json"
- "fmt"
- "testing"
- "time"
- "github.com/stretchr/testify/require"
- )
- func TestCollectOpenAIEncryptedContentDigestsRaw(t *testing.T) {
- t.Parallel()
- digests := collectOpenAIEncryptedContentDigestsRaw([]byte(`{"input":[
- {"type":"reasoning","encrypted_content":"cipher-a"},
- {"type":"compaction","encrypted_content":"cipher-b"},
- {"type":"input_text","text":"hello"},
- {"type":"reasoning"},
- {"type":"message","encrypted_content":"cipher-alien"}
- ]}`))
- // 剥离端处理不了的类型(message)不收摘要,否则该摘要永远命中却剥不掉。
- require.Equal(t, []string{
- openAIEncryptedContentDigest("cipher-a"),
- openAIEncryptedContentDigest("cipher-b"),
- }, digests)
- require.Nil(t, collectOpenAIEncryptedContentDigestsRaw([]byte(`{"model":"gpt-5"}`)))
- }
- func TestStripOpenAIInvalidEncryptedContentItems(t *testing.T) {
- t.Parallel()
- invalid := map[string]struct{}{
- openAIEncryptedContentDigest("stale-cipher"): {},
- }
- t.Run("only_strips_matching_items", func(t *testing.T) {
- t.Parallel()
- reqBody := map[string]any{
- "input": []any{
- map[string]any{"type": "reasoning", "id": "rs_1", "encrypted_content": "stale-cipher", "summary": []any{}},
- map[string]any{"type": "reasoning", "id": "rs_2", "encrypted_content": "fresh-cipher"},
- map[string]any{"type": "input_text", "text": "hello"},
- },
- }
- stripped := stripOpenAIInvalidEncryptedContentItems(reqBody, invalid)
- require.Equal(t, 1, stripped)
- input, ok := reqBody["input"].([]any)
- require.True(t, ok)
- require.Len(t, input, 3)
- first, ok := input[0].(map[string]any)
- require.True(t, ok)
- _, hasEncrypted := first["encrypted_content"]
- require.False(t, hasEncrypted, "命中项应剥离 encrypted_content")
- require.Equal(t, "rs_1", first["id"], "reasoning 骨架应保留")
- second, ok := input[1].(map[string]any)
- require.True(t, ok)
- require.Equal(t, "fresh-cipher", second["encrypted_content"], "新密文不得误删")
- })
- t.Run("compaction_removed_entirely", func(t *testing.T) {
- t.Parallel()
- reqBody := map[string]any{
- "input": []any{
- map[string]any{"type": "compaction", "encrypted_content": "stale-cipher"},
- map[string]any{"type": "input_text", "text": "hello"},
- },
- }
- stripped := stripOpenAIInvalidEncryptedContentItems(reqBody, invalid)
- require.Equal(t, 1, stripped)
- input, ok := reqBody["input"].([]any)
- require.True(t, ok)
- require.Len(t, input, 1)
- survivor, ok := input[0].(map[string]any)
- require.True(t, ok)
- require.Equal(t, "input_text", survivor["type"])
- })
- t.Run("no_hit_no_change", func(t *testing.T) {
- t.Parallel()
- reqBody := map[string]any{
- "input": []any{
- map[string]any{"type": "reasoning", "encrypted_content": "fresh-cipher"},
- },
- }
- require.Zero(t, stripOpenAIInvalidEncryptedContentItems(reqBody, invalid))
- })
- }
- func TestStripOpenAIInvalidEncryptedContentFromReplayItems(t *testing.T) {
- t.Parallel()
- invalid := map[string]struct{}{
- openAIEncryptedContentDigest("stale-cipher"): {},
- }
- t.Run("rewrites_hit_items_and_shares_the_rest", func(t *testing.T) {
- t.Parallel()
- original := json.RawMessage(`{"type":"reasoning","id":"rs_1","encrypted_content":"stale-cipher"}`)
- clean := json.RawMessage(`{"type":"input_text","text":"hello"}`)
- compaction := json.RawMessage(`{"type":"compaction","encrypted_content":"stale-cipher"}`)
- items := []json.RawMessage{original, clean, compaction}
- next, stripped := stripOpenAIInvalidEncryptedContentFromReplayItems(items, invalid)
- require.Equal(t, 2, stripped)
- require.Len(t, next, 2, "compaction 命中项应整项删除")
- var rewritten map[string]any
- require.NoError(t, json.Unmarshal(next[0], &rewritten))
- require.Equal(t, "rs_1", rewritten["id"])
- _, hasEncrypted := rewritten["encrypted_content"]
- require.False(t, hasEncrypted)
- // 未命中项正文共享;原命中项正文不被修改(整体替换语义)。
- require.Same(t, &clean[0], &next[1][0])
- require.Contains(t, string(original), "stale-cipher")
- })
- t.Run("miss_returns_original_header", func(t *testing.T) {
- t.Parallel()
- items := []json.RawMessage{json.RawMessage(`{"type":"reasoning","encrypted_content":"fresh-cipher"}`)}
- next, stripped := stripOpenAIInvalidEncryptedContentFromReplayItems(items, invalid)
- require.Zero(t, stripped)
- require.Same(t, &items[0][0], &next[0][0])
- })
- }
- func TestStripOpenAIInvalidEncryptedContentRaw(t *testing.T) {
- t.Parallel()
- invalid := map[string]struct{}{
- openAIEncryptedContentDigest("stale-cipher"): {},
- }
- payload := []byte(`{"model":"gpt-5","input":[
- {"type":"reasoning","id":"rs_1","encrypted_content":"stale-cipher"},
- {"type":"reasoning","id":"rs_2","encrypted_content":"fresh-cipher"},
- {"type":"input_text","text":"hello"}
- ],"stream":true}`)
- stripped, count, err := stripOpenAIInvalidEncryptedContentRaw(payload, invalid)
- require.NoError(t, err)
- require.Equal(t, 1, count)
- var decoded map[string]any
- require.NoError(t, json.Unmarshal(stripped, &decoded))
- require.Equal(t, "gpt-5", decoded["model"])
- input, ok := decoded["input"].([]any)
- require.True(t, ok)
- require.Len(t, input, 3)
- first, ok := input[0].(map[string]any)
- require.True(t, ok)
- _, hasEncrypted := first["encrypted_content"]
- require.False(t, hasEncrypted)
- second, ok := input[1].(map[string]any)
- require.True(t, ok)
- require.Equal(t, "fresh-cipher", second["encrypted_content"])
- t.Run("miss_fast_path_returns_original", func(t *testing.T) {
- t.Parallel()
- cleanPayload := []byte(`{"input":[{"type":"reasoning","encrypted_content":"fresh-cipher"}]}`)
- unchanged, count, err := stripOpenAIInvalidEncryptedContentRaw(cleanPayload, invalid)
- require.NoError(t, err)
- require.Zero(t, count)
- require.Same(t, &cleanPayload[0], &unchanged[0], "未命中时应原样返回,不重编码")
- })
- }
- func TestOpenAIWSStateStoreInvalidEncryptedContentLineage(t *testing.T) {
- t.Parallel()
- t.Run("mark_get_roundtrip_with_merge", func(t *testing.T) {
- t.Parallel()
- store := NewOpenAIWSStateStore(nil)
- require.False(t, store.HasAnySessionInvalidEncryptedContent())
- require.Nil(t, store.GetSessionInvalidEncryptedContentDigests(1, "session-a"))
- store.MarkSessionInvalidEncryptedContent(1, "session-a", []string{"d1", "d2"}, time.Minute)
- store.MarkSessionInvalidEncryptedContent(1, "session-a", []string{"d2", "d3"}, time.Minute)
- require.True(t, store.HasAnySessionInvalidEncryptedContent())
- digests := store.GetSessionInvalidEncryptedContentDigests(1, "session-a")
- require.Len(t, digests, 3)
- for _, digest := range []string{"d1", "d2", "d3"} {
- require.Contains(t, digests, digest)
- }
- // 组隔离:另一组同名会话不可见。
- require.Nil(t, store.GetSessionInvalidEncryptedContentDigests(2, "session-a"))
- // 返回值是拷贝,调用方修改不得影响存储。
- digests["d4"] = struct{}{}
- require.Len(t, store.GetSessionInvalidEncryptedContentDigests(1, "session-a"), 3)
- })
- t.Run("expired_binding_not_returned", func(t *testing.T) {
- t.Parallel()
- raw := NewOpenAIWSStateStore(nil)
- raw.MarkSessionInvalidEncryptedContent(1, "session-b", []string{"d1"}, time.Minute)
- store, ok := raw.(*defaultOpenAIWSStateStore)
- require.True(t, ok)
- store.sessionInvalidEncryptedMu.Lock()
- binding := store.sessionInvalidEncrypted["1:session-b"]
- binding.expiresAt = time.Now().Add(-time.Second)
- store.sessionInvalidEncrypted["1:session-b"] = binding
- store.sessionInvalidEncryptedMu.Unlock()
- require.Nil(t, store.GetSessionInvalidEncryptedContentDigests(1, "session-b"))
- })
- t.Run("per_session_capacity_degrades_gracefully", func(t *testing.T) {
- t.Parallel()
- store := NewOpenAIWSStateStore(nil)
- oversized := make([]string, 0, openAIWSInvalidEncryptedDigestsPerSession+10)
- for i := range openAIWSInvalidEncryptedDigestsPerSession + 10 {
- oversized = append(oversized, openAIEncryptedContentDigest(fmt.Sprintf("cipher-%d", i)))
- }
- store.MarkSessionInvalidEncryptedContent(1, "session-c", oversized, time.Minute)
- digests := store.GetSessionInvalidEncryptedContentDigests(1, "session-c")
- require.Len(t, digests, openAIWSInvalidEncryptedDigestsPerSession)
- })
- }
|