The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# vi:filetype=

my $ExePath;
BEGIN {
    use FindBin;
    $ExePath = "$FindBin::Bin/../haskell/bin/restyscript";
    if (!-f $ExePath) {
        $skip = "$ExePath is not found.\n";
        return;
    }
    if (!-x $ExePath) {
        $skip = "$ExePath is not an executable.\n";
        return;
    }
};
use t::OpenResty $skip ? (skip_all => $skip) : ();

plan tests => 3 * blocks();

run_tests;

__DATA__

=== TEST 1: Delete existing models
--- request
DELETE /=/model?_user=$TestAccount&_password=$TestPass&_use_cookie=1
--- response
{"success":1}



=== TEST 2: Delete existing actions
--- request
DELETE /=/action
--- response
{"success":1,"warning":"Builtin actions were skipped."}



=== TEST 3: Create a model
--- request
POST /=/model/Carrie.js
{
    "description": "我的书签",
    "columns": [
        { "name": "title", "type":"text", "label": "标题" },
        { "name": "url", "type":"text", "label": "网址" },
        { "name": "num", "type": "integer", "label": "num" }
    ]
}
--- response
{"success":1}



=== TEST 4: insert a record
--- request
POST /=/model/Carrie/~/~.js
{ "title":"hello carrie","url":"http://www.carriezh.cn/","num":"10"}
--- response
{"success":1,"rows_affected":1,"last_row":"/=/model/Carrie/id/1"}



=== TEST 5: insert another record
--- request
POST /=/model/Carrie/~/~.js
{ "title":"second","url":"http://zhangxiaojue.cn","num":"1"}
--- response
{"success":1,"rows_affected":1,"last_row":"/=/model/Carrie/id/2"}



=== TEST 6: create an action
--- request
POST /=/action/Query
{"definition":
"select * from Carrie where title = 'hello carrie' and num=10; select * from Carrie where title = 'hello carrie' and num=10;"}
--- response
{"success":1}



=== TEST 7: Invoke the action
--- request
GET /=/action/Query/~/~
--- response
[
    [{"num":"10","url":"http://www.carriezh.cn/","title":"hello carrie","id":"1"}],
    [{"num":"10","url":"http://www.carriezh.cn/","title":"hello carrie","id":"1"}]
]



=== TEST 8: Update the def to introduce vars
--- request
PUT /=/action/Query
{
    "parameters":[{"name":"num","type":"literal"}],
    "definition": "select title from Carrie where num = $num; select url from Carrie where num = $num"}
--- response
{"error":"Unrecognized key in hash: parameters","success":0}



=== TEST 9: Add a parameter
--- request
POST /=/action/Query/~
{"name":"num","type":"literal"}
--- response
{"src":"/=/model/Query/num","success":1}



=== TEST 10: Update the definition
--- request
PUT /=/action/Query
{ "definition": "select title from Carrie where num = $num; select url from Carrie where num = $num"}
--- response
{"success":1}



=== TEST 11: Invoke the action
--- request
GET /=/action/Query/num/10
--- response
[
    [{"title":"hello carrie"}],
    [{"url":"http://www.carriezh.cn/"}]
]



=== TEST 12: Invoke the action
--- request
GET /=/action/Query/num/1
--- response
[
    [{"title":"second"}],
    [{"url":"http://zhangxiaojue.cn"}]
]



=== TEST 13: Reference nonexistent models
--- request
PUT /=/action/Query
{ "definition":
"select * from BlahBlah limit 1 offset 1"}
--- response
{"success":0,"error":"Model \"BlahBlah\" not found."}



=== TEST 14: Try to reference meta models
--- request
PUT /=/action/Query
{ "definition":
"select * from _models limit 1 offset 1"}
--- response
{"error":"\"action\" (line 1, column 15):\nunexpected \"_\"\nexpecting space or model","success":0}



