<?php
//  ---------------------------------------------------------------------------------
//    _____   _             _                   _   _____
//   |  __ \ | |           | |    /\           (_) |  __ \
//   | |__) || |__    __ _ | |   /  \    _ __   _  | |__) |_ __  ___
//   |  ___/ | '_ \  / _` || |  / /\ \  | '_ \ | | |  ___/| '__|/ _ \
//   | |     | | | || (_| || | / ____ \ | |_) || | | |    | |  | (_) |
//   |_|     |_| |_| \__,_||_|/_/    \_\| .__/ |_| |_|    |_|   \___/
//                                      | |
//                                      |_|
//   PhalApi Pro 专业版
//   广州果创网络科技有限公司
//
//  ---------------------------------------------------------------------------------
//
//   一、协议的许可和权利
//    1. 您可以在完全遵守本协议的基础上，将本软件应用于商业用途；
//    2. 您可以在协议规定的约束和限制范围内修改本产品源代码或界面风格以适应您的要求；
//    3. 您拥有使用本产品中的全部内容资料、商品信息及其他信息的所有权，并独立承担与其内容相关的
//       法律义务；
//    4. 获得商业授权之后，您可以将本软件应用于商业用途，自授权时刻起，在技术支持期限内拥有通过
//       指定的方式获得指定范围内的技术支持服务；
//
//   二、协议的约束和限制
//    1. 未获商业授权之前，禁止将本软件用于商业用途（包括但不限于企业法人经营的产品、经营性产品
//       以及以盈利为目的或实现盈利产品）；
//    2. 未获商业授权之前，禁止在本产品的整体或在任何部分基础上发展任何派生版本、修改版本或第三
//       方版本用于重新开发；
//    3. 如果您未能遵守本协议的条款，您的授权将被终止，所被许可的权利将被收回并承担相应法律责任；
//
//   三、有限担保和免责声明
//    1. 本软件及所附带的文件是作为不提供任何明确的或隐含的赔偿或担保的形式提供的；
//    2. 用户出于自愿而使用本软件，您必须了解使用本软件的风险，在尚未获得商业授权之前，我们不承
//       诺提供任何形式的技术支持、使用担保，也不承担任何因使用本软件而产生问题的相关责任；
//    3. 广州果创网络科技有限公司不对使用本产品构建的商城中的内容信息承担责任，但在不侵犯用户隐
//       私信息的前提下，保留以任何方式获取用户信息及商品信息的权利；
//
//   有关本产品最终用户授权协议、商业授权与技术服务的详细内容，均由广州果创网络科技有限公司独家
//   提供。广州果创网络科技有限公司拥有在不事先通知的情况下，修改授权协议的权力，修改后的协议对
//   改变之日起的新授权用户生效。电子文本形式的授权协议如同双方书面签署的协议一样，具有完全的和
//   等同的法律效力。您一旦开始修改、安装或使用本产品，即被视为完全理解并接受本协议的各项条款，
//   在享有上述条款授予的权力的同时，受到相关的约束和限制。协议许可范围以外的行为，将直接违反本
//   授权协议并构成侵权，我们有权随时终止授权，责令停止损害，并保留追究相关责任的权力。
//
//  ---------------------------------------------------------------------------------

namespace Admin\Api;

use Admin\Common\Api;
use Base\Domain\GlobalAtConfig as GlobalAtConfigDomain;
use Base\Domain\TestSample as TestSampleDomain;
use Base\Model\TestSample as TestSampleModel;


/**
 * 接口测试模块
 */
class TestSample extends Api {

