@nocobase/test 2.0.0-beta.5 → 2.0.0-beta.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/test",
3
- "version": "2.0.0-beta.5",
3
+ "version": "2.0.0-beta.7",
4
4
  "main": "lib/index.js",
5
5
  "module": "./src/index.ts",
6
6
  "types": "./lib/index.d.ts",
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@faker-js/faker": "8.1.0",
54
- "@nocobase/server": "2.0.0-beta.5",
54
+ "@nocobase/server": "2.0.0-beta.7",
55
55
  "@playwright/test": "^1.45.3",
56
56
  "@testing-library/jest-dom": "^6.4.2",
57
57
  "@testing-library/react": "^14.0.0",
@@ -62,6 +62,7 @@
62
62
  "@vitest/coverage-istanbul": "^1.5.0",
63
63
  "@vitest/coverage-v8": "^1.5.0",
64
64
  "axios-mock-adapter": "1.22.0",
65
+ "exceljs": "^4.4.0",
65
66
  "jsdom": "^25.0.1",
66
67
  "jsdom-worker": "^0.3.0",
67
68
  "mariadb": "^2.5.6",
@@ -75,5 +76,5 @@
75
76
  "vitest-dom": "^0.1.1",
76
77
  "ws": "^8.13.0"
77
78
  },
78
- "gitHead": "ea028f41293607ac0a20d054107e2ba772d639b2"
79
+ "gitHead": "17b6e4042a4814e38b256c7b3ae34e6d0b08fd57"
79
80
  }
