mod common;
use common::run_lua;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[tokio::test]
async fn test_traefik_overview() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/overview"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"http": {
"routers": {"total": 10, "warnings": 1, "errors": 0},
"services": {"total": 8, "warnings": 0, "errors": 0},
"middlewares": {"total": 5, "warnings": 0, "errors": 0}
},
"tcp": {
"routers": {"total": 2, "warnings": 0, "errors": 0},
"services": {"total": 2, "warnings": 0, "errors": 0}
},
"features": {
"tracing": "",
"metrics": "",
"accessLog": false
}
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local ov = c.info:overview()
assert.eq(ov.http.routers.total, 10)
assert.eq(ov.http.routers.warnings, 1)
assert.eq(ov.http.services.total, 8)
assert.eq(ov.tcp.routers.total, 2)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_version() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/version"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"Version": "3.2.0",
"Codename": "picodon",
"startDate": "2026-02-10T00:00:00Z"
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local ver = c.info:version()
assert.eq(ver.Version, "3.2.0")
assert.eq(ver.Codename, "picodon")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_entrypoints() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/entrypoints"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{
"name": "web",
"address": ":80",
"transport": {"lifeCycle": {"graceTimeOut": "10s"}}
},
{
"name": "websecure",
"address": ":443",
"transport": {"lifeCycle": {"graceTimeOut": "10s"}}
}
])))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local eps = c.entrypoints:list()
assert.eq(#eps, 2)
assert.eq(eps[1].name, "web")
assert.eq(eps[1].address, ":80")
assert.eq(eps[2].name, "websecure")
assert.eq(eps[2].address, ":443")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_http_routers() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/routers"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{
"name": "dashboard@internal",
"rule": "PathPrefix(`/api`) || PathPrefix(`/dashboard`)",
"service": "api@internal",
"status": "enabled",
"entryPoints": ["traefik"]
},
{
"name": "my-app@docker",
"rule": "Host(`app.example.com`)",
"service": "my-app@docker",
"status": "enabled",
"entryPoints": ["websecure"],
"tls": {}
}
])))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local routers = c.routers:list()
assert.eq(#routers, 2)
assert.eq(routers[1].name, "dashboard@internal")
assert.eq(routers[1].status, "enabled")
assert.eq(routers[2].name, "my-app@docker")
assert.eq(routers[2].rule, "Host(`app.example.com`)")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_http_router_single() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/routers/my-app@docker"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"name": "my-app@docker",
"rule": "Host(`app.example.com`)",
"service": "my-app@docker",
"status": "enabled",
"entryPoints": ["websecure"],
"middlewares": ["auth@docker", "ratelimit@docker"],
"tls": {"certResolver": "letsencrypt"}
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local router = c.routers:get("my-app@docker")
assert.eq(router.name, "my-app@docker")
assert.eq(router.status, "enabled")
assert.eq(router.service, "my-app@docker")
assert.eq(#router.middlewares, 2)
assert.eq(router.middlewares[1], "auth@docker")
assert.eq(router.tls.certResolver, "letsencrypt")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_http_services() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/services"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{
"name": "my-app@docker",
"type": "loadbalancer",
"status": "enabled",
"loadBalancer": {
"servers": [
{"url": "http://10.0.0.1:8080"},
{"url": "http://10.0.0.2:8080"}
],
"passHostHeader": true
}
},
{
"name": "api@internal",
"type": "loadbalancer",
"status": "enabled"
}
])))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local services = c.services:list()
assert.eq(#services, 2)
assert.eq(services[1].name, "my-app@docker")
assert.eq(services[1].type, "loadbalancer")
assert.eq(#services[1].loadBalancer.servers, 2)
assert.eq(services[1].loadBalancer.servers[1].url, "http://10.0.0.1:8080")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_http_service_single() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/services/my-app@docker"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"name": "my-app@docker",
"type": "loadbalancer",
"status": "enabled",
"loadBalancer": {
"servers": [
{"url": "http://10.0.0.1:8080"},
{"url": "http://10.0.0.2:8080"},
{"url": "http://10.0.0.3:8080"}
],
"passHostHeader": true,
"healthCheck": {
"path": "/health",
"interval": "10s",
"timeout": "3s"
}
},
"serverStatus": {
"http://10.0.0.1:8080": "UP",
"http://10.0.0.2:8080": "UP",
"http://10.0.0.3:8080": "UP"
}
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local svc = c.services:get("my-app@docker")
assert.eq(svc.name, "my-app@docker")
assert.eq(svc.status, "enabled")
assert.eq(#svc.loadBalancer.servers, 3)
assert.eq(svc.loadBalancer.healthCheck.path, "/health")
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_http_middlewares() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/middlewares"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{
"name": "auth@docker",
"type": "forwardauth",
"status": "enabled",
"forwardAuth": {
"address": "http://auth-service:4181",
"trustForwardHeader": true
}
},
{
"name": "ratelimit@docker",
"type": "ratelimit",
"status": "enabled",
"rateLimit": {
"average": 100,
"burst": 50
}
}
])))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local mws = c.middlewares:list()
assert.eq(#mws, 2)
assert.eq(mws[1].name, "auth@docker")
assert.eq(mws[1].type, "forwardauth")
assert.eq(mws[2].name, "ratelimit@docker")
assert.eq(mws[2].rateLimit.average, 100)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_is_router_enabled_true() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/routers/my-app@docker"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"name": "my-app@docker",
"rule": "Host(`app.example.com`)",
"service": "my-app@docker",
"status": "enabled"
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local enabled = c.routers:is_enabled("my-app@docker")
assert.eq(enabled, true)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_is_router_enabled_false() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/routers/broken@docker"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"name": "broken@docker",
"rule": "Host(`broken.example.com`)",
"service": "broken@docker",
"status": "disabled"
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local enabled = c.routers:is_enabled("broken@docker")
assert.eq(enabled, false)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_router_has_tls_true() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/routers/secure@docker"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"name": "secure@docker",
"rule": "Host(`secure.example.com`)",
"service": "secure@docker",
"status": "enabled",
"tls": {"certResolver": "letsencrypt"}
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local has_tls = c.routers:has_tls("secure@docker")
assert.eq(has_tls, true)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_router_has_tls_false() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/routers/plain@docker"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"name": "plain@docker",
"rule": "Host(`plain.example.com`)",
"service": "plain@docker",
"status": "enabled"
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local has_tls = c.routers:has_tls("plain@docker")
assert.eq(has_tls, false)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_service_server_count() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/services/my-app@docker"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"name": "my-app@docker",
"type": "loadbalancer",
"status": "enabled",
"loadBalancer": {
"servers": [
{"url": "http://10.0.0.1:8080"},
{"url": "http://10.0.0.2:8080"},
{"url": "http://10.0.0.3:8080"}
]
}
})))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local count = c.services:server_count("my-app@docker")
assert.eq(count, 3)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}
#[tokio::test]
async fn test_traefik_healthy_routers() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api/http/routers"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{"name": "app1@docker", "rule": "Host(`a`)", "status": "enabled"},
{"name": "app2@docker", "rule": "Host(`b`)", "status": "enabled"},
{"name": "app3@docker", "rule": "Host(`c`)", "status": "enabled"},
{"name": "broken@docker", "rule": "Host(`d`)", "status": "disabled"},
{"name": "error@docker", "rule": "Host(`e`)", "status": "warning"}
])))
.mount(&server)
.await;
let script = format!(
r#"
local traefik = require("assay.traefik")
local c = traefik.client("{}")
local enabled, errored = c.routers:healthy()
assert.eq(enabled, 3)
assert.eq(errored, 2)
"#,
server.uri()
);
run_lua(&script).await.unwrap();
}