    public function getRules() {
        $requestRule = '所有的参数传递json字符串，[{"key":"参数"，"value":"值", "require":"是否必须true/false"}],如果无须请求参数，直接传递[]
                        <br>示例：[{"key":"app_key"，"value":"1", "require": "true"}, {"key":"name"，"value":"yesapi", "require": "false"}]';
        $responseRule = '所有的参数传递json字符串，[["name":"返回字段"，"operate":"判断操作", "expect":"预期结果"]],如果无须请求参数，直接传递[]
                        <br>示例：[{"name":"ret","operate":"=","expect":"200"},{"name":"data.id","operate":">","expect":"123"}], operate操作包括">", "<", "=", "<=", ">=", "!="';
        $httpHost = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
        return array(
            'getTestData' => array(),
            'addTestSample' => array(
                'sampleName' => array('name' => 'sample_name', 'type' => 'string', 'require' => true,  'desc' => '用例名'),
                'apiName' => array('name' => 'api_name', 'type' => 'string', 'require' => true, 'desc' => '接口名称'),
                'sampleRequest' => array('name' => 'sample_request', 'type' => 'string', 'require' => true, 'desc' => '请求参数'.$requestRule),
                'sampleResponse' => array('name' => 'sample_response', 'type' => 'string', 'require' => true, 'desc' => '返回参数'.$responseRule),
                'sampleHost' => array('name' => 'sample_host', 'type' => 'string',  'desc' => '接口路径,默认当前', 'default' => 'http://'.$httpHost.'/api/app.php'),
                'accessTokenConfig' => array('name' => 'access_token_config', 'type' => 'int', 'require' => true, 'desc' => '用于测试的access_token的全局配置的id'),
            ),
            'getTestSampleList' => array(
                'sampleName' => array('name' => 'sample_name', 'type' => 'string', 'desc' => '搜索用例名'),
                'apiName' => array('name' => 'api_name', 'type' => 'string', 'desc' => '搜索接口名称'),
                'sampleStatus' => array('name' => 'sample_status', 'type' => 'array', 'format' => 'explode', 'separator' => ',',  'desc' => '搜索状态(0未测试，1测试通过，2测试不通过,3网络调用无返回,4未找到配置)，多个请用,分割'),
                'page' => array('name' => 'page', 'type' => 'int', 'default' => 1,  'desc' => '当前页数'),
                'perpage' => array('name' => 'perpage', 'type' => 'int', 'default' => 10, 'desc' => '每页多少条'),
            ),
            'delTestSample' => array(
                'id' => array('name' => 'id' , 'type' => 'int', 'require' => true, 'desc' => '删除的用例id')
            ),
            'editTestSample' => array(
                'id' => array('name' => 'id' , 'type' => 'int', 'require' => true, 'desc' => '要修改的用例id'),
                'sampleName' => array('name' => 'sample_name', 'type' => 'string',  'desc' => '用例名'),
                'apiName' => array('name' => 'api_name', 'type' => 'string', 'desc' => '接口名称'),
                'sampleRequest' => array('name' => 'sample_request', 'type' => 'string', 'desc' => '请求参数'.$requestRule),
                'sampleResponse' => array('name' => 'sample_response', 'type' => 'string', 'desc' => '返回参数'.$responseRule),
                'sampleHost' => array('name' => 'sample_host', 'type' => 'string', 'desc' => '接口域名,默认当前'),
                'accessTokenConfig' => array('name' => 'access_token_config', 'type' => 'string', 'desc' => '用于测试的access_token'),
            ),
            'getTestSample' => array(
                'id' => array('name' => 'id' , 'type' => 'int', 'require' => true, 'desc' => '用例id')
            ),
            'checkTestSample' => array(
                'id' => array('name' => 'id' , 'type' => 'int', 'require' => true, 'desc' => '用例id')
            ),
            'getReqAndResParams' => array(
                'apiName' => array('name' => 'api_name', 'type' => 'string', 'require' => true, 'desc' => '接口名称'),
            ),
        );
    }

    /**
     * 获取测试结果
     * @desc 获取接口测试首页的数据
     * @return int success  样例通过数量
     * @return int fail     样例不通过数量
     * @return int noTest   样例未测试数量
     * @return int all      样例总数量
     * @return string percent 样例通过率
     */
    public function getTestData(){
        $domain = new TestSampleDomain();
        return $domain->getTestResult();
    }

