docpouch-client 0.8.2 → 0.8.3
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/dist/index.d.ts +5 -4
- package/dist/index.js +19 -4
- package/dist/types.d.ts +9 -2
- package/jest.config.js +18 -0
- package/package.json +27 -23
- package/src/__tests__/index.test.ts +489 -0
- package/src/index.ts +2 -2
- package/tsconfig.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { I_UserEntry, I_UserLogin, I_UserCreation, I_UserUpdate, I_UserDisplay, I_DocumentEntry, I_DataStructure, I_LoginResponse, I_DocumentQuery, I_StructureCreation, I_WsMessage } from "./types.js";
|
|
2
|
-
import {
|
|
1
|
+
import type { I_UserEntry, I_UserLogin, I_UserCreation, I_UserUpdate, I_UserDisplay, I_DocumentEntry, I_DataStructure, I_LoginResponse, I_DocumentQuery, I_StructureCreation, I_WsMessage, I_EventString } from "./types.js";
|
|
2
|
+
import { Socket } from "socket.io-client";
|
|
3
3
|
export default class Index {
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
private token;
|
|
6
|
-
|
|
7
|
-
constructor(baseUrl: string, callback?: (event:
|
|
6
|
+
socket: Socket;
|
|
7
|
+
constructor(baseUrl: string, callback?: (event: I_EventString, data: I_WsMessage) => void);
|
|
8
8
|
private request;
|
|
9
9
|
login(credentials: I_UserLogin): Promise<I_LoginResponse | null>;
|
|
10
10
|
listUsers(): Promise<I_UserEntry[]>;
|
|
@@ -22,4 +22,5 @@ export default class Index {
|
|
|
22
22
|
removeStructure(structureID: string): Promise<void>;
|
|
23
23
|
setToken(token: string | null): void;
|
|
24
24
|
getToken(): string | null;
|
|
25
|
+
getVersion(): string;
|
|
25
26
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
const socket_io_client_1 = require("socket.io-client");
|
|
7
|
+
const package_json_1 = __importDefault(require("../package.json"));
|
|
4
8
|
class Index {
|
|
5
9
|
constructor(baseUrl, callback) {
|
|
6
10
|
this.token = null;
|
|
7
11
|
this.baseUrl = baseUrl;
|
|
8
|
-
this.
|
|
12
|
+
this.socket = (0, socket_io_client_1.io)(baseUrl, { autoConnect: false });
|
|
9
13
|
if (callback) {
|
|
10
|
-
this.
|
|
11
|
-
this.
|
|
14
|
+
this.socket.connect();
|
|
15
|
+
this.socket.onAny((event, data) => {
|
|
16
|
+
if (event === "heartbeatPing") {
|
|
17
|
+
console.log("Ping event received:", data);
|
|
18
|
+
this.socket.emit("heartbeatPong", Date.now());
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
callback(event, data);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
12
24
|
}
|
|
13
25
|
}
|
|
14
26
|
async request(endpoint, method, body, requiresAuth = true) {
|
|
@@ -61,7 +73,7 @@ class Index {
|
|
|
61
73
|
return await this.request('/docs/list', 'GET');
|
|
62
74
|
}
|
|
63
75
|
async fetchDocument(queryObject) {
|
|
64
|
-
return await this.request(`/docs/fetch/`, '
|
|
76
|
+
return await this.request(`/docs/fetch/`, 'POST', queryObject);
|
|
65
77
|
}
|
|
66
78
|
async updateDocument(documentID, documentData) {
|
|
67
79
|
await this.request(`/docs/update/${documentID}`, 'PATCH', documentData);
|
|
@@ -88,6 +100,9 @@ class Index {
|
|
|
88
100
|
getToken() {
|
|
89
101
|
return this.token;
|
|
90
102
|
}
|
|
103
|
+
getVersion() {
|
|
104
|
+
return package_json_1.default.version;
|
|
105
|
+
}
|
|
91
106
|
}
|
|
92
107
|
exports.default = Index;
|
|
93
108
|
module.exports = Index;
|
package/dist/types.d.ts
CHANGED
|
@@ -9,17 +9,23 @@ export interface I_UserCreation {
|
|
|
9
9
|
name: string;
|
|
10
10
|
password: string;
|
|
11
11
|
email?: string;
|
|
12
|
+
department: string;
|
|
13
|
+
group: string;
|
|
12
14
|
isAdmin: boolean;
|
|
13
15
|
}
|
|
14
16
|
export interface I_UserUpdate {
|
|
15
17
|
name?: string;
|
|
16
18
|
password?: string;
|
|
17
19
|
email?: string;
|
|
20
|
+
department?: string;
|
|
21
|
+
group?: string;
|
|
18
22
|
isAdmin?: boolean;
|
|
19
23
|
}
|
|
20
24
|
export interface I_UserDisplay {
|
|
21
25
|
_id: string;
|
|
22
26
|
username: string;
|
|
27
|
+
department: string;
|
|
28
|
+
group: string;
|
|
23
29
|
email?: string;
|
|
24
30
|
}
|
|
25
31
|
export interface I_LoginResponse {
|
|
@@ -78,6 +84,7 @@ export interface I_StructureUpdate {
|
|
|
78
84
|
reference?: any;
|
|
79
85
|
fields?: any[];
|
|
80
86
|
}
|
|
87
|
+
export type I_EventString = 'subscribe' | 'unsubscribe' | 'heartbeatPong' | "heartbeatPing" | "newDocument" | "newStructure" | "newUser" | "removedID" | "changedDocument" | "changedStructure" | "changedUser" | "confirmSubscription" | "confirmUnsubscription";
|
|
81
88
|
export interface I_WsMessage {
|
|
82
89
|
newDocument?: I_DocumentEntry;
|
|
83
90
|
newStructure?: I_StructureEntry;
|
|
@@ -88,6 +95,6 @@ export interface I_WsMessage {
|
|
|
88
95
|
changedUser?: I_UserUpdate;
|
|
89
96
|
confirmSubscription?: boolean;
|
|
90
97
|
confirmUnsubscription?: boolean;
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
heartbeatPing?: number;
|
|
99
|
+
heartbeatPong?: number;
|
|
93
100
|
}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
preset: 'ts-jest',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
moduleNameMapper: {
|
|
5
|
+
'^(\\.{1,2}/.*)\\.js$': '$1',
|
|
6
|
+
},
|
|
7
|
+
transform: {
|
|
8
|
+
'^.+\\.tsx?$': ['ts-jest', {
|
|
9
|
+
useESM: true,
|
|
10
|
+
}],
|
|
11
|
+
},
|
|
12
|
+
extensionsToTreatAsEsm: ['.ts'],
|
|
13
|
+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
14
|
+
testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'],
|
|
15
|
+
collectCoverage: true,
|
|
16
|
+
coverageDirectory: 'coverage',
|
|
17
|
+
coverageReporters: ['text', 'lcov'],
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "docpouch-client",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"types": "dist/index.d.ts",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "tsc"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "docpouch-client",
|
|
3
|
+
"version": "0.8.3",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"test": "jest"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"docPouch",
|
|
12
|
+
"API"
|
|
13
|
+
],
|
|
14
|
+
"author": "Jan Frecè",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"description": "A small class to more easily access the docPouch API",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^22.15.24",
|
|
19
|
+
"socket.io-client": "^4.8.1",
|
|
20
|
+
"jest": "^29.7.0",
|
|
21
|
+
"ts-jest": "^29.3.4",
|
|
22
|
+
"@types/jest": "^29.5.14"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"typescript": "^5.8.3"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import Index from '../index.js';
|
|
2
|
+
import packetJson from '../../package.json';
|
|
3
|
+
import {
|
|
4
|
+
I_UserLogin,
|
|
5
|
+
I_UserCreation,
|
|
6
|
+
I_UserUpdate,
|
|
7
|
+
I_DocumentEntry,
|
|
8
|
+
I_DocumentQuery,
|
|
9
|
+
I_StructureCreation,
|
|
10
|
+
I_DataStructure,
|
|
11
|
+
} from '../types.js';
|
|
12
|
+
|
|
13
|
+
// Mock socket.io-client
|
|
14
|
+
jest.mock('socket.io-client', () => {
|
|
15
|
+
const mockSocket = {
|
|
16
|
+
connect: jest.fn(),
|
|
17
|
+
emit: jest.fn(),
|
|
18
|
+
onAny: jest.fn(),
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
io: jest.fn(() => mockSocket),
|
|
22
|
+
Socket: jest.requireActual('socket.io-client').Socket,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Mock fetch API
|
|
27
|
+
global.fetch = jest.fn();
|
|
28
|
+
const mockFetch = global.fetch as jest.Mock;
|
|
29
|
+
|
|
30
|
+
describe('Index Class', () => {
|
|
31
|
+
let index: Index;
|
|
32
|
+
const baseUrl = 'http://example.com';
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
// Reset mocks before each test
|
|
36
|
+
jest.clearAllMocks();
|
|
37
|
+
mockFetch.mockClear();
|
|
38
|
+
|
|
39
|
+
// Create a new instance of Index for each test
|
|
40
|
+
index = new Index(baseUrl);
|
|
41
|
+
|
|
42
|
+
// Mock successful fetch response
|
|
43
|
+
mockFetch.mockImplementation(() =>
|
|
44
|
+
Promise.resolve({
|
|
45
|
+
ok: true,
|
|
46
|
+
status: 200,
|
|
47
|
+
statusText: 'OK',
|
|
48
|
+
json: () => Promise.resolve({}),
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Test constructor
|
|
54
|
+
describe('constructor', () => {
|
|
55
|
+
it('should initialize with the provided baseUrl', () => {
|
|
56
|
+
expect(index.baseUrl).toBe(baseUrl);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should initialize socket with autoConnect: false by default', () => {
|
|
60
|
+
expect(require('socket.io-client').io).toHaveBeenCalledWith(baseUrl, { autoConnect: false });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should connect socket and set up event handler when callback is provided', () => {
|
|
64
|
+
const mockCallback = jest.fn();
|
|
65
|
+
const indexWithCallback = new Index(baseUrl, mockCallback);
|
|
66
|
+
|
|
67
|
+
expect(indexWithCallback.socket.connect).toHaveBeenCalled();
|
|
68
|
+
expect(indexWithCallback.socket.onAny).toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Test utility methods
|
|
73
|
+
describe('utility methods', () => {
|
|
74
|
+
it('should set and get token correctly', () => {
|
|
75
|
+
const token = 'test-token';
|
|
76
|
+
index.setToken(token);
|
|
77
|
+
expect(index.getToken()).toBe(token);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return null when token is not set', () => {
|
|
81
|
+
expect(index.getToken()).toBeNull();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should return the correct version', () => {
|
|
85
|
+
// Mock package.json
|
|
86
|
+
jest.mock('../../package.json', () => ({ version: packetJson.version }), { virtual: true });
|
|
87
|
+
|
|
88
|
+
// We need to re-create the index instance to pick up the mocked package.json
|
|
89
|
+
const newIndex = new Index(baseUrl);
|
|
90
|
+
expect(newIndex.getVersion()).toBe(packetJson.version);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Test user administration endpoints
|
|
95
|
+
describe('user administration endpoints', () => {
|
|
96
|
+
it('should login successfully and store token', async () => {
|
|
97
|
+
const credentials: I_UserLogin = { name: 'testuser', password: 'password' };
|
|
98
|
+
const mockResponse = { token: 'test-token', isAdmin: true };
|
|
99
|
+
|
|
100
|
+
mockFetch.mockImplementationOnce(() =>
|
|
101
|
+
Promise.resolve({
|
|
102
|
+
ok: true,
|
|
103
|
+
status: 200,
|
|
104
|
+
statusText: 'OK',
|
|
105
|
+
json: () => Promise.resolve(mockResponse),
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const result = await index.login(credentials);
|
|
110
|
+
|
|
111
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
112
|
+
`${baseUrl}/users/login`,
|
|
113
|
+
expect.objectContaining({
|
|
114
|
+
method: 'POST',
|
|
115
|
+
body: JSON.stringify(credentials),
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
expect(result).toEqual(mockResponse);
|
|
119
|
+
expect(index.getToken()).toBe('test-token');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should return null when login fails', async () => {
|
|
123
|
+
const credentials: I_UserLogin = { name: 'testuser', password: 'wrong-password' };
|
|
124
|
+
|
|
125
|
+
mockFetch.mockImplementationOnce(() =>
|
|
126
|
+
Promise.resolve({
|
|
127
|
+
ok: true,
|
|
128
|
+
status: 200,
|
|
129
|
+
statusText: 'OK',
|
|
130
|
+
json: () => Promise.resolve({}),
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const result = await index.login(credentials);
|
|
135
|
+
|
|
136
|
+
expect(result).toBeNull();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should list users', async () => {
|
|
140
|
+
const mockUsers = [{ _id: '1', name: 'user1', password: 'pass1', department: 'dept1', group: 'group1', isAdmin: false }];
|
|
141
|
+
|
|
142
|
+
mockFetch.mockImplementationOnce(() =>
|
|
143
|
+
Promise.resolve({
|
|
144
|
+
ok: true,
|
|
145
|
+
status: 200,
|
|
146
|
+
statusText: 'OK',
|
|
147
|
+
json: () => Promise.resolve(mockUsers),
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const result = await index.listUsers();
|
|
152
|
+
|
|
153
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
154
|
+
`${baseUrl}/users/list`,
|
|
155
|
+
expect.objectContaining({
|
|
156
|
+
method: 'GET',
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
expect(result).toEqual(mockUsers);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should update a user', async () => {
|
|
163
|
+
const userId = '1';
|
|
164
|
+
const userData: I_UserUpdate = { name: 'updated-user', department: 'new-dept' };
|
|
165
|
+
|
|
166
|
+
await index.updateUser(userId, userData);
|
|
167
|
+
|
|
168
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
169
|
+
`${baseUrl}/users/update/${userId}`,
|
|
170
|
+
expect.objectContaining({
|
|
171
|
+
method: 'PATCH',
|
|
172
|
+
body: JSON.stringify(userData),
|
|
173
|
+
})
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should create a user', async () => {
|
|
178
|
+
const userData: I_UserCreation = {
|
|
179
|
+
name: 'newuser',
|
|
180
|
+
password: 'password',
|
|
181
|
+
department: 'dept',
|
|
182
|
+
group: 'group',
|
|
183
|
+
isAdmin: false
|
|
184
|
+
};
|
|
185
|
+
const mockResponse = { _id: '2', username: 'newuser', department: 'dept', group: 'group' };
|
|
186
|
+
|
|
187
|
+
mockFetch.mockImplementationOnce(() =>
|
|
188
|
+
Promise.resolve({
|
|
189
|
+
ok: true,
|
|
190
|
+
status: 200,
|
|
191
|
+
statusText: 'OK',
|
|
192
|
+
json: () => Promise.resolve(mockResponse),
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const result = await index.createUser(userData);
|
|
197
|
+
|
|
198
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
199
|
+
`${baseUrl}/users/create`,
|
|
200
|
+
expect.objectContaining({
|
|
201
|
+
method: 'POST',
|
|
202
|
+
body: JSON.stringify(userData),
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
expect(result).toEqual(mockResponse);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should remove a user', async () => {
|
|
209
|
+
const userId = '1';
|
|
210
|
+
|
|
211
|
+
await index.removeUser(userId);
|
|
212
|
+
|
|
213
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
214
|
+
`${baseUrl}/users/remove/${userId}`,
|
|
215
|
+
expect.objectContaining({
|
|
216
|
+
method: 'DELETE',
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Test document management endpoints
|
|
223
|
+
describe('document management endpoints', () => {
|
|
224
|
+
it('should create a document', async () => {
|
|
225
|
+
const document: I_DocumentEntry = {
|
|
226
|
+
_id: '1',
|
|
227
|
+
title: 'Test Document',
|
|
228
|
+
type: 1,
|
|
229
|
+
subType: 2,
|
|
230
|
+
content: { data: 'test content' },
|
|
231
|
+
owner: 'user1'
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
mockFetch.mockImplementationOnce(() =>
|
|
235
|
+
Promise.resolve({
|
|
236
|
+
ok: true,
|
|
237
|
+
status: 200,
|
|
238
|
+
statusText: 'OK',
|
|
239
|
+
json: () => Promise.resolve(document),
|
|
240
|
+
})
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const result = await index.createDocument(document);
|
|
244
|
+
|
|
245
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
246
|
+
`${baseUrl}/docs/create`,
|
|
247
|
+
expect.objectContaining({
|
|
248
|
+
method: 'POST',
|
|
249
|
+
body: JSON.stringify(document),
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
expect(result).toEqual(document);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should list documents', async () => {
|
|
256
|
+
const mockDocuments = [
|
|
257
|
+
{ _id: '1', title: 'Doc1', type: 1, subType: 1, content: {}, owner: 'user1' },
|
|
258
|
+
{ _id: '2', title: 'Doc2', type: 2, subType: 2, content: {}, owner: 'user2' }
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
mockFetch.mockImplementationOnce(() =>
|
|
262
|
+
Promise.resolve({
|
|
263
|
+
ok: true,
|
|
264
|
+
status: 200,
|
|
265
|
+
statusText: 'OK',
|
|
266
|
+
json: () => Promise.resolve(mockDocuments),
|
|
267
|
+
})
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const result = await index.listDocuments();
|
|
271
|
+
|
|
272
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
273
|
+
`${baseUrl}/docs/list`,
|
|
274
|
+
expect.objectContaining({
|
|
275
|
+
method: 'GET',
|
|
276
|
+
})
|
|
277
|
+
);
|
|
278
|
+
expect(result).toEqual(mockDocuments);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should fetch documents based on query', async () => {
|
|
282
|
+
const query: I_DocumentQuery = { type: 1, owner: 'user1' };
|
|
283
|
+
const mockDocuments = [
|
|
284
|
+
{ _id: '1', title: 'Doc1', type: 1, subType: 1, content: {}, owner: 'user1' }
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
mockFetch.mockImplementationOnce(() =>
|
|
288
|
+
Promise.resolve({
|
|
289
|
+
ok: true,
|
|
290
|
+
status: 200,
|
|
291
|
+
statusText: 'OK',
|
|
292
|
+
json: () => Promise.resolve(mockDocuments),
|
|
293
|
+
})
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const result = await index.fetchDocument(query);
|
|
297
|
+
|
|
298
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
299
|
+
`${baseUrl}/docs/fetch/`,
|
|
300
|
+
expect.objectContaining({
|
|
301
|
+
method: 'POST',
|
|
302
|
+
body: JSON.stringify(query),
|
|
303
|
+
})
|
|
304
|
+
);
|
|
305
|
+
expect(result).toEqual(mockDocuments);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should update a document', async () => {
|
|
309
|
+
const documentId = '1';
|
|
310
|
+
const documentData: I_DocumentEntry = {
|
|
311
|
+
_id: '1',
|
|
312
|
+
title: 'Updated Document',
|
|
313
|
+
type: 1,
|
|
314
|
+
subType: 2,
|
|
315
|
+
content: { data: 'updated content' },
|
|
316
|
+
owner: 'user1'
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
await index.updateDocument(documentId, documentData);
|
|
320
|
+
|
|
321
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
322
|
+
`${baseUrl}/docs/update/${documentId}`,
|
|
323
|
+
expect.objectContaining({
|
|
324
|
+
method: 'PATCH',
|
|
325
|
+
body: JSON.stringify(documentData),
|
|
326
|
+
})
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should remove a document', async () => {
|
|
331
|
+
const documentId = '1';
|
|
332
|
+
|
|
333
|
+
await index.removeDocument(documentId);
|
|
334
|
+
|
|
335
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
336
|
+
`${baseUrl}/docs/remove/${documentId}`,
|
|
337
|
+
expect.objectContaining({
|
|
338
|
+
method: 'DELETE',
|
|
339
|
+
})
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Test data structure endpoints
|
|
345
|
+
describe('data structure endpoints', () => {
|
|
346
|
+
it('should create a structure', async () => {
|
|
347
|
+
const structure: I_StructureCreation = {
|
|
348
|
+
name: 'Test Structure',
|
|
349
|
+
description: 'Test Description',
|
|
350
|
+
fields: [{ name: 'field1', type: 'string' }]
|
|
351
|
+
};
|
|
352
|
+
const mockResponse: I_DataStructure = {
|
|
353
|
+
_id: '1',
|
|
354
|
+
name: 'Test Structure',
|
|
355
|
+
description: 'Test Description',
|
|
356
|
+
fields: [{ name: 'field1', type: 'string' }]
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
mockFetch.mockImplementationOnce(() =>
|
|
360
|
+
Promise.resolve({
|
|
361
|
+
ok: true,
|
|
362
|
+
status: 200,
|
|
363
|
+
statusText: 'OK',
|
|
364
|
+
json: () => Promise.resolve(mockResponse),
|
|
365
|
+
})
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const result = await index.createStructure(structure);
|
|
369
|
+
|
|
370
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
371
|
+
`${baseUrl}/structures/create`,
|
|
372
|
+
expect.objectContaining({
|
|
373
|
+
method: 'POST',
|
|
374
|
+
body: JSON.stringify(structure),
|
|
375
|
+
})
|
|
376
|
+
);
|
|
377
|
+
expect(result).toEqual(mockResponse);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should get structures', async () => {
|
|
381
|
+
const mockStructures = [
|
|
382
|
+
{ _id: '1', name: 'Structure1', description: 'Desc1', fields: [] },
|
|
383
|
+
{ _id: '2', name: 'Structure2', description: 'Desc2', fields: [] }
|
|
384
|
+
];
|
|
385
|
+
|
|
386
|
+
mockFetch.mockImplementationOnce(() =>
|
|
387
|
+
Promise.resolve({
|
|
388
|
+
ok: true,
|
|
389
|
+
status: 200,
|
|
390
|
+
statusText: 'OK',
|
|
391
|
+
json: () => Promise.resolve(mockStructures),
|
|
392
|
+
})
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
const result = await index.getStructures();
|
|
396
|
+
|
|
397
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
398
|
+
`${baseUrl}/structures/list`,
|
|
399
|
+
expect.objectContaining({
|
|
400
|
+
method: 'GET',
|
|
401
|
+
})
|
|
402
|
+
);
|
|
403
|
+
expect(result).toEqual(mockStructures);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('should update a structure', async () => {
|
|
407
|
+
const structureId = '1';
|
|
408
|
+
const structureData: I_DataStructure = {
|
|
409
|
+
name: 'Updated Structure',
|
|
410
|
+
description: 'Updated Description',
|
|
411
|
+
fields: [{ name: 'updatedField', type: 'number' }]
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
await index.updateStructure(structureId, structureData);
|
|
415
|
+
|
|
416
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
417
|
+
`${baseUrl}/structures/update/${structureId}`,
|
|
418
|
+
expect.objectContaining({
|
|
419
|
+
method: 'PATCH',
|
|
420
|
+
body: JSON.stringify(structureData),
|
|
421
|
+
})
|
|
422
|
+
);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should remove a structure', async () => {
|
|
426
|
+
const structureId = '1';
|
|
427
|
+
|
|
428
|
+
await index.removeStructure(structureId);
|
|
429
|
+
|
|
430
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
431
|
+
`${baseUrl}/structures/remove/${structureId}`,
|
|
432
|
+
expect.objectContaining({
|
|
433
|
+
method: 'DELETE',
|
|
434
|
+
})
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Test error handling
|
|
440
|
+
describe('error handling', () => {
|
|
441
|
+
it('should throw an error when API request fails', async () => {
|
|
442
|
+
mockFetch.mockImplementationOnce(() =>
|
|
443
|
+
Promise.resolve({
|
|
444
|
+
ok: false,
|
|
445
|
+
status: 500,
|
|
446
|
+
statusText: 'Internal Server Error',
|
|
447
|
+
})
|
|
448
|
+
);
|
|
449
|
+
|
|
450
|
+
await expect(index.listUsers()).rejects.toThrow('API error: 500 Internal Server Error');
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should clear token when receiving 401 or 403 status', async () => {
|
|
454
|
+
// Set a token first
|
|
455
|
+
index.setToken('test-token');
|
|
456
|
+
|
|
457
|
+
// Mock 401 response
|
|
458
|
+
mockFetch.mockImplementationOnce(() =>
|
|
459
|
+
Promise.resolve({
|
|
460
|
+
ok: false,
|
|
461
|
+
status: 401,
|
|
462
|
+
statusText: 'Unauthorized',
|
|
463
|
+
})
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
await expect(index.listUsers()).rejects.toThrow('API error: 401 Unauthorized');
|
|
467
|
+
expect(index.getToken()).toBeNull();
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Test WebSocket functionality
|
|
472
|
+
describe('WebSocket functionality', () => {
|
|
473
|
+
it('should connect socket when callback is provided', () => {
|
|
474
|
+
const mockCallback = jest.fn();
|
|
475
|
+
const indexWithCallback = new Index(baseUrl, mockCallback);
|
|
476
|
+
|
|
477
|
+
// Verify that connect was called
|
|
478
|
+
expect(indexWithCallback.socket.connect).toHaveBeenCalled();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('should set up event handler when callback is provided', () => {
|
|
482
|
+
const mockCallback = jest.fn();
|
|
483
|
+
const indexWithCallback = new Index(baseUrl, mockCallback);
|
|
484
|
+
|
|
485
|
+
// Verify that onAny was called
|
|
486
|
+
expect(indexWithCallback.socket.onAny).toHaveBeenCalled();
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
});
|
package/src/index.ts
CHANGED
package/tsconfig.json
CHANGED
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
"rootDir": "./src",
|
|
13
13
|
"declaration": true,
|
|
14
14
|
"emitDeclarationOnly": false,
|
|
15
|
-
"resolveJsonModule": true
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"isolatedModules": true
|
|
16
17
|
},
|
|
17
18
|
"include": ["**/*.ts", "**/*.tsx"],
|
|
18
19
|
"exclude": ["node_modules", "**/*.test.ts", "**/*.types.ts", "./dist/**/*"]
|
|
19
|
-
}
|
|
20
|
+
}
|