mirror of
https://gh-proxy.org/https://github.com/klinsmaya/CLIProxyAPI.git
synced 2026-11-08 01:16:47 +08:00
Merge pull request #4540 from router-for-me/test
refactor(tests): simplify credential concurrency and sentinel test fixtures
This commit is contained in:
@@ -1,12 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -14,17 +9,17 @@ import (
|
||||
)
|
||||
|
||||
type credentialConcurrencyFixtureWireConfig struct {
|
||||
LifecycleConfigRevision int64 `json:"lifecycle-config-revision"`
|
||||
ObservationBarrierRevision int64 `json:"observation-barrier-revision"`
|
||||
CPAHeartbeatTimeout time.Duration `json:"cpa-heartbeat-timeout"`
|
||||
CPACancelBound time.Duration `json:"cpa-cancel-bound"`
|
||||
ReclaimGrace time.Duration `json:"reclaim-grace"`
|
||||
CleanupInterval time.Duration `json:"cleanup-interval"`
|
||||
ReleaseFlushInterval string `json:"release-flush-interval" yaml:"release-flush-interval"`
|
||||
ReleaseMaxBackoff string `json:"release-max-backoff" yaml:"release-max-backoff"`
|
||||
BusyRetryMin string `json:"busy-retry-min" yaml:"busy-retry-min"`
|
||||
BusyRetryMax string `json:"busy-retry-max" yaml:"busy-retry-max"`
|
||||
MaxLimit int64 `json:"max-limit"`
|
||||
LifecycleConfigRevision int64
|
||||
ObservationBarrierRevision int64
|
||||
CPAHeartbeatTimeout time.Duration
|
||||
CPACancelBound time.Duration
|
||||
ReclaimGrace time.Duration
|
||||
CleanupInterval time.Duration
|
||||
ReleaseFlushInterval string `yaml:"release-flush-interval"`
|
||||
ReleaseMaxBackoff string `yaml:"release-max-backoff"`
|
||||
BusyRetryMin string `yaml:"busy-retry-min"`
|
||||
BusyRetryMax string `yaml:"busy-retry-max"`
|
||||
MaxLimit int64
|
||||
}
|
||||
|
||||
type credentialConcurrencyFixtureHotDurations struct {
|
||||
@@ -58,45 +53,44 @@ func (c credentialConcurrencyFixtureWireConfig) config() (CredentialConcurrencyC
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestCredentialConcurrencyLifecycleFixture(t *testing.T) {
|
||||
raw, errRead := os.ReadFile(filepath.Join("..", "..", "testdata", "credential-concurrency-lifecycle.json"))
|
||||
if errRead != nil {
|
||||
t.Fatal(errRead)
|
||||
}
|
||||
var fixture struct {
|
||||
Defaults credentialConcurrencyFixtureWireConfig `json:"defaults"`
|
||||
Invalid []struct {
|
||||
NodeHeartbeatTimeout time.Duration `json:"node_heartbeat_timeout"`
|
||||
Config credentialConcurrencyFixtureWireConfig `json:"config"`
|
||||
} `json:"invalid"`
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(raw))
|
||||
decoder.DisallowUnknownFields()
|
||||
if errDecode := decoder.Decode(&fixture); errDecode != nil {
|
||||
t.Fatal(errDecode)
|
||||
}
|
||||
if errTrailing := decoder.Decode(&struct{}{}); errTrailing != io.EOF {
|
||||
t.Fatalf("fixture contains trailing JSON: %v", errTrailing)
|
||||
func credentialConcurrencyWireFixture(cpaHeartbeatTimeout time.Duration) credentialConcurrencyFixtureWireConfig {
|
||||
return credentialConcurrencyFixtureWireConfig{
|
||||
CPAHeartbeatTimeout: cpaHeartbeatTimeout,
|
||||
CPACancelBound: 5 * time.Second,
|
||||
ReclaimGrace: 5 * time.Second,
|
||||
CleanupInterval: 5 * time.Second,
|
||||
ReleaseFlushInterval: "250ms",
|
||||
ReleaseMaxBackoff: "2s",
|
||||
BusyRetryMin: "250ms",
|
||||
BusyRetryMax: "1s",
|
||||
MaxLimit: 1_000_000,
|
||||
}
|
||||
}
|
||||
|
||||
defaults, errConfig := fixture.Defaults.config()
|
||||
func credentialConcurrencyConfigFixture(cpaHeartbeatTimeout time.Duration) CredentialConcurrencyConfig {
|
||||
return CredentialConcurrencyConfig{
|
||||
CPAHeartbeatTimeout: cpaHeartbeatTimeout,
|
||||
CPACancelBound: 5 * time.Second,
|
||||
ReclaimGrace: 5 * time.Second,
|
||||
CleanupInterval: 5 * time.Second,
|
||||
ReleaseFlushInterval: 250 * time.Millisecond,
|
||||
ReleaseMaxBackoff: 2 * time.Second,
|
||||
BusyRetryMin: 250 * time.Millisecond,
|
||||
BusyRetryMax: time.Second,
|
||||
MaxLimit: 1_000_000,
|
||||
}
|
||||
}
|
||||
|
||||
func TestCredentialConcurrencyLifecycleFixture(t *testing.T) {
|
||||
wireDefaults := credentialConcurrencyWireFixture(3 * time.Second)
|
||||
wireDefaults.LifecycleConfigRevision = 1
|
||||
defaults, errConfig := wireDefaults.config()
|
||||
if errConfig != nil {
|
||||
t.Fatal(errConfig)
|
||||
}
|
||||
|
||||
expectedDefaults := CredentialConcurrencyConfig{
|
||||
LifecycleConfigRevision: 1,
|
||||
ObservationBarrierRevision: 0,
|
||||
CPAHeartbeatTimeout: 3 * time.Second,
|
||||
CPACancelBound: 5 * time.Second,
|
||||
ReclaimGrace: 5 * time.Second,
|
||||
CleanupInterval: 5 * time.Second,
|
||||
ReleaseFlushInterval: 250 * time.Millisecond,
|
||||
ReleaseMaxBackoff: 2 * time.Second,
|
||||
BusyRetryMin: 250 * time.Millisecond,
|
||||
BusyRetryMax: time.Second,
|
||||
MaxLimit: 1_000_000,
|
||||
}
|
||||
expectedDefaults := credentialConcurrencyConfigFixture(3 * time.Second)
|
||||
expectedDefaults.LifecycleConfigRevision = 1
|
||||
if defaults != expectedDefaults {
|
||||
t.Fatalf("defaults = %#v, want %#v", defaults, expectedDefaults)
|
||||
}
|
||||
@@ -104,44 +98,25 @@ func TestCredentialConcurrencyLifecycleFixture(t *testing.T) {
|
||||
t.Fatalf("ValidateCredentialConcurrency(defaults) error = %v", errValidate)
|
||||
}
|
||||
|
||||
invalidFixtures := []struct {
|
||||
NodeHeartbeatTimeout time.Duration
|
||||
Config credentialConcurrencyFixtureWireConfig
|
||||
}{
|
||||
{NodeHeartbeatTimeout: 3 * time.Second, Config: credentialConcurrencyWireFixture(3 * time.Second)},
|
||||
{NodeHeartbeatTimeout: 20 * time.Second, Config: credentialConcurrencyWireFixture(0)},
|
||||
}
|
||||
expectedInvalid := []struct {
|
||||
nodeHeartbeatTimeout time.Duration
|
||||
config CredentialConcurrencyConfig
|
||||
}{
|
||||
{
|
||||
nodeHeartbeatTimeout: 3 * time.Second,
|
||||
config: CredentialConcurrencyConfig{
|
||||
CPAHeartbeatTimeout: 3 * time.Second,
|
||||
CPACancelBound: 5 * time.Second,
|
||||
ReclaimGrace: 5 * time.Second,
|
||||
CleanupInterval: 5 * time.Second,
|
||||
ReleaseFlushInterval: 250 * time.Millisecond,
|
||||
ReleaseMaxBackoff: 2 * time.Second,
|
||||
BusyRetryMin: 250 * time.Millisecond,
|
||||
BusyRetryMax: time.Second,
|
||||
MaxLimit: 1_000_000,
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeHeartbeatTimeout: 20 * time.Second,
|
||||
config: CredentialConcurrencyConfig{
|
||||
CPAHeartbeatTimeout: 0,
|
||||
CPACancelBound: 5 * time.Second,
|
||||
ReclaimGrace: 5 * time.Second,
|
||||
CleanupInterval: 5 * time.Second,
|
||||
ReleaseFlushInterval: 250 * time.Millisecond,
|
||||
ReleaseMaxBackoff: 2 * time.Second,
|
||||
BusyRetryMin: 250 * time.Millisecond,
|
||||
BusyRetryMax: time.Second,
|
||||
MaxLimit: 1_000_000,
|
||||
},
|
||||
},
|
||||
{nodeHeartbeatTimeout: 3 * time.Second, config: credentialConcurrencyConfigFixture(3 * time.Second)},
|
||||
{nodeHeartbeatTimeout: 20 * time.Second, config: credentialConcurrencyConfigFixture(0)},
|
||||
}
|
||||
if len(fixture.Invalid) != len(expectedInvalid) {
|
||||
t.Fatalf("invalid fixture count = %d, want %d", len(fixture.Invalid), len(expectedInvalid))
|
||||
if len(invalidFixtures) != len(expectedInvalid) {
|
||||
t.Fatalf("invalid fixture count = %d, want %d", len(invalidFixtures), len(expectedInvalid))
|
||||
}
|
||||
for index, expected := range expectedInvalid {
|
||||
item := fixture.Invalid[index]
|
||||
item := invalidFixtures[index]
|
||||
itemConfig, errConfig := item.Config.config()
|
||||
if errConfig != nil {
|
||||
t.Fatalf("invalid fixture %d config() error = %v", index, errConfig)
|
||||
|
||||
@@ -1,35 +1,48 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
import "testing"
|
||||
|
||||
type sentinelPayload = map[string]any
|
||||
|
||||
var (
|
||||
claudeCodeToolProgressFixture = sentinelPayload{
|
||||
"type": "tool_progress",
|
||||
"tool_use_id": "toolu_123",
|
||||
"tool_name": "Bash",
|
||||
"parent_tool_use_id": nil,
|
||||
"elapsed_time_seconds": 2.5,
|
||||
"task_id": "task_123",
|
||||
"uuid": "11111111-1111-4111-8111-111111111111",
|
||||
"session_id": "sess_123",
|
||||
}
|
||||
claudeCodeSessionStateChangedFixture = sentinelPayload{
|
||||
"type": "system",
|
||||
"subtype": "session_state_changed",
|
||||
"state": "requires_action",
|
||||
"uuid": "22222222-2222-4222-8222-222222222222",
|
||||
"session_id": "sess_123",
|
||||
}
|
||||
claudeCodeToolUseSummaryFixture = sentinelPayload{
|
||||
"type": "tool_use_summary",
|
||||
"summary": "Searched in auth/",
|
||||
"preceding_tool_use_ids": []any{"toolu_1", "toolu_2"},
|
||||
"uuid": "33333333-3333-4333-8333-333333333333",
|
||||
"session_id": "sess_123",
|
||||
}
|
||||
claudeCodeControlRequestCanUseToolFixture = sentinelPayload{
|
||||
"type": "control_request",
|
||||
"request_id": "req_123",
|
||||
"request": sentinelPayload{
|
||||
"subtype": "can_use_tool",
|
||||
"tool_name": "Bash",
|
||||
"input": sentinelPayload{"command": "npm test"},
|
||||
"tool_use_id": "toolu_123",
|
||||
"description": "Running npm test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type jsonObject = map[string]any
|
||||
|
||||
func loadClaudeCodeSentinelFixture(t *testing.T, name string) jsonObject {
|
||||
t.Helper()
|
||||
path := filepath.Join("testdata", "claude_code_sentinels", name)
|
||||
data := mustReadFile(t, path)
|
||||
var payload jsonObject
|
||||
if err := json.Unmarshal(data, &payload); err != nil {
|
||||
t.Fatalf("unmarshal %s: %v", name, err)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
func mustReadFile(t *testing.T, path string) []byte {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read %s: %v", path, err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func requireStringField(t *testing.T, obj jsonObject, key string) string {
|
||||
func requireStringField(t *testing.T, obj sentinelPayload, key string) string {
|
||||
t.Helper()
|
||||
value, ok := obj[key].(string)
|
||||
if !ok || value == "" {
|
||||
@@ -39,7 +52,7 @@ func requireStringField(t *testing.T, obj jsonObject, key string) string {
|
||||
}
|
||||
|
||||
func TestClaudeCodeSentinel_ToolProgressShape(t *testing.T) {
|
||||
payload := loadClaudeCodeSentinelFixture(t, "tool_progress.json")
|
||||
payload := claudeCodeToolProgressFixture
|
||||
if got := requireStringField(t, payload, "type"); got != "tool_progress" {
|
||||
t.Fatalf("type = %q, want tool_progress", got)
|
||||
}
|
||||
@@ -52,7 +65,7 @@ func TestClaudeCodeSentinel_ToolProgressShape(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClaudeCodeSentinel_SessionStateShape(t *testing.T) {
|
||||
payload := loadClaudeCodeSentinelFixture(t, "session_state_changed.json")
|
||||
payload := claudeCodeSessionStateChangedFixture
|
||||
if got := requireStringField(t, payload, "type"); got != "system" {
|
||||
t.Fatalf("type = %q, want system", got)
|
||||
}
|
||||
@@ -69,7 +82,7 @@ func TestClaudeCodeSentinel_SessionStateShape(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClaudeCodeSentinel_ToolUseSummaryShape(t *testing.T) {
|
||||
payload := loadClaudeCodeSentinelFixture(t, "tool_use_summary.json")
|
||||
payload := claudeCodeToolUseSummaryFixture
|
||||
if got := requireStringField(t, payload, "type"); got != "tool_use_summary" {
|
||||
t.Fatalf("type = %q, want tool_use_summary", got)
|
||||
}
|
||||
@@ -86,7 +99,7 @@ func TestClaudeCodeSentinel_ToolUseSummaryShape(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClaudeCodeSentinel_ControlRequestCanUseToolShape(t *testing.T) {
|
||||
payload := loadClaudeCodeSentinelFixture(t, "control_request_can_use_tool.json")
|
||||
payload := claudeCodeControlRequestCanUseToolFixture
|
||||
if got := requireStringField(t, payload, "type"); got != "control_request" {
|
||||
t.Fatalf("type = %q, want control_request", got)
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"type": "control_request",
|
||||
"request_id": "req_123",
|
||||
"request": {
|
||||
"subtype": "can_use_tool",
|
||||
"tool_name": "Bash",
|
||||
"input": {"command": "npm test"},
|
||||
"tool_use_id": "toolu_123",
|
||||
"description": "Running npm test"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"type": "system",
|
||||
"subtype": "session_state_changed",
|
||||
"state": "requires_action",
|
||||
"uuid": "22222222-2222-4222-8222-222222222222",
|
||||
"session_id": "sess_123"
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"type": "tool_progress",
|
||||
"tool_use_id": "toolu_123",
|
||||
"tool_name": "Bash",
|
||||
"parent_tool_use_id": null,
|
||||
"elapsed_time_seconds": 2.5,
|
||||
"task_id": "task_123",
|
||||
"uuid": "11111111-1111-4111-8111-111111111111",
|
||||
"session_id": "sess_123"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"type": "tool_use_summary",
|
||||
"summary": "Searched in auth/",
|
||||
"preceding_tool_use_ids": ["toolu_1", "toolu_2"],
|
||||
"uuid": "33333333-3333-4333-8333-333333333333",
|
||||
"session_id": "sess_123"
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"defaults": {
|
||||
"lifecycle-config-revision": 1,
|
||||
"observation-barrier-revision": 0,
|
||||
"cpa-heartbeat-timeout": 3000000000,
|
||||
"cpa-cancel-bound": 5000000000,
|
||||
"reclaim-grace": 5000000000,
|
||||
"cleanup-interval": 5000000000,
|
||||
"release-flush-interval": "250ms",
|
||||
"release-max-backoff": "2s",
|
||||
"busy-retry-min": "250ms",
|
||||
"busy-retry-max": "1s",
|
||||
"max-limit": 1000000
|
||||
},
|
||||
"invalid": [
|
||||
{
|
||||
"node_heartbeat_timeout": 3000000000,
|
||||
"config": {
|
||||
"cpa-heartbeat-timeout": 3000000000,
|
||||
"cpa-cancel-bound": 5000000000,
|
||||
"reclaim-grace": 5000000000,
|
||||
"cleanup-interval": 5000000000,
|
||||
"release-flush-interval": "250ms",
|
||||
"release-max-backoff": "2s",
|
||||
"busy-retry-min": "250ms",
|
||||
"busy-retry-max": "1s",
|
||||
"max-limit": 1000000
|
||||
}
|
||||
},
|
||||
{
|
||||
"node_heartbeat_timeout": 20000000000,
|
||||
"config": {
|
||||
"cpa-heartbeat-timeout": 0,
|
||||
"cpa-cancel-bound": 5000000000,
|
||||
"reclaim-grace": 5000000000,
|
||||
"cleanup-interval": 5000000000,
|
||||
"release-flush-interval": "250ms",
|
||||
"release-max-backoff": "2s",
|
||||
"busy-retry-min": "250ms",
|
||||
"busy-retry-max": "1s",
|
||||
"max-limit": 1000000
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user