    /**
     * 添加用例
     * @desc 添加用例接口
     * @return bool     code    成功是否成功
     * @return string   msg     添加信息
     * @return int      id      用例id
     */
    public function addTestSample(){
        $rs = array('code' => true, 'msg' => \PhalApi\T('fail to add'), 'id' => 0);
        if (!$this->checkResponseParams($this->sampleResponse)){
            $rs['code'] = false;
            $rs['msg'] = \PhalApi\T('wrong expect response');
            return $rs;
        }
        $model = new TestSampleModel;
        if($model->getBy('sample_name', $this->sampleName, false)){
            $rs['code'] = false;
            $rs['msg'] = \PhalApi\T('test case name repeat');
            return $rs;
        }
        // 使用数量+1
        $domain = new GlobalAtConfigDomain();
        $domain->incrUseNum($this->accessTokenConfig);
        $rs['id'] = $model->insert(
            array(
                'uid' => \PhalApi\DI()->context->getUid(),
                'sample_name' => $this->sampleName, 'api_name' => $this->apiName,
                'sample_request' => $this->sampleRequest, 'sample_response' => $this->sampleResponse,
                'sample_host' => $this->sampleHost, 'access_token' => $this->accessTokenConfig,
                'add_time' => date("Y-m-d H:i:s"),
            )
        );
        return $rs;
    }

    /**
     * 获取用例列表
     * @desc 获取用例列表，支持用例名称，接口名称，测试结果搜索
     * @return  array   list        用例数据
     * @return  int     total       总条数
     * @return  int     page        当前页数
     * @return  int     perpage     每页条数
     */
    public function getTestSampleList(){
        $domain = new TestSampleDomain();
        return $domain->getTestSampleListTotal($this->sampleName, $this->apiName , $this->sampleStatus, $this->page, $this->perpage);
    }

    /**
     * 删除用例
     * @desc 删除接口用例
     * @return bool     code 删除是否成功
     * @return string   msg  删除信息
     */
    public function delTestSample(){
        $rs = array('code' => false, 'msg' => \PhalApi\T('fail to delete'));
        $model = new TestSampleModel;
        $data = $model->get($this->id);
        if(empty($data)){
            $rs['msg'] = \PhalApi\T("data not exists");
            return $rs;
        }
        if($model->delete($this->id)){
            // 使用数量-1
            $domain = new GlobalAtConfigDomain();
            $domain->incrUseNum($data['access_token'], 2);
            $rs['code'] = true;
            $rs['msg'] = \PhalApi\T('succeed to delete');
        }
        return $rs;
    }

    /**
     * 编辑用例
     * @desc 编辑用例接口
     * @return bool   isUpdate  是否更新成功，true表示更新成功，false无更新
     * @return string msg       更新信息
     */
    public function editTestSample(){
        $rs = array('isUpdate' => false, 'msg' => \PhalApi\T('fail to udpate'));
        if ($this->sampleResponse !== null && !$this->checkResponseParams($this->sampleResponse)){
            $rs['isUpdate'] = false;
            $rs['msg'] = \PhalApi\T('wrong expect response');
            return $rs;
        }
        $uid = \PhalApi\DI()->context->getUid();
        $domain = new TestSampleDomain();
        $updateRows = $domain->updateTestSample($this->id, $uid, $this->sampleName, $this->apiName,
                $this->sampleRequest, $this->sampleResponse, $this->sampleHost,  $this->accessTokenConfig);
        if($updateRows > 0) {
            $rs['isUpdate'] = true;
            $rs['msg'] = \PhalApi\T('succeed to update');
        }
        return $rs;
    }

    /**
     * 获取用例,及用例的测试快照
     * @desc 获取用例接口
     * @return int id   用例id
     * @return string uid   添加用例人
     * @return string api_name  用例接口名
     * @return string sample_name   用例名称
     * @return string sample_request    用例请求参数
     * @return string sample_response   用例期待返回
     * @return string sample_host   用例接口域名
     * @return int sample_status 用例测试结果(0为测试,1测试通过,2测试失败)
     * @return string access_token  用例使用的access_token配置的id
     * @return string access_token_name 用例配置名
     * @return string add_time  添加时间
     * @return string last_time 最后测试时间
     * @return array log    测试快照
     * @return string test_time 测试时间
     * @return string test_status 用例测试结果(0为测试,1测试通过,2测试失败)
     * @return string test_result 测试返回结果
     * @return string test_detail 失败原因
     * @return string percent 测试通过率
     * @return string last_test_res 最新测试结果
     * @return int success 成功数
     * @return int fail 失败数
     */
    public function getTestSample(){
        $domain = new TestSampleDomain();
        return $domain->getTestSample($this->id);
    }

