diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..780cf339f --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,46 @@ +name: CI + +on: [push, pull_request] + +jobs: + run_tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + + - name: Installing node modules + run: npm install + working-directory: ./test + + - name: Installing Dredd + run: sudo npm install dredd --global --unsafe-perm=true --allow-root + + - name: Set up Python + uses: actions/setup-python@v2 + + - name: Install pipenv + run: pip install pipenv + + - name: Installing python modules + run: pipenv install --system + working-directory: ./test + + - name: Build the stack + run: docker-compose up -d + shell: bash + working-directory: ./test + + - name: Waiting for OpenSearch domain to be up. + run: sh ./.github/workflows/domain-check.sh + + - name: Run script file + run: | + # Disabling TLS certificate verfication as hosted on docker. + export NODE_TLS_REJECT_UNAUTHORIZED=0 + python driver-code.py + shell: bash + working-directory: ./test/scripts + diff --git a/.github/workflows/domain-check.sh b/.github/workflows/domain-check.sh new file mode 100644 index 000000000..1a3662a7e --- /dev/null +++ b/.github/workflows/domain-check.sh @@ -0,0 +1,17 @@ +counter=1 +for counter in {1..10} +do + if [ $(curl -s -o /dev/null --head -w "%{http_code}" 'https://admin:admin@localhost:9200' -H 'Content-Type:application/json' --insecure -v) -ne 200 ]; then + sleep 30s + else + # Waiting for security plugin to be initialised and become operational. + echo "Waiting for addtional 30 seconds.. for Opensearch domain to be up." + sleep 30s + break + fi +done +if [ $counter -eq 11 ]; then + echo "Unable to connect with OpenSearch URL https://localhost:9200/" + exit 1 +fi + diff --git a/test/Pipfile b/test/Pipfile new file mode 100644 index 000000000..4828ec172 --- /dev/null +++ b/test/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +prettytable = "*" + +[dev-packages] + +[requires] +python_version = "*" diff --git a/test/Pipfile.lock b/test/Pipfile.lock new file mode 100644 index 000000000..b9276b358 --- /dev/null +++ b/test/Pipfile.lock @@ -0,0 +1,36 @@ +{ + "_meta": { + "hash": { + "sha256": "ef6ec0724fab7ce6a5699d14ac568bfc3a7a1d2b0cdb19a43c90f2c6f1346132" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "*" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "prettytable": { + "hashes": [ + "sha256:118eb54fd2794049b810893653b20952349df6d3bc1764e7facd8a18064fa9b0", + "sha256:d1c34d72ea2c0ffd6ce5958e71c428eb21a3d40bf3133afe319b24aeed5af407" + ], + "index": "pypi", + "version": "==3.3.0" + }, + "wcwidth": { + "hashes": [ + "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" + ], + "version": "==0.2.5" + } + }, + "develop": {} +} diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 000000000..204a5e612 --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,56 @@ +version: '3' +services: + opensearch-node1: + image: opensearchproject/opensearch:latest + container_name: opensearch-node1 + environment: + - cluster.name=opensearch-cluster + - node.name=opensearch-node1 + - discovery.seed_hosts=opensearch-node1,opensearch-node2 + - cluster.initial_master_nodes=opensearch-node1,opensearch-node2 + - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems + hard: 65536 + volumes: + - opensearch-data1:/usr/share/opensearch/data + ports: + - 9200:9200 + - 9600:9600 # required for Performance Analyzer + networks: + - opensearch-net + + opensearch-node2: + image: opensearchproject/opensearch:latest + container_name: opensearch-node2 + environment: + - cluster.name=opensearch-cluster + - node.name=opensearch-node2 + - discovery.seed_hosts=opensearch-node1,opensearch-node2 + - cluster.initial_master_nodes=opensearch-node1,opensearch-node2 + - bootstrap.memory_lock=true + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data2:/usr/share/opensearch/data + networks: + - opensearch-net + +volumes: + opensearch-data1: + opensearch-data2: + +networks: + opensearch-net: + diff --git a/test/models/_global/ping/OpenSearchModel.yaml b/test/models/_global/ping/OpenSearchModel.yaml new file mode 100644 index 000000000..79a87b22e --- /dev/null +++ b/test/models/_global/ping/OpenSearchModel.yaml @@ -0,0 +1,55 @@ +openapi: 3.0.2 +info: + title: OpenSearch + version: '2021-11-23' +paths: + /: + get: + description: Returns whether the cluster is running. + operationId: GetPingCluster + responses: + '200': + description: GetPingCluster 200 response + content: + application/json: + schema: + $ref: '#/components/schemas/GetPingClusterResponseContent' +components: + schemas: + GetPingClusterResponseContent: + type: object + properties: + name: + type: string + cluster_name: + type: string + cluster_uuid: + type: string + version: + $ref: '#/components/schemas/intermediateStructure' + tagline: + type: string + intermediateStructure: + type: object + properties: + distribution: + type: string + number: + type: string + build_type: + type: string + build_hash: + type: string + build_date: + type: string + build_snapshot: + type: boolean + nullable: true + lucene_version: + type: string + minimum_wire_compatibility_version: + type: string + minimum_index_compatibility_version: + type: string + + \ No newline at end of file diff --git a/test/models/_global/ping/hooks.js b/test/models/_global/ping/hooks.js new file mode 100644 index 000000000..f5bf8922f --- /dev/null +++ b/test/models/_global/ping/hooks.js @@ -0,0 +1,7 @@ +const hooks = require('hooks'); + +hooks.before("/ > GET > 200 > application/json",function(transactions,done){ + transactions.expected.headers['Content-Type'] = "application/json; charset=UTF-8"; + done(); +}); + diff --git a/test/package-lock.json b/test/package-lock.json new file mode 100644 index 000000000..3be77af4e --- /dev/null +++ b/test/package-lock.json @@ -0,0 +1,119 @@ +{ + "name": "test-modules", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test-modules", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "fs": "^0.0.1-security", + "hooks": "^0.3.2", + "https": "^1.0.0", + "node-fetch": "^2.6.7" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "node_modules/hooks": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/hooks/-/hooks-0.3.2.tgz", + "integrity": "sha512-TqeFzUf12rSzcbm5lUls81jimUC8TmXZ4ANPxxeeMou09hrjBcHYhAQ0WgyN5YqNCXOzz7L6xVNl/+ctFuSeOw==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", + "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + }, + "dependencies": { + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "hooks": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/hooks/-/hooks-0.3.2.tgz", + "integrity": "sha512-TqeFzUf12rSzcbm5lUls81jimUC8TmXZ4ANPxxeeMou09hrjBcHYhAQ0WgyN5YqNCXOzz7L6xVNl/+ctFuSeOw==" + }, + "https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", + "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } +} diff --git a/test/package.json b/test/package.json new file mode 100644 index 000000000..f8d70ceb2 --- /dev/null +++ b/test/package.json @@ -0,0 +1,17 @@ +{ + "name": "test-modules", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "fs": "^0.0.1-security", + "hooks": "^0.3.2", + "https": "^1.0.0", + "node-fetch": "^2.6.7" + } +} diff --git a/test/scripts/driver-code.py b/test/scripts/driver-code.py new file mode 100644 index 000000000..8fb6e9485 --- /dev/null +++ b/test/scripts/driver-code.py @@ -0,0 +1,104 @@ +import argparse +import os +from xmlrpc.client import boolean +from prettytable import PrettyTable + +class Dredd: + def __init__(self, endpoint, user, path, test_name, test_pass): + if endpoint is not None: + self.endpoint = endpoint + else: + self.endpoint = "https://127.0.0.1:9200" + + if user is not None: + self.user = user + else: + self.user = "admin:admin" + + if path is not None: + self.path = path + else: + self.path = "" + + if test_name is not None: + self.test_name = test_name + else: + self.test_name = "" + + if test_pass is not None: + self.test_pass = test_pass + else: + self.test_pass = False + + def write_file(self): + file_obj = open("url.txt", mode='w', encoding='utf-8') + text = self.endpoint + " " + self.user + file_obj.write(text) + file_obj.seek(0,0) + file_obj.close() + + def dredd_work(self): + # Walking in test directory tree and runing dredd framework. + test_failed_count = 0 + test_passed_count = 0 + test_failed = [] + test_passed = [] + test_passes = PrettyTable() + test_fails = PrettyTable() + for dirpath, dirnames, files in os.walk("../models"+self.path): + curr_path = dirpath.split('/') + curr_dir = curr_path[len(curr_path)-1] + if files: + command = "dredd " + dirpath +"/"+ files[1]+ " " + self.endpoint+ " --user=" + self.user + " --hookfiles=" + dirpath + "/" + files[0] + if self.test_name != "": + if self.test_name == curr_dir: + result = os.system(command) + if(result != 0): + test_failed_count = test_failed_count + 1 + test_failed.append([curr_dir,dirpath]) + else: + test_passed_count = test_passed_count + 1 + test_passed.append([curr_dir,dirpath]) + else: + result = os.system(command) + if(result != 0): + test_failed_count = test_failed_count + 1 + test_failed.append([curr_dir,dirpath]) + else: + test_passed_count = test_passed_count + 1 + test_passed.append([curr_dir,dirpath]) + print("Total number of test cases: ", test_failed_count + test_passed_count ) + print("Test Passed: ",test_passed_count) + print("Test failed: ",test_failed_count) + if self.test_pass == True: + test_passes.field_names = ["Model Name", "Directory Path"] + test_passes.add_rows(test_passed) + test_passes.align='l' + print("Results: Test cases passed.",test_passes,sep="\n") + + test_fails.field_names = ["Model Name", "Directory Path"] + test_fails.add_rows(test_failed) + test_fails.align='l' + print("Results: Test cases failed.",test_fails,sep="\n") + return len(test_failed) + + +# Parsing command line arguments: +parser = argparse.ArgumentParser() + +parser.add_argument('--endpoint', type=str, required=False) +parser.add_argument('--user', type=str, required=False) +parser.add_argument('--path', type=str, required=False) +parser.add_argument('--testname', type=str, required=False) +parser.add_argument('--testpass', type=bool, required=False) +args = parser.parse_args() + +# Check whether default arguments provided by user: +obj = Dredd(args.endpoint, args.user, args.path, args.testname, args.testpass ) + +# Creating a intermediate file for storing URL. +obj.write_file() + +# Running dredd +exit(obj.dredd_work()) +