Files
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

286 lines
8.5 KiB
Go

package parser
import (
"crypto/sha256"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.kenn.io/agentsview/internal/testjsonl"
)
func TestCodexCursorCache(t *testing.T) {
digest := sha256.Sum256([]byte("first prompt"))
state := codexCursorState{
model: "gpt-5",
cwd: "/workspace/project-a",
firstUserDigest: digest,
firstUserSeen: true,
sawUserTurnAfterFirst: false,
mayReplayFirstUserPrompt: true,
forkGate: codexForkGate{
active: true,
createdMs: 1720000000000,
},
lastTaskEvent: "turn_aborted",
}
t.Run("put and get exact cleaned key", func(t *testing.T) {
cache := newCodexCursorCache(4, 4096)
rawPath := filepath.Join(t.TempDir(), "sessions", "..", "rollout.jsonl")
require.True(t, cache.Put(rawPath, 17, 101, 202, state))
got, ok := cache.Get(filepath.Clean(rawPath), 17, 101, 202)
require.True(t, ok)
assert.Equal(t, state, got)
})
t.Run("requires exact offset and known identity", func(t *testing.T) {
cache := newCodexCursorCache(4, 4096)
path := filepath.Join(t.TempDir(), "rollout.jsonl")
require.True(t, cache.Put(path, 40, 101, 202, state))
_, offsetOK := cache.Get(path, 41, 101, 202)
_, inodeOK := cache.Get(path, 40, 102, 202)
_, deviceOK := cache.Get(path, 40, 101, 203)
assert.False(t, offsetOK)
assert.False(t, inodeOK)
assert.False(t, deviceOK)
})
t.Run("same key replacement returns newest state", func(t *testing.T) {
cache := newCodexCursorCache(4, 4096)
path := filepath.Join(t.TempDir(), "rollout.jsonl")
replacement := state
replacement.model = "gpt-5.1"
replacement.lastTaskEvent = "task_complete"
require.True(t, cache.Put(path, 40, 101, 202, state))
require.True(t, cache.Put(path, 40, 101, 202, replacement))
got, ok := cache.Get(path, 40, 101, 202)
require.True(t, ok)
assert.Equal(t, replacement, got)
})
t.Run("identical warm put promotes without allocations", func(t *testing.T) {
cache := newCodexCursorCache(2, 4096)
const path = "rollout.jsonl"
other := state
other.model = "gpt-5.1"
third := state
third.model = "gpt-5.2"
require.True(t, cache.Put(path, 10, 101, 202, state))
require.True(t, cache.Put(path, 20, 101, 202, other))
putOK := false
allocations := testing.AllocsPerRun(100, func() {
putOK = cache.Put(path, 10, 101, 202, state)
})
require.True(t, putOK)
assert.Zero(t, allocations)
require.True(t, cache.Put(path, 30, 101, 202, third))
firstGot, firstOK := cache.Get(path, 10, 101, 202)
_, secondOK := cache.Get(path, 20, 101, 202)
thirdGot, thirdOK := cache.Get(path, 30, 101, 202)
require.True(t, firstOK)
assert.Equal(t, state, firstGot)
assert.False(t, secondOK)
require.True(t, thirdOK)
assert.Equal(t, third, thirdGot)
})
t.Run("offset versions coexist", func(t *testing.T) {
cache := newCodexCursorCache(4, 4096)
path := filepath.Join(t.TempDir(), "rollout.jsonl")
newer := state
newer.model = "gpt-5.2"
require.True(t, cache.Put(path, 40, 101, 202, state))
require.True(t, cache.Put(path, 80, 101, 202, newer))
oldGot, oldOK := cache.Get(path, 40, 101, 202)
newGot, newOK := cache.Get(path, 80, 101, 202)
require.True(t, oldOK)
require.True(t, newOK)
assert.Equal(t, state, oldGot)
assert.Equal(t, newer, newGot)
})
t.Run("least recently used count entry is evicted", func(t *testing.T) {
cache := newCodexCursorCache(2, 4096)
path := filepath.Join(t.TempDir(), "rollout.jsonl")
first := state
first.model = "first"
second := state
second.model = "second"
third := state
third.model = "third"
require.True(t, cache.Put(path, 10, 1, 2, first))
require.True(t, cache.Put(path, 20, 1, 2, second))
_, firstOK := cache.Get(path, 10, 1, 2)
require.True(t, firstOK)
require.True(t, cache.Put(path, 30, 1, 2, third))
_, evictedOK := cache.Get(path, 20, 1, 2)
firstGot, firstStillOK := cache.Get(path, 10, 1, 2)
thirdGot, thirdOK := cache.Get(path, 30, 1, 2)
assert.False(t, evictedOK)
require.True(t, firstStillOK)
require.True(t, thirdOK)
assert.Equal(t, first, firstGot)
assert.Equal(t, third, thirdGot)
})
t.Run("least recently used bytes are evicted", func(t *testing.T) {
cache := newCodexCursorCache(10, 900)
path := filepath.Join(t.TempDir(), "rollout.jsonl")
first := state
first.cwd = strings.Repeat("a", 200)
second := state
second.cwd = strings.Repeat("b", 200)
require.True(t, cache.Put(path, 10, 1, 2, first))
require.True(t, cache.Put(path, 20, 1, 2, second))
_, firstOK := cache.Get(path, 10, 1, 2)
secondGot, secondOK := cache.Get(path, 20, 1, 2)
assert.False(t, firstOK)
require.True(t, secondOK)
assert.Equal(t, second, secondGot)
})
t.Run("oversized entry is rejected without disturbing cache", func(t *testing.T) {
cache := newCodexCursorCache(4, 900)
path := filepath.Join(t.TempDir(), "rollout.jsonl")
oversized := state
oversized.cwd = strings.Repeat("x", 901)
require.True(t, cache.Put(path, 10, 1, 2, state))
assert.False(t, cache.Put(path, 20, 1, 2, oversized))
got, ok := cache.Get(path, 10, 1, 2)
_, oversizedOK := cache.Get(path, 20, 1, 2)
require.True(t, ok)
assert.Equal(t, state, got)
assert.False(t, oversizedOK)
})
}
func TestCodexCursorSafeResumeOffset(t *testing.T) {
t.Run("zero offset in empty file", func(t *testing.T) {
path := filepath.Join(t.TempDir(), "empty.jsonl")
require.NoError(t, os.WriteFile(path, nil, 0o644))
safe, err := codexSafeResumeOffset(path, 0)
require.NoError(t, err)
assert.True(t, safe)
})
t.Run("newline terminated offset", func(t *testing.T) {
path := filepath.Join(t.TempDir(), "complete.jsonl")
content := "{}\n"
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
safe, err := codexSafeResumeOffset(path, int64(len(content)))
require.NoError(t, err)
assert.True(t, safe)
})
t.Run("valid JSON without newline", func(t *testing.T) {
path := filepath.Join(t.TempDir(), "unterminated.jsonl")
content := "{}"
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
safe, err := codexSafeResumeOffset(path, int64(len(content)))
require.NoError(t, err)
assert.False(t, safe)
})
}
func TestCodexCursorConsumedSizeStopsBeforeValidEOF(t *testing.T) {
complete := "{}\n"
path := filepath.Join(t.TempDir(), "tail.jsonl")
require.NoError(t, os.WriteFile(path, []byte(complete+"{}"), 0o644))
consumed, err := CodexTranscriptConsumedSize(path)
require.NoError(t, err)
assert.Equal(t, int64(len(complete)), consumed)
}
func TestCodexCursorFullParseSeedBoundaries(t *testing.T) {
t.Run("empty file seeds offset zero", func(t *testing.T) {
path := createTestFile(t, "empty.jsonl", "")
provider := newCodexTestProvider(t)
sess, messages, err := provider.parseSession(path, "local", false)
require.NoError(t, err)
require.NotNil(t, sess)
assert.Empty(t, messages)
assert.Equal(t, int64(0), sess.File.Size)
info, err := os.Stat(path)
require.NoError(t, err)
inode, device := sourceFileIdentity(info)
_, ok := provider.cursorCache.Get(path, 0, inode, device)
assert.True(t, ok)
})
t.Run("newline terminated EOF seeds raw size", func(t *testing.T) {
content := testjsonl.JoinJSONL(
testjsonl.CodexSessionMetaJSON(
"seed-complete", "/workspace/project-a", "codex_cli_rs", tsEarly,
),
testjsonl.CodexMsgJSON("user", "complete prompt", tsEarlyS1),
)
path := createTestFile(t, "complete.jsonl", content)
provider := newCodexTestProvider(t)
sess, messages, err := provider.parseSession(path, "local", false)
require.NoError(t, err)
require.NotNil(t, sess)
require.Len(t, messages, 1)
assert.Equal(t, int64(len(content)), sess.File.Size)
info, err := os.Stat(path)
require.NoError(t, err)
inode, device := sourceFileIdentity(info)
_, ok := provider.cursorCache.Get(
path, int64(len(content)), inode, device,
)
assert.True(t, ok)
})
t.Run("newline-less valid EOF is parsed but not seeded", func(t *testing.T) {
content := testjsonl.CodexMsgJSON("user", "unterminated prompt", tsEarlyS1)
path := createTestFile(t, "unterminated.jsonl", content)
provider := newCodexTestProvider(t)
sess, messages, err := provider.parseSession(path, "local", false)
require.NoError(t, err)
require.NotNil(t, sess)
require.Len(t, messages, 1)
assert.Equal(t, "unterminated prompt", messages[0].Content)
assert.Equal(t, int64(len(content)), sess.File.Size)
info, err := os.Stat(path)
require.NoError(t, err)
inode, device := sourceFileIdentity(info)
_, ok := provider.cursorCache.Get(
path, int64(len(content)), inode, device,
)
assert.False(t, ok)
})
}