Skip to content

Commit a0c5b9f

Browse files
committed
Tests
1 parent c261e2a commit a0c5b9f

File tree

9 files changed

+1607
-0
lines changed

9 files changed

+1607
-0
lines changed

.kiro/settings/lsp.json

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
{
2+
"languages": {
3+
"ruby": {
4+
"name": "solargraph",
5+
"command": "solargraph",
6+
"args": [
7+
"stdio"
8+
],
9+
"file_extensions": [
10+
"rb"
11+
],
12+
"project_patterns": [
13+
"Gemfile",
14+
"Rakefile"
15+
],
16+
"exclude_patterns": [
17+
"**/vendor/**",
18+
"**/tmp/**"
19+
],
20+
"multi_workspace": false,
21+
"initialization_options": {},
22+
"request_timeout_secs": 60
23+
},
24+
"cpp": {
25+
"name": "clangd",
26+
"command": "clangd",
27+
"args": [
28+
"--background-index"
29+
],
30+
"file_extensions": [
31+
"cpp",
32+
"cc",
33+
"cxx",
34+
"c",
35+
"h",
36+
"hpp",
37+
"hxx"
38+
],
39+
"project_patterns": [
40+
"CMakeLists.txt",
41+
"compile_commands.json",
42+
"Makefile"
43+
],
44+
"exclude_patterns": [
45+
"**/build/**",
46+
"**/cmake-build-**/**"
47+
],
48+
"multi_workspace": false,
49+
"initialization_options": {},
50+
"request_timeout_secs": 60
51+
},
52+
"rust": {
53+
"name": "rust-analyzer",
54+
"command": "rust-analyzer",
55+
"args": [],
56+
"file_extensions": [
57+
"rs"
58+
],
59+
"project_patterns": [
60+
"Cargo.toml"
61+
],
62+
"exclude_patterns": [
63+
"**/target/**"
64+
],
65+
"multi_workspace": false,
66+
"initialization_options": {
67+
"cargo": {
68+
"buildScripts": {
69+
"enable": true
70+
}
71+
},
72+
"diagnostics": {
73+
"enable": true,
74+
"enableExperimental": true
75+
},
76+
"workspace": {
77+
"symbol": {
78+
"search": {
79+
"scope": "workspace"
80+
}
81+
}
82+
}
83+
},
84+
"request_timeout_secs": 60
85+
},
86+
"typescript": {
87+
"name": "typescript-language-server",
88+
"command": "typescript-language-server",
89+
"args": [
90+
"--stdio"
91+
],
92+
"file_extensions": [
93+
"ts",
94+
"js",
95+
"tsx",
96+
"jsx"
97+
],
98+
"project_patterns": [
99+
"package.json",
100+
"tsconfig.json"
101+
],
102+
"exclude_patterns": [
103+
"**/node_modules/**",
104+
"**/dist/**"
105+
],
106+
"multi_workspace": false,
107+
"initialization_options": {
108+
"preferences": {
109+
"disableSuggestions": false
110+
}
111+
},
112+
"request_timeout_secs": 60
113+
},
114+
"java": {
115+
"name": "jdtls",
116+
"command": "jdtls",
117+
"args": [],
118+
"file_extensions": [
119+
"java"
120+
],
121+
"project_patterns": [
122+
"pom.xml",
123+
"build.gradle",
124+
"build.gradle.kts",
125+
".project"
126+
],
127+
"exclude_patterns": [
128+
"**/target/**",
129+
"**/build/**",
130+
"**/.gradle/**"
131+
],
132+
"multi_workspace": false,
133+
"initialization_options": {
134+
"settings": {
135+
"java": {
136+
"compile": {
137+
"nullAnalysis": {
138+
"mode": "automatic"
139+
}
140+
},
141+
"configuration": {
142+
"annotationProcessing": {
143+
"enabled": true
144+
}
145+
}
146+
}
147+
}
148+
},
149+
"request_timeout_secs": 60
150+
},
151+
"python": {
152+
"name": "pyright",
153+
"command": "pyright-langserver",
154+
"args": [
155+
"--stdio"
156+
],
157+
"file_extensions": [
158+
"py"
159+
],
160+
"project_patterns": [
161+
"pyproject.toml",
162+
"setup.py",
163+
"requirements.txt",
164+
"pyrightconfig.json"
165+
],
166+
"exclude_patterns": [
167+
"**/__pycache__/**",
168+
"**/venv/**",
169+
"**/.venv/**",
170+
"**/.pytest_cache/**"
171+
],
172+
"multi_workspace": false,
173+
"initialization_options": {},
174+
"request_timeout_secs": 60
175+
},
176+
"go": {
177+
"name": "gopls",
178+
"command": "gopls",
179+
"args": [],
180+
"file_extensions": [
181+
"go"
182+
],
183+
"project_patterns": [
184+
"go.mod",
185+
"go.sum"
186+
],
187+
"exclude_patterns": [
188+
"**/vendor/**"
189+
],
190+
"multi_workspace": false,
191+
"initialization_options": {
192+
"usePlaceholders": true,
193+
"completeUnimported": true
194+
},
195+
"request_timeout_secs": 60
196+
}
197+
}
198+
}

