From cfc5a43e6779d6f0fd6a3942ba77fff385cc51b9 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Wed, 8 May 2024 14:14:54 +0700 Subject: [PATCH] Create new invalidatedircache command in runner This command will do nothing except run an `ls` on the requested project code. The intent is to run this command during testing after a project reset, in order to immediately invalidate the NFS dir cache on hgweb and make the new state of the project available. It will likely not be needed in production, because the only time the NFS cache has been relevant in production (or staging) is when lexentrycount is run after a project reset, and the `ls` is already baked into the lexentrycount command. But in testing, it will sometimes be necessary to reset the NFS directory cache in order to make the result of a project reset available right away, which will allow the tests to stop being flaky. --- backend/Testing/ApiTests/ApiTestBase.cs | 6 ++++++ .../SyncReverseProxy/SendReceiveServiceTests.cs | 6 +++--- frontend/tests/resetProject.test.ts | 14 +++++--------- hgweb/command-runner.sh | 6 +++++- 4 files changed, 19 insertions(+), 13 deletions(-) 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