Skip to content

Commit b6dd6de

Browse files
committed
Fix crash when viewing invalid snapshot deltas
1 parent ce8fea1 commit b6dd6de

File tree

4 files changed

+40
-4
lines changed

4 files changed

+40
-4
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/editor-history/history-chooser/history-chooser.component.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HttpErrorResponse } from '@angular/common/http';
12
import { SimpleChange } from '@angular/core';
23
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
34
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@@ -147,6 +148,17 @@ describe('HistoryChooserComponent', () => {
147148
expect(env.revertHistoryButton).toBeNull();
148149
}));
149150

151+
it('should not display the revert history button if snapshot is corrupt', fakeAsync(() => {
152+
const env = new TestEnvironment();
153+
when(mockedParatextService.getSnapshot('project01', 'MAT', 1, 'date_here')).thenReject(
154+
new HttpErrorResponse({ status: 409 })
155+
);
156+
env.triggerNgOnChanges();
157+
env.wait();
158+
expect(env.revertHistoryButton).toBeNull();
159+
expect(env.component.selectedSnapshot).toBeUndefined();
160+
}));
161+
150162
it('should revert to the snapshot', fakeAsync(() => {
151163
const env = new TestEnvironment();
152164
when(mockedDialogService.confirm(anything(), anything())).thenResolve(true);

src/SIL.XForge.Scripture/ClientApp/src/app/translate/editor/editor-history/history-chooser/history-chooser.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ export class HistoryChooserComponent implements AfterViewInit, OnChanges {
217217
// Remember the snapshot so we can apply it
218218
this.selectedSnapshot = snapshot;
219219
this.revisionSelect.emit({ revision, snapshot });
220-
});
220+
})
221+
// On error, do not emit the revision
222+
.catch(() => {});
221223
}
222224
}

src/SIL.XForge.Scripture/Controllers/ParatextController.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,12 @@ DateTime timestamp
216216
{
217217
return Ok(await _paratextService.GetSnapshotAsync(userSecret, projectId, book, chapter, timestamp));
218218
}
219+
catch (ArgumentException e)
220+
{
221+
// We want to report this exception for further investigation ("The delta is not a document.")
222+
_exceptionHandler.ReportException(e);
223+
return Conflict();
224+
}
219225
catch (DataNotFoundException)
220226
{
221227
return NotFound();

test/SIL.XForge.Scripture.Tests/Controllers/ParatextControllerTests.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,21 @@ public async Task GetRevisionHistoryAsync_Success()
315315
Assert.IsTrue(historyExists);
316316
}
317317

318+
[Test]
319+
public async Task GetSnapshotAsync_DeltaIsNotADocument()
320+
{
321+
// Set up test environment
322+
var env = new TestEnvironment();
323+
env.ParatextService.GetSnapshotAsync(Arg.Any<UserSecret>(), Project01, Book, Chapter, Timestamp)
324+
.Throws(new ArgumentException(@"The delta is not a document."));
325+
326+
// SUT
327+
ActionResult<TextSnapshot> actual = await env.Controller.GetSnapshotAsync(Project01, Book, Chapter, Timestamp);
328+
329+
env.ExceptionHandler.Received(1).ReportException(Arg.Any<ArgumentException>());
330+
Assert.IsInstanceOf<ConflictResult>(actual.Result);
331+
}
332+
318333
[Test]
319334
public async Task GetSnapshotAsync_Forbidden()
320335
{
@@ -565,14 +580,14 @@ private class TestEnvironment
565580

566581
public TestEnvironment()
567582
{
568-
IExceptionHandler exceptionHandler = Substitute.For<IExceptionHandler>();
583+
ExceptionHandler = Substitute.For<IExceptionHandler>();
569584

570585
UserAccessor = Substitute.For<IUserAccessor>();
571586
UserAccessor.UserId.Returns(User01);
572587
UserAccessor.SystemRoles.Returns([SystemRole.ServalAdmin]);
573588

574589
MemoryRepository<UserSecret> userSecrets = new MemoryRepository<UserSecret>(
575-
new[] { new UserSecret { Id = User01 } }
590+
[new UserSecret { Id = User01 }]
576591
);
577592

578593
MachineProjectService = Substitute.For<IMachineProjectService>();
@@ -600,7 +615,7 @@ public TestEnvironment()
600615
ProjectService = Substitute.For<ISFProjectService>();
601616

602617
Controller = new ParatextController(
603-
exceptionHandler,
618+
ExceptionHandler,
604619
MachineProjectService,
605620
ParatextService,
606621
ProjectService,
@@ -610,6 +625,7 @@ public TestEnvironment()
610625
}
611626

612627
public ParatextController Controller { get; }
628+
public IExceptionHandler ExceptionHandler { get; }
613629
public IMachineProjectService MachineProjectService { get; }
614630
public IParatextService ParatextService { get; }
615631
public ISFProjectService ProjectService { get; }

0 commit comments

Comments
 (0)