internal/adapter/opencode/adapter_test.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,145 @@ func TestDiscoverRelatedProjectDirs_EmptyStorage(t *testing.T) {
564564
t.Errorf("expected empty slice, got %v", related)
565565
}
566566
}
567+
568+
func TestMalformedProjectJSON(t *testing.T) {
569+
tmpDir := t.TempDir()
570+
projectDir := filepath.Join(tmpDir, "project")
571+
if err := os.MkdirAll(projectDir, 0755); err != nil {
572+
t.Fatalf("failed to create project dir: %v", err)
573+
}
574+
575+
validJSON := `{"id":"proj_valid","worktree":"/tmp/valid-project","vcs":"git","time":{"created":1767000000000,"updated":1767100000000}}`
576+
if err := os.WriteFile(filepath.Join(projectDir, "valid.json"), []byte(validJSON), 0644); err != nil {
577+
t.Fatalf("failed to write valid project: %v", err)
578+
}
579+
580+
malformedJSON := `{invalid json`
581+
if err := os.WriteFile(filepath.Join(projectDir, "bad.json"), []byte(malformedJSON), 0644); err != nil {
582+
t.Fatalf("failed to write malformed project: %v", err)
583+
}
584+
585+
a := &Adapter{
586+
storageDir: tmpDir,
587+
projectIndex: make(map[string]*Project),
588+
sessionIndex: make(map[string]string),
589+
metaCache: make(map[string]sessionMetaCacheEntry),
590+
}
591+
592+
if err := a.loadProjects(); err != nil {
593+
t.Fatalf("loadProjects should not error on malformed JSON, got: %v", err)
594+
}
595+
596+
if !a.projectsLoaded {
597+
t.Error("projectsLoaded should be true")
598+
}
599+
600+
if len(a.projectIndex) != 1 {
601+
t.Errorf("expected 1 project in index, got %d", len(a.projectIndex))
602+
}
603+
604+
if _, ok := a.projectIndex["/tmp/valid-project"]; !ok {
605+
t.Error("expected valid project to be in index")
606+
}
607+
}
608+
609+
func TestMalformedSessionJSON(t *testing.T) {
610+
tmpDir := t.TempDir()
611+
projectPath := filepath.Join(tmpDir, "myproject")
612+
if err := os.MkdirAll(projectPath, 0755); err != nil {
613+
t.Fatalf("failed to create project path: %v", err)
614+
}
615+
616+
projectDir := filepath.Join(tmpDir, "storage", "project")
617+
if err := os.MkdirAll(projectDir, 0755); err != nil {
618+
t.Fatalf("failed to create project dir: %v", err)
619+
}
620+
projectJSON := fmt.Sprintf(`{"id":"proj1","worktree":"%s","vcs":"git","time":{"created":1767000000000,"updated":1767100000000}}`, projectPath)
621+
if err := os.WriteFile(filepath.Join(projectDir, "proj1.json"), []byte(projectJSON), 0644); err != nil {
622+
t.Fatalf("failed to write project file: %v", err)
623+
}
624+
625+
sessionDir := filepath.Join(tmpDir, "storage", "session", "proj1")
626+
if err := os.MkdirAll(sessionDir, 0755); err != nil {
627+
t.Fatalf("failed to create session dir: %v", err)
628+
}
629+
630+
now := time.Now().UnixMilli()
631+
validSession := fmt.Sprintf(`{"id":"ses_good","title":"Good Session","parentID":"","time":{"created":%d,"updated":%d}}`, now, now)
632+
if err := os.WriteFile(filepath.Join(sessionDir, "ses_good.json"), []byte(validSession), 0644); err != nil {
633+
t.Fatalf("failed to write valid session: %v", err)
634+
}
635+
636+
malformedSession := `{not valid json!!!`
637+
if err := os.WriteFile(filepath.Join(sessionDir, "ses_bad.json"), []byte(malformedSession), 0644); err != nil {
638+
t.Fatalf("failed to write malformed session: %v", err)
639+
}
640+
641+
a := &Adapter{
642+
storageDir: filepath.Join(tmpDir, "storage"),
643+
projectIndex: make(map[string]*Project),
644+
sessionIndex: make(map[string]string),
645+
metaCache: make(map[string]sessionMetaCacheEntry),
646+
}
647+
648+
sessions, err := a.Sessions(projectPath)
649+
if err != nil {
650+
t.Fatalf("Sessions should not error on malformed JSON, got: %v", err)
651+
}
652+
653+
if len(sessions) != 1 {
654+
t.Fatalf("expected 1 session (malformed skipped), got %d", len(sessions))
655+
}
656+
657+
if sessions[0].ID != "ses_good" {
658+
t.Errorf("expected session ID %q, got %q", "ses_good", sessions[0].ID)
659+
}
660+
661+
if sessions[0].Name != "Good Session" {
662+
t.Errorf("expected session name %q, got %q", "Good Session", sessions[0].Name)
663+
}
664+
}
665+
666+
func TestMalformedMessageJSON(t *testing.T) {
667+
tmpDir := t.TempDir()
668+
messageDir := filepath.Join(tmpDir, "message", "ses_test")
669+
if err := os.MkdirAll(messageDir, 0755); err != nil {
670+
t.Fatalf("failed to create message dir: %v", err)
671+
}
672+
673+
now := time.Now().UnixMilli()
674+
validMsg := fmt.Sprintf(`{"id":"msg_good","sessionID":"ses_test","role":"user","time":{"created":%d}}`, now)
675+
if err := os.WriteFile(filepath.Join(messageDir, "msg_good.json"), []byte(validMsg), 0644); err != nil {
676+
t.Fatalf("failed to write valid message: %v", err)
677+
}
678+
679+
malformedMsg := `{totally broken json`
680+
if err := os.WriteFile(filepath.Join(messageDir, "msg_bad.json"), []byte(malformedMsg), 0644); err != nil {
681+
t.Fatalf("failed to write malformed message: %v", err)
682+
}
683+
684+
a := &Adapter{
685+
storageDir: tmpDir,
686+
projectIndex: make(map[string]*Project),
687+
sessionIndex: make(map[string]string),
688+
metaCache: make(map[string]sessionMetaCacheEntry),
689+
}
690+
691+
msgMap, err := a.batchReadMessages(messageDir)
692+
if err != nil {
693+
t.Fatalf("batchReadMessages should not error on malformed JSON, got: %v", err)
694+
}
695+
696+
if len(msgMap) != 1 {
697+
t.Fatalf("expected 1 message (malformed skipped), got %d", len(msgMap))
698+
}
699+
700+
msg, ok := msgMap["msg_good"]
701+
if !ok {
702+
t.Fatal("expected msg_good in result map")
703+
}
704+
705+
if msg.Role != "user" {
706+
t.Errorf("expected role %q, got %q", "user", msg.Role)
707+
}
708+
}

0 commit comments

Comments
 (0)