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

459 lines
13 KiB
Go

package parser
import (
"context"
"crypto/sha256"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestJSONLSourceSetDiscoverRecursiveStableSources(t *testing.T) {
root := t.TempDir()
writeSourceFile(t, filepath.Join(root, "b.jsonl"), "{}\n")
writeSourceFile(t, filepath.Join(root, "a.jsonl"), "{}\n")
writeSourceFile(t, filepath.Join(root, "nested", "c.jsonl"), "{}\n")
writeSourceFile(t, filepath.Join(root, "nested", "ignored.txt"), "{}\n")
writeSourceFile(t, filepath.Join(root, "nested", "upper.JSONL"), "{}\n")
roots := []string{root}
sources := NewJSONLSourceSet(AgentCodex, roots,
WithRecursive(),
WithKey(func(root, path string) string {
return mustRelSlash(t, root, path)
}),
WithProjectHint(func(root, path string) string {
rel := mustRelSlash(t, root, filepath.Dir(path))
if rel == "." {
return ""
}
return rel
}),
)
roots[0] = filepath.Join(root, "mutated")
discovered, err := sources.Discover(context.Background())
require.NoError(t, err)
require.Len(t, discovered, 3)
assert.Equal(t, []string{
"a.jsonl",
"b.jsonl",
"nested/c.jsonl",
}, sourceKeys(discovered))
assert.Equal(t, []string{"", "", "nested"}, sourceProjects(discovered))
for _, source := range discovered {
assert.Equal(t, AgentCodex, source.Provider)
assert.Equal(t, source.DisplayPath, source.FingerprintKey)
assert.NotEmpty(t, source.DisplayPath)
assert.IsType(t, JSONLSource{}, source.Opaque)
}
}
func TestJSONLSourceSetShallowDiscoveryAndFilters(t *testing.T) {
root := t.TempDir()
writeSourceFile(t, filepath.Join(root, "keep.jsonl"), "{}\n")
writeSourceFile(t, filepath.Join(root, "keep.ndjson"), "{}\n")
writeSourceFile(t, filepath.Join(root, "drop.jsonl"), "{}\n")
writeSourceFile(t, filepath.Join(root, "nested", "skip.jsonl"), "{}\n")
sources := NewJSONLSourceSet(AgentGptme, []string{root},
WithExtensions(".jsonl", ".ndjson"),
WithInclude(func(path string, _ os.FileInfo) bool {
return filepath.Base(path) != "drop.jsonl"
}),
)
discovered, err := sources.Discover(context.Background())
require.NoError(t, err)
assert.Equal(t, []string{
filepath.Join(root, "keep.jsonl"),
filepath.Join(root, "keep.ndjson"),
}, sourceDisplayPaths(discovered))
}
func TestJSONLSourceSetWatchChangedPathFindAndFingerprint(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, "nested", "session-1.jsonl")
content := "{\"role\":\"user\"}\n"
writeSourceFile(t, path, content)
writeSourceFile(t, filepath.Join(root, "nested", "notes.txt"), "{}\n")
sources := NewJSONLSourceSet(AgentCodex, []string{root},
WithRecursive(),
WithContentHashing(),
)
plan, err := sources.WatchPlan(context.Background())
require.NoError(t, err)
require.Len(t, plan.Roots, 1)
assert.Equal(t, root, plan.Roots[0].Path)
assert.True(t, plan.Roots[0].Recursive)
assert.Equal(t, []string{"*.jsonl"}, plan.Roots[0].IncludeGlobs)
assert.NotEmpty(t, plan.Roots[0].DebounceKey)
changed, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{Path: path, EventKind: "write", WatchRoot: root},
)
require.NoError(t, err)
require.Len(t, changed, 1)
assert.Equal(t, path, changed[0].Key)
assert.Equal(t, path, changed[0].DisplayPath)
assert.Equal(t, path, changed[0].FingerprintKey)
ignored, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{
Path: filepath.Join(root, "nested", "notes.txt"),
EventKind: "write",
WatchRoot: root,
},
)
require.NoError(t, err)
assert.Empty(t, ignored)
outside, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{
Path: filepath.Join(t.TempDir(), "session-1.jsonl"),
EventKind: "write",
WatchRoot: root,
},
)
require.NoError(t, err)
assert.Empty(t, outside)
found, ok, err := sources.FindSource(context.Background(), FindSourceRequest{
StoredFilePath: path,
})
require.NoError(t, err)
require.True(t, ok)
assert.Equal(t, path, found.DisplayPath)
foundByID, ok, err := sources.FindSource(context.Background(), FindSourceRequest{
RawSessionID: "session-1",
})
require.NoError(t, err)
require.True(t, ok)
assert.Equal(t, found.DisplayPath, foundByID.DisplayPath)
withoutOpaque := found
withoutOpaque.Opaque = nil
fingerprint, err := sources.Fingerprint(context.Background(), withoutOpaque)
require.NoError(t, err)
info, err := os.Stat(path)
require.NoError(t, err)
assert.Equal(t, path, fingerprint.Key)
assert.Equal(t, info.Size(), fingerprint.Size)
assert.Equal(t, info.ModTime().UnixNano(), fingerprint.MTimeNS)
assert.Equal(t, fmt.Sprintf("%x", sha256.Sum256([]byte(content))), fingerprint.Hash)
}
func TestJSONLSourceSetChangedPathClassifiesDeletedFiles(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, "nested", "deleted.jsonl")
sources := NewJSONLSourceSet(AgentCodex, []string{root},
WithRecursive(),
)
changed, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{Path: path, EventKind: "remove", WatchRoot: root},
)
require.NoError(t, err)
require.Len(t, changed, 1)
assert.Equal(t, path, changed[0].Key)
assert.Equal(t, path, changed[0].DisplayPath)
assert.Equal(t, path, changed[0].FingerprintKey)
assert.Equal(t, "nested/deleted.jsonl", changed[0].Opaque.(JSONLSource).RelPath)
shallowPath := filepath.Join(root, "nested", "ignored.jsonl")
shallowSources := NewJSONLSourceSet(AgentCodex, []string{root})
changed, err = shallowSources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{Path: shallowPath, EventKind: "remove", WatchRoot: root},
)
require.NoError(t, err)
assert.Empty(t, changed)
}
func TestJSONLSourceSetChangedPathRejectsExistingNonRegularPath(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, "nested", "not-a-source.jsonl")
require.NoError(t, os.MkdirAll(path, 0o755))
sources := NewJSONLSourceSet(AgentCodex, []string{root},
WithRecursive(),
)
changed, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{Path: path, EventKind: "write", WatchRoot: root},
)
require.NoError(t, err)
assert.Empty(t, changed)
}
func TestJSONLSourceSetChangedPathUsesPathOnlyFilterForDeletedFiles(t *testing.T) {
root := t.TempDir()
sources := NewJSONLSourceSet(AgentCodex, []string{root},
WithRecursive(),
WithIncludePath(func(root, path string) bool {
return filepath.Base(path) == "events.jsonl"
}),
)
ignored, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{
Path: filepath.Join(root, "session", "notes.jsonl"),
EventKind: "remove",
WatchRoot: root,
},
)
require.NoError(t, err)
assert.Empty(t, ignored)
changed, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{
Path: filepath.Join(root, "session", "events.jsonl"),
EventKind: "remove",
WatchRoot: root,
},
)
require.NoError(t, err)
require.Len(t, changed, 1)
assert.Equal(t, filepath.Join(root, "session", "events.jsonl"), changed[0].DisplayPath)
}
func TestJSONLSourceSetDescendPathPrunesSources(t *testing.T) {
root := t.TempDir()
keepPath := filepath.Join(root, "keep", "session.jsonl")
skipPath := filepath.Join(root, "skip", "session.jsonl")
writeSourceFile(t, keepPath, "{}\n")
writeSourceFile(t, skipPath, "{}\n")
sources := NewJSONLSourceSet(AgentCodex, []string{root},
WithRecursive(),
WithDescendPath(func(root, path string) bool {
return filepath.Base(path) != "skip"
}),
)
discovered, err := sources.Discover(context.Background())
require.NoError(t, err)
require.Len(t, discovered, 1)
assert.Equal(t, keepPath, discovered[0].DisplayPath)
changed, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{Path: skipPath, EventKind: "write", WatchRoot: root},
)
require.NoError(t, err)
assert.Empty(t, changed)
removed, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{
Path: filepath.Join(root, "skip", "removed.jsonl"),
EventKind: "remove",
WatchRoot: root,
},
)
require.NoError(t, err)
assert.Empty(t, removed)
}
func TestJSONLSourceSetDuplicateKeysKeepFirstConfiguredRoot(t *testing.T) {
firstRoot := t.TempDir()
secondRoot := t.TempDir()
firstPath := filepath.Join(firstRoot, "session.jsonl")
secondPath := filepath.Join(secondRoot, "session.jsonl")
writeSourceFile(t, firstPath, "{}\n")
writeSourceFile(t, secondPath, "{}\n")
sources := NewJSONLSourceSet(AgentCodex, []string{firstRoot, secondRoot},
WithKey(func(_, path string) string {
return filepath.Base(path)
}),
)
discovered, err := sources.Discover(context.Background())
require.NoError(t, err)
require.Len(t, discovered, 1)
assert.Equal(t, firstPath, discovered[0].DisplayPath)
found, ok, err := sources.FindSource(
context.Background(),
FindSourceRequest{StoredFilePath: secondPath},
)
require.NoError(t, err)
require.True(t, ok)
assert.Equal(t, firstPath, found.DisplayPath)
changed, err := sources.SourcesForChangedPath(
context.Background(),
ChangedPathRequest{
Path: secondPath,
EventKind: "write",
WatchRoot: secondRoot,
},
)
require.NoError(t, err)
assert.Empty(t, changed)
}
func TestJSONLSourceSetFindSourceNormalizesRawSessionID(t *testing.T) {
root := t.TempDir()
path := filepath.Join(root, "session-1.jsonl")
writeSourceFile(t, path, "{}\n")
// LookupIDValid rejects the raw, un-normalized form, so a lookup only
// succeeds when RawSessionIDForLookup runs before the validity gate and
// before the SessionIDFromPath comparison in the discovery loop. The
// on-disk session ID is "session-1" (base name without extension), which
// the raw "raw:session-1" only matches once normalized.
rejectsRaw := func(rawID string) bool {
return rawID != "" && !strings.HasPrefix(rawID, "raw:")
}
normalizing := NewJSONLSourceSet(AgentCodex, []string{root},
WithRawSessionIDForLookup(func(rawID string) string {
return strings.TrimPrefix(rawID, "raw:")
}),
WithLookupIDValid(rejectsRaw),
)
found, ok, err := normalizing.FindSource(
context.Background(),
FindSourceRequest{RawSessionID: "raw:session-1"},
)
require.NoError(t, err)
require.True(t, ok, "normalized raw session ID must resolve its source")
assert.Equal(t, path, found.DisplayPath)
// Without the normalizer the identical request is gated out: the raw form
// fails LookupIDValid and never matches the on-disk session ID. This locks
// in that the normalization step is what enables both checks.
unnormalized := NewJSONLSourceSet(AgentCodex, []string{root},
WithLookupIDValid(rejectsRaw),
)
_, ok, err = unnormalized.FindSource(
context.Background(),
FindSourceRequest{RawSessionID: "raw:session-1"},
)
require.NoError(t, err)
assert.False(t, ok, "un-normalized raw session ID must not resolve")
}
func TestJSONLSourceSetMissingRootAndInvalidLookupAreNoops(t *testing.T) {
root := t.TempDir()
sources := NewJSONLSourceSet(AgentCodex, []string{
filepath.Join(root, "missing"),
})
discovered, err := sources.Discover(context.Background())
require.NoError(t, err)
assert.Empty(t, discovered)
found, ok, err := sources.FindSource(context.Background(), FindSourceRequest{
RawSessionID: "../session",
})
require.NoError(t, err)
assert.False(t, ok)
assert.Empty(t, found)
}
func writeSourceFile(t *testing.T, path, content string) {
t.Helper()
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
}
func mustRelSlash(t *testing.T, root, path string) string {
t.Helper()
rel, err := filepath.Rel(root, path)
require.NoError(t, err)
return filepath.ToSlash(rel)
}
func sourceKeys(sources []SourceRef) []string {
keys := make([]string, 0, len(sources))
for _, source := range sources {
keys = append(keys, source.Key)
}
return keys
}
func sourceProjects(sources []SourceRef) []string {
projects := make([]string, 0, len(sources))
for _, source := range sources {
projects = append(projects, source.ProjectHint)
}
return projects
}
func sourceDisplayPaths(sources []SourceRef) []string {
paths := make([]string, 0, len(sources))
for _, source := range sources {
paths = append(paths, source.DisplayPath)
}
return paths
}
func TestCleanJSONLRootsPreservesS3Scheme(t *testing.T) {
tests := []struct {
name string
roots []string
want []string
}{
{
name: "s3 root kept verbatim",
roots: []string{"s3://bucket/laptop/raw/claude"},
want: []string{"s3://bucket/laptop/raw/claude"},
},
{
name: "s3 root with trailing slash kept verbatim",
roots: []string{"s3://bucket/laptop/raw/claude/"},
want: []string{"s3://bucket/laptop/raw/claude/"},
},
{
name: "local roots still cleaned",
roots: []string{"/tmp/foo/../bar", "/tmp/baz/"},
// filepath.Clean yields OS-native separators (backslashes on
// Windows), so build the expectation with FromSlash rather than
// hard-coding forward slashes.
want: []string{filepath.FromSlash("/tmp/bar"), filepath.FromSlash("/tmp/baz")},
},
{
name: "empty roots dropped",
roots: []string{"", "s3://bucket/x", ""},
want: []string{"s3://bucket/x"},
},
{
name: "mixed local and s3",
roots: []string{"/tmp/a/./b", "s3://bucket/y/"},
want: []string{filepath.FromSlash("/tmp/a/b"), "s3://bucket/y/"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// filepath.Clean would collapse "s3://" to "s3:/", which defeats the
// HasPrefix("s3://") checks that route discovery to the object store.
assert.Equal(t, tt.want, cleanJSONLRoots(tt.roots))
})
}
}