{"id":16300626,"url":"https://github.com/melezhik/swat","last_synced_at":"2025-10-05T19:14:55.503Z","repository":{"id":34772286,"uuid":"38754674","full_name":"melezhik/swat","owner":"melezhik","description":"Simple Web Application Test","archived":false,"fork":false,"pushed_at":"2018-08-18T02:45:22.000Z","size":741,"stargazers_count":48,"open_issues_count":3,"forks_count":12,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-03T11:05:20.514Z","etag":null,"topics":["api","smoke-test","tdd","testing","web"],"latest_commit_sha":null,"homepage":null,"language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/melezhik.png","metadata":{"files":{"readme":"README.md","changelog":"Changes","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-08T13:08:28.000Z","updated_at":"2025-03-16T08:08:17.000Z","dependencies_parsed_at":"2022-09-15T06:51:43.863Z","dependency_job_id":null,"html_url":"https://github.com/melezhik/swat","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/melezhik/swat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melezhik%2Fswat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melezhik%2Fswat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melezhik%2Fswat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melezhik%2Fswat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/melezhik","download_url":"https://codeload.github.com/melezhik/swat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/melezhik%2Fswat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278504939,"owners_count":25998034,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","smoke-test","tdd","testing","web"],"created_at":"2024-10-10T20:51:50.907Z","updated_at":"2025-10-05T19:14:55.463Z","avatar_url":"https://github.com/melezhik.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NAME\n\nSwat\n\n# SYNOPSIS\n\nWeb testinfg framework consuming [Outthentic::DSL](https://github.com/melezhik/outthentic-dsl).\n\n# What does swat stand for?\n\n    [S]imple\n    [W]eb\n    [A]application\n    [T]est\n    \n# Description\n\n* Swat is a powerful and yet simple and flexible tool for rapid automated web tests development.\n\n* Swat is a web application oriented test framework, this means that it equips you with all you need for a web test development\nand yet it's not burdened by many other \"generic\" things that you probably won't ever use.\n\n* Swat does not carry all heavy load on its shoulders, with the help of its \"elder brother\" - curl\nswat makes a http requests in a smart way. This means if you know and love curl swat might be easy way to go.\nSwat just passes all curl related parameter as is to curl and let curl do its job.\n\n* Swat is a text oriented tool, for good or for bad it does not provide any level of http DOM or xpath hacking ( _but_ see  [process http responses](#process-http-responses) section ). It does not even try to decouple http headers from a body. Actually _it just returns you a text_ where you can find and grep in old good unix way. Does this sound suspiciously simple? I believe that most of things could be tested in a simple way.\n\n* Swat is extendable by writing custom perl code, this is where you may add desired complexity to your test stories. Check out swat API for details. \n\nOk, now I hope you are ready to dive into swat tutorial! :)\n\n# Install\n\n    $ sudo apt-get install curl\n    $ sudo cpanm swat\n\nOr install from source:\n\n    # could be useful for contributors and developers\n\n    $ perl Makefile.PL\n    $ make\n    $ make test\n    $ make install\n\n# Write your swat story\n\nSwat test stories always answers on 2 type of questions:\n\n* _What kind of_ http request should be sent.\n* _What kind of_ http response should be received.\n\nAs swat is a web test oriented tool it deals with some http related stuff as:\n\n* http methods\n* http resources\n* http responses\n\nSwat leverages Unix file system to build an _analogy_ for these things:\n\n## HTTP resources\n\n_HTTP resource is just a directory_. You have to create a directory to define a http resource:\n\n    $ mkdir foo/\n    $ mkdir -p bar/baz\n\nThis code defines two http resources for your application - '/foo/' and '/bar/baz'\n\n## HTTP methods\n\n_HTTP method is just a file_. You have to create a file to define a http method.\n\n    $ touch foo/get.txt\n    $ touch foo/put.txt\n    $ touch bar/baz/post.txt\n\nObviously \\`http methods' files should be located at \\`http resource' directories.\n\nThe list below describes two http resources ( /foo, /bar/baz ) and three http methods for these resources ( GET, PUT, DELETE ):\n\n    * GET  /foo\n    * PUT  /foo\n    * POST /bar/baz\n\nHere is the list of _predefined_ file names for a http methods files:\n\n    get.txt      --\u003e GET      method\n    post.txt     --\u003e POST     method\n    put.txt      --\u003e PUT      method\n    delete.txt   --\u003e DELETE   method\n\nThere is also special predefined file name \\`meta.txt', see [meta stories](#meta-stories).\n\n# Hostname / IP Address\n\nYou need to define hostname or ip address to send request to. \n\nJust write it up to a special file  called \\`host' and swat will use it.\n\n    $ echo 'app.local' \u003e host\n    \nAs swat makes http requests with the help of curl, the host name should be compliant with curl requirements, this\nfor example means you may define a http schema or port here:\n\n    $ echo 'https://app.local' \u003e host\n\n    $ echo 'app.local:8080' \u003e host\n\n## HTTP Response\n\nSwat makes request to a given http resources with a given http methods and then validates a response.\nSwat does this with the help so called _check lists_, check lists are defined at \\`http methods' files.\n\n\nCheck list is just a list of expressions a response should match. It might be a set of plain strings or regular expressions:\n\n    $ echo 200 OK \u003e foo/get.txt\n    $ echo 'Hello I am foo' \u003e\u003e foo/get.txt\n\nThe code above defines two checks for response from \\`GET /foo':\n\n* it should contain \"200 OK\"\n* it should contain \"Hello I am foo\"\n\nYou may add some regular expressions checks as well:\n\n    # for example check if we got something like 'date':\n    $ echo 'regexp: \\d\\d\\d\\d-\\d\\d-\\d\\d' \u003e\u003e foo/get.txt\n\n# Bringing all together\n\nAll these things like http method, http resource and check list build up an essential swat entity called a _swat story_.\n\nSwat story is a very simple test plan, which could be expressed in a cucumber language as follows:\n\n    Given I have web application 'http://my.cool.app:80'\n    And I have http method 'GET'\n    And make http request 'GET /foo'\n    Then I should have response matches '200 OK'\n    And I should have response matches 'Hello I am foo'\n    And I should have response matches '\\d\\d\\d\\d-\\d\\d-\\d\\d'\n\nFrom the file system point of view swat story is a:\n\n* http method - the \\`http method' file\n* http resource - the directory where \\`http method file' located in\n* check list - the content of a \\`http method' file\n\n## Swat Project\n\nSwat project is a bunch of a related swat stories kept under a single directory. This directory is called _project root directory_.\nThe project root contains swat scenarios or stories to be parsed and executed by swat utility. See [swat runner](#swat-runner) section \nfor full explanation of this process.\n\nThis is an example swat project layout:\n\n    $ tree my/swat/project\n      my/swat/project\n      |--- host\n      |--- get.txt\n      |----foo\n      |-----|----bar\n      |           |---- post.txt\n      |--- baz\n            |--- get.txt\n\n    3 directories, 3 files\n\n\nThis project contains 3 swat stories:\n\n- GET /\n- POST foo/bar\n- POST baz\n\nDefault swat story is story executed swat by default, this is `$project_root/GET`:\n\n    $ swat # will execute GET / story\n\nYou may set default story using `-t` option:\n\n    $ swat -t foo/bar/POST\n\nOr by setting `path` variable inside swat.ini file:\n\n    $ echo path=baz/POST \u003e\u003e swat.ini\n\nYou define hostname to run request against by command line argument:\n\n    $ swat $project_root $hostname\n\nFor example:\n\n    $ swat . 127.0.0.1\n\nOr by setting via host file:\n\n    $ echo 'app.local:8080' \u003e host\n\nIf you don't defined project root explicitly, swat assume this as current working directory.\n\n    $ swat # run `GET /' story inside current working directory\n\nFollow [swat client](#swat-client) section for full explanation of swat client command line API.\n\n# Swat check lists\n\nSwat check lists complies [Outthentic DSL](https://github.com/melezhik/outthentic-dsl) format.\n\nThere are lot of possibilities here!\n\n( For full explanation of outthentic DSL please follow [documentation](https://github.com/melezhik/outthentic-dsl). )\n\nA few examples:\n\n* plain string checks\n\nOften all you need is to ensure that http response has some strings in:\n\n\n    # http response\n    200 OK\n    HELLO\n    HELLO WORLD\n\n\n    # check list\n    200 OK\n    HELLO\n\n    # swat output\n    OK - output matches '200 OK'\n    OK - output matches 'HELLO'\n\n* regular expressions\n\nYou may use regular expressions as well:\n\n\n    # http response\n    My birth day is: 1977-04-16\n\n\n    # check list\n    regexp: \\d\\d\\d\\d-\\d\\d-\\d\\d\n\n\n    # swat output\n    OK - output matches /\\d\\d\\d\\d-\\d\\d-\\d\\d/\n\nFollow [https://github.com/melezhik/outthentic-dsl#check-expressions](https://github.com/melezhik/outthentic-dsl#check-expressions) to know more.\n\n* generators\n\nYes you may generate new check list on run time:\n\n    # original check list\n   \n    Say\n    HELLO\n   \n    # this generator creates 3 new check expressions:\n   \n    generator: [ qw{ say hello again } ]\n   \n    # final check list:\n   \n    Say\n    HELLO\n    say\n    hello\n    again\n\nFollow [https://github.com/melezhik/outthentic-dsl#generators](https://github.com/melezhik/outthentic-dsl#generators) to know more.\n   \n* inline perl code\n\nWhat about inline arbitrary perl code? Well, it's easy!\n\n\n    # check list\n    regexp: number: (\\d+)\n    validator: [ ( capture()-\u003e[0] '\u003e=' 0 ), 'got none zero number') ];\n\nFollow [https://github.com/melezhik/outthentic-dsl#validators](https://github.com/melezhik/outthentic-dsl#validators) to know more.\n\n* text blocks\n\nNeed to validate that some lines goes in response successively ?\n\n        # http response\n\n        this string followed by\n        that string followed by\n        another one string\n        with that string\n        at the very end.\n\n\n        # check list\n        # this text block\n        # consists of 5 strings\n        # goes consequentially\n        # line by line:\n\n        begin:\n            # plain strings\n            this string followed by\n            that string followed by\n            another one string\n            # regexps patterns:\n        regexp: with (this|that) \\S+\n            # and the last one in a block\n            at the very end\n        end:\n\nFollow [https://github.com/melezhik/outthentic-dsl#comments-blank-lines-and-text-blocks](https://github.com/melezhik/outthentic-dsl#comments-blank-lines-and-text-blocks)\nto know more.\n\n# Swat ini files\n\nEvery swat story comes with some settings you may define to adjust swat behavior.\nThese type of settings could be defined at swat ini files.\n\nSwat ini files are file called \"swat.ini\" and located at \\`resources' directory:\n\n     foo/bar/get.txt\n     foo/bar/swat.ini\n\nThe content of swat ini file is the list of variables definitions in bash format:\n\n    $name=value\n\nAs swat ini files is bash scripts you may use bash expressions here:\n\n    if [ some condition ]; then\n        $name=value\n    fi\n\nFollowing is the list of swat variables you may define at swat ini files, it could be divided on two groups:\n\n* **swat variables**\n* **curl parameters**\n\n## swat variables\n\nSwat variables define swat  basic configuration, like default story, debug mode, etc. Here is the list:\n\n* `path` - defines default story\n\nExample:\n\n    # define default story as GET =\u003e foo/\n    path=foo/GET    \n\n\n* `skip_story` - skip story, default value is \\`0'. Set to \\`1' if you want skip store for some reasons.\n\nFor example:\n\n    # swat.ini\n    # assume that we set profile variable somewhere else\n    # \n\n    if test \"${profile}\" = 'production'; then\n        skip_story=1 # we don't want this one for production\n    fi\n\n* `ignore_http_err` - ignore http related errors; default value is \\`1' ( do not ignore ) \n\n    * swat runs curl command and then checks http status code\n    * in case of bad http status code returns (  0 or \u003e= 400 ) a proper swat assert fails\n    * settings ignore\\_http\\_err to \\`1' only makes a warning in case of bad http status code and does not trigger assert failure \n\n* `debug` - enable swat debugging\n\n    * Increasing debug value results in more low level information appeared at output\n\n    * Default value is 0, which means no debugging\n\n    * Possible values: 0,1,2,3\n\n* `debug_bytes` - number of bytes of http response to be dumped out when debug is on. default value is \\`500'.\n\n* `match_l` - in report output truncate matching strings to {match_l} bytes;  default value is \\`40'.\n\n* `swat_purge_cache` - remove swat cache files at the end of test run; default value is `\\0' ( do not remove ).\n\n## curl parameters\n\nCurl parameters relates to curl client. Here is the list:\n\n* `try_num` - a number of requests to be send in case curl get unsuccessful return,  similar to curl \\`--retry' , default value is \\`2'.\n\n* `curl_params` - additional curl parameters being add to http requests, default value is `\"\"`.\n\nHere are some examples:\n\n    # -d curl parameter\n    curl_params='-d name=daniel -d skill=lousy' # post data sending via form submit.\n\n    # --data-binary curl parameter\n    curl_params=`echo -E \"--data-binary '{\\\"name\\\":\\\"alex\\\",\\\"last_name\\\":\\\"melezhik\\\"}'\"`\n\n    # set http header\n    curl_params=\"-H 'Content-Type: application/json'\"\n\n\nFollow curl documentation to get more examples.\n\n* `curl_connect_timeout` - maximum time in seconds that you allow the connection to the server to take, follow curl documentation for full explanation.\n\n* `curl_max_time` - maximum time in seconds that you allow the whole operation to take, follow curl documentation for full explanation.\n\n* `curl_follow_redirect` - make curl to respect http redirects. default value is \\`1' ( respect ), set this setting to \\`0' if you don't want for some reasons follow http redirects\n\n* `port`  - http port of tested host, default value is \\`80'.\n\n## other variables\n\nThis is the list of helpful variables  you may use in swat ini files:\n\n* $resource\n* $resource_dir\n* $test\\_root\\_dir\n* $hostname\n* $http_method\n\n## Alternative swat ini files locations\n\nSwat try to find swat ini files at these locations ( listed in order )\n\n* **~/swat.ini** - home directory\n\n* **$project\\_root\\_directory/swat.ini** -  project root directory\n\n## Settings priority table\n\nThis table describes all possible locations for swat ini files. Swat try to find swat ini files in order:\n\n    | location                                  | order N     |\n    | --------------------------------------------------------|\n    | ~/swat.ini                                | 1           |\n    | `project_root_directory'/swat.ini         | 2           |\n    | `http resource' directory/swat.ini file   | 3           |\n    | environment variables                     | 4           |\n\n\nIn case the same variable is defined more than once at swat ini files with different locations, the file loaded last win:\n\n    curl_params=\"-H 'Foo: Bar'\" # in a ~/swat.ini\n    curl_params=\"-H 'Bar: Baz'\" # in a project_root_directory/swat.ini\n\n    # actual curl_params value:\n    \"-H 'Bar: Baz'\"\n\nIf you want concatenation mode use name=\"$name value\" expression:\n\n    curl_params=\"-H 'Foo: Bar'\" # in a ~/swat.ini\n    curl_params=\"$curl_params -H 'Bar: Baz'\" # in a project_root_directory/swat.ini\n\n    # actual curl_params value:\n    \"-H 'Foo: Bar' -H 'Bar: Baz'\"\n\nIn case you need provide default value for some variable use name=${name default_value} expression:\n\n    # port will be set 80 unless it's not set somewhere else\n    port=${port:=80} # in a ~/swat.ini\n      \n\n# Hooks\n\nHooks are extension points to hack into swat runtime phase. It's just files with perl code gets executed in the beginning of swat story.\nYou should named your hook file as \\`hook.pm' and place it into \\`resource' directory:\n\n\n    # foo/hook.pm\n    diag \"hello, I am swat hook\";\n    sub red_green_blue_generator { [ qw /red green blue/ ] }\n   \n\n    # foo/get.txt\n    generator: red_green_blue_generator()\n\n\nThere are lot of reasons why you might need a hooks. To say a few:\n\n* define swat generators\n* redefine http responses\n* process http responses \n* redefine http resources\n* call downstream stories\n* other custom code\n\n\n# Hooks API\n\nSwat hooks API provides several functions to change swat story at runtime\n\n## Redefine http responses\n\n*set_response(STRING)*\n\nUsing set_response means that you never make a real request to a web application, but instead set response on your own side.\n\nThis feature is helpful when you need to mock up http responses instead of having them requested from a real web application.\nFor example in absence of an access to a tested application or if response is too slow or it involves too much data\nwhich make it hard to execute a swat stories often.\n\nThis is an example of setting server response inside swat hook:\n\n    # hook.pm\n    set_response(\"THIS IS I FAKE RESPONSE\\n HELLO WORLD\");\n\n    # get.txt\n    THIS IS A FAKE RESPONSE\n    HELLO WORLD\n\n\nYou may call \\`set_response' more then once:\n\n\n    set_response(\"HELLO WORLD\");\n    set_response(\"HELLO WORLD2\");\n\nA final response will be:\n\n    HELLO WORLD\n    HELLO WORLD2\n\nAnother interesting idea about set\\_response feature is a _conditional_ http requests.\n\nLet say we have \\`POST /login' request for user authentication, this is a simple swat story for it:\n\n    # login/post.txt\n    200 OK\n   \nGood. But what if you need to skip authentication under some conditions, like if you are already logged in before?\nWe could write such a code:\n\n    # login/post.txt\n    generator:\n    $logged_in ? [ 'I am already logged in' : '200 OK' ]\n\n    # login/hook.pm\n    if ( ... check if user is logged in .... ){\n        set_response('I am already logged in');\n    }\n\n\n## Process http responses \n\n\n*set_response_processor(CODEREF)*\n\n\nResponse processors are custom perl function to modify content returned from server _before_ invoking a validation process.\nProcessor code should be _defined_ by calling a process_response function with parameter as reference to processor function:\n\nFor example:\n\n       set_response_processor( sub { \n          my $headers   = shift; # original response, http headers, String\n          my $body      = shift; # original response, body, String\n          $body=~s/hello/swat/;\n          return $body;          # modified response will be return value of sub {} \n        });\n        \n\nWhat you should know about processor functions:\n\n* They should be simple, it is good to follow KISS paradigm.\n\n* They are not for validating content, use check lists for this.\n\n* Basicly processor code _prepare_ a content to be validated by check lists.\n\n* When processor function gets called it is supplied with $body parameter which is a http body of original http response relieved from server.\n\n* Remember that if processor function gets called - the content get passed into validation process will be altered as if you use a classic UNIX  pipeline, see schema below.\n\n* In case of response processor function gets called, the original http headers and processor function return value be sent as input for validation process. \n\n* If no processor function gets called an original http response ( http headers and the body ) will be sent as input for validation process.\n\nThis is a rough schema of full process:\n\n\n     # without processor function\n     curl -i some-http-URL | validation-process\n\n     # with processor script exists:\n     curl -i some-http-URL | processor-function| validation-process\n\nThe _possible_ usage of processor functions:\n\n* handling json, xml, yaml data\n\nFor example:\n     \n\n      # server response in json format\n      {\n          \"Foo\": {\n              \"Bar\" : \"Baz\"\n           }\n      }\n\n      # processor function\n      my $headers   = shift;\n      my $body      = shift;\n      use JSON;\n      $hash = decode_json($body);\n      return 'Foo.Bar.Baz :'.( $hash-\u003e{Foo}-\u003e{Bar}-\u003e{Baz} ).\"\\n\";\n      \n\n      # server response in xml format\n      \u003cfoo\u003e\n        \u003cbar\u003e\n          \u003cbaz\u003eaaa\u003c/baz\u003e\n        \u003c/bar\u003e  \n      \u003c/foo\u003e\n\n      # processor function\n      my $headers   = shift;\n      my $body      = shift;\n      use XML:LibXML;\n      my $doc = XML::LibXML-\u003eparse_string($body);\n      return 'Foo.Bar.Baz :'.( $doc-\u003efind(\"string(/foo/bar/baz)\")-\u003estring_value ).\"\\n\";\n      \n\n\n## Redefine http resources\n\n*modify_resource(CODEREF)*\n\nTo modify existed resource use modify_resource function:\n\n    # foo/bar/baz/ - resource\n   \n    # hook.pm\n    modify_resource( sub { my $resource = shift; s/bar/bbaarr/, s/baz/bbaazz/ for $resource; $resource  } );\n\n    # modified resource\n    foo/bbaarr/bbaazz\n\n\n## Upstream and downstream stories\n\nSwat allow you to call one story from another, using notion of swat modules.\n\nSwat modules are reusable swat stories. Swat never executes swat modules directly, instead you have to call swat module from your swat story.\nStory calling another story is named a _upstream story_, story is being called is named a _downstream_ story.\n( This kind of analogy is taken from Jenkins CI )\n\n\nLet show how this work on a previous \\`login' example. We need to ensure that user is logged in before\ndoing some other action, like checking email list:\n\n    # email/list/get.txt\n    200 OK\n    email list\n\n    # email/list/hook.pm\n    run_swat_module( POST =\u003e '/login', { user =\u003e 'alex', password =\u003e 'swat' } )  \n\n    # and finally this is\n    # login/post.txt\n    200 OK\n\n    # login/swat.ini\n    swat_module=1 # this story is a swat module\n    curl_params=\"-d 'user=%user%' -d 'password=%password%'\"\n\nHere are the brief comments to the example above:\n\n* \\`set_module=1' declare swat story as swat module; now swat will never execute this story directly, upstream story should call it.\n\n* call \\`run\\_swat\\_module(method,resource,variables)' function inside upstream story hook to run downstream story.\n\n* you can call as many downstream stories as you wish.\n\n* you can call the same downstream story more than once.\n\nHere is an example code snippet:\n\n    # hook.pm\n    run_swat_module( GET =\u003e '/foo/' )\n    run_swat_module( POST =\u003e '/foo/bar' )\n    run_swat_module( GET =\u003e '/foo/' )\n\n\n* swat modules have a variables\n\nUse hash passed as third parameter of run_swat_module function:\n\n    run_swat_module( GET =\u003e '/foo', { var1 =\u003e 'value1', var2 =\u003e 'value2', var3=\u003e'value3'   }  )\n\nSwat _interpolates_ module variables into \\`curl_params' variable in swat module story:\n\n\n    # swat module\n    # swat.ini\n    swat_module=1\n    # initial value of curl_params variable:\n    curl_params='-d var1=%var1% -d var2=%var2% -d var3=%var3%'\n\n    # real value of curl_params variable\n    # during execution of swat module:\n    curl_param='-d var1=value1 -d var2=value2 -d var3=value3'\n\n\nUse `%[\\w\\d_]+%` placeholders in a curl_params variable to insert module variables here\n\nAccess to a module variables is provided by \\`module_variable' function:\n\n    # hook.pm\n    module_variable('var1');\n    module_variable('var2');\n\n* swat modules could call other swat modules\n\n* you can't use module variables in a story which is not a swat_module\n\nOne word about sharing state between upstream story and swat modules. As swat modules get executed in the same process\nas upstream story there is no magic about sharing data between upstream and downstream stories.\nThe straightforward way to share state is to use global variables :\n\n    # upstream story hook:\n    our $state = [ 'this is upstream story' ]\n\n    # downstream story hook:\n    push our @$state, 'I was here'\n   \nOf course more proper approaches for state sharing could be used as singeltones or something else.\n\n## swat variables accessors\n\nThere are some accessors to a common swat variables:\n\n* project\\_root\\_dir()\n\n* test\\_root\\_dir()\n\n* resource()\n\n* resource\\_dir()\n\n* http\\_method()\n\n* hostname()\n\n* ignore\\_http\\_err()\n\n* config() - returns hash of test suite configuration\n\nSee [test suite ini file](#test-suite-ini-file) section for details.\n\nBe aware of that these functions are readers.\n\n## meta stories\n\nMeta stories are special type of swat stories. \n\nThe essential property of meta story is it has no related http request:\n\n    # foo/bar story\n    mkdir foo/bar\n\n    # it's a meta story\n    touch foo/bar/meta.txt\n\nAs meta story does not have any related http request, it has no \\`http method' file either.\n\nA `meta.txt' file should be treated as meta request file denoted that story is meta.\n\nYou may live \\`meta.txt' empty file or add some useful description to be printed  when story is executed:\n\n    nano foo/bar/meta.txt\n\n        This is my cool story. \n        Take a look at this!\n\nTo run meta story you define story as `path/META`, for example:\n\n    swat -t META # run meta story defined in the project root directory\n\n    swat -t foo/META # run meta story defined in the foo/ folder\n\n    echo META \u003e swat.ini # the same as the first but via swat.ini file\n\nHow one could use meta stories?\n\nMeta stories are just _containers_ for other downstream stories. Usually one defines some downstream\nstories call inside meta story's hook file:\n\n    nano foo/bar/hook.pm\n\n        run_swat_module( POST =\u003e '/baz' );\n        run_swat_module( POST =\u003e '/baz/bar' );\n\n\n\nMeta stories are very close to upstream stories with spoofed server response, \nwith the only exclusion that as meta story has no real http request related to it, \nthere is no need for spoofing.\n\n\nMeta stories can be also called as downstream stories:\n\n    # I am downstream story\n    # you can call me from somewhere else\n    nano foo/bar/swat.ini\n        swat_module=1\n\n\n## PERL5LIB\n\nSwat adds \\`project\\_root\\_directory/lib' path to PERL5LIB path, which make it easy to add some modules and use them:\n\n    # my-app/lib/Foo/Bar/Baz.pm\n    package Foo::Bar::Baz;\n    ...\n   \n    # hook.pm\n    use Foo::Bar::Baz;\n    ...\n   \n\n# Swat runner\n\nSwat runner - is a script to run swat stories. It is called `swat`.\n\nRunner consequentially goes several phases:\n\n## A compilation phase.\n\nStories are converted into perl test files \\*.swat ( compilation phase ) and saved into temporary directory.\n\n## An execution phase.\n\nTODO:\n\n# Suite configuration\n\nSwat test suites could be configurable. Configuration files contain a supplemental data to adjust suite behavior\n\nThere are two type of configuration files are supported:\n\n* .Ini style format\n* YAML format\n\n.Ini  style configuration files are passed by \\`--ini' parameter\n\n    $ swat --ini /etc/suites/foo.ini\n\n    $ cat /etc/suites/foo.ini\n\n    [main]\n\n    foo = 1\n    bar = 2\n\nThere is no special magic behind ini files, except this should be [Config Tiny](https://metacpan.org/pod/Config::Tiny) compliant configuration file.\n\nOr you can choose YAML format for suite configuration by using `--yaml` parameter:\n\n    $ swat --yaml /etc/suites/foo.yaml\n\n    $ cat /etc/suites/foo.yaml\n\n    main:\n      foo : 1\n      bar : 2\n\n\nUnless user sets path to configuration file explicitly by `--ini` or `--yaml` swat runner looks for the \nfiles named suite.ini and _then_ ( if not found ) looks for the file named suite.yaml at the current working directory.\n\nIf configuration file is passed and read a related configuration data is accessible via config() function, for example in story.pm file:\n\n    # cat story.pm\n\n    my $foo = config()-\u003e{main}{foo};\n    my $bar = config()-\u003e{main}{bar};\n\n# Misc settings\n\n* swat\\_purge\\_cache\n\n    * Set to \\`1', in case you need to clear swat cache directory, useful when swat tests get run periodically and a lot of cache files are created.\n    * Default value is \\`0' ( do not clear cache ).\n\n* swat\\_disable\\_color - disable color output\n\n    * Set this 1 if you want to disable color output in swat test report. \n\n    * By default this setting is off.\n\n# Swat client\n\nOnce swat is installed you get swat client at the \\`PATH':\n\n    swat [[project_root_dir] [host:port]] [swat_command_line_parameters]\n\n* **project\\_root\\_dir** - swat project root directory\n\nIn case you don't set one, swat assume it equal to current working directory. Examples:\n\n    # setup project root directory explicitly\n    swat /foo/bar/baz\n\n    # project root directory is CWD\n    swat \n\n* **host** - basic URL of tested application, should be in curl compatible format\n\nIn case host parameter is missing , swat tries to read it up from \\`host' file. Examples:\n\n    # setup host explicitly\n    swat /foo/bar/baz 127.0.0.1\n\n    # host entry gets read from CWD/host file \n    swat /foo/bar/baz\n \n    # project root directory is CWD\n    # host entry gets read from CWD/host file \n    swat\n\nList of swat command line parameters:\n\n* **-t|--test**\n\nSets a certain swat story to executed, see [Running none default swat story](#running-none-default-swat-story)\n\n* **--ini** - suite configuration ini file path\n\n* **--yaml** - suite configuration yaml file path\n\nSee [suite configuration](#suite-configuration) section for details.\n\n* **--debug|d** - sets value for swat debug parameter\n\nOverride the value for swat debug variable, see [swat variables](#swat-variables) section:\n\n    swat --debug 2 # set debug to 2\n\n# Running none default swat story\n\nUse \\`-t' options to execute specific swat story, you should use `$http_resource/$http_method`\nsyntax, for example:\n\n    # run `POST foo/bar' story\n    swat example/my-app 127.0.0.1 -t foo/bar/POST\n\n    # run `META /' story\n    swat example/my-app 127.0.0.1 -t META\n\n# Examples\n\nThere is plenty of examples at ./examples directory\n\n# AUTHOR\n\n[Aleksei Melezhik](mailto:melezhik@gmail.com)\n\n# See also\n\n* [Sparrow](https://github.com/melezhik/sparrow) - Swat and Outthentic plugins manager.\n\n* [Outthentic](https://github.com/melezhik/outthentic) - Multipurpose scenarios framework.\n\n# Swat version 0.2.0 BREAKING CHANGES\n\nAs with version 0.2.0 swat removes the usage of prove ( Test::More, Test::Harness modules ), there\nare consequences of that:\n\n* swat reports is no longer a TAP\n\n* tests do not get run recursively, rewrite your tests scenarios to use modules to run your test sets\n\n* `-t` command line parameter now defines a single swat story rather than a subset of stories, it should follow \n`$http_resource/$http_method` notation\n\n* to run meta story, you should use path=http_resource/META or -t http_resource/META options.\n\n# Thanks\n\nTo God as the One Who inspires me to do my job!\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmelezhik%2Fswat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmelezhik%2Fswat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmelezhik%2Fswat/lists"}