Skip to content

Commit 61eda62

Browse files
authored
ref: Leave only valid buffer implementation (#3744)
1 parent 1566d01 commit 61eda62

File tree

2 files changed

+76
-102
lines changed

2 files changed

+76
-102
lines changed

packages/utils/src/promisebuffer.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { SentryError } from './error';
2-
import { isThenable } from './is';
32
import { SyncPromise } from './syncpromise';
43

5-
type TaskProducer<T> = () => PromiseLike<T>;
6-
74
/** A simple queue that holds promises. */
85
export class PromiseBuffer<T> {
96
/** Internal set of queued Promises */
@@ -21,22 +18,15 @@ export class PromiseBuffer<T> {
2118
/**
2219
* Add a promise to the queue.
2320
*
24-
* @param taskProducer A function producing any PromiseLike<T>; In previous versions this used to be `@param task: PromiseLike<T>`, however, Promises were instantly created on the call-site, making them fall through the buffer limit.
21+
* @param taskProducer A function producing any PromiseLike<T>; In previous versions this used to be `task: PromiseLike<T>`,
22+
* however, Promises were instantly created on the call-site, making them fall through the buffer limit.
2523
* @returns The original promise.
2624
*/
27-
public add(taskProducer: PromiseLike<T> | TaskProducer<T>): PromiseLike<T> {
28-
// NOTE: This is necessary to preserve backwards compatibility
29-
// It should accept _only_ `TaskProducer<T>` but we dont want to break other custom transports
30-
// that are utilizing our `Buffer` implementation.
31-
// see: https://github.com/getsentry/sentry-javascript/issues/3725
32-
const normalizedTaskProducer: TaskProducer<T> = isThenable(taskProducer)
33-
? () => taskProducer as PromiseLike<T>
34-
: (taskProducer as TaskProducer<T>);
35-
25+
public add(taskProducer: () => PromiseLike<T>): PromiseLike<T> {
3626
if (!this.isReady()) {
3727
return SyncPromise.reject(new SentryError('Not adding Promise due to buffer limit reached.'));
3828
}
39-
const task = normalizedTaskProducer();
29+
const task = taskProducer();
4030
if (this._buffer.indexOf(task) === -1) {
4131
this._buffer.push(task);
4232
}
Lines changed: 72 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,101 @@
1-
/* eslint-disable @typescript-eslint/no-floating-promises */
21
import { PromiseBuffer } from '../src/promisebuffer';
32
import { SyncPromise } from '../src/syncpromise';
43

54
describe('PromiseBuffer', () => {
6-
beforeEach(() => {
7-
jest.useFakeTimers();
8-
});
9-
105
describe('add()', () => {
116
test('no limit', () => {
12-
const q = new PromiseBuffer<void>();
13-
const p = jest.fn(
14-
() => new SyncPromise<void>(resolve => setTimeout(resolve, 1)),
15-
);
16-
q.add(p);
17-
expect(q.length()).toBe(1);
7+
const buffer = new PromiseBuffer();
8+
const p = jest.fn(() => new SyncPromise(resolve => setTimeout(resolve)));
9+
void buffer.add(p);
10+
expect(buffer.length()).toEqual(1);
1811
});
1912

2013
test('with limit', () => {
21-
const q = new PromiseBuffer<void>(1);
22-
let t1;
23-
const p1 = jest.fn(() => {
24-
t1 = new SyncPromise<void>(resolve => setTimeout(resolve, 1));
25-
return t1;
14+
const buffer = new PromiseBuffer(1);
15+
let task1;
16+
const producer1 = jest.fn(() => {
17+
task1 = new SyncPromise(resolve => setTimeout(resolve));
18+
return task1;
2619
});
27-
const p2 = jest.fn(
28-
() => new SyncPromise<void>(resolve => setTimeout(resolve, 1)),
29-
);
30-
expect(q.add(p1)).toEqual(t1);
31-
expect(q.add(p2)).rejects.toThrowError();
32-
expect(q.length()).toBe(1);
33-
expect(p1).toHaveBeenCalled();
34-
expect(p2).not.toHaveBeenCalled();
20+
const producer2 = jest.fn(() => new SyncPromise(resolve => setTimeout(resolve)));
21+
expect(buffer.add(producer1)).toEqual(task1);
22+
void expect(buffer.add(producer2)).rejects.toThrowError();
23+
expect(buffer.length()).toEqual(1);
24+
expect(producer1).toHaveBeenCalled();
25+
expect(producer2).not.toHaveBeenCalled();
3526
});
3627
});
3728

38-
test('resolved promises should not show up in buffer length', async () => {
39-
expect.assertions(2);
40-
const q = new PromiseBuffer<void>();
41-
const p = new SyncPromise<void>(resolve => setTimeout(resolve, 1));
42-
q.add(p).then(() => {
43-
expect(q.length()).toBe(0);
29+
describe('drain()', () => {
30+
test('without timeout', async () => {
31+
const buffer = new PromiseBuffer();
32+
for (let i = 0; i < 5; i++) {
33+
void buffer.add(() => new SyncPromise(resolve => setTimeout(resolve)));
34+
}
35+
expect(buffer.length()).toEqual(5);
36+
const result = await buffer.drain();
37+
expect(result).toEqual(true);
38+
expect(buffer.length()).toEqual(0);
4439
});
45-
expect(q.length()).toBe(1);
46-
jest.runAllTimers();
47-
});
4840

49-
test('receive promise result outside and from buffer', async () => {
50-
expect.assertions(4);
51-
const q = new PromiseBuffer<string>();
52-
const p = new SyncPromise<string>(resolve =>
53-
setTimeout(() => {
54-
resolve('test');
55-
}, 1),
56-
);
57-
q.add(p).then(result => {
58-
expect(q.length()).toBe(0);
59-
expect(result).toBe('test');
41+
test('with timeout', async () => {
42+
const buffer = new PromiseBuffer();
43+
for (let i = 0; i < 5; i++) {
44+
void buffer.add(() => new SyncPromise(resolve => setTimeout(resolve, 100)));
45+
}
46+
expect(buffer.length()).toEqual(5);
47+
const result = await buffer.drain(50);
48+
expect(result).toEqual(false);
6049
});
61-
expect(q.length()).toBe(1);
62-
p.then(result => {
63-
expect(result).toBe('test');
50+
51+
test('on empty buffer', async () => {
52+
const buffer = new PromiseBuffer();
53+
expect(buffer.length()).toEqual(0);
54+
const result = await buffer.drain();
55+
expect(result).toEqual(true);
56+
expect(buffer.length()).toEqual(0);
6457
});
65-
jest.runAllTimers();
6658
});
6759

68-
test('drain()', async () => {
69-
expect.assertions(3);
70-
const q = new PromiseBuffer<void>();
71-
for (let i = 0; i < 5; i++) {
72-
const p = new SyncPromise<void>(resolve => setTimeout(resolve, 1));
73-
q.add(p);
74-
}
75-
expect(q.length()).toBe(5);
76-
q.drain().then(result => {
77-
expect(result).toBeTruthy();
78-
expect(q.length()).toBe(0);
79-
});
80-
jest.runAllTimers();
60+
test('resolved promises should not show up in buffer length', async () => {
61+
const buffer = new PromiseBuffer();
62+
const producer = () => new SyncPromise(resolve => setTimeout(resolve));
63+
const task = buffer.add(producer);
64+
expect(buffer.length()).toEqual(1);
65+
await task;
66+
expect(buffer.length()).toEqual(0);
8167
});
8268

83-
test('drain() with timeout', async () => {
84-
expect.assertions(2);
85-
const q = new PromiseBuffer<void>();
86-
for (let i = 0; i < 5; i++) {
87-
const p = new SyncPromise<void>(resolve => setTimeout(resolve, 100));
88-
q.add(p);
69+
test('rejected promises should not show up in buffer length', async () => {
70+
const buffer = new PromiseBuffer();
71+
const producer = () => new SyncPromise((_, reject) => setTimeout(reject));
72+
const task = buffer.add(producer);
73+
expect(buffer.length()).toEqual(1);
74+
try {
75+
await task;
76+
} catch (_) {
77+
// no-empty
8978
}
90-
expect(q.length()).toBe(5);
91-
q.drain(50).then(result => {
92-
expect(result).toBeFalsy();
93-
});
94-
jest.runAllTimers();
79+
expect(buffer.length()).toEqual(0);
9580
});
9681

97-
test('drain() on empty buffer', async () => {
98-
expect.assertions(3);
99-
const q = new PromiseBuffer<void>();
100-
expect(q.length()).toBe(0);
101-
q.drain().then(result => {
102-
expect(result).toBeTruthy();
103-
expect(q.length()).toBe(0);
104-
});
105-
jest.runAllTimers();
82+
test('resolved task should give an access to the return value', async () => {
83+
const buffer = new PromiseBuffer<string>();
84+
const producer = () => new SyncPromise<string>(resolve => setTimeout(() => resolve('test')));
85+
const task = buffer.add(producer);
86+
const result = await task;
87+
expect(result).toEqual('test');
10688
});
10789

108-
test('rejecting', async () => {
90+
test('rejected task should give an access to the return value', async () => {
10991
expect.assertions(1);
110-
const q = new PromiseBuffer<void>();
111-
const p = new SyncPromise<void>((_, reject) => setTimeout(reject, 1));
112-
jest.runAllTimers();
113-
return q.add(p).then(null, () => {
114-
expect(true).toBe(true);
115-
});
92+
const buffer = new PromiseBuffer<string>();
93+
const producer = () => new SyncPromise<string>((_, reject) => setTimeout(() => reject(new Error('whoops'))));
94+
const task = buffer.add(producer);
95+
try {
96+
await task;
97+
} catch (e) {
98+
expect(e).toEqual(new Error('whoops'));
99+
}
116100
});
117101
});

0 commit comments

Comments
 (0)