=== TEST 15: Invalid method
--- request
PUT /=/action/RunAction/~/~
{"definition":""}
--- response
{"success":0,"error":"HTTP PUT method not supported for action exec."}



=== TEST 16: Empty restyscript string
--- request
PUT /=/action/RunAction
{"definition":""}
--- response
{"error":"Restyscript source must be an non-empty literal string: \"\"","success":0}
--- SKIP



=== TEST 17: GET rows
--- request
GET /=/model/Carrie/~/~
--- response
[
    {"num":"10","url":"http://www.carriezh.cn/","title":"hello carrie","id":"1"},
    {"num":"1","url":"http://zhangxiaojue.cn","title":"second","id":"2"}
]



=== TEST 18: Add a new parameter
--- request
POST /=/action/Query/~
{"name":"num","type":"literal","default":"5"}
--- response
{"success":0,"error":"Unrecognized key in hash: default"}



=== TEST 19: Add a default value
--- request
PUT /=/action/Query/num
{"default_value":"5"}
--- response
{"success":1}



=== TEST 20: Update some rows
--- request
PUT /=/action/Query
{
  "definition":
    "update Carrie set num=$num where num=10 or num=1"
}
--- response
{"success":1}



=== TEST 21: Invoke the new Query action
--- request
GET /=/action/Query/~/~
--- response
[{"success":1,"rows_affected":2}]



=== TEST 22: check rows again
--- request
GET /=/model/Carrie/~/~
--- response
[
    {"num":"5","url":"http://www.carriezh.cn/","title":"hello carrie","id":"1"},
    {"num":"5","url":"http://zhangxiaojue.cn","title":"second","id":"2"}
]



=== TEST 23: run the action again
--- request
GET /=/action/Query/~/~
--- response
[{"success":1,"rows_affected":0}]



=== TEST 24: run the action again (with argument)
--- request
GET /=/action/Query/num/7
--- response
[{"success":1,"rows_affected":0}]



=== TEST 25: Add a parameter
--- request
POST /=/action/Query/~
{"name":"col","type":"symbol"}
--- response
{"success":1,"src":"/=/model/Query/col"}



=== TEST 26: Do two updates
--- request
PUT /=/action/Query
{"definition":
    "update Carrie set $col=7 where id=1; update Carrie set $col=8 where id=2"}
--- response
{"success":1}



=== TEST 27: Run the action w/o arguments
--- request
GET /=/action/Query/~/~
--- response
{"success":0,"error":"Arguments required: col"}



=== TEST 28: Run the action with arguments
--- request
POST /=/action/Query/~/~
{"col":"_access"}
--- response
{"success":0,"error":"Bad value for parameter \"col\"."}



=== TEST 29: Run the action with arguments
--- request
POST /=/action/Query/~/~
{"col":"num"}
--- response
[{"success":1,"rows_affected":1},{"success":1,"rows_affected":1}]



=== TEST 30: Run the action with invalid arguments
--- request
POST /=/action/Query/~/~
{"col":"@#@@$^^#@"}
--- response
{"success":0,"error":"Bad value for parameter \"col\"."}



=== TEST 31: Run the action in the right way
--- request
POST /=/action/Query/~/~
{"col":"num"}
--- response
[{"rows_affected":1,"success":1},{"rows_affected":1,"success":1}]



=== TEST 32: check rows again
--- request
GET /=/model/Carrie/~/~
--- response
[
    {"num":"7","url":"http://www.carriezh.cn/","title":"hello carrie","id":"1"},
    {"num":"8","url":"http://zhangxiaojue.cn","title":"second","id":"2"}
]



=== TEST 33: remove parameters
--- request
DELETE /=/action/Query/~
--- response
{"success":0,"error":"Failed to remove parameter \"col\": it's used in the definition."}



=== TEST 34: remove parameters
--- request
PUT /=/action/Query
{"definition":"select 0"}
--- response
{"success":1}



=== TEST 35: remove parameters
--- request
DELETE /=/action/Query/~
--- response
{"success":1}



