66use Amp \Sync \LocalKeyedMutex ;
77use PHPUnit \Framework \TestCase ;
88use Symfony \Lsp \Document \DocumentStore ;
9+ use Symfony \Lsp \Feature \Configuration \StaleConfigurationValidationSnapshotException ;
910use Symfony \Lsp \Index \ApplicationSourceScanner ;
1011use Symfony \Lsp \Index \IndexCommandHandler ;
1112use Symfony \Lsp \Index \PhpRuntimeStructureHasher ;
@@ -113,6 +114,50 @@ public function testManuallyRefreshesSourceAndTrustedRuntimeIndexes(): void
113114 ]);
114115 self ::assertFalse ($ untrusted [0 ]['trusted ' ] ?? null );
115116 }
117+
118+ public function testRetriesManualRuntimeRefreshAfterAConfigurationChangeInvalidatesTheSnapshot (): void
119+ {
120+ $ projects = new ProjectRegistry ();
121+ $ projects ->replace ([$ project = new Project (
122+ $ this ->temporaryDirectory ,
123+ 'file:// ' .$ this ->temporaryDirectory ,
124+ '^8.0 ' ,
125+ )]);
126+ $ statuses = new ProjectIndexStatusRegistry ();
127+ $ sourceScanner = new ApplicationSourceScanner (
128+ $ projects ,
129+ new DocumentStore (),
130+ $ statuses ,
131+ new NullProgressReporter (),
132+ new InMemorySourceIndexStore (),
133+ new SourceIndexPayloadCodec (),
134+ new PhpRuntimeStructureHasher (),
135+ new UriToPathConverter (),
136+ new SourceFileEnumerator (new GitignoreMatcher (), new ProjectFileScopeRegistry ()),
137+ new LocalKeyedMutex (),
138+ new ServerLogger (null , new SensitiveDataRedactor ()),
139+ [],
140+ );
141+ $ runtime = new RecordingRuntimeInitializer (1 );
142+ $ workspaceTrust = new WorkspaceTrust ();
143+ $ workspaceTrust ->set ($ project , TrustStatus::Trusted);
144+ $ handler = new IndexCommandHandler (
145+ $ projects ,
146+ $ workspaceTrust ,
147+ $ sourceScanner ,
148+ new StatusRuntimeInitializer ($ runtime , $ statuses , $ projects ),
149+ $ statuses ,
150+ new RuntimeConfiguration (),
151+ );
152+
153+ $ result = $ handler ->execute ([
154+ 'command ' => IndexCommandHandler::REFRESH_COMMAND ,
155+ 'arguments ' => [$ project ->rootUri ()],
156+ ]);
157+
158+ self ::assertSame ([$ this ->temporaryDirectory , $ this ->temporaryDirectory ], $ runtime ->projects );
159+ self ::assertSame ('ready ' , $ result [0 ]['runtime ' ]['state ' ] ?? null );
160+ }
116161}
117162
118163final class RecordingRuntimeInitializer implements RuntimeInitializerInterface
@@ -123,9 +168,18 @@ final class RecordingRuntimeInitializer implements RuntimeInitializerInterface
123168 /** @var list<RuntimeRefreshPlan> */
124169 public array $ plans = [];
125170
171+ public function __construct (private int $ staleSnapshots = 0 )
172+ {
173+ }
174+
126175 public function initialize (Project $ project , ?RuntimeRefreshPlan $ plan = null , ?Cancellation $ cancellation = null ): void
127176 {
128177 $ this ->projects [] = $ project ->rootPath ();
129178 $ this ->plans [] = $ plan ?? new RuntimeRefreshPlan ();
179+ if (0 < $ this ->staleSnapshots ) {
180+ --$ this ->staleSnapshots ;
181+
182+ throw new StaleConfigurationValidationSnapshotException ();
183+ }
130184 }
131185}
0 commit comments