@furystack/websocket-api 12.0.0 → 12.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/websocket-api.spec.js +7 -1
- package/esm/websocket-api.spec.js.map +1 -1
- package/esm/websocket-integration.spec.js +53 -13
- package/esm/websocket-integration.spec.js.map +1 -1
- package/package.json +7 -7
- package/src/websocket-api.spec.ts +18 -1
- package/src/websocket-integration.spec.ts +66 -14
|
@@ -7,19 +7,23 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { Injector, Injectable } from '@furystack/inject';
|
|
8
8
|
import { usingAsync } from '@furystack/utils';
|
|
9
9
|
import { WebSocketApi } from './websocket-api.js';
|
|
10
|
-
import WebSocket from 'ws';
|
|
10
|
+
import { WebSocket } from 'ws';
|
|
11
11
|
import { useWebsockets } from './helpers.js';
|
|
12
12
|
import { describe, it, expect } from 'vitest';
|
|
13
13
|
import { getPort } from '@furystack/core/port-generator';
|
|
14
|
+
import { InMemoryStore, User, addStore } from '@furystack/core';
|
|
15
|
+
import { DefaultSession } from '@furystack/rest-service';
|
|
14
16
|
describe('WebSocketApi', () => {
|
|
15
17
|
it('Should be built', async () => {
|
|
16
18
|
await usingAsync(new Injector(), async (i) => {
|
|
19
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
17
20
|
useWebsockets(i, { port: getPort() });
|
|
18
21
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi);
|
|
19
22
|
});
|
|
20
23
|
});
|
|
21
24
|
it('Should be built with settings', async () => {
|
|
22
25
|
await usingAsync(new Injector(), async (i) => {
|
|
26
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
23
27
|
useWebsockets(i, { path: '/web-socket', port: getPort() });
|
|
24
28
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi);
|
|
25
29
|
});
|
|
@@ -27,6 +31,7 @@ describe('WebSocketApi', () => {
|
|
|
27
31
|
it('Should broadcast messages', async () => {
|
|
28
32
|
const port = getPort();
|
|
29
33
|
await usingAsync(new Injector(), async (i) => {
|
|
34
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
30
35
|
expect.assertions(5); // All 5 clients should receive the message
|
|
31
36
|
useWebsockets(i, { path: '/web-socket', port });
|
|
32
37
|
const api = i.getInstance(WebSocketApi);
|
|
@@ -49,6 +54,7 @@ describe('WebSocketApi', () => {
|
|
|
49
54
|
it('Should receive client messages', async () => {
|
|
50
55
|
const port = getPort();
|
|
51
56
|
await usingAsync(new Injector(), async (i) => {
|
|
57
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
52
58
|
expect.assertions(1);
|
|
53
59
|
const data = { value: 'alma' };
|
|
54
60
|
let ExampleWsAction = class ExampleWsAction {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-api.spec.js","sourceRoot":"","sources":["../src/websocket-api.spec.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,SAAS,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"websocket-api.spec.js","sourceRoot":"","sources":["../src/websocket-api.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { Injector } from '@furystack/inject';
|
|
2
2
|
import { WhoAmI } from './actions/whoami.js';
|
|
3
|
-
import
|
|
4
|
-
import { addStore, InMemoryStore, User } from '@furystack/core';
|
|
5
|
-
import { DefaultSession, ServerManager, useHttpAuthentication } from '@furystack/rest-service';
|
|
3
|
+
import { WebSocket } from 'ws';
|
|
4
|
+
import { addStore, InMemoryStore, StoreManager, User } from '@furystack/core';
|
|
5
|
+
import { DefaultSession, HttpUserContext, ServerManager, useHttpAuthentication } from '@furystack/rest-service';
|
|
6
6
|
import { useRestService } from '@furystack/rest-service';
|
|
7
7
|
import { useWebsockets } from './helpers.js';
|
|
8
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
9
9
|
import { getPort } from '@furystack/core/port-generator';
|
|
10
10
|
describe('WebSocket Integration tests', () => {
|
|
11
11
|
const host = 'localhost';
|
|
12
12
|
const path = '/ws';
|
|
13
13
|
let i;
|
|
14
14
|
let client;
|
|
15
|
+
let port;
|
|
15
16
|
beforeEach(async () => {
|
|
16
17
|
i = new Injector();
|
|
17
|
-
|
|
18
|
+
port = getPort();
|
|
18
19
|
useRestService({
|
|
19
20
|
injector: i,
|
|
20
21
|
api: {},
|
|
@@ -29,7 +30,7 @@ describe('WebSocket Integration tests', () => {
|
|
|
29
30
|
i.getInstance(ServerManager)
|
|
30
31
|
.getOrCreate({ port })
|
|
31
32
|
.then(() => {
|
|
32
|
-
client = new
|
|
33
|
+
client = new WebSocket(`ws://${host}:${port}/ws`);
|
|
33
34
|
client
|
|
34
35
|
.on('open', () => {
|
|
35
36
|
done();
|
|
@@ -41,15 +42,54 @@ describe('WebSocket Integration tests', () => {
|
|
|
41
42
|
afterEach(async () => {
|
|
42
43
|
await i.dispose();
|
|
43
44
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
client.close();
|
|
49
|
-
done();
|
|
45
|
+
const getWhoAmIResult = async (subjectClient) => {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
subjectClient.once('message', (data) => {
|
|
48
|
+
resolve(JSON.parse(data.toString()));
|
|
50
49
|
});
|
|
51
|
-
|
|
50
|
+
subjectClient.once('error', reject);
|
|
51
|
+
subjectClient.send('whoami');
|
|
52
52
|
});
|
|
53
|
+
};
|
|
54
|
+
describe('Authentication', () => {
|
|
55
|
+
it('Should be unauthenticated by default', async () => {
|
|
56
|
+
expect((await getWhoAmIResult(client)).currentUser).toBe(null);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
it('Should be authenticated, roles should be updated and should be logged out', async () => {
|
|
60
|
+
const testUser = { username: 'test', password: 'test', roles: [] };
|
|
61
|
+
const userStore = i.getInstance(StoreManager).getStoreFor(User, 'username');
|
|
62
|
+
userStore.add(testUser);
|
|
63
|
+
const userCtx = i.getInstance(HttpUserContext);
|
|
64
|
+
let cookie = '';
|
|
65
|
+
await userCtx.cookieLogin(testUser, {
|
|
66
|
+
setHeader: (_setCookie, cookieValue) => {
|
|
67
|
+
cookie = cookieValue;
|
|
68
|
+
return {};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const authenticatedClient = await new Promise((done, reject) => {
|
|
72
|
+
const cl = new WebSocket(`ws://${host}:${port}/ws`, {
|
|
73
|
+
headers: { cookie },
|
|
74
|
+
});
|
|
75
|
+
cl.once('open', () => {
|
|
76
|
+
done(cl);
|
|
77
|
+
}).once('error', reject);
|
|
78
|
+
});
|
|
79
|
+
const whoAmIResult = await getWhoAmIResult(authenticatedClient);
|
|
80
|
+
expect(whoAmIResult.currentUser).toEqual(testUser);
|
|
81
|
+
userStore.update(testUser.username, { ...testUser, roles: ['newFancyRole'] });
|
|
82
|
+
const updatedWhoAmIResult = await getWhoAmIResult(authenticatedClient);
|
|
83
|
+
expect(updatedWhoAmIResult.currentUser.roles).toEqual(['newFancyRole']);
|
|
84
|
+
await userCtx.cookieLogout({
|
|
85
|
+
headers: {
|
|
86
|
+
cookie,
|
|
87
|
+
},
|
|
88
|
+
}, {
|
|
89
|
+
setHeader: vi.fn(),
|
|
90
|
+
});
|
|
91
|
+
const loggedOutWhoAmIResult = await getWhoAmIResult(authenticatedClient);
|
|
92
|
+
expect(loggedOutWhoAmIResult.currentUser).toBe(null);
|
|
53
93
|
});
|
|
54
94
|
});
|
|
55
95
|
//# sourceMappingURL=websocket-integration.spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket-integration.spec.js","sourceRoot":"","sources":["../src/websocket-integration.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"websocket-integration.spec.js","sourceRoot":"","sources":["../src/websocket-integration.spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furystack/websocket-api",
|
|
3
|
-
"version": "12.0.
|
|
3
|
+
"version": "12.0.1",
|
|
4
4
|
"description": "HTTP Api FuryStack package",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/furystack/furystack",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@furystack/core": "^14.0.
|
|
38
|
-
"@furystack/inject": "^11.0.
|
|
39
|
-
"@furystack/rest-service": "^9.0.
|
|
40
|
-
"@furystack/utils": "^
|
|
37
|
+
"@furystack/core": "^14.0.2",
|
|
38
|
+
"@furystack/inject": "^11.0.1",
|
|
39
|
+
"@furystack/rest-service": "^9.0.2",
|
|
40
|
+
"@furystack/utils": "^7.0.0",
|
|
41
41
|
"ws": "^8.16.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/ws": "^8.5.10",
|
|
45
|
-
"typescript": "^5.4.
|
|
46
|
-
"vitest": "^1.
|
|
45
|
+
"typescript": "^5.4.5",
|
|
46
|
+
"vitest": "^1.5.0"
|
|
47
47
|
},
|
|
48
48
|
"gitHead": "1045d854bfd8c475b7035471d130d401417a2321"
|
|
49
49
|
}
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import { Injector, Injectable } from '@furystack/inject'
|
|
2
2
|
import { usingAsync } from '@furystack/utils'
|
|
3
3
|
import { WebSocketApi } from './websocket-api.js'
|
|
4
|
-
import WebSocket from 'ws'
|
|
4
|
+
import { WebSocket } from 'ws'
|
|
5
5
|
import type { WebSocketAction } from './models/websocket-action.js'
|
|
6
6
|
import { useWebsockets } from './helpers.js'
|
|
7
7
|
import { describe, it, expect } from 'vitest'
|
|
8
8
|
import { getPort } from '@furystack/core/port-generator'
|
|
9
|
+
import { InMemoryStore, User, addStore } from '@furystack/core'
|
|
10
|
+
import { DefaultSession } from '@furystack/rest-service'
|
|
9
11
|
|
|
10
12
|
describe('WebSocketApi', () => {
|
|
11
13
|
it('Should be built', async () => {
|
|
12
14
|
await usingAsync(new Injector(), async (i) => {
|
|
15
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
|
|
16
|
+
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
17
|
+
)
|
|
13
18
|
useWebsockets(i, { port: getPort() })
|
|
14
19
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi)
|
|
15
20
|
})
|
|
16
21
|
})
|
|
17
22
|
it('Should be built with settings', async () => {
|
|
18
23
|
await usingAsync(new Injector(), async (i) => {
|
|
24
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
|
|
25
|
+
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
26
|
+
)
|
|
27
|
+
|
|
19
28
|
useWebsockets(i, { path: '/web-socket', port: getPort() })
|
|
20
29
|
expect(i.getInstance(WebSocketApi)).toBeInstanceOf(WebSocketApi)
|
|
21
30
|
})
|
|
@@ -24,6 +33,10 @@ describe('WebSocketApi', () => {
|
|
|
24
33
|
it('Should broadcast messages', async () => {
|
|
25
34
|
const port = getPort()
|
|
26
35
|
await usingAsync(new Injector(), async (i) => {
|
|
36
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
|
|
37
|
+
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
38
|
+
)
|
|
39
|
+
|
|
27
40
|
expect.assertions(5) // All 5 clients should receive the message
|
|
28
41
|
useWebsockets(i, { path: '/web-socket', port })
|
|
29
42
|
const api = i.getInstance(WebSocketApi)
|
|
@@ -51,6 +64,10 @@ describe('WebSocketApi', () => {
|
|
|
51
64
|
it('Should receive client messages', async () => {
|
|
52
65
|
const port = getPort()
|
|
53
66
|
await usingAsync(new Injector(), async (i) => {
|
|
67
|
+
addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
|
|
68
|
+
new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }),
|
|
69
|
+
)
|
|
70
|
+
|
|
54
71
|
expect.assertions(1)
|
|
55
72
|
const data = { value: 'alma' }
|
|
56
73
|
@Injectable()
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { Injector } from '@furystack/inject'
|
|
2
2
|
import { WhoAmI } from './actions/whoami.js'
|
|
3
|
-
import
|
|
4
|
-
import { addStore, InMemoryStore, User } from '@furystack/core'
|
|
5
|
-
import { DefaultSession, ServerManager, useHttpAuthentication } from '@furystack/rest-service'
|
|
3
|
+
import { WebSocket } from 'ws'
|
|
4
|
+
import { addStore, InMemoryStore, StoreManager, User } from '@furystack/core'
|
|
5
|
+
import { DefaultSession, HttpUserContext, ServerManager, useHttpAuthentication } from '@furystack/rest-service'
|
|
6
6
|
import { useRestService } from '@furystack/rest-service'
|
|
7
7
|
import { useWebsockets } from './helpers.js'
|
|
8
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
9
9
|
import { getPort } from '@furystack/core/port-generator'
|
|
10
10
|
|
|
11
11
|
describe('WebSocket Integration tests', () => {
|
|
12
12
|
const host = 'localhost'
|
|
13
13
|
const path = '/ws'
|
|
14
14
|
let i!: Injector
|
|
15
|
-
let client:
|
|
15
|
+
let client: WebSocket
|
|
16
|
+
let port: number
|
|
16
17
|
|
|
17
18
|
beforeEach(async () => {
|
|
18
19
|
i = new Injector()
|
|
19
|
-
|
|
20
|
+
port = getPort()
|
|
20
21
|
useRestService({
|
|
21
22
|
injector: i,
|
|
22
23
|
api: {},
|
|
@@ -34,7 +35,7 @@ describe('WebSocket Integration tests', () => {
|
|
|
34
35
|
i.getInstance(ServerManager)
|
|
35
36
|
.getOrCreate({ port })
|
|
36
37
|
.then(() => {
|
|
37
|
-
client = new
|
|
38
|
+
client = new WebSocket(`ws://${host}:${port}/ws`)
|
|
38
39
|
client
|
|
39
40
|
.on('open', () => {
|
|
40
41
|
done()
|
|
@@ -47,15 +48,66 @@ describe('WebSocket Integration tests', () => {
|
|
|
47
48
|
afterEach(async () => {
|
|
48
49
|
await i.dispose()
|
|
49
50
|
})
|
|
51
|
+
const getWhoAmIResult = async (subjectClient: WebSocket) => {
|
|
52
|
+
return new Promise<{ currentUser: User }>((resolve, reject) => {
|
|
53
|
+
subjectClient.once('message', (data: any) => {
|
|
54
|
+
resolve(JSON.parse(data.toString()))
|
|
55
|
+
})
|
|
56
|
+
subjectClient.once('error', reject)
|
|
57
|
+
subjectClient.send('whoami')
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
describe('Authentication', () => {
|
|
62
|
+
it('Should be unauthenticated by default', async () => {
|
|
63
|
+
expect((await getWhoAmIResult(client)).currentUser).toBe(null)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('Should be authenticated, roles should be updated and should be logged out', async () => {
|
|
68
|
+
const testUser = { username: 'test', password: 'test', roles: [] } as User
|
|
69
|
+
|
|
70
|
+
const userStore = i.getInstance(StoreManager).getStoreFor(User, 'username')
|
|
71
|
+
userStore.add(testUser)
|
|
72
|
+
|
|
73
|
+
const userCtx = i.getInstance(HttpUserContext)
|
|
50
74
|
|
|
51
|
-
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
75
|
+
let cookie = ''
|
|
76
|
+
await userCtx.cookieLogin(testUser, {
|
|
77
|
+
setHeader: (_setCookie, cookieValue) => {
|
|
78
|
+
cookie = cookieValue as string
|
|
79
|
+
return {} as any
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const authenticatedClient = await new Promise<WebSocket>((done, reject) => {
|
|
84
|
+
const cl = new WebSocket(`ws://${host}:${port}/ws`, {
|
|
85
|
+
headers: { cookie },
|
|
57
86
|
})
|
|
58
|
-
|
|
87
|
+
cl.once('open', () => {
|
|
88
|
+
done(cl)
|
|
89
|
+
}).once('error', reject)
|
|
59
90
|
})
|
|
91
|
+
const whoAmIResult = await getWhoAmIResult(authenticatedClient)
|
|
92
|
+
expect(whoAmIResult.currentUser).toEqual(testUser)
|
|
93
|
+
|
|
94
|
+
userStore.update(testUser.username, { ...testUser, roles: ['newFancyRole'] })
|
|
95
|
+
|
|
96
|
+
const updatedWhoAmIResult = await getWhoAmIResult(authenticatedClient)
|
|
97
|
+
expect(updatedWhoAmIResult.currentUser.roles).toEqual(['newFancyRole'])
|
|
98
|
+
|
|
99
|
+
await userCtx.cookieLogout(
|
|
100
|
+
{
|
|
101
|
+
headers: {
|
|
102
|
+
cookie,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
setHeader: vi.fn(),
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
const loggedOutWhoAmIResult = await getWhoAmIResult(authenticatedClient)
|
|
111
|
+
expect(loggedOutWhoAmIResult.currentUser).toBe(null)
|
|
60
112
|
})
|
|
61
113
|
})
|