@wx-sab/renkei 1.3.2 → 1.3.4

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/lib/cli.js CHANGED
@@ -39,8 +39,8 @@ TSNode.register({
39
39
  compilerOptions: {
40
40
  strict: false,
41
41
  target: "es6",
42
- module: "commonjs",
43
- moduleResolution: "node",
42
+ module: "ESNext",
43
+ moduleResolution: "Bundler",
44
44
  declaration: false,
45
45
  removeComments: false,
46
46
  esModuleInterop: true,
@@ -1,3 +1,12 @@
1
1
  import commander from 'commander';
2
+ import { ConfigWithProjects, ServiceConfig } from '../core/interface';
3
+ export declare const readProjectConfigFile: () => Promise<ConfigWithProjects | null>;
4
+ export declare const parseCommandOptions: (param: any) => {
5
+ services: any;
6
+ folders: any;
7
+ };
8
+ export declare const handleInteractiveSelection: (config: ServiceConfig, services: string[]) => Promise<boolean>;
9
+ export declare const handleCommandLineFolders: (config: ServiceConfig, services: string[], folders: string[]) => void;
10
+ export declare const runApiCommand: (param: any) => Promise<void>;
2
11
  declare const parse: commander.Command;
3
12
  export default parse;
@@ -12,6 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.runApiCommand = exports.handleCommandLineFolders = exports.handleInteractiveSelection = exports.parseCommandOptions = exports.readProjectConfigFile = void 0;
15
16
  const promises_1 = __importDefault(require("fs/promises"));
16
17
  const path_1 = __importDefault(require("path"));
17
18
  const commander_1 = __importDefault(require("commander"));
@@ -35,12 +36,14 @@ const readProjectConfigFile = () => __awaiter(void 0, void 0, void 0, function*
35
36
  }
36
37
  return null;
37
38
  });
39
+ exports.readProjectConfigFile = readProjectConfigFile;
38
40
  const parseCommandOptions = (param) => {
39
41
  var _a, _b;
40
42
  const services = (((_a = param.service) === null || _a === void 0 ? void 0 : _a.split(',')) || []).filter(Boolean);
41
43
  const folders = (((_b = param.folder) === null || _b === void 0 ? void 0 : _b.split(',')) || []).filter(Boolean);
42
44
  return { services, folders };
43
45
  };
46
+ exports.parseCommandOptions = parseCommandOptions;
44
47
  const handleInteractiveSelection = (config, services) => __awaiter(void 0, void 0, void 0, function* () {
45
48
  const _configs = (0, config_utils_1.collectConfig)(config).filter((conf) => {
46
49
  if (services.length > 0) {
@@ -58,6 +61,7 @@ const handleInteractiveSelection = (config, services) => __awaiter(void 0, void
58
61
  return false;
59
62
  }
60
63
  });
64
+ exports.handleInteractiveSelection = handleInteractiveSelection;
61
65
  const handleCommandLineFolders = (config, services, folders) => {
62
66
  const folderMap = new Map();
63
67
  (0, config_utils_1.collectConfig)(config).forEach((conf) => {
@@ -67,26 +71,31 @@ const handleCommandLineFolders = (config, services, folders) => {
67
71
  });
68
72
  (0, config_utils_1.applyFoldersToConfig)(config, folderMap, services);
69
73
  };
70
- const parse = new commander_1.default.Command('api')
71
- .option('-s, --service <service>', '只获取指定的服务,多个服务用","分隔', ',')
72
- .option('-f, --folder <folder>', '只获取指定的文件夹,多个文件夹用","分隔', ',')
73
- .option('-i, --interactive', '启用交互式选择文件夹', false)
74
- .action((param) => __awaiter(void 0, void 0, void 0, function* () {
75
- const { services, folders } = parseCommandOptions(param);
76
- const config = yield readProjectConfigFile();
74
+ exports.handleCommandLineFolders = handleCommandLineFolders;
75
+ const runApiCommand = (param) => __awaiter(void 0, void 0, void 0, function* () {
76
+ const { services, folders } = (0, exports.parseCommandOptions)(param);
77
+ const config = yield (0, exports.readProjectConfigFile)();
77
78
  if (!config) {
78
79
  console.warn('未找到配置文件');
79
80
  return;
80
81
  }
81
82
  if (param.interactive) {
82
- const success = yield handleInteractiveSelection(config, services);
83
+ const success = yield (0, exports.handleInteractiveSelection)(config, services);
83
84
  if (!success) {
84
85
  return;
85
86
  }
86
87
  }
87
88
  else if (folders.length > 0) {
88
- handleCommandLineFolders(config, services, folders);
89
+ (0, exports.handleCommandLineFolders)(config, services, folders);
89
90
  }
90
91
  (0, generate_1.generateApis)(config, services);
92
+ });
93
+ exports.runApiCommand = runApiCommand;
94
+ const parse = new commander_1.default.Command('api')
95
+ .option('-s, --service <service>', '只获取指定的服务,多个服务用","分隔', ',')
96
+ .option('-f, --folder <folder>', '只获取指定的文件夹,多个文件夹用","分隔', ',')
97
+ .option('-i, --interactive', '启用交互式选择文件夹', false)
98
+ .action((param) => __awaiter(void 0, void 0, void 0, function* () {
99
+ yield (0, exports.runApiCommand)(param);
91
100
  }));
92
101
  exports.default = parse;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const vitest_1 = require("vitest");
16
+ const promises_1 = __importDefault(require("fs/promises"));
17
+ vitest_1.vi.mock('fs/promises', () => ({
18
+ default: { access: vitest_1.vi.fn() }
19
+ }));
20
+ vitest_1.vi.mock('commander', () => ({
21
+ default: { Command: vitest_1.vi.fn(function () {
22
+ this.option = vitest_1.vi.fn().mockReturnThis();
23
+ this.action = vitest_1.vi.fn().mockReturnThis();
24
+ return this;
25
+ }) }
26
+ }));
27
+ vitest_1.vi.mock('../core/generate', () => ({
28
+ generateApis: vitest_1.vi.fn()
29
+ }));
30
+ vitest_1.vi.mock('../core/folder-selector', () => ({
31
+ selectFoldersForServices: vitest_1.vi.fn()
32
+ }));
33
+ const api_1 = require("./api");
34
+ const generate_1 = require("../core/generate");
35
+ const folder_selector_1 = require("../core/folder-selector");
36
+ (0, vitest_1.describe)('parseCommandOptions', () => {
37
+ (0, vitest_1.it)('should parse services from comma-separated string', () => {
38
+ const result = (0, api_1.parseCommandOptions)({ service: 'svc1,svc2' });
39
+ (0, vitest_1.expect)(result.services).toEqual(['svc1', 'svc2']);
40
+ });
41
+ (0, vitest_1.it)('should parse folders from comma-separated string', () => {
42
+ const result = (0, api_1.parseCommandOptions)({ folder: 'folder1,folder2' });
43
+ (0, vitest_1.expect)(result.folders).toEqual(['folder1', 'folder2']);
44
+ });
45
+ (0, vitest_1.it)('should handle empty/undefined params', () => {
46
+ const result = (0, api_1.parseCommandOptions)({});
47
+ (0, vitest_1.expect)(result.services).toEqual([]);
48
+ (0, vitest_1.expect)(result.folders).toEqual([]);
49
+ });
50
+ (0, vitest_1.it)('should filter out empty strings from svc1,,svc2,', () => {
51
+ const result = (0, api_1.parseCommandOptions)({ service: 'svc1,,svc2,' });
52
+ (0, vitest_1.expect)(result.services).toEqual(['svc1', 'svc2']);
53
+ });
54
+ (0, vitest_1.it)('should handle default comma value for service', () => {
55
+ const result = (0, api_1.parseCommandOptions)({ service: ',' });
56
+ (0, vitest_1.expect)(result.services).toEqual([]);
57
+ });
58
+ });
59
+ (0, vitest_1.describe)('handleCommandLineFolders', () => {
60
+ (0, vitest_1.it)('should apply folders to all configs when services is empty', () => {
61
+ const config = {
62
+ serviceName: 'parent',
63
+ projects: [
64
+ { serviceName: 'child1' },
65
+ { serviceName: 'child2' }
66
+ ]
67
+ };
68
+ (0, api_1.handleCommandLineFolders)(config, [], ['folder1', 'folder2']);
69
+ (0, vitest_1.expect)(config.folders).toEqual(['folder1', 'folder2']);
70
+ (0, vitest_1.expect)(config.projects[0].folders).toEqual(['folder1', 'folder2']);
71
+ (0, vitest_1.expect)(config.projects[1].folders).toEqual(['folder1', 'folder2']);
72
+ });
73
+ (0, vitest_1.it)('should only apply to specified services', () => {
74
+ const config = {
75
+ serviceName: 'parent',
76
+ projects: [
77
+ { serviceName: 'child1' },
78
+ { serviceName: 'child2' }
79
+ ]
80
+ };
81
+ (0, api_1.handleCommandLineFolders)(config, ['child1'], ['folderA']);
82
+ (0, vitest_1.expect)(config.folders).toBeUndefined();
83
+ (0, vitest_1.expect)(config.projects[0].folders).toEqual(['folderA']);
84
+ (0, vitest_1.expect)(config.projects[1].folders).toBeUndefined();
85
+ });
86
+ (0, vitest_1.it)('should handle single config without projects', () => {
87
+ const config = {
88
+ serviceName: 'single'
89
+ };
90
+ (0, api_1.handleCommandLineFolders)(config, [], ['folder1']);
91
+ (0, vitest_1.expect)(config.folders).toEqual(['folder1']);
92
+ });
93
+ });
94
+ (0, vitest_1.describe)('readProjectConfigFile', () => {
95
+ (0, vitest_1.beforeEach)(() => {
96
+ vitest_1.vi.mocked(promises_1.default.access).mockReset();
97
+ });
98
+ (0, vitest_1.it)('should return null when no config file exists', () => __awaiter(void 0, void 0, void 0, function* () {
99
+ vitest_1.vi.mocked(promises_1.default.access).mockRejectedValue(new Error('not found'));
100
+ const result = yield (0, api_1.readProjectConfigFile)();
101
+ (0, vitest_1.expect)(result).toBeNull();
102
+ }));
103
+ (0, vitest_1.it)('should check all CONFIG_FILE_NAMES', () => __awaiter(void 0, void 0, void 0, function* () {
104
+ const checkedFiles = [];
105
+ vitest_1.vi.mocked(promises_1.default.access).mockImplementation((p) => __awaiter(void 0, void 0, void 0, function* () {
106
+ checkedFiles.push(String(p));
107
+ throw new Error('not found');
108
+ }));
109
+ yield (0, api_1.readProjectConfigFile)();
110
+ (0, vitest_1.expect)(checkedFiles).toHaveLength(2);
111
+ (0, vitest_1.expect)(checkedFiles.some(f => f.endsWith('renkei.config.js'))).toBe(true);
112
+ (0, vitest_1.expect)(checkedFiles.some(f => f.endsWith('renkei.config.ts'))).toBe(true);
113
+ }));
114
+ });
115
+ (0, vitest_1.describe)('runApiCommand', () => {
116
+ (0, vitest_1.beforeEach)(() => {
117
+ vitest_1.vi.mocked(promises_1.default.access).mockReset();
118
+ vitest_1.vi.mocked(generate_1.generateApis).mockReset();
119
+ vitest_1.vi.mocked(folder_selector_1.selectFoldersForServices).mockReset();
120
+ });
121
+ (0, vitest_1.it)('should warn and return early when config not found', () => __awaiter(void 0, void 0, void 0, function* () {
122
+ vitest_1.vi.mocked(promises_1.default.access).mockRejectedValue(new Error('not found'));
123
+ const warnSpy = vitest_1.vi.spyOn(console, 'warn').mockImplementation(() => { });
124
+ yield (0, api_1.runApiCommand)({});
125
+ (0, vitest_1.expect)(warnSpy).toHaveBeenCalledWith('未找到配置文件');
126
+ (0, vitest_1.expect)(generate_1.generateApis).not.toHaveBeenCalled();
127
+ warnSpy.mockRestore();
128
+ }));
129
+ // NOTE: The "config found" paths (interactive, folders, default) require require()
130
+ // to load .js/.ts config files, which does not work in Vitest ESM context.
131
+ // These paths are better covered by integration tests.
132
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const constants_1 = require("./constants");
5
+ (0, vitest_1.describe)('constants', () => {
6
+ (0, vitest_1.describe)('CONFIG_FILE_NAMES', () => {
7
+ (0, vitest_1.it)('should contain renkei.config.js and renkei.config.ts', () => {
8
+ (0, vitest_1.expect)(constants_1.CONFIG_FILE_NAMES).toContain('renkei.config.js');
9
+ (0, vitest_1.expect)(constants_1.CONFIG_FILE_NAMES).toContain('renkei.config.ts');
10
+ (0, vitest_1.expect)(constants_1.CONFIG_FILE_NAMES).toHaveLength(2);
11
+ });
12
+ });
13
+ (0, vitest_1.describe)('DOC_SOURCE_SWAGGER', () => {
14
+ (0, vitest_1.it)('should generate swagger source url', () => {
15
+ (0, vitest_1.expect)((0, constants_1.DOC_SOURCE_SWAGGER)('my-service')).toContain('my-service');
16
+ (0, vitest_1.expect)((0, constants_1.DOC_SOURCE_SWAGGER)('my-service')).toContain('v2/api-docs');
17
+ });
18
+ });
19
+ (0, vitest_1.describe)('DOC_SOURCE_APIFOX', () => {
20
+ (0, vitest_1.it)('should generate apifox source url with projectId', () => {
21
+ (0, vitest_1.expect)((0, constants_1.DOC_SOURCE_APIFOX)('12345')).toContain('projectId=12345');
22
+ (0, vitest_1.expect)((0, constants_1.DOC_SOURCE_APIFOX)('12345')).toContain('export/openapi');
23
+ });
24
+ });
25
+ (0, vitest_1.describe)('MOCK_SERVICE_APIFOX', () => {
26
+ (0, vitest_1.it)('should generate apifox mock url with projectId', () => {
27
+ (0, vitest_1.expect)((0, constants_1.MOCK_SERVICE_APIFOX)('12345')).toContain('12345');
28
+ (0, vitest_1.expect)((0, constants_1.MOCK_SERVICE_APIFOX)('12345')).toContain('mock.apifox.cn');
29
+ });
30
+ });
31
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const vitest_1 = require("vitest");
16
+ const axios_1 = __importDefault(require("axios"));
17
+ const inquirer_1 = __importDefault(require("inquirer"));
18
+ const utils_1 = require("./utils");
19
+ const folder_selector_1 = require("./folder-selector");
20
+ vitest_1.vi.mock('axios', () => ({
21
+ default: { get: vitest_1.vi.fn() }
22
+ }));
23
+ vitest_1.vi.mock('inquirer', () => ({
24
+ default: {
25
+ prompt: vitest_1.vi.fn(),
26
+ Separator: class Separator {
27
+ constructor(line) {
28
+ this.line = line;
29
+ }
30
+ }
31
+ }
32
+ }));
33
+ vitest_1.vi.mock('./utils', () => ({
34
+ generateConfig: vitest_1.vi.fn()
35
+ }));
36
+ (0, vitest_1.describe)('folder-selector', () => {
37
+ (0, vitest_1.beforeEach)(() => {
38
+ vitest_1.vi.clearAllMocks();
39
+ });
40
+ (0, vitest_1.describe)('fetchSwaggerDocument', () => {
41
+ (0, vitest_1.it)('should successfully fetch document when source is configured', () => __awaiter(void 0, void 0, void 0, function* () {
42
+ const mockData = { paths: {}, info: { title: 'Test API' } };
43
+ vitest_1.vi.mocked(utils_1.generateConfig).mockReturnValue({ source: 'http://example.com/swagger' });
44
+ vitest_1.vi.mocked(axios_1.default.get).mockResolvedValue({ data: mockData });
45
+ const result = yield (0, folder_selector_1.fetchSwaggerDocument)({ serviceName: 'test-service' });
46
+ (0, vitest_1.expect)(utils_1.generateConfig).toHaveBeenCalledWith({ serviceName: 'test-service' });
47
+ (0, vitest_1.expect)(axios_1.default.get).toHaveBeenCalledWith('http://example.com/swagger', {
48
+ timeout: 30000,
49
+ validateStatus: vitest_1.expect.any(Function)
50
+ });
51
+ (0, vitest_1.expect)(result).toEqual(mockData);
52
+ }));
53
+ (0, vitest_1.it)('should throw "未配置文档源" when generateConfig returns no source', () => __awaiter(void 0, void 0, void 0, function* () {
54
+ vitest_1.vi.mocked(utils_1.generateConfig).mockReturnValue({ source: undefined });
55
+ yield (0, vitest_1.expect)((0, folder_selector_1.fetchSwaggerDocument)({ serviceName: 'test-service' })).rejects.toThrow('未配置文档源');
56
+ }));
57
+ (0, vitest_1.it)('should throw "获取文档失败" on network error', () => __awaiter(void 0, void 0, void 0, function* () {
58
+ vitest_1.vi.mocked(utils_1.generateConfig).mockReturnValue({ source: 'http://example.com/swagger' });
59
+ vitest_1.vi.mocked(axios_1.default.get).mockRejectedValue(new Error('Network Error'));
60
+ yield (0, vitest_1.expect)((0, folder_selector_1.fetchSwaggerDocument)({ serviceName: 'test-service' })).rejects.toThrow('获取文档失败: Network Error');
61
+ }));
62
+ (0, vitest_1.it)('should include response.data.message in error when available', () => __awaiter(void 0, void 0, void 0, function* () {
63
+ vitest_1.vi.mocked(utils_1.generateConfig).mockReturnValue({ source: 'http://example.com/swagger' });
64
+ const error = {
65
+ response: {
66
+ data: { message: 'Server internal error' }
67
+ },
68
+ message: 'Request failed'
69
+ };
70
+ vitest_1.vi.mocked(axios_1.default.get).mockRejectedValue(error);
71
+ yield (0, vitest_1.expect)((0, folder_selector_1.fetchSwaggerDocument)({ serviceName: 'test-service' })).rejects.toThrow('获取文档失败: Server internal error');
72
+ }));
73
+ });
74
+ (0, vitest_1.describe)('extractFoldersFromSwagger', () => {
75
+ (0, vitest_1.it)('should extract folders from x-apifox-folder', () => {
76
+ const swaggerDoc = {
77
+ paths: {
78
+ '/api/users': {
79
+ get: {
80
+ 'x-apifox-folder': '用户管理/用户列表'
81
+ }
82
+ }
83
+ }
84
+ };
85
+ const result = (0, folder_selector_1.extractFoldersFromSwagger)(swaggerDoc);
86
+ (0, vitest_1.expect)(result).toHaveLength(1);
87
+ (0, vitest_1.expect)(result[0]).toEqual({
88
+ name: '用户管理',
89
+ path: '用户管理',
90
+ count: 1
91
+ });
92
+ });
93
+ (0, vitest_1.it)('should return empty array for doc without paths', () => {
94
+ const result = (0, folder_selector_1.extractFoldersFromSwagger)({});
95
+ (0, vitest_1.expect)(result).toEqual([]);
96
+ });
97
+ (0, vitest_1.it)('should return empty array for paths without x-apifox-folder', () => {
98
+ const swaggerDoc = {
99
+ paths: {
100
+ '/api/users': {
101
+ get: {
102
+ summary: 'Get users'
103
+ }
104
+ }
105
+ }
106
+ };
107
+ const result = (0, folder_selector_1.extractFoldersFromSwagger)(swaggerDoc);
108
+ (0, vitest_1.expect)(result).toEqual([]);
109
+ });
110
+ (0, vitest_1.it)('should sort folders by name', () => {
111
+ const swaggerDoc = {
112
+ paths: {
113
+ '/api/users': {
114
+ get: {
115
+ 'x-apifox-folder': '用户管理'
116
+ }
117
+ },
118
+ '/api/products': {
119
+ get: {
120
+ 'x-apifox-folder': '商品管理'
121
+ }
122
+ },
123
+ '/api/orders': {
124
+ get: {
125
+ 'x-apifox-folder': '订单管理'
126
+ }
127
+ }
128
+ }
129
+ };
130
+ const result = (0, folder_selector_1.extractFoldersFromSwagger)(swaggerDoc);
131
+ const names = result.map(f => f.name);
132
+ (0, vitest_1.expect)(names).toEqual([...names].sort());
133
+ });
134
+ (0, vitest_1.it)('should aggregate count for same folder across paths and methods', () => {
135
+ const swaggerDoc = {
136
+ paths: {
137
+ '/api/users': {
138
+ get: {
139
+ 'x-apifox-folder': '用户管理'
140
+ },
141
+ post: {
142
+ 'x-apifox-folder': '用户管理'
143
+ }
144
+ },
145
+ '/api/users/{id}': {
146
+ get: {
147
+ 'x-apifox-folder': '用户管理'
148
+ }
149
+ },
150
+ '/api/orders': {
151
+ get: {
152
+ 'x-apifox-folder': '订单管理'
153
+ },
154
+ post: {
155
+ 'x-apifox-folder': '订单管理'
156
+ }
157
+ }
158
+ }
159
+ };
160
+ const result = (0, folder_selector_1.extractFoldersFromSwagger)(swaggerDoc);
161
+ (0, vitest_1.expect)(result).toHaveLength(2);
162
+ const userFolder = result.find(f => f.name === '用户管理');
163
+ const orderFolder = result.find(f => f.name === '订单管理');
164
+ (0, vitest_1.expect)(userFolder).toBeDefined();
165
+ (0, vitest_1.expect)(userFolder.count).toBe(3);
166
+ (0, vitest_1.expect)(orderFolder).toBeDefined();
167
+ (0, vitest_1.expect)(orderFolder.count).toBe(2);
168
+ });
169
+ });
170
+ (0, vitest_1.describe)('selectFolders', () => {
171
+ (0, vitest_1.it)('should return empty array when no folders', () => __awaiter(void 0, void 0, void 0, function* () {
172
+ const result = yield (0, folder_selector_1.selectFolders)([]);
173
+ (0, vitest_1.expect)(result).toEqual([]);
174
+ (0, vitest_1.expect)(inquirer_1.default.prompt).not.toHaveBeenCalled();
175
+ }));
176
+ (0, vitest_1.it)('should return selected folder names from prompt', () => __awaiter(void 0, void 0, void 0, function* () {
177
+ const folders = [
178
+ { name: '用户管理', path: '用户管理', count: 5 },
179
+ { name: '商品管理', path: '商品管理', count: 3 }
180
+ ];
181
+ vitest_1.vi.mocked(inquirer_1.default.prompt).mockResolvedValue({
182
+ selectedFolders: ['用户管理']
183
+ });
184
+ const result = yield (0, folder_selector_1.selectFolders)(folders);
185
+ (0, vitest_1.expect)(result).toEqual(['用户管理']);
186
+ }));
187
+ (0, vitest_1.it)('should return empty array when __ALL__ is selected', () => __awaiter(void 0, void 0, void 0, function* () {
188
+ const folders = [
189
+ { name: '用户管理', path: '用户管理', count: 5 },
190
+ { name: '商品管理', path: '商品管理', count: 3 }
191
+ ];
192
+ vitest_1.vi.mocked(inquirer_1.default.prompt).mockResolvedValue({
193
+ selectedFolders: ['__ALL__']
194
+ });
195
+ const result = yield (0, folder_selector_1.selectFolders)(folders);
196
+ (0, vitest_1.expect)(result).toEqual([]);
197
+ }));
198
+ });
199
+ (0, vitest_1.describe)('selectFoldersForServices', () => {
200
+ (0, vitest_1.it)('should return selections for each service', () => __awaiter(void 0, void 0, void 0, function* () {
201
+ const configs = [
202
+ { serviceName: 'service-a' },
203
+ { serviceName: 'service-b' }
204
+ ];
205
+ const mockSwaggerDoc = {
206
+ paths: {
207
+ '/api/users': {
208
+ get: { 'x-apifox-folder': '用户管理' }
209
+ }
210
+ }
211
+ };
212
+ vitest_1.vi.mocked(utils_1.generateConfig)
213
+ .mockReturnValueOnce({ source: 'http://a.com/swagger' })
214
+ .mockReturnValueOnce({ source: 'http://b.com/swagger' });
215
+ vitest_1.vi.mocked(axios_1.default.get)
216
+ .mockResolvedValueOnce({ data: mockSwaggerDoc })
217
+ .mockResolvedValueOnce({ data: mockSwaggerDoc });
218
+ vitest_1.vi.mocked(inquirer_1.default.prompt)
219
+ .mockResolvedValueOnce({ selectedFolders: ['用户管理'] })
220
+ .mockResolvedValueOnce({ selectedFolders: ['__ALL__'] });
221
+ const result = yield (0, folder_selector_1.selectFoldersForServices)(configs);
222
+ (0, vitest_1.expect)(result).toBeInstanceOf(Map);
223
+ (0, vitest_1.expect)(result.get('service-a')).toEqual(['用户管理']);
224
+ (0, vitest_1.expect)(result.get('service-b')).toEqual([]);
225
+ }));
226
+ (0, vitest_1.it)('should handle fetch failure gracefully and set empty array', () => __awaiter(void 0, void 0, void 0, function* () {
227
+ const configs = [{ serviceName: 'failing-service' }];
228
+ vitest_1.vi.mocked(utils_1.generateConfig).mockReturnValue({ source: 'http://fail.com/swagger' });
229
+ vitest_1.vi.mocked(axios_1.default.get).mockRejectedValue(new Error('Network Error'));
230
+ const result = yield (0, folder_selector_1.selectFoldersForServices)(configs);
231
+ (0, vitest_1.expect)(result).toBeInstanceOf(Map);
232
+ (0, vitest_1.expect)(result.get('failing-service')).toEqual([]);
233
+ }));
234
+ (0, vitest_1.it)('should set empty array when no folders found in swagger doc', () => __awaiter(void 0, void 0, void 0, function* () {
235
+ const configs = [{ serviceName: 'empty-service' }];
236
+ vitest_1.vi.mocked(utils_1.generateConfig).mockReturnValue({ source: 'http://empty.com/swagger' });
237
+ vitest_1.vi.mocked(axios_1.default.get).mockResolvedValue({
238
+ data: { paths: { '/api/test': { get: {} } } }
239
+ });
240
+ const result = yield (0, folder_selector_1.selectFoldersForServices)(configs);
241
+ (0, vitest_1.expect)(result).toBeInstanceOf(Map);
242
+ (0, vitest_1.expect)(result.get('empty-service')).toEqual([]);
243
+ (0, vitest_1.expect)(inquirer_1.default.prompt).not.toHaveBeenCalled();
244
+ }));
245
+ });
246
+ });
@@ -0,0 +1 @@
1
+ export {};