Skip to content

Commit

Permalink
fix(hub connection): concurrent connect() remain in invalid state c…
Browse files Browse the repository at this point in the history
…ausing connecting/disconnecting (#68)
  • Loading branch information
stephenlautier authored Sep 12, 2023
1 parent ce7ec55 commit f8a1380
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
- **hub connection:** expose `HubConnection.connectionId`
- **deps:** export `DesiredConnectionStatus`

### Bug Fixes

- **hub connection:** concurrent `connect()` remain in invalid state causing connecting/disconnecting

### BREAKING CHANGES

- **deps:** changed `@microsoft/signalr: ^7.0.0` as `peerDependency`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"build": "nx build",
"lint": "nx lint",
"test": "nx test",
"test-ui": "vitest --ui",
"tdd": "vitest --ui",
"__CI__": "",
"_pre-release-build": "echo hook for pre-release-build",
"release": "npm publish ./dist --access=public"
Expand Down
45 changes: 35 additions & 10 deletions src/lib/hub-connection.connection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,40 @@ describe("HubConnection Specs", () => {

describe("and connected successfully", () => {

it("should have status as connected", () => new Promise<void>(done => {
conn$$ = SUT.connect().pipe(
withLatestFrom(SUT.connectionState$, (_x, y) => y),
).subscribe({
next: state => expect(state.status).toBe(ConnectionStatus.connected),
complete: done
});
// todo: convert to promise
}));
it("should have status as connected", async () => {
await lastValueFrom(SUT.connect());

const state = await lastValueFrom(SUT.connectionState$.pipe(
delay(20),
first(),
));

expect(state.status).toBe(ConnectionStatus.connected);
});

});

describe("and invoked again concurrently", () => {

beforeEach(() => {
hubBackend.connection.start = vi.fn().mockReturnValue(promiseDelayResolve(5));
hubBackend.connection.stop = vi.fn().mockReturnValue(promiseDelayResolve(5));
});

it("should connect once", async () => {
const c1$ = lastValueFrom(SUT.connect());
const c2$ = lastValueFrom(SUT.connect());

await Promise.all([c1$, c2$]);

const state = await lastValueFrom(SUT.connectionState$.pipe(
delay(20),
first(),
));

expect(state.status).toBe(ConnectionStatus.connected);
expect(hubBackend.connection.start).toHaveBeenCalledTimes(1);
});

});

Expand Down Expand Up @@ -217,7 +242,7 @@ describe("HubConnection Specs", () => {

describe("when disconnect is invoked", () => {

it("should have status as disconnected", () => {
it("should have status as disconnected", () => {
const test$ = SUT.disconnect().pipe(
tap(() => hubBackend.disconnect()),
withLatestFrom(SUT.connectionState$, (_x, y) => y),
Expand Down
4 changes: 2 additions & 2 deletions src/lib/hub-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ export class HubConnection<THub> {
connect(data?: () => Dictionary<string>): Observable<void> {
// console.warn("[connect] init", data);
this.desiredState$.next(DesiredConnectionStatus.connected);
if (this.internalConnStatus$.value === InternalConnectionStatus.connected) {
console.warn(`${this.source} session already connected`);
if (this._connectionState$.value.status !== ConnectionStatus.disconnected) {
console.warn(`${this.source} session already connecting/connected`);
return emptyNext();
}
if (data) {
Expand Down

0 comments on commit f8a1380

Please sign in to comment.