=== TEST 36: check the action
--- request
GET /=/action/Query/~
--- response
[]



=== TEST 37: Add a new parameter
--- request
POST /=/action/Query/dir
{"type":"keyword"}
--- response
{"success":1,"src":"/=/model/Query/dir"}



=== TEST 38: order by a var
--- request
PUT /=/action/Query
{"definition":
"select id from Carrie order by id $dir"}
--- response
{"success":1}



=== TEST 39: invoke it with dir = asc
--- request
GET /=/action/Query/dir/asc
--- response
[[
    {"id":"1"},
    {"id":"2"}
]]



=== TEST 40: invoke it with dir = desc
--- request
GET /=/action/Query/dir/desc
--- response
[[
    {"id":"2"},
    {"id":"1"}
]]



=== TEST 41: invoke with invalid dir
--- request
GET /=/action/Query/dir/blah'
--- response
{"error":"Invalid valud for parameter \"dir\".","success":0}



=== TEST 42: Add a new parameter
--- request
POST /=/action/Query/num
{"type":"literal"}
--- response
{"success":1,"src":"/=/model/Query/num"}



=== TEST 43: Delete rows
--- request
PUT /=/action/Query
{"definition":"delete from Carrie\n where num = $num;;"
}
--- response
{"success":1}



=== TEST 44: Invoke the action
--- request
GET /=/action/Query/num/7
--- response
[{"success":1,"rows_affected":1}]



=== TEST 45: check rows again
--- request
GET /=/model/Carrie/~/~
--- response
[{"num":"8","url":"http://zhangxiaojue.cn","title":"second","id":"2"}]



=== TEST 46: Add a new parameter Yahoo
--- request
POST /=/action/Query/~
{"name":"Yahoo","type":"literal"}
--- response
{"success":1,"src":"/=/model/Query/Yahoo"}



=== TEST 47: Add a new parameter Yahoo (twice)
--- request
POST /=/action/Query/~
{"name":"Yahoo","type":"literal"}
--- response
{"error":"Parameter \"Yahoo\" already exists in action \"Query\".","success":0}



=== TEST 48: Add a new parameter Google
--- request
POST /=/action/Query/~
{"name":"Google","type":"literal"}
--- response
{"success":1,"src":"/=/model/Query/Google"}



=== TEST 49: Insert some more data via actions
--- request
PUT /=/action/Query
{"definition":
"POST '/=/model/Carrie' || '/~/~' [{num: 5, url: 'yahoo.cn', title: $Yahoo}, {'num': 6, url: 'google' || '.com', \"title\": $Google}]"
}
--- response
{"success":1}



=== TEST 50: Invoke it
--- request
GET /=/action/Query/Yahoo/Yahoo?Google=Google
--- response
[{"success":1,"rows_affected":2,"last_row":"/=/model/Carrie/id/4"}]



=== TEST 51: Add a new parameter col
--- request
POST /=/action/Query/~
{"name":"col","type":"symbol"}
--- response
{"success":1,"src":"/=/model/Query/col"}



=== TEST 52: three GET in an action
--- request
PUT /=/action/Query
{"definition":
"GET '/=/model/Carrie' || '/' || $col || '/4'; GET '/=/model/Carrie/' || $col || '/3';\n GET '/=/model/Carrie/' || $col || '/2';"
}
--- response
{"success":1}



=== TEST 53: Invoke it
--- request
GET /=/action/Query/col/id
--- response
[
    [{"num":"6","url":"google.com","title":"Google","id":"4"}],
    [{"num":"5","url":"yahoo.cn","title":"Yahoo","id":"3"}],
    [{"num":"8","url":"http://zhangxiaojue.cn","title":"second","id":"2"}]
]



=== TEST 54: three GET in an action (with exceptions)
--- request
PUT /=/action/Query
{"definition":
"GET '/=/model/Carrie' || '/id/4'; GET '/=/blah/blah'; GET '/=/model';"
}
--- response
{"success":1}



