@softeria/ms-365-mcp-server 0.2.2 → 0.3.0

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.
@@ -0,0 +1,210 @@
1
+ import { describe, expect, it, vi, beforeEach } from 'vitest';
2
+ import { z } from 'zod';
3
+
4
+ vi.mock('fs', () => {
5
+ return {
6
+ default: { readFileSync: vi.fn().mockReturnValue('mock yaml content') },
7
+ readFileSync: vi.fn().mockReturnValue('mock yaml content'),
8
+ };
9
+ });
10
+
11
+ vi.mock('js-yaml', () => {
12
+ const mockSpec = {
13
+ paths: {
14
+ '/test/path': {
15
+ get: {
16
+ parameters: [
17
+ { name: 'filter', in: 'query', required: false, schema: { type: 'string' } },
18
+ ],
19
+ },
20
+ },
21
+ '/test/{param}/path': {
22
+ get: {
23
+ parameters: [
24
+ { name: 'filter', in: 'query', required: false, schema: { type: 'string' } },
25
+ ],
26
+ },
27
+ },
28
+ '/excel/test': {
29
+ post: {
30
+ requestBody: {
31
+ content: {
32
+ 'application/json': {
33
+ schema: { type: 'object' },
34
+ },
35
+ },
36
+ },
37
+ },
38
+ },
39
+ },
40
+ };
41
+
42
+ return {
43
+ default: { load: vi.fn().mockReturnValue(mockSpec) },
44
+ load: vi.fn().mockReturnValue(mockSpec),
45
+ };
46
+ });
47
+
48
+ vi.mock('../src/logger.mjs', () => ({
49
+ default: {
50
+ info: vi.fn(),
51
+ warn: vi.fn(),
52
+ error: vi.fn(),
53
+ },
54
+ }));
55
+
56
+ // Mock param-mapper module
57
+ vi.mock('../src/param-mapper.mjs', () => ({
58
+ createFriendlyParamName: (name) => (name.startsWith('$') ? name.substring(1) : name),
59
+ registerParamMapping: vi.fn(),
60
+ getOriginalParamName: vi.fn(),
61
+ }));
62
+
63
+ import * as fs from 'fs';
64
+ import * as yaml from 'js-yaml';
65
+ import { mapToZodType, buildRequestUrl } from '../src/openapi-helpers.mjs';
66
+
67
+ function mockProcessParameter(parameter) {
68
+ const schema = parameter.schema?.type === 'string' ? z.string() : z.any();
69
+ return parameter.required ? schema : schema.optional();
70
+ }
71
+
72
+ describe('OpenAPI Helpers', () => {
73
+ describe('mapToZodType', () => {
74
+ it('should map string schema to z.string()', () => {
75
+ const schema = { type: 'string' };
76
+ const result = mapToZodType(schema);
77
+
78
+ expect(result).toBeInstanceOf(z.ZodString);
79
+ });
80
+
81
+ it('should map number schema to z.number()', () => {
82
+ const schema = { type: 'number' };
83
+ const result = mapToZodType(schema);
84
+
85
+ expect(result).toBeInstanceOf(z.ZodNumber);
86
+ });
87
+
88
+ it('should map integer schema to z.number()', () => {
89
+ const schema = { type: 'integer' };
90
+ const result = mapToZodType(schema);
91
+
92
+ expect(result).toBeInstanceOf(z.ZodNumber);
93
+ });
94
+
95
+ it('should map boolean schema to z.boolean()', () => {
96
+ const schema = { type: 'boolean' };
97
+ const result = mapToZodType(schema);
98
+
99
+ expect(result).toBeInstanceOf(z.ZodBoolean);
100
+ });
101
+
102
+ it('should map array schema to z.array()', () => {
103
+ const schema = { type: 'array', items: { type: 'string' } };
104
+ const result = mapToZodType(schema);
105
+
106
+ expect(result).toBeInstanceOf(z.ZodArray);
107
+ });
108
+
109
+ it('should map object schema to z.object()', () => {
110
+ const schema = {
111
+ type: 'object',
112
+ properties: {
113
+ name: { type: 'string' },
114
+ age: { type: 'integer' },
115
+ },
116
+ };
117
+ const result = mapToZodType(schema);
118
+
119
+ expect(result).toBeInstanceOf(z.ZodObject);
120
+ });
121
+
122
+ it('should handle $ref schema', () => {
123
+ const schema = { $ref: '#/components/schemas/StringType' };
124
+ const result = mapToZodType(schema);
125
+
126
+ expect(result).toBeInstanceOf(z.ZodString);
127
+ });
128
+ });
129
+
130
+ describe('buildRequestUrl', () => {
131
+ it('should build a URL without parameters', () => {
132
+ const baseUrl = '/test/path';
133
+ const params = {};
134
+ const pathParams = [];
135
+ const queryParamDefs = [];
136
+
137
+ const result = buildRequestUrl(baseUrl, params, pathParams, queryParamDefs);
138
+
139
+ expect(result).toBe('/test/path');
140
+ });
141
+
142
+ it('should replace path parameters', () => {
143
+ const baseUrl = '/test/{id}/path';
144
+ const params = { id: '123' };
145
+ const pathParams = ['{id}'];
146
+ const queryParamDefs = [];
147
+
148
+ const result = buildRequestUrl(baseUrl, params, pathParams, queryParamDefs);
149
+
150
+ expect(result).toBe('/test/123/path');
151
+ });
152
+
153
+ it('should add query parameters', () => {
154
+ const baseUrl = '/test/path';
155
+ const params = { filter: 'test' };
156
+ const pathParams = [];
157
+ const queryParamDefs = [{ name: 'filter', in: 'query' }];
158
+
159
+ const result = buildRequestUrl(baseUrl, params, pathParams, queryParamDefs);
160
+
161
+ expect(result).toBe('/test/path?filter=test');
162
+ });
163
+
164
+ it('should handle Excel range addresses', () => {
165
+ const baseUrl = "/workbook/worksheets/{id}/range(address='{address}')";
166
+ const params = { id: 'Sheet1', address: 'A1:C10' };
167
+ const pathParams = ['{id}'];
168
+ const queryParamDefs = [];
169
+
170
+ const result = buildRequestUrl(baseUrl, params, pathParams, queryParamDefs);
171
+
172
+ expect(result).toBe("/workbook/worksheets/Sheet1/range(address='A1%3AC10')");
173
+ });
174
+
175
+ it('should handle array parameters', () => {
176
+ const baseUrl = '/test/path';
177
+ const params = { select: ['name', 'email'] };
178
+ const pathParams = [];
179
+ const queryParamDefs = [{ name: '$select', in: 'query' }];
180
+ const toolName = 'test-tool';
181
+
182
+ const result = buildRequestUrl(baseUrl, params, pathParams, queryParamDefs);
183
+
184
+ expect(result).toBe('/test/path?$select=name,email');
185
+ });
186
+
187
+ it('should map friendly parameter names to original names with $ prefix', () => {
188
+ const baseUrl = '/test/path';
189
+ const params = {
190
+ select: 'name,email',
191
+ filter: "contains(displayName, 'test')",
192
+ orderby: 'displayName',
193
+ };
194
+ const pathParams = [];
195
+ const queryParamDefs = [
196
+ { name: '$select', in: 'query' },
197
+ { name: '$filter', in: 'query' },
198
+ { name: '$orderby', in: 'query' },
199
+ ];
200
+ const toolName = 'test-tool';
201
+
202
+ const result = buildRequestUrl(baseUrl, params, pathParams, queryParamDefs);
203
+
204
+ // URL should contain original parameter names with $ prefix
205
+ expect(result).toContain('$select=');
206
+ expect(result).toContain('$filter=');
207
+ expect(result).toContain('$orderby=');
208
+ });
209
+ });
210
+ });
@@ -0,0 +1,56 @@
1
+ import { expect, test, describe } from 'vitest';
2
+ import {
3
+ createFriendlyParamName,
4
+ registerParamMapping,
5
+ getOriginalParamName,
6
+ transformParamsToOriginal
7
+ } from '../src/param-mapper.mjs';
8
+
9
+ describe('Parameter Mapper', () => {
10
+ test('createFriendlyParamName removes $ prefix', () => {
11
+ expect(createFriendlyParamName('$orderby')).toBe('orderby');
12
+ expect(createFriendlyParamName('$select')).toBe('select');
13
+ expect(createFriendlyParamName('$expand')).toBe('expand');
14
+ });
15
+
16
+ test('createFriendlyParamName leaves regular params unchanged', () => {
17
+ expect(createFriendlyParamName('subject')).toBe('subject');
18
+ expect(createFriendlyParamName('contentType')).toBe('contentType');
19
+ });
20
+
21
+ test('registerParamMapping and getOriginalParamName work together', () => {
22
+ // Register a mapping
23
+ registerParamMapping('list-mail-messages', 'orderby', '$orderby');
24
+
25
+ // Retrieve the original name
26
+ expect(getOriginalParamName('list-mail-messages', 'orderby')).toBe('$orderby');
27
+
28
+ // Unknown mappings return the friendly name
29
+ expect(getOriginalParamName('list-mail-messages', 'unknown')).toBe('unknown');
30
+ expect(getOriginalParamName('unknown-tool', 'orderby')).toBe('orderby');
31
+ });
32
+
33
+ test('transformParamsToOriginal converts all params', () => {
34
+ // Register mappings
35
+ registerParamMapping('list-mail-messages', 'orderby', '$orderby');
36
+ registerParamMapping('list-mail-messages', 'select', '$select');
37
+ registerParamMapping('list-mail-messages', 'top', '$top');
38
+
39
+ // Transform params
40
+ const params = {
41
+ orderby: 'receivedDateTime desc',
42
+ select: 'subject,from,receivedDateTime',
43
+ top: 10,
44
+ normalParam: 'value'
45
+ };
46
+
47
+ const transformed = transformParamsToOriginal('list-mail-messages', params);
48
+
49
+ expect(transformed).toEqual({
50
+ '$orderby': 'receivedDateTime desc',
51
+ '$select': 'subject,from,receivedDateTime',
52
+ '$top': 10,
53
+ 'normalParam': 'value'
54
+ });
55
+ });
56
+ });