diff --git a/backend/Testing/ApiTests/ApiTestBase.cs b/backend/Testing/ApiTests/ApiTestBase.cs index 89db65d10..fd448361c 100644 --- a/backend/Testing/ApiTests/ApiTestBase.cs +++ b/backend/Testing/ApiTests/ApiTestBase.cs @@ -60,4 +60,10 @@ public async Task StartLexboxProjectReset(string projectCode) var response = await HttpClient.PostAsync($"{BaseUrl}/api/project/resetProject/{projectCode}", null); response.EnsureSuccessStatusCode(); } + + public async Task InvalidateDirCache(string projectCode) + { + var response = await HttpClient.PostAsync($"{BaseUrl}/hg/command/{projectCode}/invalidatedircache", null); + response.EnsureSuccessStatusCode(); + } } diff --git a/backend/Testing/SyncReverseProxy/SendReceiveServiceTests.cs b/backend/Testing/SyncReverseProxy/SendReceiveServiceTests.cs index b2b927a85..c0a5ad230 100644 --- a/backend/Testing/SyncReverseProxy/SendReceiveServiceTests.cs +++ b/backend/Testing/SyncReverseProxy/SendReceiveServiceTests.cs @@ -117,7 +117,7 @@ public async Task SendReceiveAfterProjectReset(HgProtocol protocol) var projectConfig = _srFixture.InitLocalFlexProjectWithRepo(protocol, "SR_AfterReset"); await using var project = await RegisterProjectInLexBox(projectConfig, _adminApiTester); - await WaitForHgRefreshIntervalAsync(); + await _adminApiTester.InvalidateDirCache(projectConfig.Code); var sendReceiveParams = new SendReceiveParams(protocol, projectConfig); var srResult = _sendReceiveService.SendReceiveProject(sendReceiveParams, AdminAuth); @@ -144,7 +144,7 @@ public async Task SendReceiveAfterProjectReset(HgProtocol protocol) await _adminApiTester.HttpClient.PostAsync($"{_adminApiTester.BaseUrl}/api/project/resetProject/{projectConfig.Code}", null); await _adminApiTester.HttpClient.PostAsync($"{_adminApiTester.BaseUrl}/api/project/finishResetProject/{projectConfig.Code}", null); - await WaitForHgRefreshIntervalAsync(); // TODO 765: Remove this + await _adminApiTester.InvalidateDirCache(projectConfig.Code); // Step 2: verify project is now empty, i.e. tip is "0000000..." response = await _adminApiTester.HttpClient.GetAsync(tipUri.Uri); @@ -169,7 +169,7 @@ public async Task SendReceiveAfterProjectReset(HgProtocol protocol) var srResultStep3 = _sendReceiveService.SendReceiveProject(sendReceiveParams, AdminAuth); _output.WriteLine(srResultStep3); - await WaitForHgRefreshIntervalAsync(); // TODO 765: Remove this + await _adminApiTester.InvalidateDirCache(projectConfig.Code); // Step 4: verify project tip is same hash as original project tip response = await _adminApiTester.HttpClient.GetAsync(tipUri.Uri); diff --git a/frontend/tests/resetProject.test.ts b/frontend/tests/resetProject.test.ts index 7ba084ffb..9e1354595 100644 --- a/frontend/tests/resetProject.test.ts +++ b/frontend/tests/resetProject.test.ts @@ -59,7 +59,7 @@ test('reset project and upload .zip file', async ({ page, tempProject, tempDir } await resetProjectModel.assertGone(); // Step 4: confirm it's empty now - await page.request.get(`${testEnv.serverBaseUrl}/hg/command/${tempProject.code}/tip`); // Force an NFS cache clear, ignore result + await page.request.get(`${testEnv.serverBaseUrl}/hg/command/${tempProject.code}/invalidatedircache`); // Force an NFS cache clear const afterResetResponse = await page.request.get(`${testEnv.serverBaseUrl}/hg/${tempProject.code}/file/tip?style=json-lex`); const afterResetJson = await afterResetResponse.json() as HgWebJson; expect(afterResetJson.node).toEqual(allZeroHash); @@ -78,12 +78,8 @@ test('reset project and upload .zip file', async ({ page, tempProject, tempDir } await resetProjectModel.assertGone(); // Step 6: confirm tip hash and contents are same as before reset - // It can take a while for the server to pick up the new repo - await expect(async () => { - const afterUploadResponse = await page.request.get(`${testEnv.serverBaseUrl}/hg/${tempProject.code}/file/tip?style=json-lex`); - const afterResetJSon = await afterUploadResponse.json() as HgWebJson; - expect(afterResetJSon).toEqual(beforeResetJson); // NOT .toBe(), which would check that they're the same object. - }).toPass({ - intervals: [1_000, 3_000, 5_000], - }); + await page.request.get(`${testEnv.serverBaseUrl}/hg/command/${tempProject.code}/invalidatedircache`); // Force an NFS cache clear + const afterUploadResponse = await page.request.get(`${testEnv.serverBaseUrl}/hg/${tempProject.code}/file/tip?style=json-lex`); + const afterResetJSon = await afterUploadResponse.json() as HgWebJson; + expect(afterResetJSon).toEqual(beforeResetJson); // NOT .toBe(), which would check that they're the same object. }); diff --git a/hgweb/command-runner.sh b/hgweb/command-runner.sh index fe66a1c77..56c5fa037 100644 --- a/hgweb/command-runner.sh +++ b/hgweb/command-runner.sh @@ -1,7 +1,7 @@ #!/bin/bash # Define the list of allowed commands -allowed_commands=("verify" "tip" "wesaylexentrycount" "lexentrycount" "recover") +allowed_commands=("verify" "tip" "wesaylexentrycount" "lexentrycount" "recover", "invalidatedircache") # Get the project code and command name from the URL IFS='/' read -ra PATH_SEGMENTS <<< "$PATH_INFO" @@ -63,6 +63,10 @@ case $command_name in timeout 5 chg verify 2>&1 ;; + invalidatedircache) + # Do nothing; the ls before the case was the whole point of this command + ;; + *) # Env var PYTHONUNBUFFERED required for commands like verify and recover, so that output can stream back to the project page PYTHONUNBUFFERED=1 chg $command_name 2>&1