httpd: Add api tests
Signed-off-by: xphoniex <dj.2dixx@gmail.com>
This commit is contained in:
parent
897d439f95
commit
7962ac0a8e
|
|
@ -2195,13 +2195,17 @@ dependencies = [
|
|||
"hyper",
|
||||
"lexopt",
|
||||
"radicle",
|
||||
"radicle-cli",
|
||||
"radicle-crypto",
|
||||
"radicle-surf",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"siwe",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-logfmt",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ use std::ffi::OsString;
|
|||
use anyhow::anyhow;
|
||||
|
||||
use radicle::crypto::ssh;
|
||||
use radicle::profile;
|
||||
use radicle::Profile;
|
||||
use radicle::{profile, Profile};
|
||||
|
||||
use crate::git;
|
||||
use crate::terminal as term;
|
||||
|
|
|
|||
|
|
@ -43,3 +43,10 @@ version = "0.2.0"
|
|||
git = "https://github.com/radicle-dev/radicle-git"
|
||||
features = ["serde"]
|
||||
rev = "79a94721366490053e2d8ac1c1afa14fb0c25f09"
|
||||
|
||||
[dev-dependencies]
|
||||
hyper = { version = "0.14.17", default-features = false, features = ["client"] }
|
||||
radicle-cli = { path = "../radicle-cli" }
|
||||
radicle-crypto = { path = "../radicle-crypto" }
|
||||
tempfile = { version = "3.3.0" }
|
||||
tower = { version = "0.4", features = ["util"] }
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ use radicle::Profile;
|
|||
mod auth;
|
||||
mod axum_extra;
|
||||
mod error;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod v1;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::{env, fs};
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::Router;
|
||||
use serde_json::Value;
|
||||
use tower::ServiceExt;
|
||||
|
||||
use radicle::cob::issue::Issues;
|
||||
use radicle::git::raw as git2;
|
||||
use radicle::storage::WriteStorage;
|
||||
use radicle_cli::commands::rad_init;
|
||||
use radicle_crypto::ssh::keystore::MemorySigner;
|
||||
use radicle_crypto::Signer;
|
||||
|
||||
use crate::api::Context;
|
||||
|
||||
pub const HEAD: &str = "1e978d19f251cd9821d9d9a76d1bd436bf0690d5";
|
||||
pub const HEAD_1: &str = "f604ce9fd5b7cc77b7609beda45ea8760bee78f7";
|
||||
|
||||
const PASSWORD: &str = "radicle";
|
||||
|
||||
pub fn seed(dir: &Path) -> Context {
|
||||
let workdir = dir.join("hello-world");
|
||||
let rad_home = dir.join("radicle");
|
||||
|
||||
env::set_var("RAD_PASSPHRASE", PASSWORD);
|
||||
env::set_var("RAD_DEBUG", "1");
|
||||
|
||||
fs::create_dir_all(&workdir).unwrap();
|
||||
fs::create_dir_all(&rad_home).unwrap();
|
||||
|
||||
// add commits to workdir (repo)
|
||||
let repo = git2::Repository::init(&workdir).unwrap();
|
||||
let tree =
|
||||
radicle::git::write_tree(Path::new("README"), "Hello World!\n".as_bytes(), &repo).unwrap();
|
||||
|
||||
let sig_time = git2::Time::new(1673001014, 0);
|
||||
let sig = git2::Signature::new("Alice Liddell", "alice@radicle.xyz", &sig_time).unwrap();
|
||||
|
||||
let oid = repo
|
||||
.commit(Some("HEAD"), &sig, &sig, "Initial commit\n", &tree, &[])
|
||||
.unwrap();
|
||||
let commit = repo.find_commit(oid).unwrap();
|
||||
|
||||
repo.checkout_tree(tree.as_object(), None).unwrap();
|
||||
|
||||
fs::create_dir(workdir.join("dir1")).unwrap();
|
||||
fs::write(
|
||||
workdir.join("dir1").join("README"),
|
||||
"Hello World from dir1!\n",
|
||||
)
|
||||
.unwrap();
|
||||
let mut index = repo.index().unwrap();
|
||||
index
|
||||
.add_all(["."], git2::IndexAddOption::DEFAULT, None)
|
||||
.unwrap();
|
||||
index.write().unwrap();
|
||||
|
||||
let oid = index.write_tree().unwrap();
|
||||
let tree = repo.find_tree(oid).unwrap();
|
||||
|
||||
repo.commit(
|
||||
Some("HEAD"),
|
||||
&sig,
|
||||
&sig,
|
||||
"Add another folder\n",
|
||||
&tree,
|
||||
&[&commit],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// eq. rad auth
|
||||
let profile = radicle::Profile::init(rad_home, PASSWORD.to_owned()).unwrap();
|
||||
|
||||
// rad init
|
||||
rad_init::init(
|
||||
rad_init::Options {
|
||||
path: Some(workdir.clone()),
|
||||
name: Some("hello-world".to_string()),
|
||||
description: Some("Rad repository for tests".to_string()),
|
||||
branch: None,
|
||||
interactive: false.into(),
|
||||
setup_signing: false,
|
||||
set_upstream: false,
|
||||
},
|
||||
&profile,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// eq. rad issue new
|
||||
env::set_var("RAD_COMMIT_TIME", "1673001014");
|
||||
|
||||
let signer = MemorySigner::load(&profile.keystore, PASSWORD.to_owned().into()).unwrap();
|
||||
let storage = &profile.storage;
|
||||
let (_, id) = radicle::rad::repo(&workdir).unwrap();
|
||||
let repo = storage.repository(id).unwrap();
|
||||
let mut issues = Issues::open(*signer.public_key(), &repo).unwrap();
|
||||
issues
|
||||
.create(
|
||||
"Issue #1".to_string(),
|
||||
"Change 'hello world' to 'hello everyone'".to_string(),
|
||||
&[],
|
||||
&signer,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Context {
|
||||
profile: Arc::new(profile),
|
||||
sessions: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn request(app: &Router, path: impl ToString) -> Response {
|
||||
Response(
|
||||
app.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri(path.to_string())
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
pub struct Response(axum::response::Response);
|
||||
|
||||
impl Response {
|
||||
pub async fn json(self) -> Value {
|
||||
let body = hyper::body::to_bytes(self.0.into_body()).await.unwrap();
|
||||
serde_json::from_slice(&body).unwrap()
|
||||
}
|
||||
|
||||
pub fn status(&self) -> axum::http::StatusCode {
|
||||
self.0.status()
|
||||
}
|
||||
}
|
||||
|
|
@ -62,3 +62,38 @@ async fn delegates_projects_handler(
|
|||
|
||||
Ok::<_, Error>(Json(projects))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod routes {
|
||||
use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::api::test::{self, request, HEAD};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delegates_projects() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
&app,
|
||||
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects",
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"head": HEAD,
|
||||
"patches": 0,
|
||||
"issues": 1,
|
||||
"id": "rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp"
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -467,3 +467,438 @@ fn stats(repo: &Repository, head: Oid) -> Result<Stats, Error> {
|
|||
contributors: contributors.len(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod routes {
|
||||
use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::api::test::{self, request, HEAD, HEAD_1};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"head": HEAD,
|
||||
"patches": 0,
|
||||
"issues": 1,
|
||||
"id": "rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp"
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"name": "hello-world",
|
||||
"description": "Rad repository for tests",
|
||||
"defaultBranch": "master",
|
||||
"head": HEAD,
|
||||
"patches": 0,
|
||||
"issues": 1,
|
||||
"id": "rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_commits_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/commits").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::FOUND);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"headers": [
|
||||
{
|
||||
"header": {
|
||||
"sha1": HEAD,
|
||||
"author": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"summary": "Add another folder",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"committerTime": 1673001014
|
||||
},
|
||||
"diff": {
|
||||
"added": [
|
||||
{
|
||||
"path": "dir1/README",
|
||||
"diff": {
|
||||
"type": "plain",
|
||||
"hunks": [
|
||||
{
|
||||
"header": "@@ -0,0 +1 @@\n",
|
||||
"lines": [
|
||||
{
|
||||
"line": "Hello World from dir1!\n",
|
||||
"lineNo": 1,
|
||||
"type": "addition"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"deleted": [],
|
||||
"moved": [],
|
||||
"copied": [],
|
||||
"modified": [],
|
||||
"stats": {
|
||||
"filesChanged": 1,
|
||||
"insertions": 1,
|
||||
"deletions": 0
|
||||
}
|
||||
},
|
||||
"branches": [
|
||||
"refs/heads/master"
|
||||
]
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"sha1": HEAD_1,
|
||||
"author": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"summary": "Initial commit",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"committerTime": 1673001014
|
||||
},
|
||||
"diff": {
|
||||
"added": [
|
||||
{
|
||||
"path": "README",
|
||||
"diff": {
|
||||
"type": "plain",
|
||||
"hunks": [
|
||||
{
|
||||
"header": "@@ -0,0 +1 @@\n",
|
||||
"lines": [
|
||||
{
|
||||
"line": "Hello World!\n",
|
||||
"lineNo": 1,
|
||||
"type": "addition"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"deleted": [],
|
||||
"moved": [],
|
||||
"copied": [],
|
||||
"modified": [],
|
||||
"stats": {
|
||||
"filesChanged": 1,
|
||||
"insertions": 1,
|
||||
"deletions": 0
|
||||
}
|
||||
},
|
||||
"branches": [
|
||||
"refs/heads/master"
|
||||
]
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"commits": 2,
|
||||
"branches": 1,
|
||||
"contributors": 1
|
||||
}
|
||||
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_commits() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/commits/{HEAD}"),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"header": {
|
||||
"sha1": HEAD,
|
||||
"author": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"summary": "Add another folder",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"committerTime": 1673001014
|
||||
},
|
||||
"diff": {
|
||||
"added": [
|
||||
{
|
||||
"path": "dir1/README",
|
||||
"diff": {
|
||||
"type": "plain",
|
||||
"hunks": [
|
||||
{
|
||||
"header": "@@ -0,0 +1 @@\n",
|
||||
"lines": [
|
||||
{
|
||||
"line": "Hello World from dir1!\n",
|
||||
"lineNo": 1,
|
||||
"type": "addition"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"deleted": [],
|
||||
"moved": [],
|
||||
"copied": [],
|
||||
"modified": [],
|
||||
"stats": {
|
||||
"filesChanged": 1,
|
||||
"insertions": 1,
|
||||
"deletions": 0
|
||||
}
|
||||
},
|
||||
"branches": [
|
||||
"refs/heads/master"
|
||||
]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_tree() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/tree/{HEAD}/"),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"entries": [
|
||||
{
|
||||
"path": "README",
|
||||
"name": "README",
|
||||
"lastCommit": null,
|
||||
"kind": "blob"
|
||||
},
|
||||
{
|
||||
"path": "dir1",
|
||||
"name": "dir1",
|
||||
"lastCommit": null,
|
||||
"kind": "tree"
|
||||
}
|
||||
],
|
||||
"lastCommit": {
|
||||
"sha1": HEAD,
|
||||
"author": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"summary": "Add another folder",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"committerTime": 1673001014
|
||||
},
|
||||
"name": "",
|
||||
"path": "",
|
||||
"stats": {
|
||||
"branches": 1,
|
||||
"commits": 2,
|
||||
"contributors": 1
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
let response = request(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/tree/{HEAD}/dir1"),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"entries": [
|
||||
{
|
||||
"path": "dir1/README",
|
||||
"name": "README",
|
||||
"lastCommit": null,
|
||||
"kind": "blob"
|
||||
}
|
||||
],
|
||||
"lastCommit": null,
|
||||
"name": "dir1",
|
||||
"path": "dir1",
|
||||
"stats": {
|
||||
"branches": 1,
|
||||
"commits": 2,
|
||||
"contributors": 1
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_remotes_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/remotes").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"heads": {
|
||||
"master": HEAD
|
||||
},
|
||||
"delegate": false
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_remotes() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/remotes/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"heads": {
|
||||
"master": HEAD
|
||||
},
|
||||
"delegate": false
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_blob() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/blob/{HEAD}/README"),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"binary": false,
|
||||
"content": "Hello World!\n",
|
||||
"lastCommit": {
|
||||
"sha1": HEAD_1,
|
||||
"author": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"summary": "Initial commit",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "Alice Liddell",
|
||||
"email": "alice@radicle.xyz"
|
||||
},
|
||||
"committerTime": 1673001014
|
||||
},
|
||||
"name": "README",
|
||||
"path": "README"
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_issues_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!([
|
||||
{
|
||||
"id": "458bbd9f6d47eed3d60cd905141687ad1f99251e",
|
||||
"author": {
|
||||
"id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
},
|
||||
"title": "Issue #1",
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"discussion": [
|
||||
{
|
||||
"author": {
|
||||
"id": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
},
|
||||
"body": "Change 'hello world' to 'hello everyone'",
|
||||
"reactions": [],
|
||||
"timestamp": 1673001014,
|
||||
"replyTo": null
|
||||
}
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,3 +22,31 @@ async fn stats_handler(Extension(ctx): Extension<Context>) -> impl IntoResponse
|
|||
json!({ "projects": { "count": projects }, "users": { "count": 0 } }),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod routes {
|
||||
use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::api::test::{self, request};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_stats() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/stats").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
response.json().await,
|
||||
json!({
|
||||
"projects": {
|
||||
"count": 1
|
||||
},
|
||||
"users": {
|
||||
"count": 0
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue