Bläddra i källkod

Cypress test for Streams

and updated cypress + packages
Jamie Curnow 8 månader sedan
förälder
incheckning
6a60627833

+ 1 - 1
backend/internal/stream.js

@@ -9,7 +9,7 @@ const internalHost        = require('./host');
 const {castJsonIfNeed}    = require('../lib/helpers');
 
 function omissions () {
-	return ['is_deleted'];
+	return ['is_deleted', 'owner.is_deleted', 'certificate.is_deleted'];
 }
 
 const internalStream = {

+ 1 - 0
backend/models/stream.js

@@ -8,6 +8,7 @@ const now         = require('./now_helper');
 Model.knex(db);
 
 const boolFields = [
+	'enabled',
 	'is_deleted',
 	'tcp_forwarding',
 	'udp_forwarding',

+ 14 - 3
backend/schema/components/stream-object.json

@@ -19,9 +19,7 @@
 		"incoming_port": {
 			"type": "integer",
 			"minimum": 1,
-			"maximum": 65535,
-			"if": {"properties": {"tcp_forwarding": {"const": true}}},
-			"then": {"not": {"oneOf": [{"const": 80}, {"const": 443}]}}
+			"maximum": 65535
 		},
 		"forwarding_host": {
 			"anyOf": [
@@ -60,6 +58,19 @@
 		},
 		"meta": {
 			"type": "object"
+		},
+		"owner": {
+			"$ref": "./user-object.json"
+		},
+		"certificate": {
+			"oneOf": [
+				{
+					"type": "null"
+				},
+				{
+					"$ref": "./certificate-object.json"
+				}
+			]
 		}
 	}
 }

+ 5 - 1
docker/docker-compose.ci.yml

@@ -22,6 +22,10 @@ services:
       test: ["CMD", "/usr/bin/check-health"]
       interval: 10s
       timeout: 3s
+    expose:
+      - '80-81/tcp'
+      - '443/tcp'
+      - '1500-1503/tcp'
     networks:
       fulltest:
         aliases:
@@ -97,7 +101,7 @@ services:
       HTTP_PROXY: 'squid:3128'
       HTTPS_PROXY: 'squid:3128'
     volumes:
-      - 'cypress_logs:/results'
+      - 'cypress_logs:/test/results'
       - './dev/resolv.conf:/etc/resolv.conf:ro'
       - '/etc/localtime:/etc/localtime:ro'
     command: cypress run --browser chrome --config-file=cypress/config/ci.js

+ 1 - 1
docker/scripts/install-s6

@@ -8,7 +8,7 @@ BLUE='\E[1;34m'
 GREEN='\E[1;32m'
 RESET='\E[0m'
 
-S6_OVERLAY_VERSION=3.1.5.0
+S6_OVERLAY_VERSION=3.2.0.2
 TARGETPLATFORM=${1:-linux/amd64}
 
 # Determine the correct binary file for the architecture given

+ 3 - 4
test/cypress/Dockerfile

@@ -1,6 +1,4 @@
-FROM cypress/included:13.9.0
-
-COPY --chown=1000 ./test /test
+FROM cypress/included:14.0.1
 
 # Disable Cypress CLI colors
 ENV FORCE_COLOR=0
@@ -12,12 +10,13 @@ RUN wget "https://github.com/testssl/testssl.sh/archive/refs/tags/v3.2rc4.tar.gz
 	&& mv /tmp/testssl.sh-3.2rc4 /testssl \
 	&& rm /tmp/testssl.tgz \
 	&& apt-get update \
-	&& apt-get install -y bsdmainutils \
+	&& apt-get install -y bsdmainutils curl dnsutils \
 	&& apt-get clean \
 	&& rm -rf /var/lib/apt/lists/* \
 	&& wget "https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64" -O /bin/mkcert \
 	&& chmod +x /bin/mkcert
 
+COPY --chown=1000 ./test /test
 WORKDIR /test
 RUN yarn install && yarn cache clean
 ENTRYPOINT []

+ 213 - 0
test/cypress/e2e/api/Streams.cy.js

@@ -0,0 +1,213 @@
+/// <reference types="cypress" />
+
+describe('Streams', () => {
+	let token;
+
+	before(() => {
+		cy.getToken().then((tok) => {
+			token = tok;
+			// Set default site content
+			cy.task('backendApiPut', {
+				token: token,
+				path:  '/api/settings/default-site',
+				data: {
+					value: 'html',
+					meta: {
+						html: '<p>yay it works</p>'
+					},
+				},
+			}).then((data) => {
+				cy.validateSwaggerSchema('put', 200, '/settings/{settingID}', data);
+			});
+		});
+
+		// Create a custom cert pair
+		cy.exec('mkcert -cert-file=/test/cypress/fixtures/website1.pem -key-file=/test/cypress/fixtures/website1.key.pem website1.example.com').then((result) => {
+			expect(result.code).to.eq(0);
+			// Install CA
+			cy.exec('mkcert -install').then((result) => {
+				expect(result.code).to.eq(0);
+			});
+		});
+
+		cy.exec('rm -f /test/results/testssl.json');
+	});
+
+	it('Should be able to create TCP Stream', function() {
+		cy.task('backendApiPost', {
+			token: token,
+			path:  '/api/nginx/streams',
+			data:  {
+				incoming_port: 1500,
+				forwarding_host: '127.0.0.1',
+				forwarding_port: 80,
+				certificate_id: 0,
+				meta: {
+					dns_provider_credentials: "",
+					letsencrypt_agree: false,
+					dns_challenge: true
+				},
+				tcp_forwarding: true,
+				udp_forwarding: false
+			}
+		}).then((data) => {
+			cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
+			expect(data).to.have.property('id');
+			expect(data.id).to.be.greaterThan(0);
+			expect(data).to.have.property('enabled', true);
+			expect(data).to.have.property('tcp_forwarding', true);
+			expect(data).to.have.property('udp_forwarding', false);
+
+			cy.exec('curl --noproxy -- http://website1.example.com:1500').then((result) => {
+				expect(result.code).to.eq(0);
+				expect(result.stdout).to.contain('yay it works');
+			});
+		});
+	});
+
+	it('Should be able to create UDP Stream', function() {
+		cy.task('backendApiPost', {
+			token: token,
+			path:  '/api/nginx/streams',
+			data:  {
+				incoming_port: 1501,
+				forwarding_host: '127.0.0.1',
+				forwarding_port: 80,
+				certificate_id: 0,
+				meta: {
+					dns_provider_credentials: "",
+					letsencrypt_agree: false,
+					dns_challenge: true
+				},
+				tcp_forwarding: false,
+				udp_forwarding: true
+			}
+		}).then((data) => {
+			cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
+			expect(data).to.have.property('id');
+			expect(data.id).to.be.greaterThan(0);
+			expect(data).to.have.property('enabled', true);
+			expect(data).to.have.property('tcp_forwarding', false);
+			expect(data).to.have.property('udp_forwarding', true);
+		});
+	});
+
+	it('Should be able to create TCP/UDP Stream', function() {
+		cy.task('backendApiPost', {
+			token: token,
+			path:  '/api/nginx/streams',
+			data:  {
+				incoming_port: 1502,
+				forwarding_host: '127.0.0.1',
+				forwarding_port: 80,
+				certificate_id: 0,
+				meta: {
+					dns_provider_credentials: "",
+					letsencrypt_agree: false,
+					dns_challenge: true
+				},
+				tcp_forwarding: true,
+				udp_forwarding: true
+			}
+		}).then((data) => {
+			cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
+			expect(data).to.have.property('id');
+			expect(data.id).to.be.greaterThan(0);
+			expect(data).to.have.property('enabled', true);
+			expect(data).to.have.property('tcp_forwarding', true);
+			expect(data).to.have.property('udp_forwarding', true);
+
+			cy.exec('curl --noproxy -- http://website1.example.com:1502').then((result) => {
+				expect(result.code).to.eq(0);
+				expect(result.stdout).to.contain('yay it works');
+			});
+		});
+	});
+
+	it('Should be able to create SSL TCP Stream', function() {
+		let certID = 0;
+
+		// Create custom cert
+		cy.task('backendApiPost', {
+			token: token,
+			path:  '/api/nginx/certificates',
+			data:  {
+				provider: "other",
+				nice_name: "Custom Certificate for SSL Stream",
+			},
+		}).then((data) => {
+			cy.validateSwaggerSchema('post', 201, '/nginx/certificates', data);
+			expect(data).to.have.property('id');
+			certID = data.id;
+
+			// Upload files
+			cy.task('backendApiPostFiles', {
+				token: token,
+				path:  `/api/nginx/certificates/${certID}/upload`,
+				files:  {
+					certificate: 'website1.pem',
+					certificate_key: 'website1.key.pem',
+				},
+			}).then((data) => {
+				cy.validateSwaggerSchema('post', 200, '/nginx/certificates/{certID}/upload', data);
+				expect(data).to.have.property('certificate');
+				expect(data).to.have.property('certificate_key');
+
+				// Create the stream
+				cy.task('backendApiPost', {
+					token: token,
+					path:  '/api/nginx/streams',
+					data:  {
+						incoming_port: 1503,
+						forwarding_host: '127.0.0.1',
+						forwarding_port: 80,
+						certificate_id: certID,
+						meta: {
+							dns_provider_credentials: "",
+							letsencrypt_agree: false,
+							dns_challenge: true
+						},
+						tcp_forwarding: true,
+						udp_forwarding: false
+					}
+				}).then((data) => {
+					cy.validateSwaggerSchema('post', 201, '/nginx/streams', data);
+					expect(data).to.have.property('id');
+					expect(data.id).to.be.greaterThan(0);
+					expect(data).to.have.property("enabled", true);
+					expect(data).to.have.property('tcp_forwarding', true);
+					expect(data).to.have.property('udp_forwarding', false);
+					expect(data).to.have.property('certificate_id', certID);
+
+					// Check the ssl termination
+					cy.task('log', '[testssl.sh] Running ...');
+					cy.exec('/testssl/testssl.sh --quiet --add-ca="$(/bin/mkcert -CAROOT)/rootCA.pem" --jsonfile=/test/results/testssl.json website1.example.com:1503', {
+						timeout: 120000, // 2 minutes
+					}).then((result) => {
+						cy.task('log', '[testssl.sh] ' + result.stdout);
+
+						const allowedSeverities = ["INFO", "OK", "LOW", "MEDIUM"];
+						const ignoredIDs = [
+							'cert_chain_of_trust',
+							'cert_extlifeSpan',
+							'cert_revocation',
+							'overall_grade',
+						];
+
+						cy.readFile('/test/results/testssl.json').then((data) => {
+							// Parse each array item
+							for (let i = 0; i < data.length; i++) {
+								const item = data[i];
+								if (ignoredIDs.includes(item.id)) {
+									continue;
+								}
+								expect(item.severity).to.be.oneOf(allowedSeverities);
+							}
+						});
+					});
+				});
+			});
+		});
+	});
+
+});

+ 7 - 7
test/package.json

@@ -4,18 +4,18 @@
 	"description": "",
 	"main": "index.js",
 	"dependencies": {
-		"@jc21/cypress-swagger-validation": "^0.3.1",
-		"axios": "^1.7.7",
-		"cypress": "^13.15.0",
-		"cypress-multi-reporters": "^1.6.4",
+		"@jc21/cypress-swagger-validation": "^0.3.2",
+		"axios": "^1.7.9",
+		"cypress": "^14.0.1",
+		"cypress-multi-reporters": "^2.0.5",
 		"cypress-wait-until": "^3.0.2",
-		"eslint": "^9.12.0",
+		"eslint": "^9.19.0",
 		"eslint-plugin-align-assignments": "^1.1.2",
 		"eslint-plugin-chai-friendly": "^1.0.1",
-		"eslint-plugin-cypress": "^3.5.0",
+		"eslint-plugin-cypress": "^4.1.0",
 		"form-data": "^4.0.1",
 		"lodash": "^4.17.21",
-		"mocha": "^10.7.3",
+		"mocha": "^11.1.0",
 		"mocha-junit-reporter": "^2.2.1"
 	},
 	"scripts": {