fix: Ensure streaming HTTP streams the whole notification in a single chunk

This commit is contained in:
elf Pavlik
2024-08-02 15:50:07 -06:00
committed by Joachim Van Herwegen
parent 576eefede6
commit 3dd8602acc
4 changed files with 73 additions and 22 deletions

View File

@@ -5,7 +5,7 @@ import {
StreamingHttp2023Emitter,
} from '../../../../../src/server/notifications/StreamingHttpChannel2023/StreamingHttp2023Emitter';
import { WrappedSetMultiMap } from '../../../../../src/util/map/WrappedSetMultiMap';
import type { StreamingHttpMap } from '../../../../../src';
import type { Representation, StreamingHttpMap } from '../../../../../src';
describe('A StreamingHttp2023Emitter', (): void => {
const channel: NotificationChannel = {
@@ -13,32 +13,31 @@ describe('A StreamingHttp2023Emitter', (): void => {
topic: 'http://example.com/foo',
type: 'type',
};
const chunk = 'notification';
let stream: jest.Mocked<PassThrough>;
let streamMap: StreamingHttpMap;
let emitter: StreamingHttp2023Emitter;
let representation: BasicRepresentation;
beforeEach(async(): Promise<void> => {
stream = jest.mocked(new PassThrough());
streamMap = new WrappedSetMultiMap();
emitter = new StreamingHttp2023Emitter(streamMap);
representation = new BasicRepresentation(chunk, 'text/plain');
});
it('emits notifications to the stored Streams.', async(): Promise<void> => {
streamMap.add(channel.topic, stream);
const representation = new BasicRepresentation('notification', 'text/plain');
const spy = jest.spyOn(representation.data, 'pipe');
const spy = jest.spyOn(stream, 'write');
await expect(emitter.handle({ channel, representation })).resolves.toBeUndefined();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenLastCalledWith(stream, { end: false });
expect(spy).toHaveBeenLastCalledWith(chunk);
});
it('destroys the representation if there is no matching Stream.', async(): Promise<void> => {
const representation = new BasicRepresentation('notification', 'text/plain');
const spy = jest.spyOn(representation.data, 'pipe');
const spy = jest.spyOn(stream, 'write');
await expect(emitter.handle({ channel, representation })).resolves.toBeUndefined();
expect(spy).toHaveBeenCalledTimes(0);
expect(representation.data.destroyed).toBe(true);
@@ -50,12 +49,13 @@ describe('A StreamingHttp2023Emitter', (): void => {
streamMap.add(channel.topic, stream);
streamMap.add(channel.topic, stream2);
const representation = new BasicRepresentation('notification', 'text/plain');
const spy = jest.spyOn(representation.data, 'pipe');
const spy = jest.spyOn(stream, 'write');
const spy2 = jest.spyOn(stream2, 'write');
await expect(emitter.handle({ channel, representation })).resolves.toBeUndefined();
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledWith(stream, { end: false });
expect(spy).toHaveBeenLastCalledWith(stream2, { end: false });
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(chunk);
expect(spy2).toHaveBeenCalledTimes(1);
expect(spy2).toHaveBeenCalledWith(chunk);
});
it('only writes to the matching topic Streams.', async(): Promise<void> => {
@@ -69,10 +69,27 @@ describe('A StreamingHttp2023Emitter', (): void => {
streamMap.add(channel.topic, stream);
streamMap.add(channel2.topic, stream2);
const representation = new BasicRepresentation('notification', 'text/plain');
const spy = jest.spyOn(representation.data, 'pipe');
const spy = jest.spyOn(stream, 'write');
const spy2 = jest.spyOn(stream2, 'write');
await expect(emitter.handle({ channel, representation })).resolves.toBeUndefined();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenLastCalledWith(stream, { end: false });
expect(spy).toHaveBeenLastCalledWith(chunk);
expect(spy2).not.toHaveBeenCalled();
});
it('emits notifications in a single chunk.', async(): Promise<void> => {
streamMap.add(channel.topic, stream);
const serializationStream = new PassThrough();
// Use two chunks for the serialization stream
serializationStream.write('foo');
serializationStream.end('bar');
representation = {
data: serializationStream,
} as unknown as Representation;
const spy = jest.spyOn(stream, 'write');
await expect(emitter.handle({ channel, representation })).resolves.toBeUndefined();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenLastCalledWith('foobar');
});
});