123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- const axios = require('axios');
- const YAML = require('yaml');
- const fs = require('node:fs');
- const path_ = require('node:path');
- const url = require('node:url');
- const https = require('node:https');
- const Assert = require('./Assert');
- const log_error = require('./log_error');
- module.exports = class TestSDK {
- constructor (conf) {
- this.conf = conf;
- this.cwd = `/${conf.username}`;
- this.httpsAgent = new https.Agent({
- rejectUnauthorized: false
- })
- const url_origin = new url.URL(conf.url).origin;
- this.headers_ = {
- 'Origin': url_origin,
- 'Authorization': `Bearer ${conf.token}`
- };
- this.installAPIMethodShorthands_();
- this.assert = new Assert();
- this.sdks = {};
- this.results = [];
- this.failCount = 0;
- this.caseCount = 0;
- this.nameStack = [];
- this.packageResults = [];
- this.benchmarkResults = [];
- }
- async get_sdk (name) {
- return await this.sdks[name].create();
- }
- // === test related methods ===
- async runTestPackage (testDefinition) {
- this.nameStack.push(testDefinition.name);
- this.packageResults.push({
- name: testDefinition.name,
- failCount: 0,
- caseCount: 0,
- });
- const imported = {};
- for ( const key of Object.keys(testDefinition.import ?? {}) ) {
- imported[key] = this.sdks[key];
- }
- await testDefinition.do(this, imported);
- this.nameStack.pop();
- }
- async runBenchmark (benchDefinition) {
- const strid = '' +
- '\x1B[35;1m[bench]\x1B[0m' +
- this.nameStack.join(` \x1B[36;1m->\x1B[0m `);
- process.stdout.write(strid + ' ... \n');
- this.nameStack.push(benchDefinition.name);
- let results;
- this.benchmarkResults.push(results = {
- name: benchDefinition.name,
- start: Date.now(),
- });
- try {
- await benchDefinition.do(this);
- } catch (e) {
- results.error = e;
- } finally {
- results.end = Date.now();
- const dur = results.end - results.start;
- process.stdout.write(`...\x1B[32;1m[${dur}]\x1B[0m\n`);
- }
- }
- recordResult (result) {
- const pkg = this.packageResults[this.packageResults.length - 1];
- this.caseCount++;
- pkg.caseCount++;
- if ( ! result.success ) {
- this.failCount++;
- pkg.failCount++;
- }
- this.results.push(result);
- }
- async case (id, fn) {
- this.nameStack.push(id);
- const tabs = Array(this.nameStack.length - 2).fill(' ').join('');
- const strid = tabs + this.nameStack.join(` \x1B[36;1m->\x1B[0m `);
- process.stdout.write(strid + ' ... \n');
- try {
- await fn();
- } catch (e) {
- process.stdout.write(`${tabs}...\x1B[31;1m[FAIL]\x1B[0m\n`);
- this.recordResult({
- strid,
- e,
- success: false,
- });
- log_error(e);
- return;
- } finally {
- this.nameStack.pop();
- }
- process.stdout.write(`${tabs}...\x1B[32;1m[PASS]\x1B[0m\n`);
- this.recordResult({
- strid,
- success: true
- });
- }
- quirk (msg) {
- console.log(`\x1B[33;1mignoring known quirk: ${msg}\x1B[0m`);
- }
- // === information display methods ===
- printTestResults () {
- console.log(`\n\x1B[33;1m=== Test Results ===\x1B[0m`);
- let tbl = {};
- for ( const pkg of this.packageResults ) {
- tbl[pkg.name] = {
- passed: pkg.caseCount - pkg.failCount,
- failed: pkg.failCount,
- total: pkg.caseCount,
- }
- }
- console.table(tbl);
- process.stdout.write(`\x1B[36;1m${this.caseCount} tests were run\x1B[0m - `);
- if ( this.failCount > 0 ) {
- console.log(`\x1B[31;1m✖ ${this.failCount} tests failed!\x1B[0m`);
- } else {
- console.log(`\x1B[32;1m✔ All tests passed!\x1B[0m`)
- }
- }
- printBenchmarkResults () {
- console.log(`\n\x1B[33;1m=== Benchmark Results ===\x1B[0m`);
- let tbl = {};
- for ( const bench of this.benchmarkResults ) {
- tbl[bench.name] = {
- time: bench.end - bench.start,
- error: bench.error ? bench.error.message : '',
- }
- }
- console.table(tbl);
- }
- // === path related methods ===
- cd (path) {
- this.cwd = path_.posix.join(this.cwd, path);
- }
- resolve (path) {
- if ( path.startsWith('$') ) return path;
- if ( path.startsWith('/') ) return path;
- return path_.posix.join(this.cwd, path);
- }
- // === API calls ===
- installAPIMethodShorthands_ () {
- const p = this.resolve.bind(this);
- this.read = async path => {
- const res = await this.get('read', { path: p(path) });
- return res.data;
- }
- this.mkdir = async (path, opts) => {
- const res = await this.post('mkdir', {
- path: p(path),
- ...(opts ?? {})
- });
- return res.data;
- };
- this.write = async (path, bin, params) => {
- path = p(path);
- params = params ?? {};
- let mime = 'text/plain';
- if ( params.hasOwnProperty('mime') ) {
- mime = params.mime;
- delete params.mime;
- }
- let name = path_.posix.basename(path);
- path = path_.posix.dirname(path);
- params.path = path;
- const res = await this.upload('write', name, mime, bin, params);
- return res.data;
- }
- this.stat = async (path, params) => {
- path = p(path);
- const res = await this.post('stat', { ...params, path });
- return res.data;
- }
- this.statu = async (uid, params) => {
- const res = await this.post('stat', { ...params, uid });
- return res.data;
- }
- this.readdir = async (path, params) => {
- path = p(path);
- const res = await this.post('readdir', {
- ...params,
- path
- })
- return res.data;
- }
- this.delete = async (path, params) => {
- path = p(path);
- const res = await this.post('delete', {
- ...params,
- paths: [path]
- });
- return res.data;
- }
- this.move = async (src, dst, params = {}) => {
- src = p(src);
- dst = p(dst);
- const destination = path_.dirname(dst);
- const source = src;
- const new_name = path_.basename(dst);
- console.log('move', { destination, source, new_name });
- const res = await this.post('move', {
- ...params,
- destination,
- source,
- new_name,
- });
- return res.data;
- }
- }
- getURL (...path) {
- const apiURL = new url.URL(this.conf.url);
- apiURL.pathname = path_.posix.join(
- apiURL.pathname,
- ...path
- );
- return apiURL.href;
- };
- // === HTTP methods ===
- get (ep, params) {
- return axios.request({
- httpsAgent: this.httpsAgent,
- method: 'get',
- url: this.getURL(ep),
- params,
- headers: {
- ...this.headers_
- }
- });
- }
- post (ep, params) {
- return axios.request({
- httpsAgent: this.httpsAgent,
- method: 'post',
- url: this.getURL(ep),
- data: params,
- headers: {
- ...this.headers_,
- 'Content-Type': 'application/json',
- }
- })
- }
- upload (ep, name, mime, bin, params) {
- const adapt_file = (bin, mime) => {
- if ( typeof bin === 'string' ) {
- return new Blob([bin], { type: mime });
- }
- return bin;
- };
- const fd = new FormData();
- for ( const k in params ) fd.append(k, params[k]);
- const blob = adapt_file(bin, mime);
- fd.append('size', blob.size);
- fd.append('file', adapt_file(bin, mime), name)
- return axios.request({
- httpsAgent: this.httpsAgent,
- method: 'post',
- url: this.getURL(ep),
- data: fd,
- headers: {
- ...this.headers_,
- 'Content-Type': 'multipart/form-data'
- },
- });
- }
- async batch (ep, ops, bins) {
- const adapt_file = (bin, mime) => {
- if ( typeof bin === 'string' ) {
- return new Blob([bin], { type: mime });
- }
- return bin;
- };
- const fd = new FormData();
- fd.append('original_client_socket_id', '');
- fd.append('socket_id', '');
- fd.append('operation_id', '');
- let fileI = 0;
- for ( let i=0 ; i < ops.length ; i++ ) {
- const op = ops[i];
- fd.append('operation', JSON.stringify(op));
- }
- const files = [];
- for ( let i=0 ; i < ops.length ; i++ ) {
- const op = ops[i];
- if ( op.op === 'mkdir' ) continue;
- if ( op.op === 'mktree' ) continue;
- let mime = op.mime ?? 'text/plain';
- const file = adapt_file(bins[fileI++], mime);
- fd.append('fileinfo', JSON.stringify({
- size: file.size,
- name: op.name,
- mime,
- }));
- files.push({
- op, file,
- })
- delete op.name;
- }
- for ( const file of files ) {
- const { op, file: blob } = file;
- fd.append('file', blob, op.name);
- }
- const res = await axios.request({
- httpsAgent: this.httpsAgent,
- method: 'post',
- url: this.getURL(ep),
- data: fd,
- headers: {
- ...this.headers_,
- 'Content-Type': 'multipart/form-data'
- },
- });
- return res.data.results;
- }
- batch_json (ep, ops, bins) {
- return axios.request({
- httpsAgent: this.httpsAgent,
- method: 'post',
- url: this.getURL(ep),
- data: ops,
- headers: {
- ...this.headers_,
- 'Content-Type': 'application/json',
- },
- });
- }
- }
|