From 271a67a4b51ec5dbad15671213b151d7eb7136ce Mon Sep 17 00:00:00 2001 From: walking dev <104449470+walkingdevel@users.noreply.github.com> Date: Sat, 10 Sep 2022 17:22:56 +0000 Subject: [PATCH] tests and initial api (#199) --- .gitignore | 2 +- api/api.v | 6 ++ api/branch.v | 7 ++ api/commit.v | 7 ++ api/issue.v | 7 ++ run.sh | 2 +- src/branch_routes.v | 16 +++ src/commit_routes.v | 20 +++- src/commit_service.v | 2 +- src/issue_routes.v | 16 +++ src/issue_service.v | 6 ++ src/repo_routes.v | 8 +- src/repo_template.v | 2 +- src/templates/new.html | 2 +- tests/first_run.v | 234 ++++++++++++++++++++++++++++++++++------- 15 files changed, 288 insertions(+), 49 deletions(-) create mode 100644 api/api.v create mode 100644 api/branch.v create mode 100644 api/commit.v create mode 100644 api/issue.v diff --git a/.gitignore b/.gitignore index 735aa85..aefa1c0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ gitly *.css.map .sass-cache/ .lite_workspace.lua -static/assets/version +src/static/assets/version logs/ repos/ archives/ diff --git a/api/api.v b/api/api.v new file mode 100644 index 0000000..9703428 --- /dev/null +++ b/api/api.v @@ -0,0 +1,6 @@ +module api + +struct ApiResponse { +pub: + success bool +} diff --git a/api/branch.v b/api/branch.v new file mode 100644 index 0000000..9c72c50 --- /dev/null +++ b/api/branch.v @@ -0,0 +1,7 @@ +module api + +pub struct ApiBranchCount { + ApiResponse +pub: + result int +} diff --git a/api/commit.v b/api/commit.v new file mode 100644 index 0000000..3afc040 --- /dev/null +++ b/api/commit.v @@ -0,0 +1,7 @@ +module api + +pub struct ApiCommitCount { + ApiResponse +pub: + result int +} diff --git a/api/issue.v b/api/issue.v new file mode 100644 index 0000000..24fb3bf --- /dev/null +++ b/api/issue.v @@ -0,0 +1,7 @@ +module api + +pub struct ApiIssueCount { + ApiResponse +pub: + result int +} diff --git a/run.sh b/run.sh index 559c727..e325d33 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,3 @@ -sassc static/css/gitly.scss > static/css/gitly.css +sassc src/static/css/gitly.scss > src/static/css/gitly.css v . ./gitly diff --git a/src/branch_routes.v b/src/branch_routes.v index 6408e9e..da599f2 100644 --- a/src/branch_routes.v +++ b/src/branch_routes.v @@ -1,6 +1,22 @@ module main import vweb +import api + +['/api/v1/:user/:repo_name/branches/count'] +fn (mut app App) handle_branch_count(username string, repo_name string) vweb.Result { + // TODO: add auth checking module + if !app.exists_user_repo(username, repo_name) { + return app.not_found() + } + + count := app.get_count_repo_branches(app.repo.id) + + return app.json(api.ApiBranchCount{ + success: true + result: count + }) +} ['/:user/:repo/branches'] pub fn (mut app App) branches(user string, repo string) vweb.Result { diff --git a/src/commit_routes.v b/src/commit_routes.v index 412745d..3124414 100644 --- a/src/commit_routes.v +++ b/src/commit_routes.v @@ -3,10 +3,22 @@ module main import vweb import highlight import time +import api -['/:user/:repo/:branch_name/commits'] -pub fn (mut app App) handle_commits(username string, repo string, branch_name string) vweb.Result { - return app.commits(username, repo, branch_name, 0) +['/api/v1/:user/:repo_name/:branch_name/commits/count'] +fn (mut app App) handle_commits_count(username string, repo_name string, branch_name string) vweb.Result { + // TODO: add auth checking module + if !app.exists_user_repo(username, repo_name) { + return app.not_found() + } + + branch := app.find_repo_branch_by_name(app.repo.id, branch_name) + count := app.get_repo_commit_count(app.repo.id, branch.id) + + return app.json(api.ApiCommitCount{ + success: true + result: count + }) } ['/:username/:repo_name/commits/:branch_name/:page'] @@ -18,7 +30,7 @@ pub fn (mut app App) commits(username string, repo_name string, branch_name stri app.show_menu = true branch := app.find_repo_branch_by_name(app.repo.id, branch_name) - commits_count := app.get_count_repo_commits(app.repo.id, branch.id) + commits_count := app.get_repo_commit_count(app.repo.id, branch.id) mut commits := app.find_repo_commits_as_page(app.repo.id, branch.id, page) // TODO: move to render logic diff --git a/src/commit_service.v b/src/commit_service.v index 56e6588..7174930 100644 --- a/src/commit_service.v +++ b/src/commit_service.v @@ -98,7 +98,7 @@ fn (mut app App) find_repo_commits_as_page(repo_id int, branch_id int, page int) } } -fn (mut app App) get_count_repo_commits(repo_id int, branch_id int) int { +fn (mut app App) get_repo_commit_count(repo_id int, branch_id int) int { return sql app.db { select count from Commit where repo_id == repo_id && branch_id == branch_id } diff --git a/src/issue_routes.v b/src/issue_routes.v index ce5d94e..3390313 100644 --- a/src/issue_routes.v +++ b/src/issue_routes.v @@ -2,6 +2,22 @@ module main import vweb import validation +import api + +['/api/v1/:user/:repo_name/issues/count'] +fn (mut app App) handle_issues_count(username string, repo_name string) vweb.Result { + // TODO: add auth checking module + if !app.exists_user_repo(username, repo_name) { + return app.not_found() + } + + count := app.get_repo_issue_count(app.repo.id) + + return app.json(api.ApiIssueCount{ + success: true + result: count + }) +} ['/:user/:repo/issues/new'] pub fn (mut app App) new_issue(user string, repo string) vweb.Result { diff --git a/src/issue_service.v b/src/issue_service.v index 778dd10..aa77f58 100644 --- a/src/issue_service.v +++ b/src/issue_service.v @@ -45,6 +45,12 @@ fn (mut app App) find_repo_issues_as_page(repo_id int, page int) []Issue { } } +fn (mut app App) get_repo_issue_count(repo_id int) int { + return sql app.db { + select count from Issue where repo_id == repo_id + } +} + fn (mut app App) get_all_repo_issues(repo_id int) []Issue { issues := sql app.db { select from Issue where repo_id == repo_id && is_pr == false diff --git a/src/repo_routes.v b/src/repo_routes.v index 1186104..702010a 100644 --- a/src/repo_routes.v +++ b/src/repo_routes.v @@ -159,7 +159,7 @@ pub fn (mut app App) new() vweb.Result { } ['/new'; post] -pub fn (mut app App) handle_new_repo(name string, clone_url string, description string) vweb.Result { +pub fn (mut app App) handle_new_repo(name string, clone_url string, description string, no_redirect string) vweb.Result { mut valid_clone_url := clone_url is_clone_url_empty := validation.is_string_empty(clone_url) is_public := app.form['repo_visibility'] == 'public' @@ -245,6 +245,10 @@ pub fn (mut app App) handle_new_repo(name string, clone_url string, description app.update_repo(mut app.repo) } + if no_redirect == '1' { + return app.text('ok') + } + has_first_repo_activity := app.has_activity(app.user.id, 'first_repo') if !has_first_repo_activity { @@ -354,7 +358,7 @@ pub fn (mut app App) tree(username string, repository_name string, branch_name s items << dirs items << files - commits_count := app.get_count_repo_commits(app.repo.id, branch.id) + commits_count := app.get_repo_commit_count(app.repo.id, branch.id) has_commits := commits_count > 0 // Get readme after updating repository diff --git a/src/repo_template.v b/src/repo_template.v index 0932d1f..1820350 100644 --- a/src/repo_template.v +++ b/src/repo_template.v @@ -12,7 +12,7 @@ fn get_declension_form(count int, first_form string, second_form string) string fn (mut app App) format_commits_count(repo Repo, branch_name string) vweb.RawHtml { branch := app.find_repo_branch_by_name(repo.id, branch_name) - commits_count := app.get_count_repo_commits(repo.id, branch.id) + commits_count := app.get_repo_commit_count(repo.id, branch.id) return get_declension_form(commits_count, 'commit', 'commits') } diff --git a/src/templates/new.html b/src/templates/new.html index 18bd7c5..5ae4cba 100644 --- a/src/templates/new.html +++ b/src/templates/new.html @@ -36,7 +36,7 @@ - +
diff --git a/tests/first_run.v b/tests/first_run.v index 8eafd65..f243c4a 100644 --- a/tests/first_run.v +++ b/tests/first_run.v @@ -1,8 +1,83 @@ import os import net.http import time +import json +import api -const myfolder = os.dir(os.executable()) +const gitly_url = 'http://127.0.0.1:8080' + +const default_branch = 'main' + +const test_username = 'bob' + +const test_github_repo_url = 'https://github.com/vlang/ui' + +const test_github_repo_primary_branch = 'master' + +fn main() { + before()? + + test_index_page() + + ilog('Register the first user `$test_username`') + mut register_headers, token := register_user(test_username, '1234zxcv', 'bob@example.com') or { + exit_with_message(err.str()) + } + + ilog('Check all cookies that must be present') + assert register_headers.contains(.set_cookie) + + ilog('Ensure the login token is present after registration') + has_token := token != '' + assert has_token + + test_user_page(test_username) + test_login_with_token(test_username, token) + test_static_served() + + test_create_repo(token, 'test1', '') + assert get_repo_commit_count(token, test_username, 'test1', default_branch) == 0 + assert get_repo_issue_count(token, test_username, 'test1') == 0 + assert get_repo_branch_count(token, test_username, 'test1') == 0 + + test_create_repo(token, 'test2', test_github_repo_url) + assert get_repo_commit_count(token, test_username, 'test2', test_github_repo_primary_branch) > 0 + assert get_repo_issue_count(token, test_username, 'test2') == 0 + assert get_repo_branch_count(token, test_username, 'test2') > 0 + + after()? +} + +fn before() ? { + cd_executable_dir()? + + ilog('Make sure gitly is not running') + kill_gitly_processes() + + remove_database_if_exists()? + remove_repos_dir_if_exists()? + compile_gitly() + + ilog('Start gitly in the background, then wait till gitly starts and is responding to requests') + go run_gitly() + + wait_gitly() +} + +fn after() ? { + remove_database_if_exists()? + remove_repos_dir_if_exists()? + + ilog('Ensure gitly is stopped') + kill_gitly_processes() +} + +fn run_gitly() { + gitly_process := os.execute('./gitly &') + if gitly_process.exit_code != 0 { + exit_with_message(gitly_process.str()) + } +} [noreturn] fn exit_with_message(message string) { @@ -14,33 +89,56 @@ fn ilog(message string) { println('$time.now().format_ss_milli() | $message') } -fn main() { +fn cd_executable_dir() ? { + executable_dir := os.dir(os.executable()) // Ensure that we are always running in the gitly folder, no matter what is the starting one: - os.chdir(os.dir(myfolder))? + os.chdir(os.dir(executable_dir))? + ilog('Testing first gitly run.') +} - ilog('Make sure gitly is not running') +fn kill_gitly_processes() { os.execute('pkill -9 gitly') +} +fn remove_database_if_exists() ? { ilog('Remove old gitly DB') + if os.exists('gitly.sqlite') { os.rm('gitly.sqlite')? } +} + +fn remove_repos_dir_if_exists() ? { + ilog('Remove repos directory') + + if os.exists('repos') { + os.rmdir_all('repos')? + } +} +fn compile_gitly() { ilog('Compile gitly') + os.execute('v .') +} - ilog('Start gitly in the background, then wait till gitly starts and is responding to requests') - go run_gitly() +fn wait_gitly() { for waiting_cycles := 0; waiting_cycles < 50; waiting_cycles++ { - ilog(' wait: $waiting_cycles') + ilog('\twait: $waiting_cycles') time.sleep(100 * time.millisecond) - http.get('http://127.0.0.1:8080') or { continue } + http.get(prepare_url('')) or { continue } break } +} +fn prepare_url(path string) string { + return '$gitly_url/$path' +} + +fn test_index_page() { ilog("Ensure gitly's main page is up") - index_page_result := http.get('http://127.0.0.1:8080') or { exit_with_message(err.str()) } + index_page_result := http.get(prepare_url('')) or { exit_with_message(err.str()) } assert index_page_result.body.contains('') assert index_page_result.body.contains('') @@ -51,56 +149,116 @@ fn main() { // Make sure no one's logged in assert index_page_result.body.contains("Log in") +} - ilog('Register the first user `bob`') - mut register_result := http.post('http://127.0.0.1:8080/register', 'username=bob&password=1234zxcv&email=bob@example.com&no_redirect=1') or { - exit_with_message(err.str()) +// returns headers and token +fn register_user(username string, password string, email string) ?(http.Header, string) { + response := http.post(prepare_url('register'), 'username=$username&password=$password&email=$email&no_redirect=1') or { + return err } - ilog('Check all cookies that must be present') - assert register_result.header.contains(.set_cookie) - ilog('Ensure the login token is present after registration') - mut has_token := false mut token := '' - for val in register_result.header.values(.set_cookie) { + for val in response.header.values(.set_cookie) { token = val.find_between('token=', ';') - has_token = token != '' } - assert has_token - ilog('Testing the new user /bob page is up after registration') - user_page_result := http.get('http://127.0.0.1:8080/bob') or { exit_with_message(err.str()) } - assert user_page_result.body.contains('

bob

') + return response.header, token +} + +fn test_static_served() { + ilog('Ensure that static css is served') + css := http.get(prepare_url('css/gitly.css')) or { exit_with_message(err.str()) } + + assert css.status_code != 404 + assert css.body.contains('body') + assert css.body.contains('html') +} + +fn test_user_page(username string) { + ilog('Testing the new user /$username page is up after registration') + user_page_result := http.get(prepare_url(username)) or { exit_with_message(err.str()) } + + assert user_page_result.body.contains('

$username

') +} + +fn test_login_with_token(username string, token string) { + ilog('Try to login in with `$username` user token') - ilog('Try to login in with `bob` user token') login_result := http.fetch( method: .get cookies: { 'token': token } - url: 'http://127.0.0.1:8080/bob' + url: prepare_url(username) ) or { exit_with_message(err.str()) } - ilog('Ensure that after login, there is a signed in as `bob` message') + ilog('Ensure that after login, there is a signed in as `$username` message') + assert login_result.body.contains('Signed in as') - assert login_result.body.contains("bob") + assert login_result.body.contains("$username") +} - ilog('Ensure that static css is served') - css := http.get('http://127.0.0.1:8080/css/gitly.css') or { exit_with_message(err.str()) } +fn test_create_repo(token string, name string, clone_url string) { + description := 'test description' + repo_visibility := 'public' - println(css) + response := http.fetch( + method: .post + cookies: { + 'token': token + } + url: prepare_url('new') + data: 'name=$name&description=$description&clone_url=$clone_url&repo_visibility=$repo_visibility&no_redirect=1' + ) or { exit_with_message(err.str()) } - assert css.status_code != 404 - assert css.body.contains('body') - assert css.body.contains('html') + assert response.status_code == 200 + assert response.body == 'ok' +} - ilog('Ensure gitly is stopped') - os.execute('pkill -9 gitly') +fn get_repo_commit_count(token string, username string, repo_name string, branch_name string) int { + response := http.fetch( + method: .get + cookies: { + 'token': token + } + url: prepare_url('api/v1/$username/$repo_name/$branch_name/commits/count') + ) or { exit_with_message(err.str()) } + + response_json := json.decode(api.ApiCommitCount, response.body) or { + exit_with_message(err.str()) + } + + return response_json.result } -fn run_gitly() { - gitly_process := os.execute('./gitly &') - if gitly_process.exit_code != 0 { - exit_with_message(gitly_process.str()) +fn get_repo_issue_count(token string, username string, repo_name string) int { + response := http.fetch( + method: .get + cookies: { + 'token': token + } + url: prepare_url('api/v1/$username/$repo_name/issues/count') + ) or { exit_with_message(err.str()) } + + response_json := json.decode(api.ApiIssueCount, response.body) or { + exit_with_message(err.str()) } + + return response_json.result +} + +fn get_repo_branch_count(token string, username string, repo_name string) int { + response := http.fetch( + method: .get + cookies: { + 'token': token + } + url: prepare_url('api/v1/$username/$repo_name/branches/count') + ) or { exit_with_message(err.str()) } + + response_json := json.decode(api.ApiBranchCount, response.body) or { + exit_with_message(err.str()) + } + + return response_json.result } -- 2.39.5