Skip to content

Commit dba3fa9

Browse files
committed
Add integration tests for editor-specific rename reindex behavior
1 parent 2879e00 commit dba3fa9

1 file changed

Lines changed: 214 additions & 0 deletions

File tree

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
defmodule Expert.State.RenameProgressTest do
2+
@moduledoc """
3+
Integration tests for rename progress tracking with different editors.
4+
5+
Different editors send different events after rename operations:
6+
- VSCode: sends both file_changed and file_saved events for renamed files
7+
- Neovim: sends only file_changed for non-renamed files, file_saved for renamed files
8+
9+
These tests verify the full rename flow from State → EngineApi → Engine.
10+
"""
11+
12+
alias Expert.Configuration
13+
alias Expert.EngineApi
14+
alias Expert.State
15+
alias Engine.Commands.Rename
16+
alias Engine.Commands.RenameSupervisor
17+
alias Forge.Document
18+
19+
import Forge.EngineApi.Messages
20+
import Forge.Test.Fixtures
21+
22+
use ExUnit.Case, async: false
23+
use Patch
24+
25+
setup do
26+
start_supervised!(Expert.Application.document_store_child_spec())
27+
start_supervised!(RenameSupervisor)
28+
29+
patch(Engine.Api.Proxy, :start_buffering, :ok)
30+
patch(Engine.Commands.Reindex, :uri, fn _uri -> :ok end)
31+
patch(Engine.Search.Store, :clear, fn _uri -> :ok end)
32+
33+
if pid = Process.whereis(Rename) do
34+
Process.exit(pid, :kill)
35+
Process.sleep(10)
36+
end
37+
38+
:ok
39+
end
40+
41+
describe "VSCode editor - non-file-rename (edits only)" do
42+
test "reindex triggers after file_saved (VSCode expects save after edit)" do
43+
uri = "file:///test/lib/foo.ex"
44+
editor = vscode_editor()
45+
open_document(uri, "defmodule Foo do\nend")
46+
expect_events_before_reindex(%{uri => file_saved(uri: uri)}, reindex: [uri])
47+
48+
simulate_did_change(editor, uri)
49+
refute_reindex_triggered()
50+
51+
simulate_did_save(editor, uri)
52+
assert_reindex_triggered(reindex: [uri], delete: [])
53+
end
54+
end
55+
56+
describe "Neovim editor - non-file-rename (edits only)" do
57+
test "reindex triggers after file_changed only (Neovim doesn't auto-save)" do
58+
uri = "file:///test/lib/foo.ex"
59+
editor = neovim_editor()
60+
open_document(uri, "defmodule Foo do\nend")
61+
expect_events_before_reindex(%{uri => file_changed(uri: uri)}, reindex: [uri])
62+
63+
simulate_did_change(editor, uri)
64+
65+
assert_reindex_triggered(reindex: [uri], delete: [])
66+
end
67+
end
68+
69+
describe "VSCode editor - file rename" do
70+
test "reindex triggers after receiving file_changed for old + file_saved for new" do
71+
# Arrange
72+
old_uri = "file:///test/lib/old_module.ex"
73+
new_uri = "file:///test/lib/new_module.ex"
74+
editor = vscode_editor()
75+
76+
open_document(old_uri, "defmodule OldModule do\nend")
77+
open_document(new_uri, "defmodule NewModule do\nend")
78+
79+
expect_events_before_reindex(
80+
%{old_uri => file_changed(uri: old_uri), new_uri => file_saved(uri: new_uri)},
81+
reindex: [new_uri],
82+
delete: [old_uri]
83+
)
84+
85+
simulate_did_change(editor, old_uri)
86+
refute_reindex_triggered()
87+
88+
simulate_did_save(editor, new_uri)
89+
assert_reindex_triggered(reindex: [new_uri], delete: [old_uri])
90+
end
91+
end
92+
93+
describe "Neovim editor - file rename" do
94+
test "reindex triggers after file_saved for new file only" do
95+
old_uri = "file:///test/lib/old_neovim.ex"
96+
new_uri = "file:///test/lib/new_neovim.ex"
97+
editor = neovim_editor()
98+
99+
open_document(new_uri, "defmodule NewNeovim do\nend")
100+
101+
expect_events_before_reindex(
102+
%{new_uri => file_saved(uri: new_uri)},
103+
reindex: [new_uri],
104+
delete: [old_uri]
105+
)
106+
107+
simulate_did_save(editor, new_uri)
108+
109+
assert_reindex_triggered(reindex: [new_uri], delete: [old_uri])
110+
end
111+
end
112+
113+
# ============================================================================
114+
# Test DSL - Editor simulation
115+
# ============================================================================
116+
117+
defp vscode_editor do
118+
build_editor("Visual Studio Code")
119+
end
120+
121+
defp neovim_editor do
122+
build_editor("Neovim")
123+
end
124+
125+
defp build_editor(client_name) do
126+
project = project()
127+
config = Configuration.new(project: project, client_name: client_name)
128+
state = %State{configuration: config, initialized?: true}
129+
130+
patch(EngineApi, :broadcast, fn ^project, _msg -> :ok end)
131+
patch(EngineApi, :compile_document, fn ^project, _doc -> :ok end)
132+
patch(EngineApi, :schedule_compile, fn ^project, _ -> :ok end)
133+
134+
patch(EngineApi, :maybe_update_rename_progress, fn ^project, msg ->
135+
Rename.update_progress(msg)
136+
end)
137+
138+
%{state: state, version: 1}
139+
end
140+
141+
defp open_document(uri, content) do
142+
:ok = Document.Store.open(uri, content, 1)
143+
end
144+
145+
defp simulate_did_change(editor, uri) do
146+
new_version = editor.version + 1
147+
notification = build_did_change(uri, new_version)
148+
{:ok, new_state} = State.apply(editor.state, notification)
149+
%{editor | state: new_state, version: new_version}
150+
end
151+
152+
defp simulate_did_save(editor, uri) do
153+
notification = build_did_save(uri)
154+
{:ok, new_state} = State.apply(editor.state, notification)
155+
%{editor | state: new_state}
156+
end
157+
158+
# ============================================================================
159+
# Test DSL - Expectations
160+
# ============================================================================
161+
162+
defp expect_events_before_reindex(uri_to_expected, opts) do
163+
paths_to_reindex = Keyword.get(opts, :reindex, [])
164+
paths_to_delete = Keyword.get(opts, :delete, [])
165+
test_pid = self()
166+
167+
on_complete = fn ->
168+
send(test_pid, {:rename_complete, paths_to_reindex, paths_to_delete})
169+
end
170+
171+
{:ok, _} =
172+
RenameSupervisor.start_renaming(
173+
uri_to_expected,
174+
paths_to_reindex,
175+
paths_to_delete,
176+
fn _delta, _msg -> :ok end,
177+
on_complete
178+
)
179+
end
180+
181+
defp assert_reindex_triggered(opts) do
182+
reindex = Keyword.fetch!(opts, :reindex)
183+
delete = Keyword.fetch!(opts, :delete)
184+
assert_receive {:rename_complete, ^reindex, ^delete}
185+
end
186+
187+
defp refute_reindex_triggered do
188+
refute_receive {:rename_complete, _, _}, 50
189+
end
190+
191+
# ============================================================================
192+
# LSP notification builders
193+
# ============================================================================
194+
195+
defp build_did_change(uri, version) do
196+
%GenLSP.Notifications.TextDocumentDidChange{
197+
params: %GenLSP.Structures.DidChangeTextDocumentParams{
198+
text_document: %GenLSP.Structures.VersionedTextDocumentIdentifier{
199+
uri: uri,
200+
version: version
201+
},
202+
content_changes: [%{text: "defmodule Renamed do\nend"}]
203+
}
204+
}
205+
end
206+
207+
defp build_did_save(uri) do
208+
%GenLSP.Notifications.TextDocumentDidSave{
209+
params: %GenLSP.Structures.DidSaveTextDocumentParams{
210+
text_document: %GenLSP.Structures.TextDocumentIdentifier{uri: uri}
211+
}
212+
}
213+
end
214+
end

0 commit comments

Comments
 (0)