mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 20:56:19 -08:00
fix(stalker): serialize overlapping edits
This commit is contained in:
1 parent
a7ccdc905b
commit
7c8b94a9ed
7 files changed
+43
-7
No files matched your search
+7
@@ -51,6 +51,7 @@ describe('PlaylistInfoComponent', () => {
|
||||
};
|
||||
let dialogRef: {
|
||||
close: jest.Mock;
|
||||
disableClose: boolean;
|
||||
};
|
||||
let stalkerConnectionEditor: {
|
||||
resolveConnection: jest.Mock;
|
||||
@@ -104,6 +105,7 @@ describe('PlaylistInfoComponent', () => {
|
||||
};
|
||||
dialogRef = {
|
||||
close: jest.fn(),
|
||||
disableClose: false,
|
||||
};
|
||||
stalkerConnectionEditor = {
|
||||
applyResolvedConnection: jest.fn().mockResolvedValue(undefined),
|
||||
@@ -852,6 +854,7 @@ describe('PlaylistInfoComponent', () => {
|
||||
const secondSave = component.saveChanges(value);
|
||||
|
||||
expect(component.isSaving()).toBe(true);
|
||||
expect(dialogRef.disableClose).toBe(true);
|
||||
expect(
|
||||
stalkerConnectionEditor.resolveConnection
|
||||
).toHaveBeenCalledTimes(1);
|
||||
@@ -861,6 +864,7 @@ describe('PlaylistInfoComponent', () => {
|
||||
});
|
||||
await Promise.all([firstSave, secondSave]);
|
||||
expect(store.dispatch).not.toHaveBeenCalled();
|
||||
expect(dialogRef.disableClose).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts a bare HTTP host but rejects an address without a protocol', () => {
|
||||
@@ -872,6 +876,9 @@ describe('PlaylistInfoComponent', () => {
|
||||
|
||||
control?.setValue('https://portal.example.com');
|
||||
expect(control?.valid).toBe(true);
|
||||
|
||||
control?.setValue('HTTP://portal.example.com/c');
|
||||
expect(control?.valid).toBe(true);
|
||||
});
|
||||
|
||||
it('persists a grandfathered MAC untouched on submit', async () => {
|
||||
|
||||
+6
@@ -365,6 +365,9 @@ export class PlaylistInfoComponent {
|
||||
}
|
||||
|
||||
this.isSaving.set(true);
|
||||
if (this.dialogRef) {
|
||||
this.dialogRef.disableClose = true;
|
||||
}
|
||||
try {
|
||||
let resolvedStalkerConnection = false;
|
||||
let normalizedPlaylist: PlaylistMetaUpdate =
|
||||
@@ -446,6 +449,9 @@ export class PlaylistInfoComponent {
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
if (this.dialogRef) {
|
||||
this.dialogRef.disableClose = false;
|
||||
}
|
||||
this.isSaving.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import {
|
||||
type PlaylistMeta,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
|
||||
export const STALKER_PORTAL_URL_PATTERN = /^\s*https?:\/\/[^ "\s]+\s*$/;
|
||||
export const STALKER_PORTAL_URL_PATTERN = /^\s*https?:\/\/[^ "\s]+\s*$/i;
|
||||
|
||||
const STALKER_CONNECTION_FIELDS = [
|
||||
'portalUrl',
|
||||
|
||||
@@ -129,6 +129,24 @@ describe('Stalker edited-session coordination', () => {
|
||||
service.cancelEditDiscovery(fence);
|
||||
});
|
||||
|
||||
it('rejects an overlapping edit without retiring the first owner', async () => {
|
||||
const firstFence = await service.beginEditDiscovery(oldPlaylist);
|
||||
|
||||
await expect(
|
||||
service.beginEditDiscovery({
|
||||
...oldPlaylist,
|
||||
username: 'second-edit',
|
||||
})
|
||||
).rejects.toThrow(/already in progress/i);
|
||||
|
||||
await expect(
|
||||
service.replaceSessionAfterEdit(
|
||||
{ ...oldPlaylist, stalkerToken: 'FIRST_EDIT_TOKEN' },
|
||||
firstFence
|
||||
)
|
||||
).resolves.toEqual(oldPlaylist);
|
||||
});
|
||||
|
||||
it('prevents late pre-edit auth from restoring a cleared simple session', async () => {
|
||||
const oldAuthentication = service.ensureToken(oldPlaylist);
|
||||
while (authenticate.mock.calls.length === 0) {
|
||||
|
||||
@@ -69,6 +69,9 @@ export class StalkerEditedSessionCoordinator {
|
||||
|
||||
async beginEdit(playlist: Playlist): Promise<StalkerEditFence> {
|
||||
const playlistId = playlist._id;
|
||||
if (this.pendingEdits.has(playlistId)) {
|
||||
throw new Error('Stalker playlist edit already in progress');
|
||||
}
|
||||
const fingerprint = stalkerSessionFingerprint(playlist);
|
||||
const fence = { playlistId, owner: Symbol('stalker-edit') };
|
||||
this.pendingEdits.set(playlistId, {
|
||||
|
||||
Reference in new issue
Block a user