@usebruno/filestore 0.11.0 → 0.13.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.
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/workers/worker-script.js +1 -1
- package/dist/cjs/workers/worker-script.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/workers/worker-script.js +1 -1
- package/dist/esm/workers/worker-script.js.map +1 -1
- package/dist/formats/bru/utils/redact-large-text-blocks.d.ts.map +1 -1
- package/dist/formats/yml/common/omit-headers.d.ts +3 -0
- package/dist/formats/yml/common/omit-headers.d.ts.map +1 -0
- package/dist/formats/yml/common/scripts.d.ts +3 -5
- package/dist/formats/yml/common/scripts.d.ts.map +1 -1
- package/dist/formats/yml/index.d.ts +3 -1
- package/dist/formats/yml/index.d.ts.map +1 -1
- package/dist/formats/yml/items/parseGraphQLRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseGrpcRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/parseHttpRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyGraphQLRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyGrpcRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyHttpRequest.d.ts.map +1 -1
- package/dist/formats/yml/items/stringifyWebsocketRequest.d.ts.map +1 -1
- package/dist/formats/yml/mockServerTypes.d.ts +95 -0
- package/dist/formats/yml/mockServerTypes.d.ts.map +1 -0
- package/dist/formats/yml/parseCollection.d.ts.map +1 -1
- package/dist/formats/yml/parseEnvironment.d.ts.map +1 -1
- package/dist/formats/yml/parseFolder.d.ts.map +1 -1
- package/dist/formats/yml/parseMockServer.d.ts +4 -0
- package/dist/formats/yml/parseMockServer.d.ts.map +1 -0
- package/dist/formats/yml/stringifyCollection.d.ts.map +1 -1
- package/dist/formats/yml/stringifyEnvironment.d.ts.map +1 -1
- package/dist/formats/yml/stringifyFolder.d.ts.map +1 -1
- package/dist/formats/yml/stringifyMockServer.d.ts +4 -0
- package/dist/formats/yml/stringifyMockServer.d.ts.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/workers/WorkerQueue/index.d.ts +3 -2
- package/dist/workers/WorkerQueue/index.d.ts.map +1 -1
- package/dist/workers/index.d.ts +4 -0
- package/dist/workers/index.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/constants.ts +5 -1
- package/src/formats/bru/tests/redact-large-text-blocks.spec.js +220 -1
- package/src/formats/bru/utils/redact-large-text-blocks.ts +46 -1
- package/src/formats/yml/common/omit-headers.spec.ts +16 -0
- package/src/formats/yml/common/omit-headers.ts +12 -0
- package/src/formats/yml/common/scripts.spec.ts +155 -0
- package/src/formats/yml/common/scripts.ts +55 -29
- package/src/formats/yml/grpc-round-trip.spec.ts +110 -0
- package/src/formats/yml/index.ts +5 -1
- package/src/formats/yml/items/parseGraphQLRequest.ts +6 -16
- package/src/formats/yml/items/parseGrpcRequest.ts +14 -6
- package/src/formats/yml/items/parseHttpRequest.ts +10 -14
- package/src/formats/yml/items/stringifyGraphQLRequest.ts +8 -14
- package/src/formats/yml/items/stringifyGrpcRequest.ts +2 -1
- package/src/formats/yml/items/stringifyHttpRequest.ts +13 -13
- package/src/formats/yml/items/stringifyWebsocketRequest.ts +3 -1
- package/src/formats/yml/mockServerTypes.ts +95 -0
- package/src/formats/yml/parseCollection.spec.ts +123 -0
- package/src/formats/yml/parseCollection.ts +23 -14
- package/src/formats/yml/parseEnvironment.spec.ts +49 -0
- package/src/formats/yml/parseEnvironment.ts +6 -0
- package/src/formats/yml/parseFolder.spec.ts +18 -0
- package/src/formats/yml/parseFolder.ts +5 -1
- package/src/formats/yml/parseMockServer.spec.ts +180 -0
- package/src/formats/yml/parseMockServer.ts +113 -0
- package/src/formats/yml/script.spec.ts +159 -0
- package/src/formats/yml/stringifyCollection.spec.ts +146 -0
- package/src/formats/yml/stringifyCollection.ts +29 -19
- package/src/formats/yml/stringifyEnvironment.spec.ts +58 -0
- package/src/formats/yml/stringifyEnvironment.ts +6 -0
- package/src/formats/yml/stringifyFolder.spec.ts +23 -0
- package/src/formats/yml/stringifyFolder.ts +10 -3
- package/src/formats/yml/stringifyMockServer.spec.ts +177 -0
- package/src/formats/yml/stringifyMockServer.ts +129 -0
- package/src/formats/yml/tests/defaultEnvironment.spec.js +50 -0
- package/src/formats/yml/tests/stringifyEnvironment.spec.ts +26 -0
- package/src/formats/yml/utils.ts +1 -1
- package/src/index.ts +34 -1
- package/src/maxRedirects.spec.ts +144 -0
- package/src/types.ts +1 -0
- package/src/workers/WorkerQueue/index.ts +10 -2
- package/src/workers/index.ts +25 -1
- package/src/workers/worker-script.ts +45 -21
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { GrpcRequest as BrunoGrpcRequest } from '@usebruno/schema-types/requests/grpc';
|
|
2
|
+
import stringifyItem from './stringifyItem';
|
|
3
|
+
import parseItem from './parseItem';
|
|
4
|
+
|
|
5
|
+
const parseGrpcItem = (yml: string) => parseItem(yml).request as BrunoGrpcRequest;
|
|
6
|
+
|
|
7
|
+
const grpcItem = (script: Record<string, string | null>, tests: string | null = null) => ({
|
|
8
|
+
uid: 'i1',
|
|
9
|
+
type: 'grpc-request',
|
|
10
|
+
name: 'say hello',
|
|
11
|
+
seq: 1,
|
|
12
|
+
request: {
|
|
13
|
+
url: 'grpc://localhost:50051',
|
|
14
|
+
method: '/hello.Greeter/SayHello',
|
|
15
|
+
methodType: 'unary',
|
|
16
|
+
headers: [],
|
|
17
|
+
auth: { mode: 'none' },
|
|
18
|
+
body: { mode: 'grpc', grpc: [{ name: 'message 1', content: '{}' }] },
|
|
19
|
+
script,
|
|
20
|
+
tests,
|
|
21
|
+
vars: { req: [], res: [] },
|
|
22
|
+
assertions: []
|
|
23
|
+
}
|
|
24
|
+
}) as any;
|
|
25
|
+
|
|
26
|
+
describe('grpc lifecycle hooks — stringify/parse round trip', () => {
|
|
27
|
+
it('writes every hook as a grpc-prefixed script type and reads them back', () => {
|
|
28
|
+
const yml = stringifyItem(grpcItem({
|
|
29
|
+
beforeCallStart: 'bru.setVar("a", 1);',
|
|
30
|
+
afterCallEnd: 'bru.setVar("b", 2);',
|
|
31
|
+
beforeMessageSend: 'bru.setVar("c", 3);',
|
|
32
|
+
afterMessageReceive: 'bru.setVar("d", 4);'
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
expect(yml).toContain('grpc:before-call-start');
|
|
36
|
+
expect(yml).toContain('grpc:after-call-end');
|
|
37
|
+
expect(yml).toContain('grpc:before-message-send');
|
|
38
|
+
expect(yml).toContain('grpc:after-message-receive');
|
|
39
|
+
|
|
40
|
+
const { script } = parseGrpcItem(yml);
|
|
41
|
+
expect(script!.beforeCallStart).toBe('bru.setVar("a", 1);');
|
|
42
|
+
expect(script!.afterCallEnd).toBe('bru.setVar("b", 2);');
|
|
43
|
+
expect(script!.beforeMessageSend).toBe('bru.setVar("c", 3);');
|
|
44
|
+
expect(script!.afterMessageReceive).toBe('bru.setVar("d", 4);');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('preserves a multiline hook verbatim', () => {
|
|
48
|
+
const beforeCallStart = ['const now = 1;', 'if (now) {', ' bru.setVar("startedAt", now);', '}'].join('\n');
|
|
49
|
+
|
|
50
|
+
const yml = stringifyItem(grpcItem({ beforeCallStart, afterCallEnd: null }));
|
|
51
|
+
const { script } = parseGrpcItem(yml);
|
|
52
|
+
|
|
53
|
+
expect(script!.beforeCallStart).toBe(beforeCallStart);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('round-trips one hook without inventing the others', () => {
|
|
57
|
+
const yml = stringifyItem(grpcItem({ beforeCallStart: null, afterCallEnd: 'bru.setVar("b", 2);' }));
|
|
58
|
+
|
|
59
|
+
expect(yml).not.toContain('grpc:before-call-start');
|
|
60
|
+
expect(yml).not.toContain('grpc:before-message-send');
|
|
61
|
+
expect(yml).not.toContain('grpc:after-message-receive');
|
|
62
|
+
|
|
63
|
+
const { script } = parseGrpcItem(yml);
|
|
64
|
+
expect(script!.beforeCallStart).toBeNull();
|
|
65
|
+
expect(script!.afterCallEnd).toBe('bru.setVar("b", 2);');
|
|
66
|
+
expect(script!.beforeMessageSend).toBeNull();
|
|
67
|
+
expect(script!.afterMessageReceive).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('writes no scripts block when every hook is empty', () => {
|
|
71
|
+
const yml = stringifyItem(grpcItem({
|
|
72
|
+
beforeCallStart: null,
|
|
73
|
+
afterCallEnd: ' ',
|
|
74
|
+
beforeMessageSend: null,
|
|
75
|
+
afterMessageReceive: ' '
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
expect(yml).not.toContain('scripts:');
|
|
79
|
+
|
|
80
|
+
const { script } = parseGrpcItem(yml);
|
|
81
|
+
expect(script!.beforeCallStart).toBeNull();
|
|
82
|
+
expect(script!.afterCallEnd).toBeNull();
|
|
83
|
+
expect(script!.beforeMessageSend).toBeNull();
|
|
84
|
+
expect(script!.afterMessageReceive).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('keeps tests alongside the hooks', () => {
|
|
88
|
+
const yml = stringifyItem(
|
|
89
|
+
grpcItem({ beforeCallStart: 'bru.setVar("a", 1);', afterCallEnd: null }, 'test("ok", () => {});')
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const request = parseGrpcItem(yml);
|
|
93
|
+
expect(request.script!.beforeCallStart).toBe('bru.setVar("a", 1);');
|
|
94
|
+
expect(request.tests).toBe('test("ok", () => {});');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('survives a second round trip unchanged', () => {
|
|
98
|
+
const item = grpcItem({
|
|
99
|
+
beforeCallStart: 'bru.setVar("a", 1);',
|
|
100
|
+
afterCallEnd: 'bru.setVar("b", 2);',
|
|
101
|
+
beforeMessageSend: 'bru.setVar("c", 3);',
|
|
102
|
+
afterMessageReceive: 'bru.setVar("d", 4);'
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const firstPass = stringifyItem(item);
|
|
106
|
+
const secondPass = stringifyItem(parseItem(firstPass));
|
|
107
|
+
|
|
108
|
+
expect(secondPass).toBe(firstPass);
|
|
109
|
+
});
|
|
110
|
+
});
|
package/src/formats/yml/index.ts
CHANGED
|
@@ -2,19 +2,23 @@ import parseYmlItem from './parseItem';
|
|
|
2
2
|
import parseYmlFolder from './parseFolder';
|
|
3
3
|
import parseYmlCollection from './parseCollection';
|
|
4
4
|
import parseYmlEnvironment from './parseEnvironment';
|
|
5
|
+
import parseYmlMockServer from './parseMockServer';
|
|
5
6
|
|
|
6
7
|
import stringifyYmlItem from './stringifyItem';
|
|
7
8
|
import stringifyYmlFolder from './stringifyFolder';
|
|
8
9
|
import stringifyYmlCollection from './stringifyCollection';
|
|
9
10
|
import stringifyYmlEnvironment from './stringifyEnvironment';
|
|
11
|
+
import stringifyYmlMockServer from './stringifyMockServer';
|
|
10
12
|
|
|
11
13
|
export {
|
|
12
14
|
parseYmlItem,
|
|
13
15
|
parseYmlFolder,
|
|
14
16
|
parseYmlCollection,
|
|
15
17
|
parseYmlEnvironment,
|
|
18
|
+
parseYmlMockServer,
|
|
16
19
|
stringifyYmlItem,
|
|
17
20
|
stringifyYmlFolder,
|
|
18
21
|
stringifyYmlCollection,
|
|
19
|
-
stringifyYmlEnvironment
|
|
22
|
+
stringifyYmlEnvironment,
|
|
23
|
+
stringifyYmlMockServer
|
|
20
24
|
};
|
|
@@ -9,6 +9,8 @@ import { toBrunoPostResponseVariables } from '../common/actions';
|
|
|
9
9
|
import { toBrunoScripts } from '../common/scripts';
|
|
10
10
|
import { toBrunoAssertions } from '../common/assertions';
|
|
11
11
|
import { uuid, ensureString } from '../../../utils';
|
|
12
|
+
import { utils } from '@usebruno/common';
|
|
13
|
+
const { toBool, toMaxRedirects } = utils;
|
|
12
14
|
|
|
13
15
|
const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
|
|
14
16
|
const info = ocRequest.info;
|
|
@@ -110,11 +112,7 @@ const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
|
|
|
110
112
|
if (ocRequest.settings) {
|
|
111
113
|
const settings: BrunoHttpItemSettings = {};
|
|
112
114
|
|
|
113
|
-
|
|
114
|
-
settings.encodeUrl = ocRequest.settings.encodeUrl;
|
|
115
|
-
} else {
|
|
116
|
-
settings.encodeUrl = true;
|
|
117
|
-
}
|
|
115
|
+
settings.encodeUrl = toBool(ocRequest.settings.encodeUrl, true);
|
|
118
116
|
|
|
119
117
|
if (typeof ocRequest.settings.timeout === 'number') {
|
|
120
118
|
settings.timeout = ocRequest.settings.timeout;
|
|
@@ -124,17 +122,9 @@ const parseGraphQLRequest = (ocRequest: GraphQLRequest): BrunoItem => {
|
|
|
124
122
|
settings.timeout = 0;
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
settings.followRedirects = true;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (typeof ocRequest.settings.maxRedirects === 'number') {
|
|
134
|
-
settings.maxRedirects = ocRequest.settings.maxRedirects;
|
|
135
|
-
} else {
|
|
136
|
-
settings.maxRedirects = 5;
|
|
137
|
-
}
|
|
125
|
+
settings.followRedirects = toBool(ocRequest.settings.followRedirects, true);
|
|
126
|
+
settings.maxRedirects = toMaxRedirects(ocRequest.settings.maxRedirects);
|
|
127
|
+
settings.forwardAuthorizationHeader = toBool(ocRequest.settings.forwardAuthorizationHeader, true);
|
|
138
128
|
|
|
139
129
|
brunoItem.settings = settings;
|
|
140
130
|
}
|
|
@@ -51,8 +51,10 @@ const parseGrpcRequest = (ocRequest: GrpcRequest): BrunoItem => {
|
|
|
51
51
|
grpc: []
|
|
52
52
|
},
|
|
53
53
|
script: {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
beforeCallStart: null,
|
|
55
|
+
beforeMessageSend: null,
|
|
56
|
+
afterMessageReceive: null,
|
|
57
|
+
afterCallEnd: null
|
|
56
58
|
},
|
|
57
59
|
vars: {
|
|
58
60
|
req: [],
|
|
@@ -80,11 +82,17 @@ const parseGrpcRequest = (ocRequest: GrpcRequest): BrunoItem => {
|
|
|
80
82
|
// scripts
|
|
81
83
|
const scripts = toBrunoScripts(runtime?.scripts);
|
|
82
84
|
if (scripts?.script && brunoRequest.script) {
|
|
83
|
-
if (scripts.script.
|
|
84
|
-
brunoRequest.script.
|
|
85
|
+
if (scripts.script.beforeCallStart) {
|
|
86
|
+
brunoRequest.script.beforeCallStart = scripts.script.beforeCallStart;
|
|
85
87
|
}
|
|
86
|
-
if (scripts.script.
|
|
87
|
-
brunoRequest.script.
|
|
88
|
+
if (scripts.script.beforeMessageSend) {
|
|
89
|
+
brunoRequest.script.beforeMessageSend = scripts.script.beforeMessageSend;
|
|
90
|
+
}
|
|
91
|
+
if (scripts.script.afterMessageReceive) {
|
|
92
|
+
brunoRequest.script.afterMessageReceive = scripts.script.afterMessageReceive;
|
|
93
|
+
}
|
|
94
|
+
if (scripts.script.afterCallEnd) {
|
|
95
|
+
brunoRequest.script.afterCallEnd = scripts.script.afterCallEnd;
|
|
88
96
|
}
|
|
89
97
|
}
|
|
90
98
|
if (scripts?.tests) {
|
|
@@ -10,7 +10,10 @@ import { toBrunoPostResponseVariables } from '../common/actions';
|
|
|
10
10
|
import { toBrunoScripts } from '../common/scripts';
|
|
11
11
|
import { toBrunoAssertions } from '../common/assertions';
|
|
12
12
|
import { toBrunoApp } from '../common/app';
|
|
13
|
+
import { normalizeOmitHeaders } from '../common/omit-headers';
|
|
13
14
|
import { uuid, ensureString } from '../../../utils';
|
|
15
|
+
import { utils } from '@usebruno/common';
|
|
16
|
+
const { toBool, toMaxRedirects } = utils;
|
|
14
17
|
|
|
15
18
|
const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
|
|
16
19
|
const info = ocRequest.info;
|
|
@@ -113,11 +116,7 @@ const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
|
|
|
113
116
|
if (ocRequest.settings) {
|
|
114
117
|
const settings: BrunoHttpItemSettings = {};
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
settings.encodeUrl = ocRequest.settings.encodeUrl;
|
|
118
|
-
} else {
|
|
119
|
-
settings.encodeUrl = true;
|
|
120
|
-
}
|
|
119
|
+
settings.encodeUrl = toBool(ocRequest.settings.encodeUrl, true);
|
|
121
120
|
|
|
122
121
|
if (typeof ocRequest.settings.timeout === 'number') {
|
|
123
122
|
settings.timeout = ocRequest.settings.timeout;
|
|
@@ -127,16 +126,13 @@ const parseHttpRequest = (ocRequest: HttpRequest): BrunoItem => {
|
|
|
127
126
|
settings.timeout = 0;
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
settings.followRedirects = true;
|
|
134
|
-
}
|
|
129
|
+
settings.followRedirects = toBool(ocRequest.settings.followRedirects, true);
|
|
130
|
+
settings.maxRedirects = toMaxRedirects(ocRequest.settings.maxRedirects);
|
|
131
|
+
settings.forwardAuthorizationHeader = toBool(ocRequest.settings.forwardAuthorizationHeader, true);
|
|
135
132
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
settings.maxRedirects = 5;
|
|
133
|
+
const omitHeaders = normalizeOmitHeaders(ocRequest.settings.omitHeaders);
|
|
134
|
+
if (omitHeaders) {
|
|
135
|
+
settings.omitHeaders = omitHeaders;
|
|
140
136
|
}
|
|
141
137
|
|
|
142
138
|
brunoItem.settings = settings;
|
|
@@ -8,7 +8,7 @@ import type { Assertion } from '@opencollection/types/common/assertions';
|
|
|
8
8
|
import type { Action } from '@opencollection/types/common/actions';
|
|
9
9
|
import type { HttpRequestParam, HttpRequestHeader } from '@opencollection/types/requests/http';
|
|
10
10
|
import { stringifyYml } from '../utils';
|
|
11
|
-
import { isNonEmptyString
|
|
11
|
+
import { isNonEmptyString } from '../../../utils';
|
|
12
12
|
import { toOpenCollectionAuth } from '../common/auth';
|
|
13
13
|
import { toOpenCollectionHttpHeaders } from '../common/headers';
|
|
14
14
|
import { toOpenCollectionParams } from '../common/params';
|
|
@@ -16,6 +16,8 @@ import { toOpenCollectionVariables } from '../common/variables';
|
|
|
16
16
|
import { toOpenCollectionActions } from '../common/actions';
|
|
17
17
|
import { toOpenCollectionScripts } from '../common/scripts';
|
|
18
18
|
import { toOpenCollectionAssertions } from '../common/assertions';
|
|
19
|
+
import { resolveTimeoutSetting, toMaxRedirects } from '@usebruno/common/utils';
|
|
20
|
+
import { HTTP_SCRIPT_KEYS } from '@usebruno/common';
|
|
19
21
|
|
|
20
22
|
const stringifyGraphQLRequest = (item: BrunoItem): string => {
|
|
21
23
|
try {
|
|
@@ -96,7 +98,7 @@ const stringifyGraphQLRequest = (item: BrunoItem): string => {
|
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
// scripts
|
|
99
|
-
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest);
|
|
101
|
+
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest, HTTP_SCRIPT_KEYS);
|
|
100
102
|
if (scripts) {
|
|
101
103
|
runtime.scripts = scripts;
|
|
102
104
|
hasRuntime = true;
|
|
@@ -132,12 +134,7 @@ const stringifyGraphQLRequest = (item: BrunoItem): string => {
|
|
|
132
134
|
settings.encodeUrl = true;
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
|
|
136
|
-
if (isNumber(timeout)) {
|
|
137
|
-
settings.timeout = timeout;
|
|
138
|
-
} else {
|
|
139
|
-
settings.timeout = 0;
|
|
140
|
-
}
|
|
137
|
+
settings.timeout = resolveTimeoutSetting(httpSettings?.timeout);
|
|
141
138
|
|
|
142
139
|
if (httpSettings?.followRedirects === true) {
|
|
143
140
|
settings.followRedirects = true;
|
|
@@ -147,12 +144,9 @@ const stringifyGraphQLRequest = (item: BrunoItem): string => {
|
|
|
147
144
|
settings.followRedirects = true;
|
|
148
145
|
}
|
|
149
146
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
} else {
|
|
154
|
-
settings.maxRedirects = 5;
|
|
155
|
-
}
|
|
147
|
+
settings.maxRedirects = toMaxRedirects(httpSettings?.maxRedirects);
|
|
148
|
+
|
|
149
|
+
settings.forwardAuthorizationHeader = httpSettings?.forwardAuthorizationHeader ?? true;
|
|
156
150
|
|
|
157
151
|
ocRequest.settings = settings;
|
|
158
152
|
|
|
@@ -12,6 +12,7 @@ import { toOpenCollectionAuth } from '../common/auth';
|
|
|
12
12
|
import { toOpenCollectionVariables } from '../common/variables';
|
|
13
13
|
import { toOpenCollectionScripts } from '../common/scripts';
|
|
14
14
|
import { toOpenCollectionAssertions } from '../common/assertions';
|
|
15
|
+
import { GRPC_SCRIPT_KEYS } from '@usebruno/common';
|
|
15
16
|
|
|
16
17
|
const stringifyGrpcRequest = (item: BrunoItem): string => {
|
|
17
18
|
try {
|
|
@@ -103,7 +104,7 @@ const stringifyGrpcRequest = (item: BrunoItem): string => {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// scripts
|
|
106
|
-
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest);
|
|
107
|
+
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest, GRPC_SCRIPT_KEYS);
|
|
107
108
|
if (scripts) {
|
|
108
109
|
runtime.scripts = scripts;
|
|
109
110
|
hasRuntime = true;
|
|
@@ -17,7 +17,10 @@ import { toOpenCollectionVariables } from '../common/variables';
|
|
|
17
17
|
import { toOpenCollectionActions } from '../common/actions';
|
|
18
18
|
import { toOpenCollectionScripts } from '../common/scripts';
|
|
19
19
|
import { toOpenCollectionAssertions } from '../common/assertions';
|
|
20
|
-
import {
|
|
20
|
+
import { normalizeOmitHeaders } from '../common/omit-headers';
|
|
21
|
+
import { isNonEmptyString } from '../../../utils';
|
|
22
|
+
import { resolveTimeoutSetting, toMaxRedirects } from '@usebruno/common/utils';
|
|
23
|
+
import { HTTP_SCRIPT_KEYS } from '@usebruno/common';
|
|
21
24
|
|
|
22
25
|
const stringifyHttpRequest = (item: BrunoItem): string => {
|
|
23
26
|
try {
|
|
@@ -84,7 +87,7 @@ const stringifyHttpRequest = (item: BrunoItem): string => {
|
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
// scripts
|
|
87
|
-
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest);
|
|
90
|
+
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest, HTTP_SCRIPT_KEYS);
|
|
88
91
|
if (scripts) {
|
|
89
92
|
runtime.scripts = scripts;
|
|
90
93
|
hasRuntime = true;
|
|
@@ -120,12 +123,7 @@ const stringifyHttpRequest = (item: BrunoItem): string => {
|
|
|
120
123
|
settings.encodeUrl = true;
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
|
|
124
|
-
if (isNumber(timeout)) {
|
|
125
|
-
settings.timeout = timeout;
|
|
126
|
-
} else {
|
|
127
|
-
settings.timeout = 0;
|
|
128
|
-
}
|
|
126
|
+
settings.timeout = resolveTimeoutSetting(httpSettings?.timeout);
|
|
129
127
|
|
|
130
128
|
if (httpSettings?.followRedirects === true) {
|
|
131
129
|
settings.followRedirects = true;
|
|
@@ -135,11 +133,13 @@ const stringifyHttpRequest = (item: BrunoItem): string => {
|
|
|
135
133
|
settings.followRedirects = true;
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
settings.maxRedirects = toMaxRedirects(httpSettings?.maxRedirects);
|
|
137
|
+
|
|
138
|
+
settings.forwardAuthorizationHeader = httpSettings?.forwardAuthorizationHeader ?? true;
|
|
139
|
+
|
|
140
|
+
const omitHeaders = normalizeOmitHeaders(httpSettings?.omitHeaders);
|
|
141
|
+
if (omitHeaders) {
|
|
142
|
+
settings.omitHeaders = omitHeaders;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
ocRequest.settings = settings;
|
|
@@ -11,6 +11,7 @@ import { toOpenCollectionAuth } from '../common/auth';
|
|
|
11
11
|
import { toOpenCollectionHttpHeaders } from '../common/headers';
|
|
12
12
|
import { toOpenCollectionVariables } from '../common/variables';
|
|
13
13
|
import { toOpenCollectionScripts } from '../common/scripts';
|
|
14
|
+
import { HTTP_SCRIPT_KEYS } from '@usebruno/common';
|
|
14
15
|
|
|
15
16
|
const stringifyWebsocketRequest = (item: BrunoItem): string => {
|
|
16
17
|
try {
|
|
@@ -92,7 +93,8 @@ const stringifyWebsocketRequest = (item: BrunoItem): string => {
|
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
// scripts
|
|
95
|
-
|
|
96
|
+
// TODO: Modify to WS scripts once WS scripts are implemented.
|
|
97
|
+
const scripts: Scripts | undefined = toOpenCollectionScripts(brunoRequest, HTTP_SCRIPT_KEYS);
|
|
96
98
|
if (scripts) {
|
|
97
99
|
runtime.scripts = scripts;
|
|
98
100
|
hasRuntime = true;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { HttpRequestHeader, HttpResponseHeader, HttpRequestParam, HttpRequestBody } from '@opencollection/types/requests/http';
|
|
2
|
+
|
|
3
|
+
export interface MockRuleCondition {
|
|
4
|
+
target?: string;
|
|
5
|
+
key?: string;
|
|
6
|
+
operator?: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface MockRouteRules {
|
|
11
|
+
operator?: 'AND' | 'OR';
|
|
12
|
+
conditions?: MockRuleCondition[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface MockRouteEntry {
|
|
16
|
+
name?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
request?: {
|
|
19
|
+
method?: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
headers?: HttpRequestHeader[];
|
|
22
|
+
params?: HttpRequestParam[];
|
|
23
|
+
body?: HttpRequestBody;
|
|
24
|
+
};
|
|
25
|
+
response?: {
|
|
26
|
+
status?: number;
|
|
27
|
+
statusText?: string;
|
|
28
|
+
headers?: HttpResponseHeader[];
|
|
29
|
+
body?: {
|
|
30
|
+
type?: string;
|
|
31
|
+
data?: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
rules?: MockRouteRules;
|
|
35
|
+
copiedFrom?: {
|
|
36
|
+
example?: string | null;
|
|
37
|
+
requestPath?: string | null;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface MockServerFile {
|
|
42
|
+
info?: {
|
|
43
|
+
name?: string;
|
|
44
|
+
type?: 'mock';
|
|
45
|
+
};
|
|
46
|
+
mock?: {
|
|
47
|
+
port?: number;
|
|
48
|
+
delay?: number;
|
|
49
|
+
source?: {
|
|
50
|
+
type?: 'collection' | 'spec';
|
|
51
|
+
path?: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
routes?: MockRouteEntry[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface BrunoMockRoute {
|
|
58
|
+
name: string;
|
|
59
|
+
description: string;
|
|
60
|
+
request: {
|
|
61
|
+
url: string;
|
|
62
|
+
method: string;
|
|
63
|
+
headers: any[];
|
|
64
|
+
params: any[];
|
|
65
|
+
body: any;
|
|
66
|
+
};
|
|
67
|
+
response: {
|
|
68
|
+
status: number;
|
|
69
|
+
statusText: string;
|
|
70
|
+
headers: any[];
|
|
71
|
+
body: {
|
|
72
|
+
type: string;
|
|
73
|
+
content: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
rules: {
|
|
77
|
+
operator: 'AND' | 'OR';
|
|
78
|
+
conditions: Array<{ target: string; key: string; operator: string; value: string }>;
|
|
79
|
+
};
|
|
80
|
+
copiedFrom?: {
|
|
81
|
+
exampleName: string | null;
|
|
82
|
+
requestPathname: string | null;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface BrunoMockServer {
|
|
87
|
+
name: string;
|
|
88
|
+
port: number | null;
|
|
89
|
+
delay: number;
|
|
90
|
+
source: {
|
|
91
|
+
type: 'collection' | 'spec';
|
|
92
|
+
path: string;
|
|
93
|
+
} | null;
|
|
94
|
+
routes: BrunoMockRoute[];
|
|
95
|
+
}
|
|
@@ -52,6 +52,129 @@ request:
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
describe('parseCollection — script execution flow', () => {
|
|
56
|
+
it('reads flow: sequential from the bruno extension', () => {
|
|
57
|
+
const yml = `opencollection: "1.0.0"
|
|
58
|
+
info:
|
|
59
|
+
name: c
|
|
60
|
+
extensions:
|
|
61
|
+
bruno:
|
|
62
|
+
scripts:
|
|
63
|
+
flow: sequential
|
|
64
|
+
`;
|
|
65
|
+
const { brunoConfig } = parseCollection(yml);
|
|
66
|
+
expect(brunoConfig.scripts?.flow).toBe('sequential');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('reads flow: sandwich from the bruno extension', () => {
|
|
70
|
+
const yml = `opencollection: "1.0.0"
|
|
71
|
+
info:
|
|
72
|
+
name: c
|
|
73
|
+
extensions:
|
|
74
|
+
bruno:
|
|
75
|
+
scripts:
|
|
76
|
+
flow: sandwich
|
|
77
|
+
`;
|
|
78
|
+
const { brunoConfig } = parseCollection(yml);
|
|
79
|
+
expect(brunoConfig.scripts?.flow).toBe('sandwich');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('ignores an unrecognized flow value', () => {
|
|
83
|
+
const yml = `opencollection: "1.0.0"
|
|
84
|
+
info:
|
|
85
|
+
name: c
|
|
86
|
+
extensions:
|
|
87
|
+
bruno:
|
|
88
|
+
scripts:
|
|
89
|
+
flow: parallel
|
|
90
|
+
`;
|
|
91
|
+
const { brunoConfig } = parseCollection(yml);
|
|
92
|
+
expect(brunoConfig.scripts?.flow).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('has no scripts.flow when the file does not set one', () => {
|
|
96
|
+
const { brunoConfig } = parseCollection('opencollection: "1.0.0"\ninfo:\n name: c\n');
|
|
97
|
+
expect(brunoConfig.scripts?.flow).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('parseCollection — client certificates', () => {
|
|
102
|
+
it('maps pem and pkcs12 certs to Bruno cert/pfx shapes', () => {
|
|
103
|
+
const yml = `opencollection: "1.0.0"
|
|
104
|
+
info:
|
|
105
|
+
name: c
|
|
106
|
+
config:
|
|
107
|
+
clientCertificates:
|
|
108
|
+
- domain: localhost
|
|
109
|
+
type: pem
|
|
110
|
+
certificateFilePath: ./certs/client-cert.pem
|
|
111
|
+
privateKeyFilePath: ./certs/client-key.pem
|
|
112
|
+
passphrase: secret
|
|
113
|
+
- domain: example.com
|
|
114
|
+
type: pkcs12
|
|
115
|
+
pkcs12FilePath: ./certs/client.pfx
|
|
116
|
+
`;
|
|
117
|
+
const { brunoConfig } = parseCollection(yml);
|
|
118
|
+
|
|
119
|
+
expect(brunoConfig.clientCertificates.certs).toEqual([
|
|
120
|
+
{
|
|
121
|
+
domain: 'localhost',
|
|
122
|
+
type: 'cert',
|
|
123
|
+
certFilePath: './certs/client-cert.pem',
|
|
124
|
+
keyFilePath: './certs/client-key.pem',
|
|
125
|
+
passphrase: 'secret'
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
domain: 'example.com',
|
|
129
|
+
type: 'pfx',
|
|
130
|
+
pfxFilePath: './certs/client.pfx',
|
|
131
|
+
passphrase: ''
|
|
132
|
+
}
|
|
133
|
+
]);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('drops certs with an unrecognized type', () => {
|
|
137
|
+
const yml = `opencollection: "1.0.0"
|
|
138
|
+
info:
|
|
139
|
+
name: c
|
|
140
|
+
config:
|
|
141
|
+
clientCertificates:
|
|
142
|
+
- domain: localhost
|
|
143
|
+
type: bogus
|
|
144
|
+
certificateFilePath: ./certs/client-cert.pem
|
|
145
|
+
`;
|
|
146
|
+
const { brunoConfig } = parseCollection(yml);
|
|
147
|
+
|
|
148
|
+
expect(brunoConfig.clientCertificates.certs).toEqual([]);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('reads a per-cert disabled flag when set to true, otherwise leaves it off', () => {
|
|
152
|
+
const yml = `opencollection: "1.0.0"
|
|
153
|
+
info:
|
|
154
|
+
name: c
|
|
155
|
+
config:
|
|
156
|
+
clientCertificates:
|
|
157
|
+
- domain: localhost
|
|
158
|
+
type: pem
|
|
159
|
+
certificateFilePath: ./certs/client-cert.pem
|
|
160
|
+
privateKeyFilePath: ./certs/client-key.pem
|
|
161
|
+
disabled: true
|
|
162
|
+
- domain: example.com
|
|
163
|
+
type: pkcs12
|
|
164
|
+
pkcs12FilePath: ./certs/client.pfx
|
|
165
|
+
disabled: true
|
|
166
|
+
- domain: other.com
|
|
167
|
+
type: pkcs12
|
|
168
|
+
pkcs12FilePath: ./certs/other.pfx
|
|
169
|
+
`;
|
|
170
|
+
const { brunoConfig } = parseCollection(yml);
|
|
171
|
+
|
|
172
|
+
expect(brunoConfig.clientCertificates.certs[0].disabled).toBe(true);
|
|
173
|
+
expect(brunoConfig.clientCertificates.certs[1].disabled).toBe(true);
|
|
174
|
+
expect(brunoConfig.clientCertificates.certs[2]).not.toHaveProperty('disabled');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
55
178
|
describe('parseCollection — reading the collection version', () => {
|
|
56
179
|
it('reads the version from the file as text', () => {
|
|
57
180
|
const { brunoConfig } = parseCollection('opencollection: "1.0.0"\ninfo:\n name: c\n version: v1.0.0\n');
|