mod common;
use common::run_lua;
use wiremock::matchers::{header, method, path, query_param};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[tokio::test]
async fn test_require_gitlab() {
let script = r#"
local mod = require("assay.gitlab")
assert.not_nil(mod)
assert.not_nil(mod.client)
"#;
run_lua(script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_sub_objects_exist() {
let script = r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("http://localhost", { token = "test" })
assert.not_nil(c.projects)
assert.not_nil(c.files)
assert.not_nil(c.commits)
assert.not_nil(c.branches)
assert.not_nil(c.tags)
assert.not_nil(c.merge_requests)
assert.not_nil(c.pipelines)
assert.not_nil(c.jobs)
assert.not_nil(c.releases)
assert.not_nil(c.issues)
assert.not_nil(c.groups)
assert.not_nil(c.registry)
assert.not_nil(c.hooks)
assert.not_nil(c.users)
assert.not_nil(c.environments)
assert.not_nil(c.deploy_tokens)
"#;
run_lua(script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_projects_get() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42"))
.and(header("PRIVATE-TOKEN", "glpat-test"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"id": 42,
"name": "demo-project",
"default_branch": "main",
"web_url": "https://gitlab.example.com/demo/demo-project"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local p = c.projects:get(42)
assert.eq(p.id, 42)
assert.eq(p.name, "demo-project")
assert.eq(p.default_branch, "main")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_projects_get_404_returns_nil() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/999"))
.respond_with(ResponseTemplate::new(404).set_body_json(serde_json::json!({
"message": "404 Project Not Found"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local p = c.projects:get(999)
assert.eq(p, nil)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_files_raw() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/repository/files/src%2Fmain.lua/raw"))
.and(query_param("ref", "dev"))
.respond_with(ResponseTemplate::new(200).set_body_string("print(\"hello\")"))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local content = c.files:raw(42, "src/main.lua", {{ ref = "dev" }})
assert.eq(content, 'print("hello")')
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_commits_create() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/api/v4/projects/42/repository/commits"))
.and(header("PRIVATE-TOKEN", "glpat-test"))
.respond_with(ResponseTemplate::new(201).set_body_json(serde_json::json!({
"id": "abc123def456",
"short_id": "abc123d",
"title": "Update config files",
"message": "Update config files",
"author_name": "Automation"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local result = c.commits:create(42, {{
branch = "main",
commit_message = "Update config files",
actions = {{
{{ action = "update", file_path = "config.yaml", content = "key: value" }},
}},
}})
assert.eq(result.short_id, "abc123d")
assert.eq(result.title, "Update config files")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_commits_get() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/repository/commits/abc123"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"id": "abc123def456789",
"short_id": "abc123d",
"title": "Initial commit",
"author_name": "Demo User"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local commit = c.commits:get(42, "abc123")
assert.eq(commit.short_id, "abc123d")
assert.eq(commit.title, "Initial commit")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_branches_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/repository/branches"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "name": "main", "default": true, "protected": true },
{ "name": "dev", "default": false, "protected": false }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local branches = c.branches:list(42)
assert.eq(#branches, 2)
assert.eq(branches[1].name, "main")
assert.eq(branches[1].protected, true)
assert.eq(branches[2].name, "dev")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_branches_create() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/api/v4/projects/42/repository/branches"))
.respond_with(ResponseTemplate::new(201).set_body_json(serde_json::json!({
"name": "feat/new-feature",
"commit": { "id": "abc123", "short_id": "abc123" }
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local b = c.branches:create(42, {{ branch = "feat/new-feature", ref = "main" }})
assert.eq(b.name, "feat/new-feature")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_tags_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/repository/tags"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "name": "v1.0.0", "message": "First release" },
{ "name": "v0.9.0", "message": "Beta" }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local tags = c.tags:list(42)
assert.eq(#tags, 2)
assert.eq(tags[1].name, "v1.0.0")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_merge_requests_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/merge_requests"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "iid": 1, "title": "Add feature", "state": "opened", "author": { "username": "alice" } },
{ "iid": 2, "title": "Fix bug", "state": "merged", "author": { "username": "bob" } }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local mrs = c.merge_requests:list(42)
assert.eq(#mrs, 2)
assert.eq(mrs[1].iid, 1)
assert.eq(mrs[1].title, "Add feature")
assert.eq(mrs[2].author.username, "bob")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_merge_requests_create() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/api/v4/projects/42/merge_requests"))
.respond_with(ResponseTemplate::new(201).set_body_json(serde_json::json!({
"iid": 10,
"title": "New feature",
"state": "opened",
"web_url": "https://gitlab.example.com/demo/project/-/merge_requests/10"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local mr = c.merge_requests:create(42, {{
source_branch = "feat/new-feature",
target_branch = "main",
title = "New feature",
}})
assert.eq(mr.iid, 10)
assert.eq(mr.title, "New feature")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_merge_requests_merge() {
let server = MockServer::start().await;
Mock::given(method("PUT"))
.and(path("/api/v4/projects/42/merge_requests/10/merge"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"iid": 10,
"state": "merged",
"merge_commit_sha": "abc123def456"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local result = c.merge_requests:merge(42, 10, {{ squash = true }})
assert.eq(result.state, "merged")
assert.eq(result.merge_commit_sha, "abc123def456")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_pipelines_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/pipelines"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "id": 100, "status": "success", "ref": "main", "sha": "abc123" },
{ "id": 101, "status": "running", "ref": "dev", "sha": "def456" }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local pipes = c.pipelines:list(42)
assert.eq(#pipes, 2)
assert.eq(pipes[1].id, 100)
assert.eq(pipes[1].status, "success")
assert.eq(pipes[2].status, "running")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_pipelines_create() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/api/v4/projects/42/pipeline"))
.respond_with(ResponseTemplate::new(201).set_body_json(serde_json::json!({
"id": 200,
"status": "pending",
"ref": "main"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local pipe = c.pipelines:create(42, {{ ref = "main" }})
assert.eq(pipe.id, 200)
assert.eq(pipe.status, "pending")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_pipelines_jobs() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/pipelines/100/jobs"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "id": 500, "name": "build", "status": "success", "stage": "build" },
{ "id": 501, "name": "test", "status": "failed", "stage": "test" }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local jobs = c.pipelines:jobs(42, 100)
assert.eq(#jobs, 2)
assert.eq(jobs[1].name, "build")
assert.eq(jobs[2].status, "failed")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_issues_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/issues"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "iid": 1, "title": "Bug report", "state": "opened", "labels": ["bug"] },
{ "iid": 2, "title": "Feature request", "state": "closed", "labels": ["enhancement"] }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local issues = c.issues:list(42)
assert.eq(#issues, 2)
assert.eq(issues[1].iid, 1)
assert.eq(issues[1].labels[1], "bug")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_issues_create() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/api/v4/projects/42/issues"))
.respond_with(ResponseTemplate::new(201).set_body_json(serde_json::json!({
"iid": 5,
"title": "New issue",
"state": "opened"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local issue = c.issues:create(42, {{
title = "New issue",
description = "Something to track",
}})
assert.eq(issue.iid, 5)
assert.eq(issue.state, "opened")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_releases_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/releases"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "tag_name": "v1.0.0", "name": "Version 1.0.0" },
{ "tag_name": "v0.9.0", "name": "Version 0.9.0" }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local releases = c.releases:list(42)
assert.eq(#releases, 2)
assert.eq(releases[1].tag_name, "v1.0.0")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_groups_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/groups"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "id": 10, "name": "demo-group", "full_path": "demo-group" },
{ "id": 11, "name": "infra", "full_path": "demo-group/infra" }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local groups = c.groups:list()
assert.eq(#groups, 2)
assert.eq(groups[1].name, "demo-group")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_registry_repositories() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/registry/repositories"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "id": 1, "name": "", "path": "demo-group/demo-project" },
{ "id": 2, "name": "api", "path": "demo-group/demo-project/api" }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local repos = c.registry:repositories(42)
assert.eq(#repos, 2)
assert.eq(repos[2].name, "api")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_users_current() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/user"))
.and(header("PRIVATE-TOKEN", "glpat-test"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"id": 1,
"username": "demo-user",
"email": "user@example.com"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local user = c.users:current()
assert.eq(user.username, "demo-user")
assert.eq(user.email, "user@example.com")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_oauth_token_auth() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/user"))
.and(header("Authorization", "Bearer oauth-test-token"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"id": 1,
"username": "oauth-user"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ oauth_token = "oauth-test-token" }})
local user = c.users:current()
assert.eq(user.username, "oauth-user")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_repository_compare() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/repository/compare"))
.and(query_param("from", "main"))
.and(query_param("to", "dev"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"commits": [
{ "short_id": "abc123", "title": "feat: add feature" }
],
"diffs": [
{ "old_path": "README.md", "new_path": "README.md" }
]
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local diff = c.repository:compare(42, "main", "dev")
assert.eq(#diff.commits, 1)
assert.eq(diff.commits[1].title, "feat: add feature")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_hooks_list() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42/hooks"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{ "id": 1, "url": "https://hooks.example.com/push", "push_events": true },
{ "id": 2, "url": "https://hooks.example.com/mr", "merge_requests_events": true }
])))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "glpat-test" }})
local hooks = c.hooks:list(42)
assert.eq(#hooks, 2)
assert.eq(hooks[1].push_events, true)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_gitlab_error_propagation() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/v4/projects/42"))
.respond_with(ResponseTemplate::new(403).set_body_json(serde_json::json!({
"message": "403 Forbidden"
})))
.mount(&server)
.await;
let script = format!(
r#"
local gitlab = require("assay.gitlab")
local c = gitlab.client("{}", {{ token = "bad-token" }})
local ok, err = pcall(function() c.projects:get(42) end)
assert.eq(ok, false)
assert.contains(tostring(err), "HTTP 403")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}