    /**
     * 测试用例
     * @desc 测试用例接口
     * @return  int     state       0表示未测试，1表示测试通过，2表示测试不通过，3表示网络异常无返回，4表示未找到配置或配置错误
     * @return  string  msg         用例信息
     * @return  string  checkRes    测试错误信息结果，测试通过为空
     */
    public function checkTestSample(){
        $rs = array("state" => 0, "msg" => \PhalApi\T('test case not exists'), 'checkRes' => "");
        $domain = new TestSampleDomain();
        $data = $domain->checkSample($this->id);
        if($data){
            $rs['state'] = $data['state'];
            $rs['checkRes'] = $data['checkRes'];
            $rs['msg'] = \PhalApi\T("finish to test");
        }
        return $rs;
    }

    /**
     * 获取接口的请求参数与返回参数
     * @desc 获取某个参数的请求参数和返回参数
     * @return bool code 获取状态
     * @return string msg   获取的信息
     * @return array requestParams  接口请求参数，包括（name名字 require是否必须 desc描述）
     * @return array responseParams 接口返回参数，包括（type类型 name名字 desc描述）
     */
    public function getReqAndResParams(){
        $rs = array('code' => false, 'msg' => 'Class No Exist', 'requestParams' => array(), 'responseParams' => array());
        $services = explode('.', trim($this->apiName));
        $namespace = $services[0];
        $className = $services[1];
        $action = strtolower($services[2]);
        $apiClass = '\\' . str_replace('_', '\\', $namespace)
            . '\\Api\\' . str_replace('_', '\\', ucfirst($className));
        if (!class_exists($apiClass)) {
            return $rs;
        }
        $api = new $apiClass();
        $requestRule =  $api->getRules();
        $requestRule = array_change_key_case($requestRule, CASE_LOWER);
        $reqParams = array();
        if(isset($requestRule[$action])){
            foreach ($requestRule[$action] as $key => $value){
                $reqParams[$key] = array(
                    'name' => $value['name'],
                    'require' => isset($value['require']) && $value['require'] ? '必须' : '可选',
                    'desc' => isset($value['desc']) && $value['desc'] ? $value['desc'] : '',
                );
            }
        }
        if(key_exists('*', $requestRule)){
            foreach ($requestRule['*'] as $key => $value){
                $reqParams[$key] = array(
                    'name' => $value['name'],
                    'require' => isset($value['require']) && $value['require'] ? '必须' : '可选',
                );
            }
        }
        $rs['code'] = true;
        $rs['msg'] = "";
        $rs['requestParams'] = $reqParams;
        try {
            $rMethod = new \ReflectionMethod($api, $action);
            $docCommentArr = explode("\n", $rMethod->getDocComment());
            $resParams = array();
            foreach ($docCommentArr as $item){
                $pos = stripos($item, '@return');
                if ($pos === FALSE) {
                    continue;
                }
                $returnCommentArr = explode(' ', substr($item, $pos + 8));
                $resParams[$returnCommentArr[1]] = array(
                    'name' => 'data.'.$returnCommentArr[1],
                    'type' => $returnCommentArr[0],
                    'desc' => $returnCommentArr[2],
                );
            }
            $rs['responseParams'] = $resParams;
        } catch (\ReflectionException $e) {
            $rs['msg'] = $e->getMessage();
        }
        return $rs;
    }

    /**
     * 测试全部的接口用例
     * @desc 测试所有的接口用例
     */
    public function testAllSample(){
        $domain = new TestSampleDomain();
        return $domain->testAllSample();
    }

    /**
     * 获取测试全部的细节
     * @desc 获取测试全部的情况
     * @return int  total  总的用例(0表示未开始测试)
     * @return int  test   已测试完成的用例
     */
    public function getAllTestDetail(){
        $rs = array('total' => 0, 'test' => 0);
        $content = file_get_contents(API_ROOT."/bin/test/test_detail.txt");
        $list = explode('+',  rtrim($content, '+'));
        return json_decode($list[count($list) - 1], true);
    }

    /**
     * @desc 检查返回的参数规则
     * @param $sampleResponse
     * @return bool
     */
    protected function checkResponseParams($sampleResponse){
        $response = json_decode($sampleResponse, true);
        if (is_array($response)){
            foreach ($response as $item){
                $check = array(">", "<", "=", "<=", ">=", "!=");
                if((count($item) !== 3) || !in_array($item['operate'], $check)){
                    return false;
                }
            }
            return true;
        }
        return false;
    }


}