=== TEST 55: Invoke it
--- request
GET /=/action/Query/~/~
--- response
[
    [{"id":"4","num":"6","title":"Google","url":"google.com"}],
    {"error":"Handler for the \"blah\" category not found.","success":0},
    [{"description":"我的书签","name":"Carrie","src":"/=/model/Carrie"}]
]



=== TEST 56: Invoke it using yaml
--- request
GET /=/action/Query/~/~.yml
--- format: YAML
--- response
---
-
  -
    id: 4
    num: 6
    title: Google
    url: google.com
-
  error: Handler for the "blah" category not found.
  success: 0
-
  -
    description: "\xE6\x88\x91\xE7\x9A\x84\xE4\xB9\xA6\xE7\xAD\xBE"
    name: Carrie
    src: /=/model/Carrie



=== TEST 57: Add a new parameter model
--- request
POST /=/action/Query/~
{"name":"model","type":"symbol"}
--- response
{"success":1,"src":"/=/model/Query/model"}



=== TEST 58: delete mixed in 2 GET
--- request
PUT /=/action/Query
{"definition":
"DELETE '/=/model/'||$model|| '/id/4';\n GET ('/=/model/'||$model||'/~/~') ; delete from Carrie where id = 3\n ;GET '/=/' || ('model/' || $model ||'/~/~')"
}
--- response
{"success":1}



=== TEST 59: Invoke it
--- request
GET /=/action/Query/model/Carrie
--- response
[
    {"rows_affected":1,"success":1},
    [
        {"id":"2","num":"8","title":"second","url":"http://zhangxiaojue.cn"},
        {"id":"3","num":"5","title":"Yahoo","url":"yahoo.cn"}
    ],
    {"rows_affected":1,"success":1},
    [{"id":"2","num":"8","title":"second","url":"http://zhangxiaojue.cn"}]
]



=== TEST 60: access another account
--- request
POST /=/action/Query2
{"definition":
"DELETE '/=/model?_user=' || $user || '&_password=' || $pass;\nPOST '/=/model/Another' {\"description\":\"a model in another account\"};\n GET '/=/model';\n GET '/=/model?_user=$TestAccount2&_password=$TestPass2'",
"parameters":[
    {"name":"user","type":"literal"},
    {"name":"pass","type":"literal"}
]}
--- response
{"success":1}



=== TEST 61: Invoke it
--- request
POST /=/action/Query2/user/$TestAccount2
{"pass":"$TestPass2"}
--- response
[
    {"success":1},
    {"success":1,"warning":"No 'columns' specified for model \"Another\"."},
    [
      {"description":"我的书签","name":"Carrie","src":"/=/model/Carrie"},
      {"description":"a model in another account","name":"Another","src":"/=/model/Another"}
    ],
    []
]



=== TEST 62: check Test account 2:
--- request
GET /=/model?_user=$TestAccount2&_password=$TestPass2
--- response
[]



=== TEST 63: recheck Test account 1:
--- request
GET /=/model?_user=$TestAccount&_password=$TestPass&_use_cookie=1
--- response
[
    {"src":"/=/model/Carrie","name":"Carrie","description":"我的书签"},
    {"src":"/=/model/Another","name":"Another","description":"a model in another account"}
]



=== TEST 64: NewComment
--- request
POST /=/action/NewComment
{
    "description":"New comment",
    "parameters":[
        { "name": "sender", "type": "literal" },
        { "name": "email", "type": "literal" },
        { "name": "url", "type": "literal" },
        { "name": "body", "type": "literal" },
        { "name": "post_id", "type": "literal" }
    ],
    "definition":"POST '/=/model/Comment/~/~' { sender: $sender, email: $email, url: $url, body: $body, post: $post_id }; update Carrie set num = num + 1 where id = $post_id;"
}
--- response
{"success":1}



=== TEST 65: logout
--- request
GET /=/logout
--- response
{"success":1}