├── .gitattributes ├── .github └── workflows │ ├── qa.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── SonarQube.Analysis.xml ├── build ├── build.bat ├── package-loader.os └── packagedef ├── src ├── NUnitTests │ ├── EngineHelpWrapper.cs │ ├── MainTestClass.cs │ ├── NUnitTests.csproj │ └── Tests │ │ ├── external.os │ │ └── testrunner.os ├── TestApp │ ├── Program.cs │ ├── Resourses │ │ └── testScript.os │ └── TestApp.csproj ├── clientSSH.sln └── clientSSH │ ├── Connection.cs │ ├── Scp.cs │ ├── Stream.cs │ ├── app.config │ ├── clientSSH.cs │ └── clientSSH.csproj └── tools ├── .env ├── docker-compose.yml ├── openssh ├── Dockerfile └── files │ ├── prepare-ssh.sh │ ├── sftp-key │ ├── sftp-key.ppk │ ├── sftp-key.pub │ └── sshd_config ├── runtests.bat ├── transform-coverage.xslt └── transform-result.xslt /.gitattributes: -------------------------------------------------------------------------------- 1 | tools/** -linguist-detectable 2 | build/** -linguist-detectable 3 | *.os -linguist-detectable 4 | *.bsl -linguist-detectable 5 | *.xml -linguist-detectable -------------------------------------------------------------------------------- /.github/workflows/qa.yml: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (C) 2022 Artem Kuznetsov and contributors 3 | # All rights reserved. 4 | 5 | name: Контроль качества 6 | # Любой пуш и pr в проекте но с фильтром по основному проекту 7 | on: 8 | push: 9 | branches: 10 | - main 11 | - master 12 | - develop 13 | pull_request: 14 | branches: 15 | - develop 16 | 17 | env: 18 | # отключение безопасности установки, от 2020-10-01 19 | ACTIONS_ALLOW_UNSECURE_COMMANDS: true 20 | BRANCH_NAME: ${{ github.head_ref || github.ref_name }} 21 | PROJECT_KEY: "clientSSH" 22 | JAVA_HOME: "C:\\Program Files\\Java\\zulu-jdk-fx-17.0.12" 23 | 24 | jobs: 25 | build: 26 | if: github.repository == 'arkuznetsov/clientSSH' 27 | runs-on: ${{ matrix.os }} 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | os: [clientSSH] 32 | 33 | steps: 34 | # Загрузка проекта 35 | - name: Актуализация 36 | uses: actions/checkout@v2 37 | with: 38 | # Disabling shadow clone is recomended 39 | fetch-depth: 0 40 | 41 | # Установка NUnit3 42 | - name: Установка NUnit3 43 | shell: pwsh 44 | run: | 45 | curl -o "./tools/nuget.exe" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe 46 | ./tools/nuget.exe restore ./src 47 | ./tools/nuget.exe install NUnit.ConsoleRunner -Version 3.6.1 -OutputDirectory ./tools 48 | 49 | # Установка инструментов .NET 50 | - name: Установка инструментов .NET 51 | run: | 52 | dotnet tool update --global dotnet-sonarscanner 53 | dotnet tool update --global dotnet-coverage 54 | 55 | # Сборка и запуск контейнера OpenSSH 56 | - name: Сборка и запуск контейнера OpenSSH 57 | env: 58 | SSH_TEST_USER: ${{ secrets.SSH_TEST_USER }} 59 | SSH_TEST_PWD: ${{ secrets.SSH_TEST_PWD }} 60 | run: | 61 | echo PWD: $PWD 62 | mkdir -p ./tools/openssh/upload 63 | chmod -R 700 ./tools/openssh/upload 64 | docker-compose --file ./tools/docker-compose.yml up --build -d 65 | 66 | # Извлечение версии пакета 67 | - name: Получение packagedef 68 | shell: pwsh 69 | run: echo "::set-output name=data::$(type ./build/packagedef)\n" 70 | id: packagedef_data 71 | 72 | - name: Извлечение версии пакета 73 | uses: actions-ecosystem/action-regex-match@v2 74 | with: 75 | text: ${{ steps.packagedef_data.outputs.data }} 76 | regex: '.Версия\("((?:\d+\.)+\d+)"\)' 77 | id: extract_version 78 | 79 | # Начало анализа проекта в SonarQube (ветка) 80 | - name: Начало анализа проекта в SonarQube (branch) 81 | if: github.event_name == 'push' 82 | run: dotnet sonarscanner begin 83 | /s:${{ github.workspace }}/SonarQube.Analysis.xml 84 | /k:${{ env.PROJECT_KEY }} 85 | /v:${{ steps.extract_version.outputs.group1 }} 86 | /d:sonar.host.url="${{ secrets.SONARQUBE_HOST }}" 87 | /d:sonar.login="${{ secrets.SONARQUBE_TOKEN }}" 88 | /d:sonar.branch.name="${{ env.BRANCH_NAME }}" 89 | /d:sonar.cs.vscoveragexml.reportsPaths=./build/reports/coverage.xml 90 | /d:sonar.cs.nunit.reportsPaths=./build/reports/nunit-result.xml 91 | 92 | # Начало анализа проекта в SonarQube (PR) 93 | # https://docs.sonarqube.org/latest/analysis/pull-request/ 94 | - name: Начало анализа проекта в SonarQube (pull-request) 95 | if: github.event_name == 'pull_request' 96 | run: dotnet sonarscanner begin 97 | /s:${{ github.workspace }}/SonarQube.Analysis.xml 98 | /k:${{ env.PROJECT_KEY }} 99 | /d:sonar.host.url="${{ secrets.SONARQUBE_HOST }}" 100 | /d:sonar.login="${{ secrets.SONARQUBE_TOKEN }}" 101 | /d:sonar.branch.name="${{ env.BRANCH_NAME }}" 102 | /d:sonar.pullrequest.key=${{ github.event.pull_request.number }} 103 | /d:sonar.pullrequest.branch=${{ github.event.pull_request.head.ref }} 104 | /d:sonar.pullrequest.base=${{ github.event.pull_request.base.ref }} 105 | /d:sonar.scm.revision=${{ github.event.pull_request.head.sha }} 106 | /d:sonar.cs.vscoveragexml.reportsPaths=./build/reports/coverage.xml 107 | /d:sonar.cs.nunit.reportsPaths=./build/reports/nunit-result.xml 108 | 109 | # Сборка компоненты для тестирования 110 | - name: Сборка компоненты для тестирования 111 | run: | 112 | dotnet restore ./src 113 | dotnet build ./src --configuration Debug 114 | 115 | # Запуск тестов NUnit3 116 | - name: Запуск тестов NUnit3 и сборка покрытия 117 | env: 118 | SSH_TEST_USER: ${{ secrets.SSH_TEST_USER }} 119 | SSH_TEST_PWD: ${{ secrets.SSH_TEST_PWD }} 120 | run: | 121 | mkdir "./build/reports" 122 | dotnet-coverage collect -o ./build/reports/coverage.xml -f xml "./tools/NUnit.ConsoleRunner.3.6.1/tools/nunit3-console.exe ./src/NUnitTests/bin/Debug/net48/NUnitTests.dll --result=./build/reports/nunit-result.xml" 123 | 124 | # Завершение анализа проекта в SonarQube 125 | - name: Завершение анализа проекта в SonarQube 126 | run: dotnet sonarscanner end /d:sonar.login="${{ secrets.SONARQUBE_TOKEN }}" 127 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # Copyright (C) 2022 Artem Kuznetsov and contributors 3 | # All rights reserved. 4 | 5 | name: Подготовка релиза и публикация в хабе 6 | # Только события создания и изменения релиза 7 | on: 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | env: 13 | PACKAGE_MASK: ClientSSH-*.ospx 14 | ACTIONS_ALLOW_UNSECURE_COMMANDS: true 15 | 16 | jobs: 17 | build: 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: [clientSSH] 23 | oscript_version: ['1.8.4'] 24 | 25 | steps: 26 | # Загрузка проекта 27 | - name: Актуализация 28 | uses: actions/checkout@v2.3.4 29 | 30 | # Установка OneScript конкретной версии 31 | - name: Установка OneScript 32 | uses: otymko/setup-onescript@v1.1 33 | with: 34 | version: ${{ matrix.oscript_version }} 35 | 36 | # Установка зависимостей пакета 37 | - name: Установка зависимостей 38 | run: | 39 | cd build 40 | opm install opm 41 | opm install 42 | cd .. 43 | 44 | # Сборка проекта 45 | - name: Сборка проекта 46 | run: ./build/build.bat 47 | 48 | # Заливка артефактов 49 | - name: Заливка артефактов 50 | uses: actions/upload-artifact@v2 51 | with: 52 | name: package.zip 53 | path: ./build/${{ env.PACKAGE_MASK }} 54 | 55 | # Заливка в релиз 56 | - name: Заливка в релиз 57 | uses: AButler/upload-release-assets@v1.0 58 | with: 59 | files: ./build/${{ env.PACKAGE_MASK }} 60 | repo-token: ${{ secrets.GITHUB_TOKEN }} 61 | 62 | # Публикация в hub.oscript.io 63 | - name: Публикация в hub.oscript.io 64 | run: opm push -f ./build/${{ env.PACKAGE_MASK }} --token ${{ env.TOKEN }} -c stable 65 | env: 66 | TOKEN: ${{ secrets.OSHUB_TOKEN }} 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | \.idea/ 2 | \.vs/ 3 | \.vscode/ 4 | \.sonarqube/ 5 | src/NUnitTests/bin/ 6 | src/NUnitTests/obj/ 7 | src/clientSSH/bin/ 8 | src/clientSSH/obj/ 9 | src/TestApp/bin/ 10 | src/TestApp/obj/ 11 | build/bin/ 12 | build/reports/ 13 | *.ospx 14 | map.json 15 | tools/nuget.exe 16 | tools/test 17 | tools/NUnit.ConsoleRunner* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alexey Sosnoviy aka Lab, 2022 Artem Kuznetsov aka KTB 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub release](https://img.shields.io/github/release/ArKuznetsov/clientSSH.svg?style=flat-square)](https://github.com/ArKuznetsov/clientSSH/releases) 2 | [![GitHub license](https://img.shields.io/github/license/ArKuznetsov/clientSSH.svg?style=flat-square)](https://github.com/ArKuznetsov/clientSSH/blob/develop/LICENSE) 3 | [![GitHub Releases](https://img.shields.io/github/downloads/ArKuznetsov/clientSSH/latest/total?style=flat-square)](https://github.com/ArKuznetsov/clientSSH/releases) 4 | [![GitHub All Releases](https://img.shields.io/github/downloads/ArKuznetsov/clientSSH/total?style=flat-square)](https://github.com/ArKuznetsov/clientSSH/releases) 5 | 6 | [![Build Status](https://img.shields.io/github/workflow/status/ArKuznetsov/clientSSH/%D0%9A%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D1%8C%20%D0%BA%D0%B0%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%B0)](https://github.com/arkuznetsov/clientSSH/actions/) 7 | [![Quality Gate](https://open.checkbsl.org/api/project_badges/measure?project=clientSSH&metric=alert_status)](https://open.checkbsl.org/dashboard/index/clientSSH) 8 | [![Coverage](https://open.checkbsl.org/api/project_badges/measure?project=clientSSH&metric=coverage)](https://open.checkbsl.org/dashboard/index/clientSSH) 9 | [![Tech debt](https://open.checkbsl.org/api/project_badges/measure?project=clientSSH&metric=sqale_index)](https://open.checkbsl.org/dashboard/index/clientSSH) 10 | 11 | Checked by Silver Bulleters SonarQube BSL plugin 12 | 13 | # Oscript SSH client 14 | 15 | ## SSH клиент для oscript 16 | 17 | ## Примеры использования 18 | ### SSH клиент 19 | 20 | ```bsl 21 | #Использовать ClientSSH 22 | 23 | КлиентSSH = Новый КлиентSSH("127.0.0.1", 22, "user", "password"); 24 | Соединение = КлиентSSH.ПолучитьСоединение(); 25 | Результат = Соединение.ВыполнитьКоманду("echo 123"); 26 | 27 | Соединение.Отключиться(); 28 | 29 | ``` 30 | 31 | ### Клиент для конфигуратора в режиме Агента 32 | 33 | Запустить конфигуратор в режиме агента: 34 | ` 35 | 1cv8.exe DESIGNER /F"<ПутьКБазе>" /AgentMode /Visible /AgentSSHHostKeyAuto /AgentBaseDir "<ПутьКПапкеВыгрузки>" 36 | ` 37 | 38 | 39 | ```bsl 40 | #Использовать ClientSSH 41 | 42 | КлиентSSH = Новый КлиентSSH("127.0.0.1", 1543, "admin", ""); 43 | Поток = КлиентSSH.ПолучитьПоток(); 44 | 45 | // Следующие строки обязательны, иначе скрипт зависает 46 | // вариант для 8.3.16 и выше 47 | Результат = Поток.ЗаписатьВПоток("options set --show-prompt=no"); 48 | // вариант для 8.3.15 и ниже 49 | Результат = Поток.ЗаписатьВПоток("options set --show-prompt=no --output-format=json"); 50 | 51 | Результат = Поток.ЗаписатьВПоток("common connect-ib"); 52 | Результат = Поток.ЗаписатьВПоток("config dump-config-to-files --dir ."); 53 | Результат = Поток.ЗаписатьВПоток("common disconnect-ib"); 54 | 55 | Поток.Отключиться(); 56 | 57 | ``` 58 | 59 | 60 | ### Авторизация ssh с ключом 61 | 62 | ```bsl 63 | #Использовать ClientSSH 64 | 65 | КлиентSSH = Новый КлиентSSH("127.0.0.1", 22, "user", ""); 66 | КлиентSSH.УстановитьКлюч("ПутьКСекретномуКлючу", "СекретнаяФраза"); 67 | Соединение = КлиентSSH.ПолучитьСоединение(); 68 | Результат = Соединение.ВыполнитьКоманду("echo 123"); 69 | 70 | Соединение.Отключиться(); 71 | 72 | ``` 73 | 74 | ### Передача файлов 75 | 76 | ```bsl 77 | #Использовать ClientSSH 78 | 79 | КлиентSSH = Новый КлиентSSH("127.0.0.1", 1543, "admin", ""); 80 | Scp = КлиентSSH.ПолучитьScp(); 81 | Scp.ОтправитьФайл("C:\cf\1Cv8.cf", "/1Cv8.cf"); 82 | 83 | Scp.ПолучитьФайл("/1Cv8.cf", "C:\cf\1Cv8_2.cf"); 84 | Scp.Отключиться(); 85 | 86 | ``` 87 | 88 | ## Известные проблемы: 89 | * Вешается поток, если не передать следующие настройки: 90 | - для 8.3.16 и выше 91 | - `Поток.ЗаписатьВПоток("options set --show-prompt=no");` 92 | - для 8.3.15 и ниже 93 | - `Поток.ЗаписатьВПоток("options set --show-prompt=no --output-format=json");` 94 | * В папке выгрузки создается файл `agentbasedir.json` и подпапка с именем пользователя (Особенность режима Агента) 95 | 96 | Пример json-файла 97 | 98 | ```json 99 | { 100 | "usersInfo": [ 101 | { 102 | "name": "Администратор", 103 | "dir": "0" 104 | } 105 | ] 106 | } 107 | ``` 108 | -------------------------------------------------------------------------------- /SonarQube.Analysis.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | clientSSH 22 | 23 | 26 | clientSSH 27 | 28 | 34 | 35 | 36 | ./src/NUnitTests,./src/TestApp 37 | 38 | ./src/**/*.cs 39 | true 40 | git 41 | 42 | 45 | UTF-8 46 | 47 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /build/build.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | echo %~dp0 4 | @chcp 65001 5 | 6 | set OSC_BUILD_NAME=clientSSH 7 | 8 | set OSC_BUILD_ROOT=%~dp0 9 | set OSC_BUILD_CWD=%OSC_BUILD_ROOT%.. 10 | set OSC_BUILD_SRC=%OSC_BUILD_ROOT%../src 11 | set OSC_BUILD_BIN=%OSC_BUILD_ROOT%bin 12 | set OSC_BUILD_BIN_OUT=%OSC_BUILD_SRC%\%OSC_BUILD_NAME%\bin\Release\net48 13 | 14 | set OSC_BUILD_TOOLS=%OSC_BUILD_ROOT%../tools 15 | set OSC_BUILD_NUGET=%OSC_BUILD_TOOLS%/nuget.exe 16 | 17 | IF EXIST "%OSC_BUILD_ROOT%.env" ( 18 | FOR /f "usebackq tokens=*" %%a in ("%OSC_BUILD_ROOT%.env") DO ( 19 | FOR /F "tokens=1,2 delims==" %%b IN ("%%a") DO ( 20 | set "%%b=%%c" 21 | ) 22 | ) 23 | ) 24 | 25 | IF NOT EXIST "%OSC_BUILD_NUGET%" ( 26 | @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" curl -o "%OSC_BUILD_NUGET%" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe 27 | ) 28 | 29 | @"%OSC_BUILD_NUGET%" restore %OSC_BUILD_SRC% 30 | 31 | @dotnet restore %OSC_BUILD_SRC% 32 | rem @dotnet msbuild %OSC_BUILD_SRC% -property:Configuration=Release 33 | @dotnet build %OSC_BUILD_SRC% --configuration Release 34 | 35 | @rd /S /Q "%OSC_BUILD_BIN%" 36 | @mkdir "%OSC_BUILD_BIN%" 37 | @xcopy "%OSC_BUILD_BIN_OUT%\*.dll" "%OSC_BUILD_BIN%\" 38 | @xcopy "%OSC_BUILD_BIN_OUT%\*.xml" "%OSC_BUILD_BIN%\" 39 | @del /F /Q "%OSC_BUILD_BIN%\OneScript*.*" "%OSC_BUILD_BIN%\ScriptEngine*.*" "%OSC_BUILD_BIN%\DotNetZip*.*" "%OSC_BUILD_BIN%\Newtonsoft*.*" 40 | 41 | @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" curl -o "%OSC_BUILD_ROOT%OneScriptDocumenter.zip" https://github.com/dmpas/OneScriptDocumenter/releases/download/1.0.14/documenter.zip 42 | @"C:\Program Files\7-Zip\7z.exe" x -o%OSC_BUILD_ROOT%OneScriptDocumenter -y %OSC_BUILD_ROOT%OneScriptDocumenter.zip 43 | @del /F /Q "%OSC_BUILD_ROOT%OneScriptDocumenter*.*" 44 | 45 | @%OSC_BUILD_ROOT%OneScriptDocumenter\OneScriptDocumenter.exe json %OSC_BUILD_BIN%\syntaxHelp.json %OSC_BUILD_BIN_OUT%\%OSC_BUILD_NAME%.dll 46 | 47 | @rd /S /Q "%OSC_BUILD_ROOT%OneScriptDocumenter" 48 | 49 | opm build -o %OSC_BUILD_ROOT% %OSC_BUILD_ROOT% 50 | @exit /b %ERRORLEVEL% -------------------------------------------------------------------------------- /build/package-loader.os: -------------------------------------------------------------------------------- 1 | Процедура ПриЗагрузкеБиблиотеки(Знач КаталогБиблиотеки, СтандартнаяОбработка, Отказ) 2 | 3 | СтандартнаяОбработка = Ложь; 4 | Отказ = Ложь; 5 | 6 | ПодключитьВнешнююКомпоненту(ОбъединитьПути(КаталогБиблиотеки, "bin", "ClientSSH.dll")); 7 | 8 | КонецПроцедуры 9 | 10 | -------------------------------------------------------------------------------- /build/packagedef: -------------------------------------------------------------------------------- 1 | Описание 2 | .Имя("ClientSSH") 3 | .Версия("0.5.2") 4 | .Автор("a.sosnoviy aka lab; Artem Kuznetsov") 5 | .АдресАвтора("int-it@yandex.ru; ArKuznetsov@gmail.com") 6 | .Описание("ssh client для oscript") 7 | .ВерсияСреды("1.8") 8 | .ВключитьФайл("bin") 9 | .ВключитьФайл("package-loader.os") 10 | .ВключитьФайл("../README.md") 11 | .ВключитьФайл("../LICENSE") 12 | -------------------------------------------------------------------------------- /src/NUnitTests/EngineHelpWrapper.cs: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------- 2 | Use of this source code is governed by an MIT-style 3 | license that can be found in the LICENSE file or at 4 | https://opensource.org/licenses/MIT. 5 | ---------------------------------------------------------- 6 | // Codebase: https://github.com/ArKuznetsov/clientSSH/ 7 | ----------------------------------------------------------*/ 8 | 9 | using System; 10 | using System.IO; 11 | using System.Reflection; 12 | using ScriptEngine.Machine.Contexts; 13 | using ScriptEngine.HostedScript.Library; 14 | using ScriptEngine.Machine; 15 | using ScriptEngine.Environment; 16 | using ScriptEngine.HostedScript; 17 | 18 | namespace NUnitTests 19 | { 20 | public class EngineHelpWrapper : IHostApplication 21 | { 22 | 23 | private HostedScriptEngine _engine; 24 | private string _resourceName; 25 | private ScriptEngine.ModuleImage _module; 26 | 27 | private UserScriptContextInstance _testModule; 28 | private ArrayImpl _testMethods; 29 | 30 | public EngineHelpWrapper(string resourceName) 31 | { 32 | _engine = new HostedScriptEngine(); 33 | _engine.Initialize(); 34 | 35 | // Тут можно указать любой класс из компоненты 36 | // Если проектов компонент несколько, то надо взять по классу из каждой из них 37 | _engine.AttachAssembly(System.Reflection.Assembly.GetAssembly(typeof(oscriptcomponent.ClientSSH))); 38 | 39 | // Подключаем тестовую оболочку 40 | _engine.AttachAssembly(System.Reflection.Assembly.GetAssembly(typeof(EngineHelpWrapper))); 41 | 42 | var testrunnerSource = LoadFromAssemblyResource("NUnitTests.Tests.testrunner.os"); 43 | var testrunnerModule = _engine.GetCompilerService().Compile(testrunnerSource); 44 | 45 | { 46 | var mi = _engine.GetType().GetMethod("SetGlobalEnvironment", 47 | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance); 48 | mi.Invoke(_engine, new object[] {this, testrunnerSource}); 49 | } 50 | 51 | _engine.LoadUserScript(new ScriptEngine.UserAddedScript() 52 | { 53 | Type = ScriptEngine.UserAddedScriptType.Class, 54 | Image = testrunnerModule, 55 | Symbol = "TestRunner" 56 | }); 57 | 58 | var testRunner = AttachedScriptsFactory.ScriptFactory("TestRunner", new IValue[] { }); 59 | TestRunner = ValueFactory.Create(testRunner); 60 | 61 | _resourceName = resourceName; 62 | var source = LoadFromAssemblyResource(_resourceName); 63 | _module = _engine.GetCompilerService().Compile(source); 64 | 65 | _engine.LoadUserScript(new ScriptEngine.UserAddedScript() 66 | { 67 | Type = ScriptEngine.UserAddedScriptType.Class, 68 | Image = _module, 69 | Symbol = _resourceName 70 | }); 71 | 72 | _testModule = AttachedScriptsFactory.ScriptFactory(_resourceName, new IValue[] { }); 73 | 74 | int methodIndex = _testModule.FindMethod("ПолучитьСписокТестов"); 75 | 76 | { 77 | IValue ivTests; 78 | _testModule.CallAsFunction(methodIndex, new IValue[] { TestRunner }, out ivTests); 79 | _testMethods = ivTests as ArrayImpl; 80 | } 81 | 82 | } 83 | 84 | public HostedScriptEngine Engine 85 | { 86 | get 87 | { 88 | return _engine; 89 | } 90 | } 91 | 92 | public ArrayImpl TestMethods 93 | { 94 | get 95 | { 96 | return _testMethods; 97 | } 98 | } 99 | 100 | public IValue TestRunner { get; private set; } 101 | 102 | public int RunTestMethod(string methodName, out string testException) 103 | { 104 | testException = ""; 105 | 106 | int methodIndex; 107 | 108 | if (ValueFactory.Create() == _testMethods.Find(ValueFactory.Create(methodName))) 109 | { 110 | return -1; 111 | } 112 | 113 | try 114 | { 115 | methodIndex = _testModule.FindMethod(methodName); 116 | } 117 | catch 118 | { 119 | return -1; 120 | } 121 | 122 | try 123 | { 124 | _testModule.CallAsProcedure(methodIndex, new IValue[] { }); 125 | } 126 | catch (Exception e) 127 | { 128 | testException = e.ToString(); 129 | return 1; 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | public ICodeSource LoadFromAssemblyResource(string resourceName) 136 | { 137 | var asm = Assembly.GetExecutingAssembly(); 138 | string codeSource; 139 | 140 | using (Stream s = asm.GetManifestResourceStream(resourceName)) 141 | { 142 | using (StreamReader r = new StreamReader(s)) 143 | { 144 | codeSource = r.ReadToEnd(); 145 | } 146 | } 147 | 148 | return _engine.Loader.FromString(codeSource); 149 | } 150 | 151 | public void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary) 152 | { 153 | Console.WriteLine(str); 154 | } 155 | 156 | public string[] GetCommandLineArguments() 157 | { 158 | return new string[] { }; 159 | } 160 | 161 | public bool InputString(out string result, string prompt, int maxLen, bool multiline) 162 | { 163 | result = ""; 164 | return false; 165 | } 166 | 167 | public void ShowExceptionInfo(Exception exc) 168 | { 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/NUnitTests/MainTestClass.cs: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------- 2 | Use of this source code is governed by an MIT-style 3 | license that can be found in the LICENSE file or at 4 | https://opensource.org/licenses/MIT. 5 | ---------------------------------------------------------- 6 | // Codebase: https://github.com/ArKuznetsov/clientSSH/ 7 | ----------------------------------------------------------*/ 8 | 9 | using NUnit.Framework; 10 | using oscriptcomponent; 11 | using System.Collections.Generic; 12 | using ScriptEngine.HostedScript.Library; 13 | 14 | // Используется NUnit 3.6 15 | 16 | namespace NUnitTests 17 | { 18 | [TestFixture] 19 | public class MainTestClass 20 | { 21 | 22 | private EngineHelpWrapper _host; 23 | 24 | public static List TestCases 25 | { 26 | get 27 | { 28 | var testCases = new List(); 29 | EngineHelpWrapper host = new EngineHelpWrapper("NUnitTests.Tests.external.os"); 30 | 31 | foreach (var ivTestMethod in host.TestMethods) 32 | { 33 | testCases.Add(new TestCaseData(ivTestMethod.ToString())); 34 | } 35 | 36 | return testCases; 37 | } 38 | } 39 | 40 | [OneTimeSetUp] 41 | public void Initialize() 42 | { 43 | _host = new EngineHelpWrapper("NUnitTests.Tests.external.os"); 44 | } 45 | 46 | [Test] 47 | [Category("Create SSH client")] 48 | public void TestAsInternalObjects() 49 | { 50 | var item1 = new ClientSSH("", 22, "", ""); 51 | 52 | Assert.IsNotNull(item1, "Ошибка создания экземпляра класса {0}", typeof(ClientSSH)); 53 | } 54 | 55 | [TestCaseSource(nameof(TestCases))] 56 | [Category("Test SSH client")] 57 | public void TestAsExternalObjects(string testCase) 58 | { 59 | string testException; 60 | 61 | int result = _host.RunTestMethod(testCase, out testException); 62 | 63 | switch (result) 64 | { 65 | case -1: 66 | Assert.Ignore("Тест: {0} не реализован!", testCase); 67 | break; 68 | case 0: 69 | Assert.Pass(); 70 | break; 71 | case 1: 72 | Assert.Fail("Тест: {0} провален с сообщением: {1}", testCase, testException); 73 | break; 74 | default: 75 | Assert.Warn("Тест: {0} вернул неожиданный результат: {1}", testCase, result); 76 | break; 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/NUnitTests/NUnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net48;netstandard2.1 5 | Debug;Release 6 | AnyCPU 7 | 8 | 9 | 10 | {D0415022-7655-4764-BFD8-D5E0616C2BB2} 11 | ClientSSH 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/NUnitTests/Tests/external.os: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------- 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file or at 4 | // https://opensource.org/licenses/MIT. 5 | // ---------------------------------------------------------- 6 | // Codebase: https://github.com/ArKuznetsov/clientSSH/ 7 | // ---------------------------------------------------------- 8 | 9 | // #Использовать "build" 10 | 11 | Перем юТест; 12 | Перем СерверАдрес; 13 | Перем СерверПорт; 14 | Перем ПользовательИмя; 15 | Перем ТекущийКаталог; 16 | Перем ПользовательПароль; 17 | Перем ПутьККлючу; 18 | 19 | Перем СчетчикТестов; 20 | 21 | //////////////////////////////////////////////////////////////////// 22 | // Программный интерфейс 23 | 24 | Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт 25 | 26 | юТест = ЮнитТестирование; 27 | 28 | ВсеТесты = Новый Массив; 29 | 30 | ВсеТесты.Добавить("ПередЗапускомТестов"); 31 | 32 | ВсеТесты.Добавить("ТестДолжен_СоздатьЛибу"); 33 | 34 | ВсеТесты.Добавить("ТестДолжен_ПолучитьПоток"); 35 | ВсеТесты.Добавить("ТестДолжен_ПолучитьСоединение"); 36 | 37 | ВсеТесты.Добавить("ТестДолжен_Подключиться"); 38 | ВсеТесты.Добавить("ТестДолжен_ПодключитьсяСКлючом"); 39 | ВсеТесты.Добавить("ТестДолжен_ПолучитьСодержимоеКаталога"); 40 | ВсеТесты.Добавить("ТестДолжен_СоздатьКаталогПоПаролю"); 41 | ВсеТесты.Добавить("ТестДолжен_СоздатьКаталогПоКлючу"); 42 | ВсеТесты.Добавить("ТестДолжен_ПроверитьСуществованиеКаталогаПоПаролю"); 43 | ВсеТесты.Добавить("ТестДолжен_ПроверитьСуществованиеКаталогаПоКлючу"); 44 | ВсеТесты.Добавить("ТестДолжен_УдалитьКаталогПоПаролю"); 45 | ВсеТесты.Добавить("ТестДолжен_УдалитьКаталогПоКлючу"); 46 | ВсеТесты.Добавить("ТестДолжен_ОтправитьФайлПоПаролю"); 47 | ВсеТесты.Добавить("ТестДолжен_ОтправитьФайлПоКлючу"); 48 | ВсеТесты.Добавить("ТестДолжен_ПолучитьФайлПоПаролю"); 49 | ВсеТесты.Добавить("ТестДолжен_ПолучитьФайлПоКлючу"); 50 | ВсеТесты.Добавить("ТестДолжен_УдалитьФайлПоПаролю"); 51 | ВсеТесты.Добавить("ТестДолжен_УдалитьФайлПоКлючу"); 52 | 53 | Возврат ВсеТесты; 54 | 55 | КонецФункции // ПолучитьСписокТестов() 56 | 57 | Процедура ПередЗапускомТестов() Экспорт 58 | 59 | ВывестиЗаголовокТеста("Подготовка: Окружение для тестирования"); 60 | 61 | СерверАдрес = "localhost"; 62 | СерверПорт = 2222; 63 | ПользовательИмя = ПолучитьПеременнуюСреды("SSH_TEST_USER"); 64 | ПользовательПароль = ПолучитьПеременнуюСреды("SSH_TEST_PWD"); 65 | 66 | ТекущийКаталог = ПолучитьПеременнуюСреды("OSC_TEST_CWD"); 67 | Если НЕ ЗначениеЗаполнено(ТекущийКаталог) Тогда 68 | ТекущийКаталог = ТекущийСценарий().Каталог; 69 | КонецЕсли; 70 | 71 | ПутьККлючу = ПолучитьПеременнуюСреды("SSH_TEST_KEY_PATH"); 72 | Если НЕ ЗначениеЗаполнено(ПутьККлючу) Тогда 73 | ПутьККлючу = ОбъединитьПути(ТекущийКаталог, "tools", "openssh"); 74 | ПутьККлючу = ОбъединитьПути(ПутьККлючу, "files", "sftp-key"); 75 | КонецЕсли; 76 | 77 | ПутьККомпоненте = ПолучитьПеременнуюСреды("OSC_TEST_LIB"); 78 | Если НЕ ЗначениеЗаполнено(ПутьККомпоненте) Тогда 79 | ПутьККомпоненте = ОбъединитьПути(ТекущийКаталог, "src", "clientSSH", "bin"); 80 | ПутьККомпоненте = ОбъединитьПути(ПутьККомпоненте, "Debug", "net48", "ClientSSH.dll"); 81 | КонецЕсли; 82 | 83 | Попытка 84 | ПодключитьВнешнююКомпоненту(ПутьККомпоненте); 85 | Сообщить("Компонента подключена."); 86 | Исключение 87 | ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); 88 | Сообщить(СтрШаблон("Ошибка подключения компоненты:%1%2", Символы.ПС, ТекстОшибки)); 89 | КонецПопытки; 90 | 91 | КонецПроцедуры // ПередЗапускомТестов() 92 | 93 | Процедура ТестДолжен_СоздатьЛибу() Экспорт 94 | 95 | ВывестиЗаголовокТеста("Тест: Создание библиотеки подключения SSH"); 96 | 97 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 98 | юТест.ПроверитьРавенство(ТипЗнч(Либа), Тип("КлиентSSH"), "Не создался экзепляр"); 99 | 100 | КонецПроцедуры // ТестДолжен_СоздатьЛибу() 101 | 102 | Процедура ТестДолжен_ПолучитьПоток() Экспорт 103 | 104 | ВывестиЗаголовокТеста("Тест: Получение потока SSH"); 105 | 106 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 107 | юТест.ПроверитьРавенство(ТипЗнч(Либа), Тип("КлиентSSH"), "Не создался экзепляр"); 108 | 109 | Поток = Либа.ПолучитьПоток(1); 110 | юТест.ПроверитьРавенство(ТипЗнч(Поток), Тип("ПотокSSH"), "Не удалось получить поток"); 111 | 112 | Поток.Отключиться(); 113 | 114 | КонецПроцедуры // ТестДолжен_ПолучитьПоток() 115 | 116 | Процедура ТестДолжен_ПолучитьСоединение() Экспорт 117 | 118 | ВывестиЗаголовокТеста("Тест: Получение соединения SSH"); 119 | 120 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 121 | юТест.ПроверитьРавенство(ТипЗнч(Либа), Тип("КлиентSSH"), "Не создался экзепляр"); 122 | 123 | Соединение = Либа.ПолучитьСоединение(); 124 | юТест.ПроверитьРавенство(ТипЗнч(Соединение), Тип("СоединениеSSH"), "Не удалось получить соединение"); 125 | 126 | КонецПроцедуры // ТестДолжен_ПолучитьСоединение() 127 | 128 | Процедура ТестДолжен_Подключиться() Экспорт 129 | 130 | ВывестиЗаголовокТеста("Тест: Подключение к серверу SSH"); 131 | 132 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 133 | Соединение = Либа.ПолучитьСоединение(); 134 | 135 | Результат = Соединение.ВыполнитьКоманду("echo Привет"); 136 | 137 | // А = Символы.ПС; // Падает 138 | Результат = СтрЗаменить(Результат, Символ(10), ""); 139 | юТест.ПроверитьРавенство(Результат, "Привет", "Нет ответа комманды"); 140 | Результат = Соединение.ВыполнитьКоманду("echo test"); 141 | Результат = СтрЗаменить(Результат, Символ(10), ""); 142 | юТест.ПроверитьРавенство(Результат, "test", "Нет ответа комманды"); 143 | 144 | Соединение.Отключиться(); 145 | 146 | КонецПроцедуры // ТестДолжен_Подключиться() 147 | 148 | Процедура ТестДолжен_ПодключитьсяСКлючом() Экспорт 149 | 150 | ВывестиЗаголовокТеста("Тест: Подключение к серверу SSH по ключу"); 151 | 152 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 153 | Либа.УстановитьКлюч(ПутьККлючу, ""); 154 | 155 | Соединение = Либа.ПолучитьСоединение(); 156 | 157 | Результат = Соединение.ВыполнитьКоманду("echo Привет"); 158 | 159 | // А = Символы.ПС; // Падает 160 | Результат = СтрЗаменить(Результат, Символ(10), ""); 161 | юТест.ПроверитьРавенство(Результат, "Привет", "Нет ответа комманды"); 162 | Результат = Соединение.ВыполнитьКоманду("echo test"); 163 | Результат = СтрЗаменить(Результат, Символ(10), ""); 164 | юТест.ПроверитьРавенство(Результат, "test", "Нет ответа комманды"); 165 | 166 | Соединение.Отключиться(); 167 | 168 | КонецПроцедуры // ТестДолжен_ПодключитьсяСКлючом() 169 | 170 | Процедура ТестДолжен_ПолучитьСодержимоеКаталога() Экспорт 171 | 172 | ВывестиЗаголовокТеста("Тест: Получение содержимого каталога на сервере SSH"); 173 | 174 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 175 | Scp = Либа.ПолучитьScp(); 176 | 177 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 178 | СодержимоеКаталога = Scp.СодержимоеКаталога("."); 179 | 180 | Scp.Отключиться(); 181 | 182 | Сообщить(СтрШаблон("Найден каталог: %1", СодержимоеКаталога.Получить("upload").ПолноеИмя)); 183 | 184 | юТест.ПроверитьРавенство(СодержимоеКаталога.Получить("upload").Имя, 185 | "upload", 186 | "Не удалось получить содержимое каталога"); 187 | 188 | КонецПроцедуры // ТестДолжен_ПолучитьСодержимоеКаталога() 189 | 190 | Процедура ТестДолжен_СоздатьКаталогПоПаролю() Экспорт 191 | 192 | ВывестиЗаголовокТеста("Тест: Создание каталога на сервере SSH с указанием пароля"); 193 | 194 | ИмяТестовогоКаталога = "testFolder1"; 195 | 196 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 197 | КаталогДляПроверки = Новый Файл(ОбъединитьПути(ПутьККаталогуЛокальный, ИмяТестовогоКаталога)); 198 | 199 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 200 | Scp = Либа.ПолучитьScp(); 201 | 202 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 203 | Scp.СоздатьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 204 | 205 | Scp.Отключиться(); 206 | 207 | юТест.ПроверитьИстину(КаталогДляПроверки.Существует(), "Не удалось создать каталог по паролю"); 208 | 209 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 210 | 211 | КонецПроцедуры // ТестДолжен_СоздатьКаталогПоПаролю() 212 | 213 | Процедура ТестДолжен_СоздатьКаталогПоКлючу() Экспорт 214 | 215 | ВывестиЗаголовокТеста("Тест: Создание каталога на сервере SSH с указанием ключа"); 216 | 217 | ИмяТестовогоКаталога = "testFolder1"; 218 | 219 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 220 | КаталогДляПроверки = Новый Файл(ОбъединитьПути(ПутьККаталогуЛокальный, ИмяТестовогоКаталога)); 221 | 222 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ""); 223 | Либа.УстановитьКлюч(ПутьККлючу, ""); 224 | Scp = Либа.ПолучитьScp(); 225 | 226 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 227 | 228 | Scp.СоздатьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 229 | 230 | Scp.Отключиться(); 231 | 232 | юТест.ПроверитьИстину(КаталогДляПроверки.Существует(), "Не удалось создать каталог по ключу"); 233 | 234 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 235 | 236 | КонецПроцедуры // ТестДолжен_СоздатьКаталогПоКлючу() 237 | 238 | Процедура ТестДолжен_ПроверитьСуществованиеКаталогаПоПаролю() Экспорт 239 | 240 | ВывестиЗаголовокТеста("Тест: Проверка существования каталога на сервере SSH с указанием пароля"); 241 | 242 | ИмяТестовогоКаталога = "testFolder1"; 243 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 244 | 245 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 246 | Scp = Либа.ПолучитьScp(); 247 | 248 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 249 | 250 | Scp.СоздатьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 251 | РезультатПроверки = Scp.Существует(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 252 | 253 | Scp.Отключиться(); 254 | 255 | юТест.ПроверитьИстину(РезультатПроверки, "Ошибка проверки существования каталога по паролю"); 256 | 257 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 258 | 259 | КонецПроцедуры // ТестДолжен_ПроверитьСуществованиеКаталогаПоПаролю() 260 | 261 | Процедура ТестДолжен_ПроверитьСуществованиеКаталогаПоКлючу() Экспорт 262 | 263 | ВывестиЗаголовокТеста("Тест: Проверка существования каталога на сервере SSH с указанием ключа"); 264 | 265 | ИмяТестовогоКаталога = "testFolder1"; 266 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 267 | 268 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ""); 269 | Либа.УстановитьКлюч(ПутьККлючу, ""); 270 | Scp = Либа.ПолучитьScp(); 271 | 272 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 273 | 274 | Scp.СоздатьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 275 | РезультатПроверки = Scp.Существует(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 276 | 277 | Scp.Отключиться(); 278 | 279 | юТест.ПроверитьИстину(РезультатПроверки, "Ошибка проверки существования каталога по ключу"); 280 | 281 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 282 | 283 | КонецПроцедуры // ТестДолжен_ПроверитьСуществованиеКаталогаПоКлючу() 284 | 285 | Процедура ТестДолжен_УдалитьКаталогПоПаролю() Экспорт 286 | 287 | ВывестиЗаголовокТеста("Тест: Удаление каталога на сервере SSH с указанием пароля"); 288 | 289 | ИмяТестовогоКаталога = "testFolder1"; 290 | 291 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 292 | КаталогДляПроверки = Новый Файл(ОбъединитьПути(ПутьККаталогуЛокальный, ИмяТестовогоКаталога)); 293 | 294 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 295 | Scp = Либа.ПолучитьScp(); 296 | 297 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 298 | Scp.СоздатьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 299 | 300 | юТест.ПроверитьИстину(КаталогДляПроверки.Существует(), "Не удалось создать каталог по паролю"); 301 | 302 | Scp.УдалитьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 303 | 304 | юТест.ПроверитьЛожь(КаталогДляПроверки.Существует(), "Не удалось удалить каталог по паролю"); 305 | 306 | Scp.Отключиться(); 307 | 308 | КонецПроцедуры // ТестДолжен_УдалитьКаталогПоПаролю() 309 | 310 | Процедура ТестДолжен_УдалитьКаталогПоКлючу() Экспорт 311 | 312 | ВывестиЗаголовокТеста("Тест: Удаление каталога на сервере SSH с указанием ключа"); 313 | 314 | ИмяТестовогоКаталога = "testFolder1"; 315 | 316 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 317 | КаталогДляПроверки = Новый Файл(ОбъединитьПути(ПутьККаталогуЛокальный, ИмяТестовогоКаталога)); 318 | 319 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ""); 320 | Либа.УстановитьКлюч(ПутьККлючу, ""); 321 | Scp = Либа.ПолучитьScp(); 322 | 323 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 324 | Scp.СоздатьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 325 | 326 | юТест.ПроверитьИстину(КаталогДляПроверки.Существует(), "Не удалось создать каталог по ключу"); 327 | 328 | Scp.УдалитьКаталог(СтрШаблон("./upload/%1", ИмяТестовогоКаталога)); 329 | 330 | юТест.ПроверитьЛожь(КаталогДляПроверки.Существует(), "Не удалось удалить каталог по ключу"); 331 | 332 | Scp.Отключиться(); 333 | 334 | КонецПроцедуры // ТестДолжен_УдалитьКаталогПоКлючу() 335 | 336 | Процедура ТестДолжен_ОтправитьФайлПоПаролю() Экспорт 337 | 338 | ВывестиЗаголовокТеста("Тест: Отправка файла на сервер SSH с указанием пароля"); 339 | 340 | ИмяТестовогоФайла = "testFile1.txt"; 341 | ПутьККаталогу = ОбъединитьПути(ТекущийКаталог, "test", "testFolder1"); 342 | ТестовыйФайл = НовыйТекстовыйФайл(ИмяТестовогоФайла, "Это тестовый файл!", ПутьККаталогу); 343 | 344 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 345 | ФайлДляПроверки = Новый Файл(ОбъединитьПути(ПутьККаталогуЛокальный, ТестовыйФайл.Имя)); 346 | 347 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 348 | Scp = Либа.ПолучитьScp(); 349 | 350 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 351 | Scp.ОтправитьФайл(ТестовыйФайл.ПолноеИмя, СтрШаблон("./upload/%1", ТестовыйФайл.Имя)); 352 | 353 | Scp.Отключиться(); 354 | 355 | юТест.ПроверитьИстину(ФайлДляПроверки.Существует(), "Не удалось отправить файл по паролю"); 356 | 357 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 358 | УдалитьФайлы(ПутьККаталогу, ПолучитьМаскуВсеФайлы()); 359 | УдалитьФайлы(ПутьККаталогу); 360 | 361 | КонецПроцедуры // ТестДолжен_ОтправитьФайлПоПаролю() 362 | 363 | Процедура ТестДолжен_ОтправитьФайлПоКлючу() Экспорт 364 | 365 | ВывестиЗаголовокТеста("Тест: Отправка файла на сервер SSH с указанием ключа"); 366 | 367 | ИмяТестовогоФайла = "testFile1.txt"; 368 | ПутьККаталогу = ОбъединитьПути(ТекущийКаталог, "test", "testFolder1"); 369 | ТестовыйФайл = НовыйТекстовыйФайл(ИмяТестовогоФайла, "Это тестовый файл!", ПутьККаталогу); 370 | 371 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 372 | ФайлДляПроверки = Новый Файл(ОбъединитьПути(ПутьККаталогуЛокальный, ТестовыйФайл.Имя)); 373 | 374 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ""); 375 | Либа.УстановитьКлюч(ПутьККлючу, ""); 376 | Scp = Либа.ПолучитьScp(); 377 | 378 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 379 | Scp.ОтправитьФайл(ТестовыйФайл.ПолноеИмя, СтрШаблон("./upload/%1", ТестовыйФайл.Имя)); 380 | 381 | Scp.Отключиться(); 382 | 383 | юТест.ПроверитьИстину(ФайлДляПроверки.Существует(), "Не удалось отправить файл по ключу"); 384 | 385 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 386 | УдалитьФайлы(ПутьККаталогу, ПолучитьМаскуВсеФайлы()); 387 | УдалитьФайлы(ПутьККаталогу); 388 | 389 | КонецПроцедуры // ТестДолжен_ОтправитьФайлПоКлючу() 390 | 391 | Процедура ТестДолжен_ПолучитьФайлПоПаролю() Экспорт 392 | 393 | ВывестиЗаголовокТеста("Тест: Получение файла с сервера SSH с указанием пароля"); 394 | 395 | ИмяТестовогоФайла = "testFile1.txt"; 396 | КонтрольныйТекст = "Это тестовый файл!"; 397 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 398 | ТестовыйФайл = НовыйТекстовыйФайл(ИмяТестовогоФайла, КонтрольныйТекст, ПутьККаталогуЛокальный); 399 | 400 | ПутьККаталогу = ОбъединитьПути(ТекущийКаталог, "test", "testFolder1"); 401 | ФайлДляПроверки = НовыйТекстовыйФайл(ТестовыйФайл.Имя, "", ПутьККаталогу); 402 | 403 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 404 | Scp = Либа.ПолучитьScp(); 405 | Scp.ПолучитьФайл(СтрШаблон("./upload/%1", ТестовыйФайл.Имя), ФайлДляПроверки.ПолноеИмя); 406 | Scp.Отключиться(); 407 | 408 | ДокументДляПроверки = Новый ТекстовыйДокумент(); 409 | ДокументДляПроверки.Прочитать(ФайлДляПроверки.ПолноеИмя); 410 | 411 | юТест.ПроверитьРавенство(СокрЛП(ДокументДляПроверки.ПолучитьТекст()), 412 | КонтрольныйТекст, 413 | "Не удалось получить файл по паролю"); 414 | 415 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 416 | УдалитьФайлы(ПутьККаталогу, ПолучитьМаскуВсеФайлы()); 417 | УдалитьФайлы(ПутьККаталогу); 418 | 419 | КонецПроцедуры // ТестДолжен_ПолучитьФайлПоПаролю() 420 | 421 | Процедура ТестДолжен_ПолучитьФайлПоКлючу() Экспорт 422 | 423 | ВывестиЗаголовокТеста("Тест: Получение файла с сервера SSH с указанием ключа"); 424 | 425 | ИмяТестовогоФайла = "testFile1.txt"; 426 | КонтрольныйТекст = "Это тестовый файл!"; 427 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 428 | ТестовыйФайл = НовыйТекстовыйФайл(ИмяТестовогоФайла, КонтрольныйТекст, ПутьККаталогуЛокальный); 429 | 430 | ПутьККаталогу = ОбъединитьПути(ТекущийКаталог, "test", "testFolder1"); 431 | ФайлДляПроверки = НовыйТекстовыйФайл(ТестовыйФайл.Имя, "", ПутьККаталогу); 432 | 433 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ""); 434 | Либа.УстановитьКлюч(ПутьККлючу, ""); 435 | Scp = Либа.ПолучитьScp(); 436 | Scp.ПолучитьФайл(СтрШаблон("./upload/%1", ТестовыйФайл.Имя), ФайлДляПроверки.ПолноеИмя); 437 | Scp.Отключиться(); 438 | 439 | ДокументДляПроверки = Новый ТекстовыйДокумент(); 440 | ДокументДляПроверки.Прочитать(ФайлДляПроверки.ПолноеИмя); 441 | 442 | юТест.ПроверитьРавенство(СокрЛП(ДокументДляПроверки.ПолучитьТекст()), 443 | КонтрольныйТекст, 444 | "Не удалось получить файл по ключу"); 445 | 446 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 447 | УдалитьФайлы(ПутьККаталогу, ПолучитьМаскуВсеФайлы()); 448 | УдалитьФайлы(ПутьККаталогу); 449 | 450 | КонецПроцедуры // ТестДолжен_ПолучитьФайлПоКлючу() 451 | 452 | Процедура ТестДолжен_УдалитьФайлПоПаролю() Экспорт 453 | 454 | ВывестиЗаголовокТеста("Тест: Удаление файла с сервера SSH с указанием пароля"); 455 | 456 | ИмяТестовогоФайла = "testFile1.txt"; 457 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 458 | ТестовыйФайл = НовыйТекстовыйФайл(ИмяТестовогоФайла, "Это тестовый файл!", ПутьККаталогуЛокальный); 459 | 460 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ПользовательПароль); 461 | Scp = Либа.ПолучитьScp(); 462 | 463 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 464 | 465 | РезультатПроверки = Scp.Существует(СтрШаблон("./upload/%1", ТестовыйФайл.Имя)); 466 | 467 | юТест.ПроверитьИстину(РезультатПроверки, "Не удалось проверить существование файла по паролю"); 468 | 469 | Scp.УдалитьФайл(СтрШаблон("./upload/%1", ТестовыйФайл.Имя)); 470 | 471 | юТест.ПроверитьЛожь(ТестовыйФайл.Существует(), "Не удалось удалить файл по паролю"); 472 | 473 | Scp.Отключиться(); 474 | 475 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 476 | 477 | КонецПроцедуры // ТестДолжен_УдалитьФайлПоПаролю() 478 | 479 | Процедура ТестДолжен_УдалитьФайлПоКлючу() Экспорт 480 | 481 | ВывестиЗаголовокТеста("Тест: Удаление файла с сервера SSH с указанием ключа"); 482 | 483 | ИмяТестовогоФайла = "testFile1.txt"; 484 | ПутьККаталогуЛокальный = ОбъединитьПути(ТекущийКаталог, "tools", "openssh", "upload"); 485 | ТестовыйФайл = НовыйТекстовыйФайл(ИмяТестовогоФайла, "Это тестовый файл!", ПутьККаталогуЛокальный); 486 | 487 | Либа = Новый КлиентSSH(СерверАдрес, СерверПорт, ПользовательИмя, ""); 488 | Либа.УстановитьКлюч(ПутьККлючу, ""); 489 | Scp = Либа.ПолучитьScp(); 490 | 491 | юТест.ПроверитьРавенство(ТипЗнч(Scp), Тип("СоединениеSCP"), "Не получилось scp"); 492 | 493 | РезультатПроверки = Scp.Существует(СтрШаблон("./upload/%1", ТестовыйФайл.Имя)); 494 | 495 | юТест.ПроверитьИстину(РезультатПроверки, "Не удалось проверить существование файла по ключу"); 496 | 497 | Scp.УдалитьФайл(СтрШаблон("./upload/%1", ТестовыйФайл.Имя)); 498 | 499 | юТест.ПроверитьЛожь(ТестовыйФайл.Существует(), "Не удалось удалить файл по ключу"); 500 | 501 | Scp.Отключиться(); 502 | 503 | УдалитьФайлы(ПутьККаталогуЛокальный, ПолучитьМаскуВсеФайлы()); 504 | 505 | КонецПроцедуры // ТестДолжен_УдалитьФайлПоКлючу() 506 | 507 | Процедура ТестДолжен_Упасть() Экспорт 508 | 509 | ВывестиЗаголовокТеста("Тест: Должен упасть"); 510 | 511 | ВызватьИсключение "Этот тест упал, как и должен был!"; 512 | 513 | КонецПроцедуры // ТестДолжен_Упасть() 514 | 515 | Функция НовыйТекстовыйФайл(ИмяФайла, Знач Содержимое = "", Знач ПутьККаталогу = "") 516 | 517 | Текст = Новый ТекстовыйДокумент(); 518 | Если ЗначениеЗаполнено(Содержимое) Тогда 519 | Текст.УстановитьТекст(Содержимое); 520 | КонецЕсли; 521 | 522 | Если НЕ ЗначениеЗаполнено(ПутьККаталогу) Тогда 523 | ПутьККаталогу = ПолучитьИмяВременногоФайла(); 524 | КонецЕсли; 525 | 526 | ПутьКФайлу = ОбъединитьПути(ПутьККаталогу, ИмяФайла); 527 | 528 | Каталог = Новый Файл(ПутьККаталогу); 529 | Если НЕ (Каталог.Существует() И Каталог.ЭтоКаталог()) Тогда 530 | СоздатьКаталог(ПутьККаталогу); 531 | КонецЕсли; 532 | 533 | Текст.Записать(ПутьКФайлу); 534 | 535 | Возврат Новый Файл(ПутьКФайлу); 536 | 537 | КонецФункции // НовыйТекстовыйФайл() 538 | 539 | Процедура ВывестиЗаголовокТеста(Знач Заголовок = "") 540 | 541 | Если СчетчикТестов = Неопределено Тогда 542 | СчетчикТестов = 1; 543 | КонецЕсли; 544 | 545 | Сообщить(СтрШаблон("%1. %2", СчетчикТестов, СтрЗаменить(Заголовок, Символы.ПС, " "))); 546 | 547 | СчетчикТестов = СчетчикТестов + 1; 548 | 549 | КонецПроцедуры // ВывестиЗаголовокТеста() 550 | -------------------------------------------------------------------------------- /src/NUnitTests/Tests/testrunner.os: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////// 2 | // 3 | // Объект-помощник для приемочного и юнит-тестирования (урезанный testrunner) 4 | // 5 | ////////////////////////////////////////////////////////////////// 6 | 7 | Перем Пути; 8 | Перем КомандаЗапуска; 9 | Перем НаборТестов; 10 | Перем РезультатТестирования; 11 | 12 | Перем ПутьЛогФайлаJUnit; 13 | 14 | Перем НомерТестаДляЗапуска; 15 | Перем НаименованиеТестаДляЗапуска; 16 | 17 | Перем Рефлектор; 18 | 19 | Перем ЗначенияСостоянияТестов; 20 | Перем СтруктураПараметровЗапуска; 21 | 22 | Перем НаборОшибок; 23 | Перем НаборНереализованныхТестов; 24 | 25 | Перем ВсегоТестов; 26 | Перем ВыводитьОшибкиПодробно; 27 | 28 | Перем ВременныеФайлы; 29 | 30 | ////////////////////////////////////////////////////////////////////////////// 31 | // Программный интерфейс 32 | // 33 | 34 | Функция ФорматДСО(ДопСообщениеОшибки) 35 | Если ДопСообщениеОшибки = "" Тогда 36 | Возврат ""; 37 | КонецЕсли; 38 | 39 | Возврат Символы.ПС + ДопСообщениеОшибки; 40 | КонецФункции 41 | 42 | Функция ПредставлениеПериода(ДатаНачала, ДатаОкончания, ФорматнаяСтрока = Неопределено) 43 | Возврат "с "+ДатаНачала+" по "+ДатаОкончания; 44 | КонецФункции 45 | 46 | Процедура ПодробныеОписанияОшибок(Знач ВключитьПодробноеОписание) Экспорт 47 | ВыводитьОшибкиПодробно = ВключитьПодробноеОписание; 48 | КонецПроцедуры 49 | 50 | //{ МЕТОДЫ ДЛЯ ПРОВЕРКИ ЗНАЧЕНИЙ (assertions). 51 | 52 | Процедура Проверить(Условие, ДопСообщениеОшибки = "") Экспорт 53 | Если Не Условие Тогда 54 | СообщениеОшибки = "Переданный параметр ("+Формат(Условие, "БЛ=ложь; БИ=истина")+") не является Истиной, а хотели, чтобы являлся." + ФорматДСО(ДопСообщениеОшибки); 55 | ВызватьИсключение(СообщениеОшибки); 56 | КонецЕсли; 57 | КонецПроцедуры 58 | 59 | Процедура ПроверитьИстину(Условие, ДопСообщениеОшибки = "") Экспорт 60 | Проверить(Условие, ДопСообщениеОшибки); 61 | КонецПроцедуры 62 | 63 | Процедура ПроверитьЛожь(Условие, ДопСообщениеОшибки = "") Экспорт 64 | Если Условие Тогда 65 | СообщениеОшибки = "Переданный параметр ("+Формат(Условие, "БЛ=ложь; БИ=истина")+") не является Ложью, а хотели, чтобы являлся." + ФорматДСО(ДопСообщениеОшибки); 66 | ВызватьИсключение(СообщениеОшибки); 67 | КонецЕсли; 68 | КонецПроцедуры 69 | 70 | Процедура ПроверитьДату(_Дата, _Период, ДопСообщениеОшибки = "") Экспорт 71 | Если _Дата < _Период.ДатаНачала или _Дата > _Период.ДатаОкончания Тогда 72 | представление = ПредставлениеПериода(_Период.ДатаНачала, _Период.ДатаОкончания, "ФП = Истина"); 73 | СообщениеОшибки = "Переданный параметр ("+Формат(_Дата, "ДФ='dd.MM.yyyy HH:mm:ss'")+") не входит в период "+представление+", а хотели, чтобы являлся." + ФорматДСО(ДопСообщениеОшибки); 74 | ВызватьИсключение(СообщениеОшибки); 75 | КонецЕсли; 76 | КонецПроцедуры 77 | 78 | Процедура ПроверитьРавенство(ПервоеЗначение, ВтороеЗначение, ДопСообщениеОшибки = "") Экспорт 79 | Если ПервоеЗначение <> ВтороеЗначение Тогда 80 | СообщениеОшибки = "Сравниваемые значения ("+ПервоеЗначение+"; "+ВтороеЗначение+") не равны, а хотели, чтобы были равны." + ФорматДСО(ДопСообщениеОшибки); 81 | ВызватьИсключение(СообщениеОшибки); 82 | КонецЕсли; 83 | КонецПроцедуры 84 | 85 | Процедура ПроверитьНеРавенство(ПервоеЗначение, ВтороеЗначение, ДопСообщениеОшибки = "") Экспорт 86 | Если ПервоеЗначение = ВтороеЗначение Тогда 87 | СообщениеОшибки = "Сравниваемые значения ("+ПервоеЗначение+"; "+ВтороеЗначение+") равны, а хотели, чтобы были не равны." + ФорматДСО(ДопСообщениеОшибки); 88 | ВызватьИсключение(СообщениеОшибки); 89 | КонецЕсли; 90 | КонецПроцедуры 91 | 92 | Процедура ПроверитьБольше(_Больше, _Меньше, ДопСообщениеОшибки = "") Экспорт 93 | Если _Больше <= _Меньше Тогда 94 | СообщениеОшибки = "Первый параметр ("+_Больше+") меньше или равен второму ("+_Меньше+") а хотели, чтобы был больше." + ФорматДСО(ДопСообщениеОшибки); 95 | ВызватьИсключение(СообщениеОшибки); 96 | КонецЕсли; 97 | КонецПроцедуры 98 | 99 | Процедура ПроверитьБольшеИлиРавно(_Больше, _Меньше, ДопСообщениеОшибки = "") Экспорт 100 | Если _Больше < _Меньше Тогда 101 | СообщениеОшибки = "Первый параметр ("+_Больше+") меньше второго ("+_Меньше+") а хотели, чтобы был больше или равен." + ФорматДСО(ДопСообщениеОшибки); 102 | ВызватьИсключение(СообщениеОшибки); 103 | КонецЕсли; 104 | КонецПроцедуры 105 | 106 | Процедура ПроверитьМеньше(проверяемоеЗначение1, проверяемоеЗначение2, СообщениеОбОшибке = "") Экспорт 107 | Если проверяемоеЗначение1 >= проверяемоеЗначение2 Тогда 108 | ВызватьИсключение "Значение <"+проверяемоеЗначение1+"> больше или равно, чем <"+проверяемоеЗначение2+">, а ожидалось меньше"+ 109 | ФорматДСО(СообщениеОбОшибке); 110 | КонецЕсли; 111 | КонецПроцедуры 112 | 113 | Процедура ПроверитьМеньшеИлиРавно(проверяемоеЗначение1, проверяемоеЗначение2, СообщениеОбОшибке = "") Экспорт 114 | Если проверяемоеЗначение1 > проверяемоеЗначение2 Тогда 115 | ВызватьИсключение "Значение <"+проверяемоеЗначение1+"> больше, чем <"+проверяемоеЗначение2+">, а ожидалось меньше или равно"+ 116 | ФорматДСО(СообщениеОбОшибке); 117 | КонецЕсли; 118 | КонецПроцедуры 119 | 120 | // проверка идет через ЗначениеЗаполнено, но мутабельные значение всегда считаем заполненными 121 | Процедура ПроверитьЗаполненность(ПроверяемоеЗначение, ДопСообщениеОшибки = "") Экспорт 122 | Попытка 123 | фЗаполнено = ЗначениеЗаполнено(ПроверяемоеЗначение); 124 | Исключение 125 | Возврат; 126 | КонецПопытки; 127 | Если НЕ фЗаполнено Тогда 128 | ВызватьИсключение "Значение ("+ПроверяемоеЗначение+") не заполнено, а ожидалась заполненность" + ФорматДСО(ДопСообщениеОшибки); 129 | КонецЕсли; 130 | КонецПроцедуры 131 | 132 | Процедура ПроверитьНеЗаполненность(ПроверяемоеЗначение, ДопСообщениеОшибки = "") Экспорт 133 | СообщениеОшибки = "Значение ("+ПроверяемоеЗначение+") заполнено, а ожидалась незаполненность" + ФорматДСО(ДопСообщениеОшибки); 134 | Попытка 135 | фЗаполнено = ЗначениеЗаполнено(ПроверяемоеЗначение); 136 | Исключение 137 | ВызватьИсключение СообщениеОшибки; 138 | КонецПопытки; 139 | Если фЗаполнено Тогда 140 | ВызватьИсключение СообщениеОшибки; 141 | КонецЕсли; 142 | КонецПроцедуры 143 | 144 | Процедура ПроверитьВхождение(строка, подстрокаПоиска, ДопСообщениеОшибки = "") Экспорт 145 | Если Найти(строка, подстрокаПоиска) = 0 Тогда 146 | СообщениеОшибки = "Искали в <"+строка+"> подстроку <"+подстрокаПоиска+">, но не нашли." + ФорматДСО(ДопСообщениеОшибки); 147 | ВызватьИсключение(СообщениеОшибки); 148 | КонецЕсли; 149 | КонецПроцедуры 150 | 151 | Процедура ТестПройден() Экспорт 152 | КонецПроцедуры 153 | 154 | Процедура ТестПровален(ДопСообщениеОшибки) Экспорт 155 | СообщениеОшибки = "Тест провален." + ФорматДСО(ДопСообщениеОшибки); 156 | ВызватьИсключение(СообщениеОшибки); 157 | КонецПроцедуры 158 | 159 | //} 160 | 161 | // { временные файлы 162 | Функция ИмяВременногоФайла(Знач Расширение = "tmp") Экспорт 163 | Если ВременныеФайлы = Неопределено Тогда 164 | ВременныеФайлы = Новый Массив; 165 | КонецЕсли; 166 | 167 | ИмяВремФайла = ПолучитьИмяВременногоФайла(Расширение); 168 | ВременныеФайлы.Добавить(ИмяВремФайла); 169 | Возврат ИмяВремФайла; 170 | КонецФункции 171 | 172 | Процедура УдалитьВременныеФайлы() Экспорт 173 | 174 | Если ВременныеФайлы <> Неопределено Тогда 175 | Для Каждого ИмяФайла Из ВременныеФайлы Цикл 176 | Попытка 177 | УдалитьФайлы(ИмяФайла); 178 | Исключение 179 | Сообщить("Не удален временный файл: " + ИмяФайла + " 180 | |-" + ОписаниеОшибки()); 181 | КонецПопытки; 182 | КонецЦикла; 183 | 184 | ВременныеФайлы.Очистить(); 185 | 186 | КонецЕсли; 187 | 188 | КонецПроцедуры 189 | // } 190 | Функция ВСтрокеСодержатсяТолькоЦифры(Знач ИсходнаяСтрока) Экспорт 191 | 192 | рез = Ложь; 193 | ДлинаСтроки = СтрДлина(ИсходнаяСтрока); 194 | Для Сч = 1 По ДлинаСтроки Цикл 195 | ТекущийСимвол = КодСимвола(Сред(ИсходнаяСтрока, Сч, 1)); 196 | Если 48 <= ТекущийСимвол И ТекущийСимвол <= 57 Тогда 197 | рез = Истина; 198 | Иначе 199 | рез = Ложь; 200 | Прервать; 201 | КонецЕсли; 202 | КонецЦикла; 203 | Возврат рез; 204 | КонецФункции 205 | 206 | // Выводит сообщение. В тестах ВСЕГДА должна использоваться ВМЕСТО метода Сообщить(). 207 | // 208 | 209 | Функция ВывестиПредупреждение(Ошибка) Экспорт 210 | 211 | НужныйТекстОшибки = Ошибка; 212 | 213 | ВывестиСообщение("ПРЕДУПРЕЖДЕНИЕ: " + НужныйТекстОшибки, СтатусСообщения.Внимание); 214 | 215 | Возврат НужныйТекстОшибки; 216 | КонецФункции 217 | 218 | Функция ВывестиСообщение(ТекстСообщения, Статус = Неопределено) Экспорт 219 | Если Статус = Неопределено Тогда 220 | Статус = СтатусСообщения.Обычное; 221 | КонецЕсли; 222 | 223 | Сообщить(ТекстСообщения, Статус); 224 | КонецФункции 225 | 226 | // Вызывает исключение с заданным текстом ошибки для прерывания выполнения тестового случая. 227 | // 228 | Функция ПрерватьТест(ТекстОшибки) Экспорт 229 | 230 | ВызватьИсключение ТекстОшибки; 231 | 232 | КонецФункции 233 | 234 | Функция ВывестиОшибку(Ошибка) Экспорт 235 | 236 | НужныйТекстОшибки = Ошибка; 237 | 238 | ВывестиСообщение("ОШИБКА: " + НужныйТекстОшибки, СтатусСообщения.Важное); 239 | 240 | Возврат НужныйТекстОшибки; 241 | КонецФункции 242 | 243 | ВыводитьОшибкиПодробно = Ложь; 244 | 245 | -------------------------------------------------------------------------------- /src/TestApp/Program.cs: -------------------------------------------------------------------------------- 1 | // Исполняемое приложение для запуска компоненты под отладчиком 2 | 3 | // В проекте TestApp в "Ссылки" ("References") должен быть добавлен проект компоненты 4 | // В проекте TestApp должны быть подключены NuGet пакеты OneScript и OneScript.Library 5 | 6 | using System; 7 | using System.IO; 8 | using System.Reflection; 9 | using ScriptEngine.HostedScript; 10 | using ScriptEngine.HostedScript.Library; 11 | 12 | namespace TestApp 13 | { 14 | class MainClass : IHostApplication 15 | { 16 | 17 | static readonly string SCRIPT = GetStringFromResource("TestApp.Resourses.testScript.os"); 18 | 19 | public static HostedScriptEngine StartEngine() 20 | { 21 | var engine = new ScriptEngine.HostedScript.HostedScriptEngine(); 22 | engine.Initialize(); 23 | 24 | // Тут можно указать любой класс из компоненты 25 | // Если проектов компонент несколько, то надо взять по классу из каждой из них 26 | engine.AttachAssembly(System.Reflection.Assembly.GetAssembly(typeof(oscriptcomponent.ClientSSH))); 27 | 28 | return engine; 29 | } 30 | 31 | public static void Main(string[] args) 32 | { 33 | var engine = StartEngine(); 34 | var script = engine.Loader.FromString(SCRIPT); 35 | var process = engine.CreateProcess(new MainClass(), script); 36 | 37 | var result = process.Start(); // Запускаем наш тестовый скрипт 38 | 39 | Console.WriteLine("Result = {0}", result); 40 | 41 | // ВАЖНО: движок перехватывает исключения, для отладки можно пользоваться только точками останова. 42 | } 43 | 44 | public void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary) 45 | { 46 | Console.WriteLine(str); 47 | } 48 | 49 | public void ShowExceptionInfo(Exception exc) 50 | { 51 | Console.WriteLine(exc.ToString()); 52 | } 53 | 54 | public bool InputString(out string result, string prompt, int maxLen, bool multiline) 55 | { 56 | throw new NotImplementedException(); 57 | } 58 | 59 | public string[] GetCommandLineArguments() 60 | { 61 | return new string[] { "1", "2", "3" }; // Здесь можно зашить список аргументов командной строки 62 | } 63 | 64 | static private string GetStringFromResource(string resourceName) 65 | { 66 | var asm = Assembly.GetExecutingAssembly(); 67 | string codeSource; 68 | 69 | using (Stream s = asm.GetManifestResourceStream(resourceName)) 70 | { 71 | using (StreamReader r = new StreamReader(s)) 72 | { 73 | codeSource = r.ReadToEnd(); 74 | } 75 | } 76 | 77 | return codeSource; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/TestApp/Resourses/testScript.os: -------------------------------------------------------------------------------- 1 | // Отладочный скрипт 2 | // в котором уже подключена наша компонента 3 | Порт = 22; 4 | 5 | Клиент = Новый КлиентSSH("localhost", Порт, "", ""); 6 | 7 | Сообщить("Ок!"); -------------------------------------------------------------------------------- /src/TestApp/TestApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net48;netstandard2.1 5 | Debug;Release 6 | AnyCPU 7 | 8 | Exe 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {D0415022-7655-4764-BFD8-D5E0616C2BB2} 17 | ClientSSH 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/clientSSH.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{DFE334BF-3243-4939-BD9E-045170645703}") = "ClientSSH", "clientSSH\ClientSSH.csproj", "{D0415022-7655-4764-BFD8-D5E0616C2BB2}" 5 | EndProject 6 | Project("{DFE334BF-3243-4939-BD9E-045170645703}") = "TestApp", "TestApp\TestApp.csproj", "{4EA8C040-413C-4289-9C75-FF1306DD0560}" 7 | EndProject 8 | Project("{DFE334BF-3243-4939-BD9E-045170645703}") = "NUnitTests", "NUnitTests\NUnitTests.csproj", "{105A994F-2E45-4698-9CDD-8C0C16B8CFA5}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D0415022-7655-4764-BFD8-D5E0616C2BB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {D0415022-7655-4764-BFD8-D5E0616C2BB2}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {D0415022-7655-4764-BFD8-D5E0616C2BB2}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {D0415022-7655-4764-BFD8-D5E0616C2BB2}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {4EA8C040-413C-4289-9C75-FF1306DD0560}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {4EA8C040-413C-4289-9C75-FF1306DD0560}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {4EA8C040-413C-4289-9C75-FF1306DD0560}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {4EA8C040-413C-4289-9C75-FF1306DD0560}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {105A994F-2E45-4698-9CDD-8C0C16B8CFA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {105A994F-2E45-4698-9CDD-8C0C16B8CFA5}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {105A994F-2E45-4698-9CDD-8C0C16B8CFA5}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {105A994F-2E45-4698-9CDD-8C0C16B8CFA5}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /src/clientSSH/Connection.cs: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------- 2 | Use of this source code is governed by an MIT-style 3 | license that can be found in the LICENSE file or at 4 | https://opensource.org/licenses/MIT. 5 | ---------------------------------------------------------- 6 | // Codebase: https://github.com/ArKuznetsov/clientSSH/ 7 | ----------------------------------------------------------*/ 8 | 9 | using System.Text; 10 | using ScriptEngine.Machine.Contexts; 11 | using Renci.SshNet; 12 | 13 | namespace oscriptcomponent 14 | { 15 | /// 16 | /// Класс Соединение 17 | /// 18 | [ContextClass("СоединениеSSH", "ConnectionSSH")] 19 | public class Connection : AutoContext 20 | { 21 | 22 | private readonly SshClient _sshClient; 23 | 24 | public Connection(SshClient ssh) 25 | { 26 | _sshClient = ssh; 27 | _sshClient.Connect(); 28 | } 29 | 30 | 31 | /// 32 | /// Выполнить комманду 33 | /// 34 | /// Результат выполнения 35 | [ContextMethod("ВыполнитьКоманду")] 36 | public string Execute(string command) 37 | { 38 | 39 | var result = ""; 40 | using(var cmd = _sshClient.CreateCommand(command, Encoding.UTF8)) 41 | { 42 | 43 | cmd.Execute(); 44 | result = cmd.Result; 45 | } 46 | 47 | return result; 48 | 49 | } 50 | 51 | /// 52 | /// Закрыть соединение 53 | /// 54 | [ContextMethod("Отключиться")] 55 | public void Disconnect() 56 | { 57 | 58 | _sshClient.Disconnect(); 59 | 60 | } 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/clientSSH/Scp.cs: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------- 2 | Use of this source code is governed by an MIT-style 3 | license that can be found in the LICENSE file or at 4 | https://opensource.org/licenses/MIT. 5 | ---------------------------------------------------------- 6 | // Codebase: https://github.com/ArKuznetsov/clientSSH/ 7 | ----------------------------------------------------------*/ 8 | 9 | using System.IO; 10 | using ScriptEngine.Machine.Contexts; 11 | using ScriptEngine.Machine; 12 | using ScriptEngine.HostedScript.Library; 13 | using Renci.SshNet; 14 | using Renci.SshNet.Sftp; 15 | 16 | namespace oscriptcomponent 17 | { 18 | /// 19 | /// Класс Соединение 20 | /// 21 | [ContextClass("СоединениеSCP", "ConnectionSCP")] 22 | public class Scp : AutoContext 23 | { 24 | 25 | private readonly SftpClient _sftpClient; 26 | 27 | /// 28 | /// Конструктор 29 | /// 30 | /// 31 | public Scp(SftpClient scp) 32 | { 33 | _sftpClient = scp; 34 | _sftpClient.Connect(); 35 | } 36 | 37 | /// 38 | /// Существует 39 | /// 40 | /// Путь к файлу или каталогу 41 | /// 42 | /// true если файл или каталог существует; в противном случае false. 43 | /// 44 | [ContextMethod("Существует")] 45 | public IValue Exists(string path) 46 | { 47 | 48 | return ValueFactory.Create(_sftpClient.Exists(path)); 49 | 50 | } 51 | 52 | /// 53 | /// Содержимое каталога 54 | /// 55 | /// Путь к каталогу 56 | /// 57 | /// Соответствие - Список файлов и каталогов в указанном каталоге. 58 | /// Ключ - Имя файла / каталога 59 | /// Значение - Структура - описание файла / каталога 60 | /// Имя - имя файла 61 | /// ПолноеИмя - полный путь к файлу 62 | /// ЭтоФайл - это файл 63 | /// ЭтоКаталог - это каталог 64 | /// 65 | [ContextMethod("СодержимоеКаталога")] 66 | public IValue ListDirectory(string path) 67 | { 68 | 69 | var SrcList = _sftpClient.ListDirectory(path); 70 | 71 | MapImpl ResultList = new MapImpl(); 72 | 73 | IValue ResultItemKey; 74 | StructureImpl ResultItemValue; 75 | 76 | foreach (SftpFile item in SrcList) 77 | { 78 | ResultItemKey = ValueFactory.Create(item.Name); 79 | 80 | ResultItemValue = new StructureImpl(); 81 | ResultItemValue.Insert("Имя", ValueFactory.Create(item.Name)); 82 | ResultItemValue.Insert("ПолноеИмя", ValueFactory.Create(item.FullName)); 83 | ResultItemValue.Insert("ЭтоФайл", ValueFactory.Create(item.IsRegularFile)); 84 | ResultItemValue.Insert("ЭтоКаталог", ValueFactory.Create(item.IsDirectory)); 85 | ResultItemValue.Insert("Размер", ValueFactory.Create(item.Length)); 86 | 87 | ResultList.Insert(ResultItemKey, ResultItemValue); 88 | } 89 | return ValueFactory.Create(ResultList); 90 | 91 | } 92 | 93 | /// 94 | /// Создать каталог 95 | /// 96 | /// Путь к новому каталогу. 97 | [ContextMethod("СоздатьКаталог")] 98 | public void CreateDirectory(string path) 99 | { 100 | 101 | _sftpClient.CreateDirectory(path); 102 | 103 | } 104 | 105 | /// 106 | /// Удалить каталог 107 | /// 108 | /// Путь к удаляемому каталогу. 109 | [ContextMethod("УдалитьКаталог")] 110 | public void DeleteDirectory(string path) 111 | { 112 | 113 | _sftpClient.DeleteDirectory(path); 114 | 115 | } 116 | 117 | /// 118 | /// Отправить Файл 119 | /// 120 | /// Путь к отправляемому файлу. 121 | /// Путь к файлу на сервере. 122 | /// если указано true, то существующий файл на сервере будет перезаписан. 123 | [ContextMethod("ОтправитьФайл")] 124 | public void UploadFile(string fileName, string dest, IValue canOwerwrite = null) 125 | { 126 | using (var file = new FileStream(@fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) 127 | { 128 | if (canOwerwrite == null) 129 | { 130 | _sftpClient.UploadFile(file, dest); 131 | } 132 | else 133 | { 134 | _sftpClient.UploadFile(file, dest, canOwerwrite.AsBoolean()); 135 | } 136 | } 137 | 138 | } 139 | 140 | /// 141 | /// Получить Файл 142 | /// 143 | /// Путь к файлу на сервере. 144 | /// Путь для загрузки файла. 145 | [ContextMethod("ПолучитьФайл")] 146 | public void DownloadFile(string src, string dest) 147 | { 148 | 149 | using (var file = new FileStream(@dest, FileMode.OpenOrCreate, FileAccess.Write)) 150 | { 151 | _sftpClient.DownloadFile(src, file); 152 | } 153 | 154 | } 155 | 156 | /// 157 | /// Удалить файл 158 | /// 159 | /// Путь к файлу для удаления. 160 | [ContextMethod("УдалитьФайл")] 161 | public void DeleteFile(string path) 162 | { 163 | 164 | _sftpClient.DeleteFile(path); 165 | 166 | } 167 | 168 | /// 169 | /// Закрыть соединение 170 | /// 171 | [ContextMethod("Отключиться")] 172 | public void Disconnect() 173 | { 174 | 175 | _sftpClient.Disconnect(); 176 | 177 | } 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /src/clientSSH/Stream.cs: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------- 2 | Use of this source code is governed by an MIT-style 3 | license that can be found in the LICENSE file or at 4 | https://opensource.org/licenses/MIT. 5 | ---------------------------------------------------------- 6 | // Codebase: https://github.com/ArKuznetsov/clientSSH/ 7 | ----------------------------------------------------------*/ 8 | 9 | using System.Text; 10 | using ScriptEngine.Machine.Contexts; 11 | using Renci.SshNet; 12 | 13 | namespace oscriptcomponent 14 | { 15 | /// 16 | /// Класс Поток 17 | /// 18 | [ContextClass("ПотокSSH", "StreamSSH")] 19 | public class Stream : AutoContext 20 | { 21 | private readonly SshClient _sshClient; 22 | private readonly ShellStream _sshStream; 23 | 24 | public Stream(SshClient ssh, int timeout = 0) 25 | { 26 | _sshClient = ssh; 27 | _sshClient.Connect(); 28 | 29 | _sshStream = _sshClient.CreateShellStreamNoTerminal(); 30 | 31 | int endTime = timeout; 32 | 33 | while (!_sshStream.DataAvailable && (timeout == 0 || endTime > 0)) 34 | { 35 | System.Threading.Thread.Sleep(200); 36 | endTime = endTime - 200; 37 | } 38 | 39 | var line = _sshStream.Read(); 40 | _sshStream.Flush(); 41 | } 42 | 43 | /// 44 | /// Записать в поток 45 | /// 46 | /// РезультатВыполнения 47 | [ContextMethod("ЗаписатьВПоток")] 48 | public string WriteLine(string command) 49 | { 50 | _sshStream.Flush(); 51 | _sshStream.WriteLine(command); 52 | 53 | StringBuilder output = new StringBuilder(); 54 | 55 | string line; 56 | 57 | while (!_sshStream.DataAvailable) 58 | System.Threading.Thread.Sleep(200); 59 | 60 | var num = 0; 61 | 62 | while (_sshStream.DataAvailable) 63 | { 64 | if (num > 1) 65 | { 66 | output.Append('\n'); 67 | } 68 | line = _sshStream.ReadLine(); 69 | output.Append(line); 70 | num++; 71 | 72 | } 73 | 74 | return output.ToString(); 75 | } 76 | 77 | /// 78 | /// Закрыть соединение 79 | /// 80 | [ContextMethod("Отключиться")] 81 | public void DisconnectStream() 82 | { 83 | _sshClient.Disconnect(); 84 | } 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/clientSSH/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/clientSSH/clientSSH.cs: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------- 2 | Use of this source code is governed by an MIT-style 3 | license that can be found in the LICENSE file or at 4 | https://opensource.org/licenses/MIT. 5 | ---------------------------------------------------------- 6 | // Codebase: https://github.com/ArKuznetsov/clientSSH/ 7 | ----------------------------------------------------------*/ 8 | 9 | using Renci.SshNet; 10 | using ScriptEngine.Machine.Contexts; 11 | using ScriptEngine.Machine; 12 | 13 | namespace oscriptcomponent 14 | { 15 | /// 16 | /// КлиентSSH 17 | /// 18 | [ContextClass("КлиентSSH", "ClientSSH")] 19 | public class ClientSSH : AutoContext 20 | { 21 | private readonly string _host; 22 | private readonly int _port; 23 | private readonly string _user; 24 | private readonly string _pass; 25 | private PrivateKeyFile _keyfile; 26 | private bool _keyFileIsset; 27 | 28 | public ClientSSH(string host, int port, string user, string pass) 29 | { 30 | 31 | _host = host; 32 | _port = port; 33 | _user = user; 34 | _pass = pass; 35 | 36 | } 37 | 38 | 39 | /// 40 | /// Получить Поток 41 | /// 42 | [ContextMethod("ПолучитьПоток")] 43 | public Stream CreateStream(int timeout = 0) 44 | { 45 | 46 | var sclient = getSshClient(); 47 | 48 | return new Stream(sclient, timeout); 49 | } 50 | 51 | 52 | /// 53 | /// Получить Соединение 54 | /// 55 | [ContextMethod("ПолучитьСоединение")] 56 | public Connection Create() 57 | { 58 | 59 | var sclient = getSshClient(); 60 | return new Connection(sclient); 61 | 62 | 63 | } 64 | 65 | /// 66 | /// Получить SCP 67 | /// 68 | [ContextMethod("ПолучитьSCP")] 69 | public Scp CreateScp() 70 | { 71 | 72 | if (_keyFileIsset) 73 | { 74 | 75 | var scplient = new SftpClient(_host, _port, _user, _keyfile); 76 | return new Scp(scplient); 77 | 78 | } 79 | else 80 | { 81 | var scplient = new SftpClient(_host, _port, _user, _pass); 82 | return new Scp(scplient); 83 | } 84 | 85 | } 86 | 87 | 88 | 89 | /// 90 | /// Установить ключ 91 | /// 92 | [ContextMethod("УстановитьКлюч")] 93 | public void SetSshKey(string keyfile, string pass = "") 94 | { 95 | 96 | _keyfile = new PrivateKeyFile(keyfile, pass); 97 | _keyFileIsset = true; 98 | 99 | } 100 | 101 | /// 102 | /// Создает КлиентSSH 103 | /// 104 | /// КлиентSSH 105 | /// Хост> 106 | /// Хост> 107 | /// Хост> 108 | /// Хост> 109 | [ScriptConstructor] 110 | public static IRuntimeContextInstance Constructor(IValue host, IValue port, IValue user, IValue pass) 111 | { 112 | return new ClientSSH(host.AsString(), (int) port.AsNumber() , user.AsString(), pass.AsString()); 113 | } 114 | 115 | private SshClient getSshClient() 116 | { 117 | 118 | if (_keyFileIsset) 119 | { 120 | 121 | var sclient = new SshClient(_host, _port, _user, _keyfile); 122 | return sclient; 123 | 124 | } 125 | else 126 | { 127 | var sclient = new SshClient(_host, _port, _user, _pass); 128 | return sclient; 129 | } 130 | 131 | 132 | } 133 | 134 | 135 | } 136 | } -------------------------------------------------------------------------------- /src/clientSSH/clientSSH.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net48;netstandard2.1 5 | Debug;Release 6 | AnyCPU 7 | 8 | 9 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\ClientSSH.xml 10 | 11 | 12 | $(MSBuildProjectDirectory)\bin\$(Configuration)\$(TargetFramework)\ClientSSH.xml 13 | 14 | 15 | 16 | 1.0.0.3 17 | 18 | 19 | 1.16.0 20 | 21 | 22 | 13.0.2 23 | 24 | 25 | 1.8.4 26 | 27 | 28 | 2024.1.0 29 | 30 | 31 | -------------------------------------------------------------------------------- /tools/.env: -------------------------------------------------------------------------------- 1 | SSH_TEST_USER=user 2 | SSH_TEST_PWD=P@ssw0rd 3 | -------------------------------------------------------------------------------- /tools/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | sftp: 3 | image: onescript-ssh/test-ssh 4 | build: 5 | context: "./openssh" 6 | args: 7 | SSH_TEST_USER: ${SSH_TEST_USER} 8 | SSH_TEST_PWD: ${SSH_TEST_PWD} 9 | container_name: test_ssh 10 | volumes: 11 | - ./openssh/upload:/home/${SSH_TEST_USER}/upload:rw 12 | ports: 13 | - "2222:22" 14 | -------------------------------------------------------------------------------- /tools/openssh/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye 2 | 3 | ARG SSH_TEST_USER=user 4 | ARG SSH_TEST_PWD=P@ssw0rd 5 | ENV SSH_TEST_USER=${SSH_TEST_USER} 6 | ENV SSH_TEST_PWD=${SSH_TEST_PWD} 7 | 8 | COPY files/sshd_config /etc/ssh/sshd_config 9 | COPY files/prepare-ssh.sh /tmp/ 10 | COPY files/sftp-key.pub /tmp/ 11 | 12 | # Steps done in one RUN layer: 13 | # - Install packages 14 | # - OpenSSH needs /var/run/sshd to run 15 | # - Remove generic host keys, entrypoint generates unique keys 16 | RUN apt-get update && \ 17 | apt-get -y install openssh-server && \ 18 | rm -rf /var/lib/apt/lists/* && \ 19 | mkdir -p /var/run/sshd && \ 20 | chmod +x /tmp/prepare-ssh.sh && \ 21 | /tmp/prepare-ssh.sh 22 | 23 | EXPOSE 22 24 | 25 | ENTRYPOINT ["/usr/sbin/sshd","-D","-e"] 26 | 27 | -------------------------------------------------------------------------------- /tools/openssh/files/prepare-ssh.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create user if needed 4 | getent passwd ${SSH_TEST_USER} > /dev/null 5 | if [ $? != 0 ]; then 6 | useradd -m -s /bin/bash ${SSH_TEST_USER} 7 | passwd ${SSH_TEST_USER} <> /home/${SSH_TEST_USER}/.ssh/authorized_keys 23 | chmod -R 700 /home/${SSH_TEST_USER}/.ssh && chmod -R 600 /home/${SSH_TEST_USER}/.ssh/* 24 | chown -R ${SSH_TEST_USER} /home/${SSH_TEST_USER}/.ssh 25 | -------------------------------------------------------------------------------- /tools/openssh/files/sftp-key: -------------------------------------------------------------------------------- 1 | -----BEGIN OPENSSH PRIVATE KEY----- 2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW 3 | QyNTUxOQAAACDx2PBgIk6XT1cewWdeJI7mspOOVuJqj2ZzCvqBwQMMfwAAAJDwkc868JHP 4 | OgAAAAtzc2gtZWQyNTUxOQAAACDx2PBgIk6XT1cewWdeJI7mspOOVuJqj2ZzCvqBwQMMfw 5 | AAAECq8psmXPysf7Nyz7P0jEFv4sDVnXolDXNu8UJa10VUcfHY8GAiTpdPVx7BZ14kjuay 6 | k45W4mqPZnMK+oHBAwx/AAAADXJvb3RAc3BvcnQtMDI= 7 | -----END OPENSSH PRIVATE KEY----- 8 | -------------------------------------------------------------------------------- /tools/openssh/files/sftp-key.ppk: -------------------------------------------------------------------------------- 1 | PuTTY-User-Key-File-3: ssh-ed25519 2 | Encryption: none 3 | Comment: root@sport-02 4 | Public-Lines: 2 5 | AAAAC3NzaC1lZDI1NTE5AAAAIPHY8GAiTpdPVx7BZ14kjuayk45W4mqPZnMK+oHB 6 | Awx/ 7 | Private-Lines: 1 8 | AAAAIKrymyZc/Kx/s3LPs/SMQW/iwNWdeiUNc27xQlrXRVRx 9 | Private-MAC: e9b48a294d980654aa59204057f28c2f1e991629bcfaa3e84ed6061ebb0ecf74 10 | -------------------------------------------------------------------------------- /tools/openssh/files/sftp-key.pub: -------------------------------------------------------------------------------- 1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPHY8GAiTpdPVx7BZ14kjuayk45W4mqPZnMK+oHBAwx/ root@sport-02 2 | -------------------------------------------------------------------------------- /tools/openssh/files/sshd_config: -------------------------------------------------------------------------------- 1 | Protocol 2 2 | UseDNS no 3 | 4 | HostKey /etc/ssh/id_key 5 | 6 | # Limited access 7 | PubkeyAuthentication yes 8 | PermitRootLogin no 9 | X11Forwarding no 10 | AllowTcpForwarding yes 11 | #PermitRootLogin without-password 12 | 13 | Subsystem sftp /usr/lib/openssh/sftp-server 14 | # Force sftp and chroot jail 15 | #Subsystem sftp internal-sftp 16 | #ForceCommand internal-sftp 17 | #ChrootDirectory /upload/ 18 | 19 | # Enable this for more logs 20 | LogLevel VERBOSE 21 | -------------------------------------------------------------------------------- /tools/runtests.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | echo %~dp0 4 | @chcp 65001 5 | 6 | set OSC_TEST_NAME=ClientSSH 7 | 8 | set OSC_TEST_ROOT=%~dp0 9 | set OSC_TEST_CWD=%OSC_TEST_ROOT%.. 10 | set OSC_TEST_SRC=%OSC_TEST_ROOT%../src 11 | 12 | set OSC_TEST_LIB=%OSC_TEST_SRC%/%OSC_TEST_NAME%/bin/Debug/net48/%OSC_TEST_NAME%.dll 13 | 14 | set OSC_TEST_TOOLS=%OSC_TEST_ROOT% 15 | set OSC_TEST_NUGET=%OSC_TEST_TOOLS%nuget.exe 16 | set OSC_TEST_NUNIT3=%OSC_TEST_TOOLS%NUnit.ConsoleRunner.3.6.1 17 | set OSC_TEST_NUNIT3_TESTS=%OSC_TEST_SRC%/NUnitTests/bin/Debug/net48/NUnitTests.dll 18 | 19 | set OSC_TEST_REPORTS=%OSC_TEST_ROOT%../build/reports 20 | 21 | set SSH_TEST_KEY_PATH=%OSC_TEST_TOOLS%openssh/files/sftp-key 22 | 23 | IF EXIST "%OSC_TEST_ROOT%.env" ( 24 | FOR /f "usebackq tokens=*" %%a in ("%OSC_TEST_ROOT%.env") DO ( 25 | FOR /F "tokens=1,2 delims==" %%b IN ("%%a") DO ( 26 | set "%%b=%%c" 27 | ) 28 | ) 29 | ) 30 | 31 | IF NOT EXIST "%OSC_TEST_NUGET%" ( 32 | @"C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" curl -o "%OSC_TEST_NUGET%" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe 33 | ) 34 | 35 | @%OSC_TEST_NUGET% restore %OSC_TEST_SRC% 36 | 37 | IF NOT EXIST "%OSC_TEST_NUNIT3%" ( 38 | @%OSC_TEST_NUGET% install NUnit.ConsoleRunner -Version 3.6.1 -OutputDirectory %OSC_TEST_TOOLS% 39 | ) 40 | 41 | @dotnet tool install --global dotnet-coverage 42 | @dotnet restore %OSC_TEST_SRC% 43 | @dotnet build %OSC_TEST_SRC% --configuration Debug 44 | 45 | IF EXIST "%OSC_TEST_ROOT%docker-compose.yml" ( 46 | @docker-compose --file %OSC_TEST_ROOT%docker-compose.yml --env-file %OSC_TEST_ROOT%.env up --build -d 47 | ) 48 | 49 | @mkdir "%OSC_TEST_REPORTS%" 50 | @dotnet-coverage collect -o %OSC_TEST_REPORTS%/coverage.xml -f xml "%OSC_TEST_NUNIT3%/tools/nunit3-console.exe %OSC_TEST_NUNIT3_TESTS% --result=%OSC_TEST_REPORTS%/nunit-result.xml" 51 | rem @rd /S /Q "%OSC_TEST_NUNIT3%" 52 | 53 | IF EXIST "%OSC_TEST_ROOT%docker-compose.yml" ( 54 | @docker-compose --file %OSC_TEST_ROOT%docker-compose.yml down 55 | ) 56 | 57 | @exit /b %ERRORLEVEL% -------------------------------------------------------------------------------- /tools/transform-coverage.xslt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tools/transform-result.xslt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | --------------------------------------------------------------------------------