mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 21:26:15 -08:00
fix(stalker): discard stale import attempts
This commit is contained in:
1 parent
a44f578ddc
commit
c1f7306ed5
2 files changed
+94
No files matched your search
+78
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable max-lines -- the import lifecycle matrix shares one state-machine harness */
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Store } from '@ngrx/store';
|
||||
@@ -407,6 +408,83 @@ describe('StalkerPortalImportComponent', () => {
|
||||
expect(component.connectionStage()).toBe('idle');
|
||||
});
|
||||
|
||||
it('discards a ready attempt returned after the form was cleared during open', async () => {
|
||||
const open = deferred<StalkerSessionConnectionOutcome>();
|
||||
session.open.mockReturnValueOnce(open.promise);
|
||||
|
||||
const add = component.addPlaylist();
|
||||
await Promise.resolve();
|
||||
component.clearForm();
|
||||
open.resolve(READY_OUTCOME);
|
||||
await add;
|
||||
|
||||
expect(session.discard).toHaveBeenCalledWith('attempt-import-1');
|
||||
expect(playlists.persistStalkerConnection).not.toHaveBeenCalled();
|
||||
expect(component.connectionStage()).toBe('idle');
|
||||
});
|
||||
|
||||
it('discards a challenge returned after the form was cleared during continuation', async () => {
|
||||
session.open.mockResolvedValueOnce({
|
||||
attemptRef: 'attempt-origin-stale',
|
||||
challengeRef: 'challenge-origin-stale',
|
||||
finalOrigin: 'https://redirected.example',
|
||||
kind: 'origin-approval-required',
|
||||
requestId: 'request-origin-stale',
|
||||
sourceOrigin: 'https://portal.example',
|
||||
});
|
||||
const continuation = deferred<StalkerSessionConnectionOutcome>();
|
||||
session.continue.mockReturnValueOnce(continuation.promise);
|
||||
await component.addPlaylist();
|
||||
|
||||
const approval = component.respondToOriginApproval(true);
|
||||
await Promise.resolve();
|
||||
component.clearForm();
|
||||
session.discard.mockClear();
|
||||
continuation.resolve({
|
||||
attemptNumber: 1,
|
||||
attemptRef: 'attempt-origin-stale',
|
||||
challengeRef: 'challenge-credentials-stale',
|
||||
kind: 'credentials-required',
|
||||
requestId: 'request-credentials-stale',
|
||||
});
|
||||
await approval;
|
||||
|
||||
expect(session.discard).toHaveBeenCalledWith('attempt-origin-stale');
|
||||
expect(component.connectionStage()).toBe('idle');
|
||||
});
|
||||
|
||||
it('discards a ready attempt returned after destruction during credential validation', async () => {
|
||||
session.open.mockResolvedValueOnce({
|
||||
attemptNumber: 1,
|
||||
attemptRef: 'attempt-credentials-stale',
|
||||
challengeRef: 'challenge-credentials-stale',
|
||||
kind: 'credentials-required',
|
||||
requestId: 'request-credentials-stale',
|
||||
});
|
||||
const continuation = deferred<StalkerSessionConnectionOutcome>();
|
||||
session.continue.mockReturnValueOnce(continuation.promise);
|
||||
await component.addPlaylist();
|
||||
component.form.patchValue({
|
||||
password: 'entered-password',
|
||||
username: 'entered-user',
|
||||
});
|
||||
|
||||
const submit = component.submitCredentials();
|
||||
await Promise.resolve();
|
||||
component.ngOnDestroy();
|
||||
session.discard.mockClear();
|
||||
continuation.resolve({
|
||||
...READY_OUTCOME,
|
||||
attemptRef: 'attempt-credentials-stale',
|
||||
});
|
||||
await submit;
|
||||
|
||||
expect(session.discard).toHaveBeenCalledWith(
|
||||
'attempt-credentials-stale'
|
||||
);
|
||||
expect(playlists.persistStalkerConnection).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
function setValidForm(): void {
|
||||
component.form.patchValue({
|
||||
_id: 'playlist-1',
|
||||
|
||||
+16
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable max-lines -- the challenge-driven import state machine is kept in one lifecycle owner */
|
||||
import {
|
||||
Component,
|
||||
OnDestroy,
|
||||
@@ -155,6 +156,7 @@ export class StalkerPortalImportComponent implements OnDestroy {
|
||||
provisionalReason: 'import',
|
||||
});
|
||||
if (runId !== this.runId) {
|
||||
await this.discardStaleOutcome(outcome);
|
||||
return;
|
||||
}
|
||||
await this.handleOutcome(outcome, runId);
|
||||
@@ -186,6 +188,8 @@ export class StalkerPortalImportComponent implements OnDestroy {
|
||||
});
|
||||
if (runId === this.runId) {
|
||||
await this.handleOutcome(outcome, runId);
|
||||
} else {
|
||||
await this.discardStaleOutcome(outcome);
|
||||
}
|
||||
} catch {
|
||||
if (runId === this.runId) {
|
||||
@@ -242,6 +246,8 @@ export class StalkerPortalImportComponent implements OnDestroy {
|
||||
});
|
||||
if (runId === this.runId) {
|
||||
await this.handleOutcome(outcome, runId);
|
||||
} else {
|
||||
await this.discardStaleOutcome(outcome);
|
||||
}
|
||||
} catch {
|
||||
if (runId === this.runId) {
|
||||
@@ -376,6 +382,16 @@ export class StalkerPortalImportComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private async discardStaleOutcome(
|
||||
outcome: StalkerSessionConnectionOutcome
|
||||
): Promise<void> {
|
||||
const attemptRef =
|
||||
'attemptRef' in outcome ? outcome.attemptRef : undefined;
|
||||
if (attemptRef !== undefined) {
|
||||
await this.session.discard(attemptRef).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
|
||||
private fail(reason: StalkerSessionFailureReason): void {
|
||||
this.failureReason.set(reason);
|
||||
this.connectionStage.set('failure');
|
||||
|
||||
Reference in new issue
Block a user