@@ -0,0 +1,353 @@
1
+ import { sleep, check } from 'k6';
2
+ import { vu } from 'k6/execution';
3
+ import http from 'k6/http';
4
+
5
+ import { setup as baseSetup } from './setup.js';
6
+
7
+ const appCount = Number.parseInt(__ENV.IMPORT_APP_COUNT, 10);
8
+ const iterations = Number.parseInt(__ENV.IMPORT_CONCURRENCY_PER_APP, 10);
9
+
10
+ const appNames = [...Array(appCount).keys()].map((index) => `import-test-apps-${index}`);
11
+ const importDataTestCollection = JSON.parse(open(__ENV.COLLECTION_DEFINED_JSON_PATH));
12
+ const importDataTestData = open(__ENV.IMPORT_TEMPLATE_WITH_MOCK_DATA, 'b');
13
+
14
+ export const options = {
15
+ setupTimeout: '30m',
16
+ scenarios: {
17
+ 'import-async': {
18
+ executor: 'per-vu-iterations',
19
+ vus: appNames.length,
20
+ iterations,
21
+ maxDuration: '30m',
22
+ },
23
+ },
24
+ };
25
+
26
+ export function setup() {
27
+ const { token } = baseSetup();
28
+
29
+ let appList = getAppList({ token, appNames });
30
+ const appMap = Object.fromEntries(appList.data.map((app) => [app.name, app]));
31
+ for (const appName of appNames) {
32
+ if (appMap[appName]) {
33
+ startApp({ token, name: appName });
34
+ continue;
35
+ }
36
+ createApp({ token, name: appName });
37
+ startApp({ token, name: appName });
38
+ }
39
+ do {
40
+ appList = getAppList({ token, appNames });
41
+ sleep(15);
42
+ } while (!appList.data.every((app) => app.status === 'running'));
43
+
44
+ for (const appName of appNames) {
45
+ let enabledPluginList = listEnabledPlugin({ token, appName });
46
+ let enabledPluginNames = (enabledPluginList.data ?? []).map(({ name }) => name);
47
+ if (enabledPluginNames.includes('action-import-pro')) {
48
+ continue;
49
+ }
50
+
51
+ enablePlugins({ token, appName, pluginNames: ['action-import-pro'] });
52
+ do {
53
+ enabledPluginList = listEnabledPlugin({ token, appName });
54
+ enabledPluginNames = (enabledPluginList.data ?? []).map(({ name }) => name);
55
+ sleep(15);
56
+ } while (!enabledPluginNames.includes('action-import-pro'));
57
+ }
58
+
59
+ for (const appName of appNames) {
60
+ const collectionList = findCollection({ token, appName, collectionName: 'importDataTest' });
61
+ const [testCollection] = collectionList.data;
62
+ if (testCollection?.name === importDataTestCollection.name) {
63
+ destroyCollection({ token, appName });
64
+ }
65
+ createCollection({ token, appName });
66
+ }
67
+
68
+ return { token };
69
+ }
70
+
71
+ const getAppList = ({ token, appNames = [] }) => {
72
+ const filterName = appNames.length ? `&${appNames.map((name) => `filter[name][$in]=${name}`).join('&')}` : '';
73
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/applications:list?pageSize=50&sort[]=-createdAt${filterName}`;
74
+ const params = {
75
+ headers: {
76
+ 'Proxy-Connection': `keep-alive`,
77
+ Authorization: `Bearer ${token}`,
78
+ Accept: `application/json, text/plain, */*`,
79
+ 'X-Timezone': `+08:00`,
80
+ 'Content-Type': `application/json`,
81
+ 'X-With-ACL-Meta': `true`,
82
+ 'X-Locale': `en-US`,
83
+ Origin: `${__ENV.ORIGIN}`,
84
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
85
+ 'Accept-Encoding': `gzip, deflate`,
86
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
87
+ },
88
+ cookies: {},
89
+ };
90
+ const resp = http.request('GET', url, null, params);
91
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
92
+ return JSON.parse(resp.body);
93
+ };
94
+
95
+ const createApp = ({ token, name }) => {
96
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/applications:create`;
97
+ const payload = JSON.stringify({
98
+ name,
99
+ options: { autoStart: false, authManager: {} },
100
+ displayName: name,
101
+ });
102
+ const params = {
103
+ headers: {
104
+ 'Proxy-Connection': `keep-alive`,
105
+ Authorization: `Bearer ${token}`,
106
+ Accept: `application/json, text/plain, */*`,
107
+ 'X-Timezone': `+08:00`,
108
+ 'Content-Type': `application/json`,
109
+ 'X-With-ACL-Meta': `true`,
110
+ 'X-Locale': `en-US`,
111
+ Origin: `${__ENV.ORIGIN}`,
112
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
113
+ 'Accept-Encoding': `gzip, deflate`,
114
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
115
+ },
116
+ cookies: {},
117
+ };
118
+ const resp = http.request('POST', url, payload, params);
119
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
120
+ return JSON.parse(resp.body);
121
+ };
122
+
123
+ const enablePlugins = ({ token, appName, pluginNames = [] }) => {
124
+ if (!appNames.length) {
125
+ return;
126
+ }
127
+ const filterByTk = `${pluginNames.map((name) => `filterByTk[]=${name}`).join('&')}`;
128
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/pm:enable?${filterByTk}`;
129
+ const params = {
130
+ headers: {
131
+ 'Proxy-Connection': `keep-alive`,
132
+ Authorization: `Bearer ${token}`,
133
+ Accept: `application/json, text/plain, */*`,
134
+ 'X-Timezone': `+08:00`,
135
+ 'Content-Type': `application/json`,
136
+ 'X-With-ACL-Meta': `true`,
137
+ 'X-Locale': `en-US`,
138
+ Origin: `${__ENV.ORIGIN}`,
139
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
140
+ 'Accept-Encoding': `gzip, deflate`,
141
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
142
+ 'X-App': appName,
143
+ },
144
+ cookies: {},
145
+ };
146
+ const resp = http.request('POST', url, null, params);
147
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
148
+ return JSON.parse(resp.body);
149
+ };
150
+
151
+ const listEnabledPlugin = ({ token, appName }) => {
152
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/pm:listEnabled`;
153
+ const params = {
154
+ headers: {
155
+ 'Proxy-Connection': `keep-alive`,
156
+ Authorization: `Bearer ${token}`,
157
+ Accept: `application/json, text/plain, */*`,
158
+ 'X-Timezone': `+08:00`,
159
+ 'Content-Type': `application/json`,
160
+ 'X-With-ACL-Meta': `true`,
161
+ 'X-Locale': `en-US`,
162
+ Origin: `${__ENV.ORIGIN}`,
163
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
164
+ 'Accept-Encoding': `gzip, deflate`,
165
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
166
+ 'X-App': appName,
167
+ },
168
+ cookies: {},
169
+ };
170
+ const resp = http.request('GET', url, null, params);
171
+ return JSON.parse(resp.body);
172
+ };
173
+
174
+ const startApp = ({ token, name }) => {
175
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/applications:start?filterByTk=${name}`;
176
+ const params = {
177
+ headers: {
178
+ 'Proxy-Connection': `keep-alive`,
179
+ Authorization: `Bearer ${token}`,
180
+ Accept: `application/json, text/plain, */*`,
181
+ 'X-Timezone': `+08:00`,
182
+ 'Content-Type': `application/json`,
183
+ 'X-With-ACL-Meta': `true`,
184
+ 'X-Locale': `en-US`,
185
+ Origin: `${__ENV.ORIGIN}`,
186
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
187
+ 'Accept-Encoding': `gzip, deflate`,
188
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
189
+ },
190
+ cookies: {},
191
+ };
192
+ const resp = http.request('POST', url, null, params);
193
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
194
+ return JSON.parse(resp.body);
195
+ };
196
+
197
+ const findCollection = ({ token, appName, collectionName }) => {
198
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/collections:list?pageSize=50&filter[name]=${collectionName}`;
199
+ const params = {
200
+ headers: {
201
+ 'Proxy-Connection': `keep-alive`,
202
+ Authorization: `Bearer ${token}`,
203
+ Accept: `application/json, text/plain, */*`,
204
+ 'X-Timezone': `+08:00`,
205
+ 'Content-Type': `application/json`,
206
+ 'X-With-ACL-Meta': `true`,
207
+ 'X-Locale': `en-US`,
208
+ Origin: `${__ENV.ORIGIN}`,
209
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
210
+ 'Accept-Encoding': `gzip, deflate`,
211
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
212
+ 'X-App': appName,
213
+ },
214
+ cookies: {},
215
+ };
216
+ const resp = http.request('GET', url, null, params);
217
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
218
+ return JSON.parse(resp.body);
219
+ };
220
+
221
+ const destroyCollection = ({ token, appName }) => {
222
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/collections:destroy?filterByTk=${importDataTestCollection.name}&cascade=true`;
223
+ const params = {
224
+ headers: {
225
+ 'Proxy-Connection': `keep-alive`,
226
+ Authorization: `Bearer ${token}`,
227
+ Accept: `application/json, text/plain, */*`,
228
+ 'X-Timezone': `+08:00`,
229
+ 'Content-Type': `application/json`,
230
+ 'X-With-ACL-Meta': `true`,
231
+ 'X-Locale': `en-US`,
232
+ Origin: `${__ENV.ORIGIN}`,
233
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
234
+ 'Accept-Encoding': `gzip, deflate`,
235
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
236
+ 'X-App': appName,
237
+ },
238
+ cookies: {},
239
+ };
240
+ const resp = http.request('POST', url, null, params);
241
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
242
+ return JSON.parse(resp.body);
243
+ };
244
+
245
+ const createCollection = ({ token, appName }) => {
246
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/collections:create`;
247
+ const payload = JSON.stringify({
248
+ name: importDataTestCollection.name,
249
+ title: importDataTestCollection.title,
250
+ template: importDataTestCollection.template,
251
+ autoGenId: importDataTestCollection.autoGenId,
252
+ fields: importDataTestCollection.fields,
253
+ logging: true,
254
+ view: false,
255
+ });
256
+ const params = {
257
+ headers: {
258
+ 'Proxy-Connection': `keep-alive`,
259
+ Authorization: `Bearer ${token}`,
260
+ Accept: `application/json, text/plain, */*`,
261
+ 'X-Timezone': `+08:00`,
262
+ 'Content-Type': `application/json`,
263
+ 'X-With-ACL-Meta': `true`,
264
+ 'X-Locale': `en-US`,
265
+ Origin: `${__ENV.ORIGIN}`,
266
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
267
+ 'Accept-Encoding': `gzip, deflate`,
268
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
269
+ 'X-App': appName,
270
+ },
271
+ cookies: {},
272
+ };
273
+ const resp = http.request('POST', url, payload, params);
274
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
275
+ return JSON.parse(resp.body);
276
+ };
277
+
278
+ const importData = ({ token, appName }) => {
279
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/importDataTest:importXlsx?mode=auto`;
280
+ const columns = JSON.stringify(
281
+ importDataTestCollection.fields
282
+ .filter((field) => field.name.startsWith('column') || field.name === 'id')
283
+ .map((field) => ({
284
+ dataIndex: [field.name],
285
+ defaultTitle: field.uiSchema.title,
286
+ })),
287
+ );
288
+ const payload = {
289
+ file: http.file(
290
+ importDataTestData,
291
+ 'importDataTest-import-template.xlsx',
292
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
293
+ ),
294
+ columns,
295
+ explain: '',
296
+ };
297
+ const params = {
298
+ headers: {
299
+ 'Proxy-Connection': `keep-alive`,
300
+ Authorization: `Bearer ${token}`,
301
+ Accept: `application/json, text/plain, */*`,
302
+ 'X-Timezone': `+08:00`,
303
+ 'X-With-ACL-Meta': `true`,
304
+ 'X-Locale': `en-US`,
305
+ Origin: `${__ENV.ORIGIN}`,
306
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
307
+ 'Accept-Encoding': `gzip, deflate`,
308
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
309
+ 'X-App': appName,
310
+ },
311
+ cookies: {},
312
+ };
313
+ const resp = http.request('POST', url, payload, params);
314
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
315
+ return JSON.parse(resp.body);
316
+ };
317
+
318
+ const getTask = ({ token, appName, taskId }) => {
319
+ const url = http.url`${__ENV.TARGET_ORIGIN}/api/asyncTasks:list?sort=-createdAt&filter[id]=${taskId}`;
320
+ const params = {
321
+ headers: {
322
+ 'Proxy-Connection': `keep-alive`,
323
+ Authorization: `Bearer ${token}`,
324
+ Accept: `application/json, text/plain, */*`,
325
+ 'X-Timezone': `+08:00`,
326
+ 'Content-Type': `application/json`,
327
+ 'X-With-ACL-Meta': `true`,
328
+ 'X-Locale': `en-US`,
329
+ Origin: `${__ENV.ORIGIN}`,
330
+ Referer: `${__ENV.ORIGIN}/admin/settings/multi-app-manager`,
331
+ 'Accept-Encoding': `gzip, deflate`,
332
+ 'Accept-Language': `zh-CN,zh;q=0.9`,
333
+ 'X-App': appName,
334
+ },
335
+ cookies: {},
336
+ };
337
+ const resp = http.request('GET', url, null, params);
338
+ check(resp, { 'status equals 200': (r) => r.status === 200 });
339
+ return JSON.parse(resp.body);
340
+ };
341
+
342
+ export default function ({ token }) {
343
+ const appName = appNames[vu.idInTest - 1];
344
+ const receipt = importData({ token, appName });
345
+ console.log(`app id: ${appName} taskId: ${receipt?.data?.taskId}`);
346
+ let status;
347
+ do {
348
+ const taskResp = getTask({ token, appName, taskId: receipt.data.taskId });
349
+ const [task] = taskResp.data ?? [];
350
+ status = task?.status;
351
+ sleep(60);
352
+ } while (status !== 1);
353
+ }
@@ -0,0 +1,236 @@
1
+ import { createMockServer } from '@nocobase/test';
2
+ import { CollectionRepository } from '@nocobase/plugin-data-source-main';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ import ExcelJS from 'exceljs';
7
+ import { faker } from '@faker-js/faker';
8
+ import request from 'superagent';
9
+
10
+ const storagePath = path.resolve(process.cwd(), 'storage', 'perf', 'importDataTest');
11
+
12
+ export default async function main() {
13
+ const app = await createMockServer({
14
+ plugins: ['nocobase'],
15
+ });
16
+ const columns = [...Array(30).keys()];
17
+ const importDataTestCollection = {
18
+ name: 'importDataTest',
19
+ title: 'ImportDataTest',
20
+ template: 'general',
21
+ autoGenId: false,
22
+ filterTargetKey: 'id',
23
+ fields: [
24
+ ...columns.map((index) => ({
25
+ type: 'string',
26
+ name: 'column' + index,
27
+ interface: 'input',
28
+ uiSchema: {
29
+ type: 'string',
30
+ 'x-component': 'Input',
31
+ title: 'column' + index,
32
+ },
33
+ })),
34
+ {
35
+ name: 'id',
36
+ type: 'bigInt',
37
+ autoIncrement: true,
38
+ primaryKey: true,
39
+ allowNull: false,
40
+ uiSchema: {
41
+ type: 'number',
42
+ title: 'ID',
43
+ 'x-component': 'InputNumber',
44
+ 'x-read-pretty': true,
45
+ },
46
+ interface: 'integer',
47
+ },
48
+ {
49
+ name: 'createdAt',
50
+ interface: 'createdAt',
51
+ type: 'date',
52
+ field: 'createdAt',
53
+ uiSchema: {
54
+ type: 'datetime',
55
+ title: '{{t("Created at")}}',
56
+ 'x-component': 'DatePicker',
57
+ 'x-component-props': {},
58
+ 'x-read-pretty': true,
59
+ },
60
+ },
61
+ {
62
+ name: 'createdBy',
63
+ interface: 'createdBy',
64
+ type: 'belongsTo',
65
+ target: 'users',
66
+ foreignKey: 'createdById',
67
+ uiSchema: {
68
+ type: 'object',
69
+ title: '{{t("Created by")}}',
70
+ 'x-component': 'AssociationField',
71
+ 'x-component-props': {
72
+ fieldNames: {
73
+ value: 'id',
74
+ label: 'nickname',
75
+ },
76
+ },
77
+ 'x-read-pretty': true,
78
+ },
79
+ },
80
+ {
81
+ type: 'date',
82
+ field: 'updatedAt',
83
+ name: 'updatedAt',
84
+ interface: 'updatedAt',
85
+ uiSchema: {
86
+ type: 'datetime',
87
+ title: '{{t("Last updated at")}}',
88
+ 'x-component': 'DatePicker',
89
+ 'x-component-props': {},
90
+ 'x-read-pretty': true,
91
+ },
92
+ },
93
+ {
94
+ type: 'belongsTo',
95
+ target: 'users',
96
+ foreignKey: 'updatedById',
97
+ name: 'updatedBy',
98
+ interface: 'updatedBy',
99
+ uiSchema: {
100
+ type: 'object',
101
+ title: '{{t("Last updated by")}}',
102
+ 'x-component': 'AssociationField',
103
+ 'x-component-props': {
104
+ fieldNames: {
105
+ value: 'id',
106
+ label: 'nickname',
107
+ },
108
+ },
109
+ 'x-read-pretty': true,
110
+ },
111
+ },
112
+ ],
113
+ };
114
+ const importDataTestCollectionPath = path.resolve(storagePath, 'importDataTest.collection.json');
115
+ const writeStream = fs.createWriteStream(importDataTestCollectionPath);
116
+ writeStream.write(JSON.stringify(importDataTestCollection, null, 4));
117
+ app.logger.info('generate collection json on:' + importDataTestCollectionPath);
118
+
119
+ const db = app.db;
120
+ db.collection(importDataTestCollection);
121
+ await db.sync();
122
+ const CollectionRepo = db.getRepository('collections') as CollectionRepository;
123
+ await CollectionRepo.db2cm('importDataTest');
124
+
125
+ const importTemplateXlsxPath = await download(
126
+ app
127
+ .agent()
128
+ .post('/importDataTest:downloadXlsxTemplate')
129
+ .send({
130
+ title: 'ImportDataTest',
131
+ explain: '',
132
+ columns: [
133
+ ...columns.map((index) => ({
134
+ dataIndex: ['column' + index],
135
+ defaultTitle: 'column' + index,
136
+ })),
137
+ {
138
+ dataIndex: ['id'],
139
+ defaultTitle: 'ID',
140
+ },
141
+ ],
142
+ }),
143
+ );
144
+ app.logger.info('download import template on:' + importTemplateXlsxPath);
145
+
146
+ const workbook = new ExcelJS.Workbook();
147
+ await workbook.xlsx.readFile(importTemplateXlsxPath);
148
+ const worksheet = workbook.getWorksheet('Sheet 1');
149
+ if (!worksheet) {
150
+ throw new Error('Sheet 1 not exist');
151
+ }
152
+ for (let i = 0; i < 10000; i++) {
153
+ const data = [];
154
+ for (let i = 0; i < columns.length; i++) {
155
+ switch (i) {
156
+ case 0:
157
+ data[i] = faker.string.uuid();
158
+ break;
159
+ case 1:
160
+ data[i] = faker.person.firstName();
161
+ break;
162
+ case 2:
163
+ data[i] = faker.person.middleName();
164
+ break;
165
+ case 3:
166
+ data[i] = faker.person.lastName();
167
+ break;
168
+ case 4:
169
+ data[i] = faker.person.gender();
170
+ break;
171
+ case 5:
172
+ data[i] = faker.person.bio();
173
+ break;
174
+ case 6:
175
+ data[i] = faker.internet.email();
176
+ break;
177
+ default:
178
+ data[i] = faker.word.words({ count: { min: 5, max: 10 } });
179
+ break;
180
+ }
181
+ }
182
+ data[data.length] = i + 1;
183
+ worksheet.addRow(data);
184
+ }
185
+
186
+ await workbook.xlsx.writeFile(importTemplateXlsxPath);
187
+ app.logger.info('fill import template with random data on:' + importTemplateXlsxPath);
188
+ }
189
+
190
+ function getFilename(res) {
191
+ const disposition = res.headers['content-disposition'];
192
+ if (!disposition) return 'download.file';
193
+
194
+ const match = disposition.match(/filename\*?=(?:UTF-8'')?["']?([^"';]+)/i);
195
+ return decodeURIComponent(match?.[1] ?? 'download.file');
196
+ }
197
+
198
+ async function download(request: request.SuperAgentRequest): Promise<string> {
199
+ return new Promise<string>((resolve, reject) => {
200
+ let importTemplateXlsxPath = '';
201
+ const tmpDir = os.tmpdir();
202
+ const tmpFile = path.join(tmpDir, `download-${Date.now()}-${crypto.randomUUID()}`);
203
+ const writeStream = fs.createWriteStream(tmpFile);
204
+ request
205
+ .on('response', (res) => {
206
+ const filename = getFilename(res);
207
+ importTemplateXlsxPath = path.resolve(storagePath, filename);
208
+ })
209
+ .pipe(writeStream)
210
+ .on('finish', () => {
211
+ fs.mkdir(storagePath, { recursive: true }, (err) => {
212
+ if (err) {
213
+ reject(err);
214
+ return;
215
+ }
216
+ try {
217
+ fs.renameSync(tmpFile, importTemplateXlsxPath);
218
+ resolve(importTemplateXlsxPath);
219
+ } catch (e) {
220
+ reject(e);
221
+ }
222
+ });
223
+ })
224
+ .on('error', reject);
225
+ });
226
+ }
227
+
228
+ main()
229
+ .then(() => {
230
+ console.log('Data generation completed.');
231
+ process.exit(0);
232
+ })
233
+ .catch((error) => {
234
+ console.error('Error during data generation:', error);
235
+ process.exit(1);
236
+ });
@@ -0,0 +1,16 @@
1
+ const http = require('k6/http');
2
+ const { check } = require('k6');
3
+
4
+ export function setup() {
5
+ // 在测试开始前执行一次
6
+ let res = http.post(`${__ENV.TARGET_ORIGIN}/api/auth:signIn`, {
7
+ account: 'nocobase',
8
+ password: 'admin123',
9
+ });
10
+
11
+ check(res, { 'login succeeded': (r) => r.status === 200 });
12
+
13
+ // 假设返回里有 token
14
+ let data = res.json('data');
15
+ return { token: data.token };
16
+ }