mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-07 14:08:38 +00:00
457 lines
14 KiB
JavaScript
457 lines
14 KiB
JavaScript
import { describe, it, expect } from '@jest/globals';
|
|
import openApiToBruno from '../../../src/openapi/openapi-to-bruno';
|
|
|
|
describe('openapi server variables to environment variables', () => {
|
|
it('should create individual environment variables from server variables', () => {
|
|
const spec = `
|
|
openapi: '3.0.0'
|
|
info:
|
|
title: 'Server Variables API'
|
|
version: '1.0.0'
|
|
paths:
|
|
/test:
|
|
get:
|
|
summary: 'Test'
|
|
responses:
|
|
'200':
|
|
description: 'OK'
|
|
servers:
|
|
- url: '{protocol}://{host}:{port}/v1'
|
|
description: 'Main Server'
|
|
variables:
|
|
protocol:
|
|
default: 'https'
|
|
host:
|
|
default: 'api.example.com'
|
|
port:
|
|
default: '443'
|
|
`;
|
|
const result = openApiToBruno(spec);
|
|
const env = result.environments[0];
|
|
expect(env.name).toBe('Main Server');
|
|
expect(env.variables).toHaveLength(4); // baseUrl + 3 variables
|
|
|
|
const baseUrl = env.variables.find((v) => v.name === 'baseUrl');
|
|
expect(baseUrl.value).toBe('{{protocol}}://{{host}}:{{port}}/v1');
|
|
expect(baseUrl.enabled).toBe(true);
|
|
expect(baseUrl.type).toBe('text');
|
|
|
|
const protocol = env.variables.find((v) => v.name === 'protocol');
|
|
expect(protocol.value).toBe('https');
|
|
expect(protocol.enabled).toBe(true);
|
|
expect(protocol.type).toBe('text');
|
|
|
|
const host = env.variables.find((v) => v.name === 'host');
|
|
expect(host.value).toBe('api.example.com');
|
|
|
|
const port = env.variables.find((v) => v.name === 'port');
|
|
expect(port.value).toBe('443');
|
|
});
|
|
|
|
it('should use plain resolved URL when server has no variables', () => {
|
|
const spec = `
|
|
openapi: '3.0.0'
|
|
info:
|
|
title: 'No Variables API'
|
|
version: '1.0.0'
|
|
paths:
|
|
/test:
|
|
get:
|
|
summary: 'Test'
|
|
responses:
|
|
'200':
|
|
description: 'OK'
|
|
servers:
|
|
- url: 'https://api.example.com/v1'
|
|
`;
|
|
const result = openApiToBruno(spec);
|
|
const env = result.environments[0];
|
|
expect(env.variables).toHaveLength(1);
|
|
expect(env.variables[0].name).toBe('baseUrl');
|
|
expect(env.variables[0].value).toBe('https://api.example.com/v1');
|
|
});
|
|
|
|
it('should handle multiple servers with different variables', () => {
|
|
const spec = `
|
|
openapi: '3.0.0'
|
|
info:
|
|
title: 'Multi Server API'
|
|
version: '1.0.0'
|
|
paths:
|
|
/test:
|
|
get:
|
|
summary: 'Test'
|
|
responses:
|
|
'200':
|
|
description: 'OK'
|
|
servers:
|
|
- url: '{protocol}://{host}/v1'
|
|
description: 'Production'
|
|
variables:
|
|
protocol:
|
|
default: 'https'
|
|
host:
|
|
default: 'api.prod.com'
|
|
- url: 'https://staging.example.com/v1'
|
|
description: 'Staging'
|
|
`;
|
|
const result = openApiToBruno(spec);
|
|
|
|
// Production env: template + variables
|
|
const prodEnv = result.environments[0];
|
|
expect(prodEnv.name).toBe('Production');
|
|
expect(prodEnv.variables).toHaveLength(3); // baseUrl + protocol + host
|
|
expect(prodEnv.variables.find((v) => v.name === 'baseUrl').value).toBe('{{protocol}}://{{host}}/v1');
|
|
expect(prodEnv.variables.find((v) => v.name === 'protocol').value).toBe('https');
|
|
expect(prodEnv.variables.find((v) => v.name === 'host').value).toBe('api.prod.com');
|
|
|
|
// Staging env: plain URL, no extra variables
|
|
const stagingEnv = result.environments[1];
|
|
expect(stagingEnv.name).toBe('Staging');
|
|
expect(stagingEnv.variables).toHaveLength(1);
|
|
expect(stagingEnv.variables[0].value).toBe('https://staging.example.com/v1');
|
|
});
|
|
|
|
it('should use first enum value when no default is provided', () => {
|
|
const spec = `
|
|
openapi: '3.0.0'
|
|
info:
|
|
title: 'Enum Only API'
|
|
version: '1.0.0'
|
|
paths:
|
|
/test:
|
|
get:
|
|
summary: 'Test'
|
|
responses:
|
|
'200':
|
|
description: 'OK'
|
|
servers:
|
|
- url: '{protocol}://example.com'
|
|
variables:
|
|
protocol:
|
|
enum:
|
|
- https
|
|
- http
|
|
`;
|
|
const result = openApiToBruno(spec);
|
|
const env = result.environments[0];
|
|
const protocolVar = env.variables.find((v) => v.name === 'protocol');
|
|
expect(protocolVar.value).toBe('https');
|
|
});
|
|
|
|
it('should use empty string when variable has neither default nor enum', () => {
|
|
const spec = `
|
|
openapi: '3.0.0'
|
|
info:
|
|
title: 'No Default No Enum API'
|
|
version: '1.0.0'
|
|
paths:
|
|
/test:
|
|
get:
|
|
summary: 'Test'
|
|
responses:
|
|
'200':
|
|
description: 'OK'
|
|
servers:
|
|
- url: '{basePath}/v1'
|
|
variables:
|
|
basePath: {}
|
|
`;
|
|
const result = openApiToBruno(spec);
|
|
const env = result.environments[0];
|
|
const basePathVar = env.variables.find((v) => v.name === 'basePath');
|
|
expect(basePathVar.value).toBe('');
|
|
});
|
|
|
|
it('should strip trailing slash from template baseUrl', () => {
|
|
const spec = `
|
|
openapi: '3.0.0'
|
|
info:
|
|
title: 'Trailing Slash API'
|
|
version: '1.0.0'
|
|
paths:
|
|
/test:
|
|
get:
|
|
summary: 'Test'
|
|
responses:
|
|
'200':
|
|
description: 'OK'
|
|
servers:
|
|
- url: '{protocol}://{host}/'
|
|
variables:
|
|
protocol:
|
|
default: 'https'
|
|
host:
|
|
default: 'api.example.com'
|
|
`;
|
|
const result = openApiToBruno(spec);
|
|
const env = result.environments[0];
|
|
const baseUrl = env.variables.find((v) => v.name === 'baseUrl');
|
|
expect(baseUrl.value).toBe('{{protocol}}://{{host}}');
|
|
});
|
|
|
|
it('should use server.name for environment name when present', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'Named Server API', version: '1.0.0' },
|
|
paths: {
|
|
'/test': {
|
|
get: {
|
|
summary: 'Test',
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
}
|
|
},
|
|
servers: [
|
|
{
|
|
url: 'https://api.example.com',
|
|
name: 'Production',
|
|
description: 'Production server'
|
|
},
|
|
{
|
|
url: 'https://staging.example.com',
|
|
description: 'Staging server'
|
|
},
|
|
{
|
|
url: 'https://dev.example.com'
|
|
}
|
|
]
|
|
};
|
|
const result = openApiToBruno(spec);
|
|
expect(result.environments[0].name).toBe('Production'); // prefers name over description
|
|
expect(result.environments[1].name).toBe('Staging server'); // falls back to description
|
|
expect(result.environments[2].name).toBe('Environment 3'); // falls back to index
|
|
});
|
|
});
|
|
|
|
describe('operation-level servers to request vars', () => {
|
|
it('should set request vars.req with baseUrl when operation has its own servers', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'Op Server API', version: '1.0.0' },
|
|
servers: [{ url: 'https://api.example.com' }],
|
|
paths: {
|
|
'/files': {
|
|
get: {
|
|
summary: 'Get files',
|
|
operationId: 'getFiles',
|
|
servers: [{ url: 'https://files.example.com' }],
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
}
|
|
}
|
|
};
|
|
const result = openApiToBruno(spec);
|
|
const getFiles = result.items.find((i) => i.name === 'Get files');
|
|
|
|
expect(getFiles.request.vars).toBeDefined();
|
|
expect(getFiles.request.vars.req).toHaveLength(1);
|
|
expect(getFiles.request.vars.req[0]).toMatchObject({
|
|
name: 'baseUrl',
|
|
value: 'https://files.example.com',
|
|
enabled: true
|
|
});
|
|
expect(getFiles.request.vars.res).toEqual([]);
|
|
});
|
|
|
|
it('should create template baseUrl and variable entries for server with variables', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'Var Server API', version: '1.0.0' },
|
|
servers: [{ url: 'https://api.example.com' }],
|
|
paths: {
|
|
'/regional': {
|
|
get: {
|
|
summary: 'Regional data',
|
|
servers: [{
|
|
url: '{protocol}://{region}.example.com/v2',
|
|
variables: {
|
|
protocol: { default: 'https' },
|
|
region: { default: 'us-east' }
|
|
}
|
|
}],
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
}
|
|
}
|
|
};
|
|
const result = openApiToBruno(spec);
|
|
const regional = result.items.find((i) => i.name === 'Regional data');
|
|
|
|
expect(regional.request.vars.req).toHaveLength(3); // baseUrl + protocol + region
|
|
|
|
const baseUrlVar = regional.request.vars.req.find((v) => v.name === 'baseUrl');
|
|
expect(baseUrlVar.value).toBe('{{protocol}}://{{region}}.example.com/v2');
|
|
|
|
const protocolVar = regional.request.vars.req.find((v) => v.name === 'protocol');
|
|
expect(protocolVar.value).toBe('https');
|
|
|
|
const regionVar = regional.request.vars.req.find((v) => v.name === 'region');
|
|
expect(regionVar.value).toBe('us-east');
|
|
});
|
|
|
|
it('should NOT set request vars when no operation servers exist', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'No Override API', version: '1.0.0' },
|
|
servers: [{ url: 'https://api.example.com' }],
|
|
paths: {
|
|
'/test': {
|
|
get: {
|
|
summary: 'Test',
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
}
|
|
}
|
|
};
|
|
const result = openApiToBruno(spec);
|
|
const test = result.items.find((i) => i.name === 'Test');
|
|
|
|
expect(test.request.vars).toBeUndefined();
|
|
});
|
|
|
|
it('should only set vars on the operation that defines servers', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'Selective API', version: '1.0.0' },
|
|
servers: [{ url: 'https://api.example.com' }],
|
|
paths: {
|
|
'/data': {
|
|
get: {
|
|
summary: 'Get data',
|
|
servers: [{ url: 'https://data-server.example.com' }],
|
|
responses: { 200: { description: 'OK' } }
|
|
},
|
|
post: {
|
|
summary: 'Post data',
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
}
|
|
}
|
|
};
|
|
const result = openApiToBruno(spec);
|
|
const getData = result.items.find((i) => i.name === 'Get data');
|
|
const postData = result.items.find((i) => i.name === 'Post data');
|
|
|
|
// GET has operation-level servers — should have vars
|
|
expect(getData.request.vars).toBeDefined();
|
|
expect(getData.request.vars.req[0].value).toBe('https://data-server.example.com');
|
|
|
|
// POST has no operation-level servers — should NOT have vars
|
|
expect(postData.request.vars).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('x-bruno-variants import', () => {
|
|
it('should import duplicate operation variants as separate requests', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'Variant API', version: '1.0.0' },
|
|
servers: [{ url: 'https://api.example.com' }],
|
|
paths: {
|
|
'/users': {
|
|
get: {
|
|
'summary': 'Get active users',
|
|
'parameters': [{ name: 'status', in: 'query', example: 'active' }],
|
|
'responses': { 200: { description: 'OK' } },
|
|
'x-bruno-variants': [
|
|
{
|
|
summary: 'Get inactive users',
|
|
parameters: [{ name: 'status', in: 'query', example: 'inactive' }],
|
|
responses: { 200: { description: 'OK' } }
|
|
},
|
|
{
|
|
summary: 'Get pending users',
|
|
parameters: [{ name: 'status', in: 'query', example: 'pending' }],
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const result = openApiToBruno(spec);
|
|
const requests = result.items.filter((item) => item.type === 'http-request');
|
|
|
|
expect(requests.map((request) => request.name)).toEqual([
|
|
'Get active users',
|
|
'Get inactive users',
|
|
'Get pending users'
|
|
]);
|
|
expect(requests.map((request) => request.request.params[0].value)).toEqual(['active', 'inactive', 'pending']);
|
|
expect(requests.every((request) => !request.request['x-bruno-variants'])).toBe(true);
|
|
});
|
|
|
|
it('should import variant operation-level servers as request baseUrl vars', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'Variant Server API', version: '1.0.0' },
|
|
servers: [{ url: 'https://api.example.com' }],
|
|
paths: {
|
|
'/data': {
|
|
get: {
|
|
'summary': 'Get data',
|
|
'servers': [{ url: 'https://data.example.com' }],
|
|
'responses': { 200: { description: 'OK' } },
|
|
'x-bruno-variants': [
|
|
{
|
|
summary: 'Get audit data',
|
|
servers: [{ url: 'https://audit.example.com' }],
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const result = openApiToBruno(spec);
|
|
const data = result.items.find((item) => item.name === 'Get data');
|
|
const auditData = result.items.find((item) => item.name === 'Get audit data');
|
|
|
|
expect(data.request.vars.req[0]).toMatchObject({
|
|
name: 'baseUrl',
|
|
value: 'https://data.example.com'
|
|
});
|
|
expect(auditData.request.vars.req[0]).toMatchObject({
|
|
name: 'baseUrl',
|
|
value: 'https://audit.example.com'
|
|
});
|
|
});
|
|
|
|
it('should group variants by their own tags', () => {
|
|
const spec = {
|
|
openapi: '3.0.0',
|
|
info: { title: 'Variant Folder API', version: '1.0.0' },
|
|
servers: [{ url: 'https://api.example.com' }],
|
|
paths: {
|
|
'/users': {
|
|
get: {
|
|
'summary': 'Get active users',
|
|
'tags': ['Active Users'],
|
|
'responses': { 200: { description: 'OK' } },
|
|
'x-bruno-variants': [
|
|
{
|
|
summary: 'Get inactive users',
|
|
tags: ['Inactive Users'],
|
|
responses: { 200: { description: 'OK' } }
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const result = openApiToBruno(spec);
|
|
|
|
expect(result.items.map((folder) => ({
|
|
name: folder.name,
|
|
requests: folder.items.map((request) => request.name)
|
|
}))).toEqual([
|
|
{ name: 'Active_Users', requests: ['Get active users'] },
|
|
{ name: 'Inactive_Users', requests: ['Get inactive users'] }
|
|
]);
|
|
});
|
|
});
|