mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-01 16:44:16 +00:00
ca certs fixes and tests (#5429)
Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
This commit is contained in:
6
tests/ssl/basic-ssl/collections/badssl/bruno.json
Normal file
6
tests/ssl/basic-ssl/collections/badssl/bruno.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "badssl",
|
||||
"type": "collection",
|
||||
"ignore": ["node_modules", ".git"]
|
||||
}
|
||||
5
tests/ssl/basic-ssl/collections/badssl/package.json
Normal file
5
tests/ssl/basic-ssl/collections/badssl/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "badssl",
|
||||
"version": "1.0.0",
|
||||
"description": "Bruno test collection for basic ssl testing"
|
||||
}
|
||||
15
tests/ssl/basic-ssl/collections/badssl/request.bru
Normal file
15
tests/ssl/basic-ssl/collections/badssl/request.bru
Normal file
@@ -0,0 +1,15 @@
|
||||
meta {
|
||||
name: request
|
||||
type: http
|
||||
seq: 6
|
||||
}
|
||||
|
||||
get {
|
||||
url: https://www.badssl.com
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
assert {
|
||||
res.status: eq 200
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "self-signed-badssl",
|
||||
"type": "collection",
|
||||
"ignore": ["node_modules", ".git"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "self-signed-badssl",
|
||||
"version": "1.0.0",
|
||||
"description": "Bruno test collection for basic ssl testing"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
meta {
|
||||
name: request
|
||||
type: http
|
||||
seq: 6
|
||||
}
|
||||
|
||||
get {
|
||||
url: https://self-signed.badssl.com
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
assert {
|
||||
res.status: eq 200
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('basic ssl success', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('badssl').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/ssl/basic-ssl/collections/badssl"],
|
||||
"preferences": {
|
||||
"request": {
|
||||
"sslVerification": true,
|
||||
"customCaCertificate": {
|
||||
"enabled": false,
|
||||
"filePath": ""
|
||||
},
|
||||
"keepDefaultCaCertificates": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/ssl/basic-ssl/collections/self-signed-badssl"],
|
||||
"preferences": {
|
||||
"request": {
|
||||
"sslVerification": true,
|
||||
"customCaCertificate": {
|
||||
"enabled": false,
|
||||
"filePath": ""
|
||||
},
|
||||
"keepDefaultCaCertificates": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('self signed rejected', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('self-signed-badssl').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/ssl/basic-ssl/collections/self-signed-badssl"],
|
||||
"preferences": {
|
||||
"request": {
|
||||
"sslVerification": false,
|
||||
"customCaCertificate": {
|
||||
"enabled": false,
|
||||
"filePath": ""
|
||||
},
|
||||
"keepDefaultCaCertificates": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('self signed success with validation disabled', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('self-signed-badssl').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
});
|
||||
6
tests/ssl/custom-ca-certs/collection/bruno.json
Normal file
6
tests/ssl/custom-ca-certs/collection/bruno.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": "1",
|
||||
"name": "custom-ca-certs",
|
||||
"type": "collection",
|
||||
"ignore": ["node_modules", ".git"]
|
||||
}
|
||||
5
tests/ssl/custom-ca-certs/collection/package.json
Normal file
5
tests/ssl/custom-ca-certs/collection/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "custom-ca-certs",
|
||||
"version": "1.0.0",
|
||||
"description": "Bruno test collection for CA certificates and HTTPS server testing"
|
||||
}
|
||||
16
tests/ssl/custom-ca-certs/collection/request.bru
Normal file
16
tests/ssl/custom-ca-certs/collection/request.bru
Normal file
@@ -0,0 +1,16 @@
|
||||
meta {
|
||||
name: request
|
||||
type: http
|
||||
seq: 6
|
||||
}
|
||||
|
||||
get {
|
||||
url: https://localhost:8090
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
assert {
|
||||
res.status: eq 200
|
||||
res.body: eq helloworld
|
||||
}
|
||||
1
tests/ssl/custom-ca-certs/server/.gitignore
vendored
Normal file
1
tests/ssl/custom-ca-certs/server/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
certs
|
||||
225
tests/ssl/custom-ca-certs/server/helpers/certs.js
Normal file
225
tests/ssl/custom-ca-certs/server/helpers/certs.js
Normal file
@@ -0,0 +1,225 @@
|
||||
const { execCommand, execCommandSilent, detectPlatform } = require('./platform');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
function createCertsDir(certsDir) {
|
||||
if (fs.existsSync(certsDir)) {
|
||||
fs.rmSync(certsDir, { recursive: true, force: true });
|
||||
}
|
||||
fs.mkdirSync(certsDir, { recursive: true });
|
||||
}
|
||||
|
||||
function generateCertificates(certsDir) {
|
||||
execCommand('openssl version');
|
||||
|
||||
// Generate CA private key
|
||||
execCommand('openssl genrsa -out ca-key.pem 4096', certsDir);
|
||||
|
||||
// Create CA configuration file with proper CA extensions and subject (LibreSSL/OpenSSL compatible)
|
||||
const caConfigContent = `[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
x509_extensions = v3_ca
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = Dev
|
||||
L = Local
|
||||
O = Local Dev CA
|
||||
CN = Local Dev CA
|
||||
|
||||
[v3_ca]
|
||||
basicConstraints = critical, CA:TRUE
|
||||
keyUsage = critical, keyCertSign, cRLSign
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer:always`;
|
||||
|
||||
fs.writeFileSync(path.join(certsDir, 'ca.conf'), caConfigContent);
|
||||
|
||||
// Generate CA certificate with proper CA extensions using config file (no -subj needed)
|
||||
execCommand('openssl req -new -x509 -key ca-key.pem -out ca-cert.pem -days 3650 -config ca.conf', certsDir);
|
||||
|
||||
// Generate server private key and CSR
|
||||
execCommand('openssl genrsa -out localhost-key.pem 4096', certsDir);
|
||||
|
||||
// Create server CSR configuration file
|
||||
const serverCsrConfigContent = `[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = US
|
||||
ST = Dev
|
||||
L = Local
|
||||
O = Local Dev
|
||||
CN = localhost`;
|
||||
|
||||
fs.writeFileSync(path.join(certsDir, 'localhost-csr.conf'), serverCsrConfigContent);
|
||||
execCommand('openssl req -new -key localhost-key.pem -out localhost.csr -config localhost-csr.conf', certsDir);
|
||||
|
||||
// Create server certificate configuration file (LibreSSL/OpenSSL compatible)
|
||||
const serverConfigContent = `[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
prompt = no
|
||||
|
||||
[req_distinguished_name]
|
||||
C = Country Name
|
||||
ST = State or Province Name
|
||||
L = Locality Name
|
||||
O = Organization Name
|
||||
CN = Common Name
|
||||
|
||||
[v3_req]
|
||||
keyUsage = critical, keyEncipherment, dataEncipherment, digitalSignature
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = @alt_names
|
||||
basicConstraints = critical, CA:FALSE
|
||||
authorityKeyIdentifier = keyid:always,issuer:always
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = localhost
|
||||
DNS.2 = localhost.localdomain
|
||||
IP.1 = 127.0.0.1
|
||||
IP.2 = ::1
|
||||
IP.3 = ::ffff:127.0.0.1`;
|
||||
|
||||
fs.writeFileSync(path.join(certsDir, 'localhost.conf'), serverConfigContent);
|
||||
execCommand('openssl x509 -req -in localhost.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out localhost-cert.pem -days 730 -extensions v3_req -extfile localhost.conf', certsDir);
|
||||
|
||||
const platform = detectPlatform();
|
||||
if (platform === 'windows') {
|
||||
execCommand('openssl x509 -in ca-cert.pem -outform DER -out ca-cert.der', certsDir);
|
||||
execCommand('openssl pkcs12 -export -out localhost.p12 -inkey localhost-key.pem -in localhost-cert.pem -certfile ca-cert.pem -password pass:', certsDir);
|
||||
execCommand('openssl x509 -in localhost-cert.pem -outform DER -out localhost-cert.der', certsDir);
|
||||
}
|
||||
|
||||
if (platform !== 'windows') {
|
||||
execCommand('chmod 600 ca-key.pem localhost-key.pem', certsDir);
|
||||
execCommand('chmod 644 ca-cert.pem localhost-cert.pem', certsDir);
|
||||
}
|
||||
|
||||
['localhost.csr', 'localhost.conf', 'localhost-csr.conf', 'ca.conf', 'ca-cert.srl'].forEach(file => {
|
||||
const filePath = path.join(certsDir, file);
|
||||
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
||||
});
|
||||
|
||||
// Validate certificate chain
|
||||
validateCertificateChain(certsDir);
|
||||
}
|
||||
|
||||
function validateCertificateChain(certsDir) {
|
||||
try {
|
||||
// Verify CA certificate is valid and has proper CA extensions
|
||||
const caVerifyOutput = execCommandSilent('openssl x509 -in ca-cert.pem -text -noout', certsDir).toString();
|
||||
|
||||
if (!caVerifyOutput.includes('CA:TRUE')) {
|
||||
throw new Error('CA certificate missing basicConstraints=CA:TRUE');
|
||||
}
|
||||
|
||||
if (!caVerifyOutput.includes('Certificate Sign')) {
|
||||
throw new Error('CA certificate missing keyCertSign in keyUsage');
|
||||
}
|
||||
|
||||
// Verify server certificate is valid and signed by CA
|
||||
const serverVerifyOutput = execCommandSilent('openssl x509 -in localhost-cert.pem -text -noout', certsDir).toString();
|
||||
|
||||
if (!serverVerifyOutput.includes('CA:FALSE')) {
|
||||
throw new Error('Server certificate should have basicConstraints=CA:FALSE');
|
||||
}
|
||||
|
||||
if (!serverVerifyOutput.includes('TLS Web Server Authentication')) {
|
||||
throw new Error('Server certificate missing serverAuth in extendedKeyUsage');
|
||||
}
|
||||
|
||||
// Verify certificate chain
|
||||
execCommandSilent('openssl verify -CAfile ca-cert.pem localhost-cert.pem', certsDir);
|
||||
|
||||
console.log('✅ Certificate chain validation passed');
|
||||
} catch (error) {
|
||||
console.error('❌ Certificate validation failed:', error.message);
|
||||
throw new Error(`Certificate validation failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function addCAToTruststore(certsDir) {
|
||||
const platform = detectPlatform();
|
||||
|
||||
switch (platform) {
|
||||
case 'macos': {
|
||||
const macCertPath = path.join(certsDir, 'ca-cert.pem');
|
||||
execCommand(`sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${macCertPath}"`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'linux': {
|
||||
const linuxCertPath = path.join(certsDir, 'ca-cert.pem');
|
||||
execCommand(`sudo cp "${linuxCertPath}" /usr/local/share/ca-certificates/bruno-ca.crt`);
|
||||
execCommand('sudo update-ca-certificates');
|
||||
break;
|
||||
}
|
||||
|
||||
case 'windows': {
|
||||
const winCertPath = path.join(certsDir, 'ca-cert.der');
|
||||
|
||||
// Escape backslashes for PowerShell
|
||||
const psPath = winCertPath.replace(/\\/g, '\\\\');
|
||||
|
||||
// PowerShell .NET method (works reliably in CI)
|
||||
const psCommand = [
|
||||
`$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2('${psPath}');`,
|
||||
`$store = New-Object System.Security.Cryptography.X509Certificates.X509Store('Root','LocalMachine');`,
|
||||
`$store.Open('ReadWrite');`,
|
||||
`$store.Add($cert);`,
|
||||
`$store.Close();`,
|
||||
// Verify cert was added by checking if it exists in LocalMachine\Root
|
||||
`$verifyStore = New-Object System.Security.Cryptography.X509Certificates.X509Store('Root','LocalMachine');`,
|
||||
`$verifyStore.Open('ReadOnly');`,
|
||||
`$found = $verifyStore.Certificates | Where-Object { $_.Thumbprint -eq $cert.Thumbprint };`,
|
||||
`$verifyStore.Close();`,
|
||||
`if (-not $found) { throw 'Certificate was not added to LocalMachine\Root' };`
|
||||
].join(' ');
|
||||
|
||||
execCommand(`powershell -Command "${psCommand}"`);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyCertificates(certsDir) {
|
||||
const platform = detectPlatform();
|
||||
// Core PEM files required for all platforms
|
||||
const requiredFiles = ['ca-cert.pem', 'ca-key.pem', 'localhost-cert.pem', 'localhost-key.pem'];
|
||||
|
||||
// Verify required PEM files exist
|
||||
for (const file of requiredFiles) {
|
||||
const filePath = path.join(certsDir, file);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`missing certificate file: ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check Windows-specific files but don't require them (they're optional fallbacks)
|
||||
if (platform === 'windows') {
|
||||
const windowsFiles = ['ca-cert.der', 'localhost.p12', 'localhost-cert.der'];
|
||||
for (const file of windowsFiles) {
|
||||
const filePath = path.join(certsDir, file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
console.log(`✅ Windows certificate file available: ${file}`);
|
||||
} else {
|
||||
console.log(`⚠️ Windows certificate file missing (but not required): ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createCertsDir,
|
||||
generateCertificates,
|
||||
addCAToTruststore,
|
||||
verifyCertificates
|
||||
};
|
||||
|
||||
60
tests/ssl/custom-ca-certs/server/helpers/platform.js
Normal file
60
tests/ssl/custom-ca-certs/server/helpers/platform.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const { execSync } = require('node:child_process');
|
||||
const os = require('node:os');
|
||||
|
||||
function execCommand(command, cwd = process.cwd()) {
|
||||
return execSync(command, {
|
||||
cwd,
|
||||
stdio: 'inherit',
|
||||
timeout: 30000
|
||||
});
|
||||
}
|
||||
|
||||
function execCommandSilent(command, cwd = process.cwd()) {
|
||||
return execSync(command, {
|
||||
cwd,
|
||||
stdio: 'pipe',
|
||||
timeout: 30000
|
||||
});
|
||||
}
|
||||
|
||||
function detectPlatform() {
|
||||
const platform = os.platform();
|
||||
switch (platform) {
|
||||
case 'darwin': return 'macos';
|
||||
case 'linux': return 'linux';
|
||||
case 'win32': return 'windows';
|
||||
default: throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
|
||||
function killProcessOnPort(port) {
|
||||
const platform = detectPlatform();
|
||||
|
||||
try {
|
||||
switch (platform) {
|
||||
case 'macos':
|
||||
execCommand(`lsof -ti :${port} | xargs kill -9`);
|
||||
break;
|
||||
case 'linux':
|
||||
execCommand(`lsof -ti :${port} | xargs kill -9`);
|
||||
break;
|
||||
case 'windows':
|
||||
const result = execCommandSilent(`netstat -ano | findstr :${port}`);
|
||||
const lines = result.toString().split('\n');
|
||||
for (const line of lines) {
|
||||
const match = line.trim().match(/\s+(\d+)$/);
|
||||
if (match) {
|
||||
execCommandSilent(`taskkill /F /PID ${match[1]}`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
execCommand,
|
||||
execCommandSilent,
|
||||
detectPlatform,
|
||||
killProcessOnPort
|
||||
};
|
||||
74
tests/ssl/custom-ca-certs/server/index.js
Normal file
74
tests/ssl/custom-ca-certs/server/index.js
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const https = require('node:https');
|
||||
const { killProcessOnPort } = require('./helpers/platform');
|
||||
|
||||
function createServer(certsDir, port = 8090) {
|
||||
const serverOptions = {
|
||||
key: fs.readFileSync(path.join(certsDir, 'localhost-key.pem')),
|
||||
cert: fs.readFileSync(path.join(certsDir, 'localhost-cert.pem')),
|
||||
ca: fs.readFileSync(path.join(certsDir, 'ca-cert.pem'))
|
||||
}
|
||||
|
||||
const server = https.createServer(serverOptions, (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
||||
res.end('helloworld');
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
server.listen(port, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(server);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shutdownServer(server, cleanup) {
|
||||
const shutdown = (signal) => {
|
||||
console.log(`🛑 Received ${signal}, shutting down`);
|
||||
|
||||
if (cleanup) cleanup();
|
||||
|
||||
if (server) {
|
||||
server.close(() => process.exit(0));
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
process.on('SIGINT', () => shutdown('SIGINT'));
|
||||
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
||||
}
|
||||
|
||||
async function startServer() {
|
||||
const certsDir = path.join(__dirname, 'certs');
|
||||
const port = 8090;
|
||||
|
||||
console.log('🚀 Starting HTTPS test server');
|
||||
|
||||
try {
|
||||
killProcessOnPort(port);
|
||||
|
||||
console.log(`🌐 Creating server on port ${port}`);
|
||||
const server = await createServer(certsDir, port);
|
||||
|
||||
shutdownServer(server, () => {
|
||||
console.log('✨ Server cleanup completed');
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Server startup failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
startServer();
|
||||
}
|
||||
|
||||
module.exports = { startServer };
|
||||
106
tests/ssl/custom-ca-certs/server/readme.md
Normal file
106
tests/ssl/custom-ca-certs/server/readme.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# CA Certificates Test Server
|
||||
|
||||
A Node.js HTTPS test server with self-signed certificate generation for testing SSL/TLS connections in Bruno.
|
||||
|
||||
## Overview
|
||||
|
||||
This server provides two main functionalities:
|
||||
1. **Certificate Generation** - Creates a complete CA certificate chain for testing
|
||||
2. **HTTPS Server** - Runs a secure server using the generated certificates
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Generate Certificates
|
||||
|
||||
Generate the required CA certificates and add them to your system's truststore:
|
||||
|
||||
```bash
|
||||
node scripts/generate-certs.js
|
||||
```
|
||||
|
||||
This will:
|
||||
- Create a `certs/` directory
|
||||
- Generate CA certificate, server certificate, and private keys
|
||||
- Verify the certificate chain
|
||||
- Add the CA certificate to your system's truststore (macOS/Linux/Windows)
|
||||
|
||||
**Generated Files:**
|
||||
- `certs/ca-cert.pem` - Certificate Authority certificate
|
||||
- `certs/ca-key.pem` - CA private key
|
||||
- `certs/localhost-cert.pem` - Server certificate for localhost
|
||||
- `certs/localhost-key.pem` - Server private key
|
||||
|
||||
**Windows-Specific Files (automatically generated on Windows):**
|
||||
- `certs/ca-cert.der` - CA certificate in DER format (for Windows certificate store)
|
||||
- `certs/localhost.p12` - PKCS#12 bundle containing server certificate and key
|
||||
- `certs/localhost-cert.der` - Server certificate in DER format
|
||||
|
||||
### Certificate Installation Details
|
||||
|
||||
The certificate generation script automatically adds the CA certificate to your system's truststore:
|
||||
|
||||
**macOS:** Uses `security add-trusted-cert` to add the CA to the System keychain
|
||||
**Linux:** Copies the CA certificate to `/usr/local/share/ca-certificates/` and runs `update-ca-certificates`
|
||||
**Windows:** Uses PowerShell to add the CA certificate to the LocalMachine\Root certificate store
|
||||
|
||||
> **Note:** On Windows, the script requires Administrator privileges to install certificates to the machine-wide certificate store. If you encounter permission issues, run your terminal as Administrator.
|
||||
|
||||
### 2. Run HTTPS Server
|
||||
|
||||
Start the HTTPS server on port 8090:
|
||||
|
||||
```bash
|
||||
node index.js
|
||||
```
|
||||
|
||||
The server will:
|
||||
- Load certificates from the `certs/` directory
|
||||
- Start an HTTPS server on `https://localhost:8090`
|
||||
- Serve a simple "helloworld" response
|
||||
- Handle graceful shutdown on SIGINT/SIGTERM
|
||||
|
||||
## Testing
|
||||
|
||||
Once the server is running, you can test SSL connections:
|
||||
|
||||
### Unix/Linux/macOS
|
||||
```bash
|
||||
# Test with curl
|
||||
curl https://localhost:8090
|
||||
|
||||
# Test certificate verification
|
||||
openssl s_client -connect localhost:8090 -CAfile certs/ca-cert.pem
|
||||
```
|
||||
|
||||
### Windows
|
||||
```powershell
|
||||
# Test with curl (if available)
|
||||
curl https://localhost:8090
|
||||
|
||||
# Test with PowerShell Invoke-WebRequest
|
||||
Invoke-WebRequest -Uri https://localhost:8090
|
||||
|
||||
# Test certificate verification with OpenSSL
|
||||
openssl s_client -connect localhost:8090 -CAfile certs/ca-cert.pem
|
||||
|
||||
# Verify certificate is installed in Windows certificate store
|
||||
Get-ChildItem -Path Cert:\LocalMachine\Root | Where-Object { $_.Subject -like "*Local Dev CA*" }
|
||||
|
||||
# Test with .NET WebClient (alternative method)
|
||||
$client = New-Object System.Net.WebClient
|
||||
$client.DownloadString("https://localhost:8090")
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
server/
|
||||
├── index.js # Main HTTPS server
|
||||
├── scripts/
|
||||
│ └── generate-certs.js # Certificate generation script
|
||||
├── helpers/
|
||||
│ ├── certs.js # Certificate management utilities
|
||||
│ └── platform.js # Platform-specific utilities
|
||||
├── certs/ # Generated certificates (created by script)
|
||||
└── readme.md # This file
|
||||
```
|
||||
46
tests/ssl/custom-ca-certs/server/scripts/generate-certs.js
Normal file
46
tests/ssl/custom-ca-certs/server/scripts/generate-certs.js
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const path = require('node:path');
|
||||
const {
|
||||
createCertsDir,
|
||||
generateCertificates,
|
||||
addCAToTruststore,
|
||||
verifyCertificates
|
||||
} = require('../helpers/certs');
|
||||
|
||||
/**
|
||||
* Setup CA certificates for testing server
|
||||
*/
|
||||
async function setup() {
|
||||
console.log('🔧 Setting up CA certificates for test server');
|
||||
|
||||
const certsDir = path.join(__dirname, '..', 'certs');
|
||||
|
||||
try {
|
||||
console.log('📁 Creating certificates directory');
|
||||
createCertsDir(certsDir);
|
||||
|
||||
console.log('🔐 Generating certificates');
|
||||
generateCertificates(certsDir);
|
||||
|
||||
console.log('✅ Verifying certificates');
|
||||
verifyCertificates(certsDir);
|
||||
|
||||
console.log('🛡️ Adding CA to truststore');
|
||||
addCAToTruststore(certsDir);
|
||||
|
||||
console.log('🎉 CA certificate setup completed successfully');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ Generate certs failed:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
setup()
|
||||
.then(() => process.exit(0))
|
||||
.catch(() => process.exit(1));
|
||||
}
|
||||
|
||||
module.exports = { setup };
|
||||
@@ -0,0 +1,57 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('custom invalid ca cert added to the config and keep default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/ssl/custom-ca-certs/collection"],
|
||||
"preferences": {
|
||||
"request": {
|
||||
"sslVerification": true,
|
||||
"customCaCertificate": {
|
||||
"enabled": true,
|
||||
"filePath": "{{projectRoot}}/tests/ssl/custom-ca-certs/server/certs/ca-key.pem"
|
||||
},
|
||||
"keepDefaultCaCertificates": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('custom invalid ca cert added to the config and NO default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(0);
|
||||
await expect(parseInt(failed)).toBe(1);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/ssl/custom-ca-certs/collection"],
|
||||
"preferences": {
|
||||
"request": {
|
||||
"sslVerification": true,
|
||||
"customCaCertificate": {
|
||||
"enabled": true,
|
||||
"filePath": "{{projectRoot}}/tests/ssl/custom-ca-certs/server/certs/ca-key.pem"
|
||||
},
|
||||
"keepDefaultCaCertificates": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('custom valid ca cert added to the config and keep default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/ssl/custom-ca-certs/collection"],
|
||||
"preferences": {
|
||||
"request": {
|
||||
"sslVerification": true,
|
||||
"customCaCertificate": {
|
||||
"enabled": true,
|
||||
"filePath": "{{projectRoot}}/tests/ssl/custom-ca-certs/server/certs/ca-cert.pem"
|
||||
},
|
||||
"keepDefaultCaCertificates": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { test, expect } from '../../../../../playwright';
|
||||
|
||||
test.describe.serial('custom valid ca cert added to the config and NO default ca certs', () => {
|
||||
test('developer mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init dev mode
|
||||
await page.getByText('custom-ca-certs').click();
|
||||
await page.getByLabel('Developer Mode(use only if').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
|
||||
test('safe mode', async ({ pageWithUserData: page }) => {
|
||||
|
||||
// init safe mode
|
||||
await page.getByText('Developer Mode').click();
|
||||
await page.getByLabel('Safe Mode').check();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
test.setTimeout(2 * 60 * 1000);
|
||||
await page.locator('.collection-actions').hover();
|
||||
await page.locator('.collection-actions .icon').click();
|
||||
await page.getByText('Run', { exact: true }).click();
|
||||
await page.getByRole('button', { name: 'Run Collection' }).click();
|
||||
await page.getByRole('button', { name: 'Run Again' }).waitFor({ timeout: 2 * 60 * 1000 });
|
||||
|
||||
const result = await page.getByText('Total Requests: ').innerText();
|
||||
const matches = result.match(/Total Requests: (\d+), Passed: (\d+), Failed: (\d+), Skipped: (\d+)/);
|
||||
if (!matches) {
|
||||
throw new Error('Could not parse test results');
|
||||
}
|
||||
const [totalRequests, passed, failed, skipped] = matches.slice(1);
|
||||
await expect(parseInt(totalRequests)).toBe(1);
|
||||
await expect(parseInt(passed)).toBe(1);
|
||||
await expect(parseInt(failed)).toBe(0);
|
||||
await expect(parseInt(skipped)).toBe(0);
|
||||
await expect(parseInt(passed)).toBe(parseInt(totalRequests) - parseInt(skipped) - parseInt(failed));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"maximized": true,
|
||||
"lastOpenedCollections": ["{{projectRoot}}/tests/ssl/custom-ca-certs/collection"],
|
||||
"preferences": {
|
||||
"request": {
|
||||
"sslVerification": true,
|
||||
"customCaCertificate": {
|
||||
"enabled": true,
|
||||
"filePath": "{{projectRoot}}/tests/ssl/custom-ca-certs/server/certs/ca-cert.pem"
|
||||
},
|
||||
"keepDefaultCaCertificates": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user