Files
CLIProxyAPI/examples/plugin/jshandler/interceptor_test.go
Luis Pater 2aeb41cecf feat(pluginhost, jshandler): integrate HostCallbackID with interceptors and JS engine logging
- Added `HostCallbackID` to request, response, and stream chunk interceptors for enhanced context tracking.
- Updated JavaScript engine to support custom console logging with `HostCallbackID` forwarding.
- Introduced tests verifying proper integration of `HostCallbackID` in all interceptor flows and engine logging.
- Enhanced logging and error handling for consistent callback-related logic implementation.
2026-06-09 14:36:42 +08:00

140 lines
3.8 KiB
Go

package main
import (
"net/http"
"os"
"path/filepath"
"strings"
"testing"
)
func TestApplyJSBeforeRequestUsesReturnedCtxBody(t *testing.T) {
scriptPath := filepath.Join(t.TempDir(), "before.js")
script := `
function on_before_request(ctx) {
var req = JSON.parse(ctx.body);
req.messages[0].content = req.messages[0].content.replace("sensitive_word", "safe_word");
ctx.body = JSON.stringify(req);
ctx.headers["X-Plugin"] = "updated";
return ctx;
}
`
if errWrite := os.WriteFile(scriptPath, []byte(script), 0600); errWrite != nil {
t.Fatalf("os.WriteFile() error = %v", errWrite)
}
plugin := &jsHandlerPlugin{cfg: defaultJSHandlerConfig()}
headers := http.Header{"X-Plugin": []string{"original"}}
processed, _, errApply := plugin.applyJSBeforeRequest(
scriptPath,
[]byte(`{"messages":[{"role":"user","content":"contains sensitive_word"}]}`),
"gpt-test",
"openai",
headers,
"",
)
if errApply != nil {
t.Fatalf("applyJSBeforeRequest() error = %v", errApply)
}
if body := string(processed); !strings.Contains(body, "safe_word") || strings.Contains(body, "sensitive_word") {
t.Fatalf("processed body = %q, want sensitive word rewritten", body)
}
if got := headers.Get("X-Plugin"); got != "updated" {
t.Fatalf("header X-Plugin = %q, want updated", got)
}
}
func TestApplyJSAfterResponseUsesFrozenNativeHistoryChunks(t *testing.T) {
scriptPath := filepath.Join(t.TempDir(), "stream.js")
script := `
function on_after_stream_response(ctx) {
if (!Object.isFrozen(ctx.history_chunks)) {
throw new Error("history_chunks is not frozen");
}
var original = ctx.history_chunks[0];
try {
ctx.history_chunks[0] = "changed";
} catch (e) {
}
if (ctx.history_chunks[0] !== original) {
throw new Error("history_chunks item was changed");
}
try {
ctx.history_chunks = ["changed"];
} catch (e) {
}
if (ctx.history_chunks[0] !== original) {
throw new Error("history_chunks property was replaced");
}
return { chunk: ctx.chunk + "|ok" };
}
`
if errWrite := os.WriteFile(scriptPath, []byte(script), 0600); errWrite != nil {
t.Fatalf("os.WriteFile() error = %v", errWrite)
}
plugin := &jsHandlerPlugin{cfg: defaultJSHandlerConfig()}
chunk := `data: {"choices":[{"delta":{},"finish_reason":null}]}`
processedBody, _, changed, errApply := plugin.applyJSAfterResponse(
scriptPath,
"gpt-test",
"openai",
nil,
nil,
"",
&chunk,
http.Header{},
true,
[]string{`data: {"choices":[{"delta":{"tool_calls":[{"index":0}]}}]}`},
"",
)
if errApply != nil {
t.Fatalf("applyJSAfterResponse() error = %v", errApply)
}
if !changed {
t.Fatal("applyJSAfterResponse() changed = false, want true")
}
if processedBody != chunk+"|ok" {
t.Fatalf("applyJSAfterResponse() body = %q, want %q", processedBody, chunk+"|ok")
}
}
func TestApplyJSAfterResponseDispatchesNonStreamHook(t *testing.T) {
scriptPath := filepath.Join(t.TempDir(), "nonstream.js")
script := `
function on_after_stream_response(ctx) {
throw new Error("stream hook should not run");
}
function on_after_nonstream_response(ctx) {
return { body: ctx.body + "|nonstream" };
}
`
if errWrite := os.WriteFile(scriptPath, []byte(script), 0600); errWrite != nil {
t.Fatalf("os.WriteFile() error = %v", errWrite)
}
plugin := &jsHandlerPlugin{cfg: defaultJSHandlerConfig()}
processedBody, _, changed, errApply := plugin.applyJSAfterResponse(
scriptPath,
"gpt-test",
"openai",
nil,
nil,
`{"ok":true}`,
nil,
http.Header{},
false,
nil,
"",
)
if errApply != nil {
t.Fatalf("applyJSAfterResponse() error = %v", errApply)
}
if !changed {
t.Fatal("applyJSAfterResponse() changed = false, want true")
}
if processedBody != `{"ok":true}|nonstream` {
t.Fatalf("applyJSAfterResponse() body = %q", processedBody)
}
}