Fixes found through integration test crate
This commit is contained in:
parent
b53e06ef55
commit
f17a214865
12 changed files with 815 additions and 63 deletions
|
|
@ -4,4 +4,8 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
progress = "0.2.0"
|
||||
szurubooru-client = { version = "0.1.0", path = "../szurubooru-client" }
|
||||
tokio = { version = "1.39.2", features = ["full", "test-util", "tracing"] }
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
|
|
|||
|
|
@ -15,15 +15,15 @@ services:
|
|||
## or FQDNs/IP addresses if these services are running outside of Docker
|
||||
POSTGRES_HOST: sql
|
||||
## Credentials for database:
|
||||
POSTGRES_USER:
|
||||
POSTGRES_PASSWORD:
|
||||
POSTGRES_USER: pguser
|
||||
POSTGRES_PASSWORD: pgpassword
|
||||
## Commented Values are Default:
|
||||
#POSTGRES_DB: defaults to same as POSTGRES_USER
|
||||
#POSTGRES_PORT: 5432
|
||||
#LOG_SQL: 0 (1 for verbose SQL logs)
|
||||
THREADS:
|
||||
volumes:
|
||||
- "${MOUNT_DATA}:/data"
|
||||
- "sz-data:/data"
|
||||
- "${SERVER_MOUNT}/config.yaml:/opt/app/config.yaml"
|
||||
|
||||
client:
|
||||
|
|
@ -34,7 +34,7 @@ services:
|
|||
BACKEND_HOST: server
|
||||
BASE_URL: ${BASE_URL}
|
||||
volumes:
|
||||
- "${MOUNT_DATA}:/data:ro"
|
||||
- "sz-data:/data:ro"
|
||||
ports:
|
||||
- "${PORT}:80"
|
||||
|
||||
|
|
@ -42,7 +42,10 @@ services:
|
|||
image: postgres:11-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER:
|
||||
POSTGRES_PASSWORD:
|
||||
volumes:
|
||||
- "${MOUNT_SQL}:/var/lib/postgresql/data"
|
||||
POSTGRES_USER: pguser
|
||||
POSTGRES_PASSWORD: pgpassword
|
||||
#volumes:
|
||||
# - "${MOUNT_SQL}/sqldata:/var/lib/postgresql/data"
|
||||
|
||||
volumes:
|
||||
sz-data:
|
||||
|
|
|
|||
BIN
szurubooru-integration-test/folly.jpg
Normal file
BIN
szurubooru-integration-test/folly.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
286
szurubooru-integration-test/src/main.rs
Normal file
286
szurubooru-integration-test/src/main.rs
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
use std::error::Error;
|
||||
use std::process::Stdio;
|
||||
use std::time::Duration;
|
||||
use szurubooru_client::models::*;
|
||||
use szurubooru_client::*;
|
||||
use tokio::process::Command;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing::{error, info, instrument};
|
||||
use tracing_subscriber::filter::EnvFilter;
|
||||
use tracing_subscriber::fmt::format::FmtSpan;
|
||||
|
||||
#[tokio::main]
|
||||
#[tracing::instrument]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let ev = EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy();
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
//.with_span_events(FmtSpan::ACTIVE)
|
||||
.with_target(false)
|
||||
.with_level(false)
|
||||
.with_env_filter(ev)
|
||||
.init();
|
||||
|
||||
//info!("Starting Szurubooru instance...");
|
||||
let anon_client = start_instance().await;
|
||||
|
||||
let create_user = CreateUpdateUserBuilder::default()
|
||||
.name("integration_user")
|
||||
.password("integration_password")
|
||||
.rank(UserRank::Administrator)
|
||||
.avatar_style(UserAvatarStyle::Gravatar)
|
||||
.build()
|
||||
.expect("Unable to create CreateUser object");
|
||||
let _user = anon_client
|
||||
.request()
|
||||
.create_user(&create_user)
|
||||
.await
|
||||
.expect("Error creating user");
|
||||
|
||||
let auth_client = SzurubooruClient::new_with_basic_auth(
|
||||
"http://localhost:9801",
|
||||
"integration_user",
|
||||
"integration_password",
|
||||
true,
|
||||
)?;
|
||||
|
||||
test_tag_categories(&auth_client).await;
|
||||
test_tags(&auth_client).await;
|
||||
|
||||
Command::new("sh")
|
||||
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
||||
.arg("-c")
|
||||
.arg("./stop_szurubooru.sh")
|
||||
.stderr(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stdin(Stdio::null())
|
||||
.status()
|
||||
.await
|
||||
.expect("Failed to stop szurubooru");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
async fn start_instance() -> SzurubooruClient {
|
||||
info!("Starting Szurubooru instance...");
|
||||
let anon_client = SzurubooruClient::new_anonymous("http://localhost:9801", true)
|
||||
.expect("Can't create anonymous client");
|
||||
|
||||
Command::new("sh")
|
||||
.current_dir(env!("CARGO_MANIFEST_DIR"))
|
||||
.arg("-c")
|
||||
.arg("./start_szurubooru.sh")
|
||||
.stderr(Stdio::null())
|
||||
.stdout(Stdio::null())
|
||||
.stdin(Stdio::null())
|
||||
.status()
|
||||
.await
|
||||
.expect("Failed to start szurubooru");
|
||||
|
||||
let mut connected = false;
|
||||
let mut error = None;
|
||||
|
||||
for _ in 0..5 {
|
||||
let info = anon_client.request().get_global_info().await;
|
||||
if info.is_ok() {
|
||||
connected = true;
|
||||
break;
|
||||
} else if let Err(e) = info {
|
||||
error = Some(e);
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
if !connected {
|
||||
panic!(
|
||||
"{}",
|
||||
format!("Unable to connect to instance. Last error: {error:?}")
|
||||
);
|
||||
}
|
||||
|
||||
anon_client
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(client), level = "INFO")]
|
||||
async fn test_tag_categories(client: &SzurubooruClient) {
|
||||
info!("Testing tag categories");
|
||||
let tag_cats = client
|
||||
.request()
|
||||
.list_tag_categories()
|
||||
.await
|
||||
.expect("Could not list tag categories");
|
||||
assert_eq!(tag_cats.results.len(), 1);
|
||||
|
||||
info!("Creating tag category");
|
||||
let create_tag_cat = CreateUpdateTagCategoryBuilder::default()
|
||||
.name("my_tag_cat".to_string())
|
||||
.color("purple".to_string())
|
||||
.order(1)
|
||||
.build()
|
||||
.expect("Unable to build create_tag_cat object");
|
||||
let result_tag_cat = client
|
||||
.request()
|
||||
.create_tag_category(&create_tag_cat)
|
||||
.await
|
||||
.expect("Unable to create tag category");
|
||||
assert_eq!(result_tag_cat.name, Some("my_tag_cat".to_string()));
|
||||
let tag_cats = client
|
||||
.request()
|
||||
.list_tag_categories()
|
||||
.await
|
||||
.expect("Could not list tag categories again");
|
||||
assert_eq!(tag_cats.results.len(), 2);
|
||||
|
||||
info!("GETting tag category");
|
||||
let tag_res = client
|
||||
.request()
|
||||
.get_tag_category("my_tag_cat")
|
||||
.await
|
||||
.expect("Could not fetch tag category");
|
||||
assert_eq!(result_tag_cat.color, tag_res.color);
|
||||
|
||||
info!("Updating tag category");
|
||||
let update_tag_cat = CreateUpdateTagCategoryBuilder::default()
|
||||
.version(tag_res.version)
|
||||
.color("red".to_string())
|
||||
.build()
|
||||
.expect("Could not create update for tag category");
|
||||
let update_res = client
|
||||
.request()
|
||||
.update_tag_category("my_tag_cat", &update_tag_cat)
|
||||
.await
|
||||
.expect("Unable to update tag category");
|
||||
assert_eq!(update_res.color, Some("red".to_string()));
|
||||
|
||||
info!("Deleting tag category");
|
||||
client
|
||||
.request()
|
||||
.delete_tag_category("my_tag_cat", update_res.version)
|
||||
.await
|
||||
.expect("Could not delete tag category");
|
||||
let tag_cats = client
|
||||
.request()
|
||||
.list_tag_categories()
|
||||
.await
|
||||
.expect("Could not list tag categories again");
|
||||
assert_eq!(tag_cats.results.len(), 1);
|
||||
}
|
||||
|
||||
#[instrument(skip(client))]
|
||||
async fn test_tags(client: &SzurubooruClient) {
|
||||
info!("Testing tag functions");
|
||||
|
||||
info!("Listing tags");
|
||||
let tag_list = client
|
||||
.request()
|
||||
.list_tags(None)
|
||||
.await
|
||||
.expect("Could not list tags");
|
||||
assert_eq!(tag_list.total, 0);
|
||||
|
||||
info!("Creating tag");
|
||||
let cutag = CreateUpdateTagBuilder::default()
|
||||
.names(vec!["foo".to_string(), "foo2".to_string()])
|
||||
.category("default".to_string())
|
||||
.description("The foo tag".to_string())
|
||||
.build()
|
||||
.expect("Could not build the CreateUpdateTag");
|
||||
let tag_res = client
|
||||
.request()
|
||||
.create_tag(&cutag)
|
||||
.await
|
||||
.expect("Could not create tag");
|
||||
|
||||
assert_eq!(
|
||||
tag_res.names,
|
||||
Some(vec!["foo".to_string(), "foo2".to_string()])
|
||||
);
|
||||
assert!(tag_res.description.is_some());
|
||||
|
||||
let tag_list = client
|
||||
.request()
|
||||
.list_tags(None)
|
||||
.await
|
||||
.expect("Could not list tags");
|
||||
assert_eq!(tag_list.total, 1);
|
||||
|
||||
info!("Testing field selection");
|
||||
let tag_list = client
|
||||
.with_fields(vec!["version", "names", "category"])
|
||||
.list_tags(None)
|
||||
.await
|
||||
.expect("Could not list tags");
|
||||
assert_eq!(tag_list.total, 1);
|
||||
assert!(tag_list.results.first().unwrap().description.is_none());
|
||||
|
||||
info!("Updating tag");
|
||||
let utag = CreateUpdateTagBuilder::default()
|
||||
.version(tag_res.version)
|
||||
.description("The foo2 tag".to_string())
|
||||
.build()
|
||||
.expect("Could not build the CreateUpdateTag");
|
||||
let tag_res2 = client
|
||||
.request()
|
||||
.update_tag("foo", &utag)
|
||||
.await
|
||||
.expect("Could not update tag");
|
||||
assert_ne!(tag_res.description, tag_res2.description);
|
||||
|
||||
info!("Getting tag");
|
||||
let tag_res3 = client
|
||||
.request()
|
||||
.get_tag("foo")
|
||||
.await
|
||||
.expect("Could not fetch tag");
|
||||
assert_eq!(tag_res2.description, tag_res3.description);
|
||||
|
||||
info!("Creating a second tag");
|
||||
let cutag = CreateUpdateTagBuilder::default()
|
||||
.names(vec!["bar".to_string()])
|
||||
.category("default".to_string())
|
||||
.description("The foo tag".to_string())
|
||||
.build()
|
||||
.expect("Could not build the CreateUpdateTag");
|
||||
let bar_tag = client
|
||||
.request()
|
||||
.create_tag(&cutag)
|
||||
.await
|
||||
.expect("Unable to create second tag");
|
||||
let tag_list = client
|
||||
.request()
|
||||
.list_tags(None)
|
||||
.await
|
||||
.expect("Could not list tags");
|
||||
assert_eq!(tag_list.total, 2);
|
||||
|
||||
info!("Merging tags");
|
||||
let merge_tag = MergeTagsBuilder::default()
|
||||
.remove_version(bar_tag.version)
|
||||
.remove(bar_tag.names.as_ref().unwrap().first().unwrap())
|
||||
.merge_to_version(tag_res3.version)
|
||||
.merge_to(tag_res3.names.as_ref().unwrap().first().unwrap())
|
||||
.build()
|
||||
.expect("Could not create merge tags object");
|
||||
let merged_tag = client
|
||||
.request()
|
||||
.merge_tag(&merge_tag)
|
||||
.await
|
||||
.expect("Could not merge tags");
|
||||
assert_eq!(tag_res3.names, merged_tag.names);
|
||||
let tag_list = client
|
||||
.request()
|
||||
.list_tags(None)
|
||||
.await
|
||||
.expect("Could not list tags");
|
||||
assert_eq!(tag_list.total, 1);
|
||||
|
||||
info!("Deleting tag");
|
||||
client
|
||||
.request()
|
||||
.delete_tag("foo", merged_tag.version)
|
||||
.await
|
||||
.expect("Could not delete tag");
|
||||
}
|
||||
|
||||
//TODO: test Tag siblings once we've tested a post
|
||||
12
szurubooru-integration-test/start_szurubooru.sh
Normal file → Executable file
12
szurubooru-integration-test/start_szurubooru.sh
Normal file → Executable file
|
|
@ -1,9 +1,11 @@
|
|||
#!/bin/bash
|
||||
|
||||
export MOUNT_DATA=szurubooru/data
|
||||
export SERVER_MOUNT=szurubooru/server
|
||||
export MOUNT_SQL=szurubooru/pgsql
|
||||
export BASE_URL=http://localhost:5000
|
||||
export PORT=5000
|
||||
#export MOUNT_DATA=szurubooru/data
|
||||
export SERVER_MOUNT=./szurubooru/server
|
||||
#export MOUNT_SQL=szurubooru/pgsql
|
||||
export BASE_URL=http://localhost:9801
|
||||
export PORT=9801
|
||||
|
||||
docker compose down || true
|
||||
|
||||
docker compose up -d
|
||||
|
|
|
|||
3
szurubooru-integration-test/stop_szurubooru.sh
Executable file
3
szurubooru-integration-test/stop_szurubooru.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
docker compose down
|
||||
179
szurubooru-integration-test/szurubooru/server/config.yaml
Normal file
179
szurubooru-integration-test/szurubooru/server/config.yaml
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
# rather than editing this file, it is strongly suggested to create config.yaml
|
||||
# and override only what you need.
|
||||
|
||||
# shown in the website title and on the front page
|
||||
name: integrationland
|
||||
# full url to the homepage of this szurubooru site, with no trailing slash
|
||||
domain: https://localhost:9801
|
||||
# used to salt the users' password hashes and generate filenames for static content
|
||||
secret: awdopk1mdqsapdoawd21!!!#asw
|
||||
|
||||
# Delete thumbnails and source files on post delete
|
||||
# Original functionality is no, to mitigate the impacts of admins going
|
||||
# on unchecked post purges.
|
||||
delete_source_files: no
|
||||
|
||||
thumbnails:
|
||||
avatar_width: 300
|
||||
avatar_height: 300
|
||||
post_width: 300
|
||||
post_height: 300
|
||||
|
||||
# settings used to download files from the web on behalf of the api users
|
||||
user_agent:
|
||||
max_dl_filesize: 25.0E+6 # maximum filesize limit in bytes
|
||||
|
||||
# automatically convert animated GIF uploads to video formats
|
||||
convert:
|
||||
gif:
|
||||
to_webm: false
|
||||
to_mp4: false
|
||||
|
||||
# allow posts to be uploaded even if some image processing errors occur
|
||||
allow_broken_uploads: false
|
||||
|
||||
# used to send password reset e-mails
|
||||
smtp:
|
||||
host: # example: localhost
|
||||
port: # example: 25
|
||||
user: # example: bot
|
||||
pass: # example: groovy123
|
||||
from: # example: noreply@example.com
|
||||
# if host is left empty the password reset feature will be disabled,
|
||||
# in which case it is recommended to fill contactEmail so that users
|
||||
# know who to contact when they want to reset their password
|
||||
|
||||
contact_email: # example: bob@example.com. Meant for manual password reset procedures
|
||||
|
||||
enable_safety: yes
|
||||
|
||||
tag_name_regex: ^\S+$
|
||||
tag_category_name_regex: ^[^\s%+#/]+$
|
||||
|
||||
pool_name_regex: ^\S+$
|
||||
pool_category_name_regex: ^[^\s%+#/]+$
|
||||
|
||||
# don't make these more restrictive unless you want to annoy people; if you do
|
||||
# customize them, make sure to update the instructions in the registration form
|
||||
# template as well.
|
||||
password_regex: '^.{5,}$'
|
||||
user_name_regex: '^[a-zA-Z0-9_-]{1,32}$'
|
||||
|
||||
# webhooks to call when events occur (such as post/tag/user/etc. changes)
|
||||
# the listed urls will be called with a HTTP POST request with a payload
|
||||
# containing a snapshot resource as JSON. See doc/API.md for details
|
||||
webhooks:
|
||||
# - https://api.example.com/webhooks/
|
||||
|
||||
default_rank: regular
|
||||
|
||||
privileges:
|
||||
'users:create:self': anonymous # Registration permission
|
||||
'users:create:any': administrator
|
||||
'users:list': regular
|
||||
'users:view': regular
|
||||
'users:edit:any:name': moderator
|
||||
'users:edit:any:pass': moderator
|
||||
'users:edit:any:email': moderator
|
||||
'users:edit:any:avatar': moderator
|
||||
'users:edit:any:rank': moderator
|
||||
'users:edit:self:name': regular
|
||||
'users:edit:self:pass': regular
|
||||
'users:edit:self:email': regular
|
||||
'users:edit:self:avatar': regular
|
||||
'users:edit:self:rank': moderator # one can't promote themselves or anyone to upper rank than their own.
|
||||
'users:delete:any': administrator
|
||||
'users:delete:self': regular
|
||||
|
||||
'user_tokens:list:any': administrator
|
||||
'user_tokens:list:self': regular
|
||||
'user_tokens:create:any': administrator
|
||||
'user_tokens:create:self': regular
|
||||
'user_tokens:edit:any': administrator
|
||||
'user_tokens:edit:self': regular
|
||||
'user_tokens:delete:any': administrator
|
||||
'user_tokens:delete:self': regular
|
||||
|
||||
'posts:create:anonymous': regular
|
||||
'posts:create:identified': regular
|
||||
'posts:list': anonymous
|
||||
'posts:reverse_search': regular
|
||||
'posts:view': anonymous
|
||||
'posts:view:featured': anonymous
|
||||
'posts:edit:content': power
|
||||
'posts:edit:flags': regular
|
||||
'posts:edit:notes': regular
|
||||
'posts:edit:relations': regular
|
||||
'posts:edit:safety': power
|
||||
'posts:edit:source': regular
|
||||
'posts:edit:tags': regular
|
||||
'posts:edit:thumbnail': power
|
||||
'posts:feature': moderator
|
||||
'posts:delete': moderator
|
||||
'posts:score': regular
|
||||
'posts:merge': moderator
|
||||
'posts:favorite': regular
|
||||
'posts:bulk-edit:tags': power
|
||||
'posts:bulk-edit:safety': power
|
||||
'posts:bulk-edit:delete': power
|
||||
|
||||
'tags:create': regular
|
||||
'tags:edit:names': power
|
||||
'tags:edit:category': power
|
||||
'tags:edit:description': power
|
||||
'tags:edit:implications': power
|
||||
'tags:edit:suggestions': power
|
||||
'tags:list': regular
|
||||
'tags:view': anonymous
|
||||
'tags:merge': moderator
|
||||
'tags:delete': moderator
|
||||
|
||||
'tag_categories:create': moderator
|
||||
'tag_categories:edit:name': moderator
|
||||
'tag_categories:edit:color': moderator
|
||||
'tag_categories:edit:order': moderator
|
||||
'tag_categories:list': anonymous
|
||||
'tag_categories:view': anonymous
|
||||
'tag_categories:delete': moderator
|
||||
'tag_categories:set_default': moderator
|
||||
|
||||
'pools:create': regular
|
||||
'pools:edit:names': power
|
||||
'pools:edit:category': power
|
||||
'pools:edit:description': power
|
||||
'pools:edit:posts': power
|
||||
'pools:list': regular
|
||||
'pools:view': anonymous
|
||||
'pools:merge': moderator
|
||||
'pools:delete': moderator
|
||||
|
||||
'pool_categories:create': moderator
|
||||
'pool_categories:edit:name': moderator
|
||||
'pool_categories:edit:color': moderator
|
||||
'pool_categories:list': anonymous
|
||||
'pool_categories:view': anonymous
|
||||
'pool_categories:delete': moderator
|
||||
'pool_categories:set_default': moderator
|
||||
|
||||
'comments:create': regular
|
||||
'comments:delete:any': moderator
|
||||
'comments:delete:own': regular
|
||||
'comments:edit:any': moderator
|
||||
'comments:edit:own': regular
|
||||
'comments:list': regular
|
||||
'comments:view': regular
|
||||
'comments:score': regular
|
||||
|
||||
'snapshots:list': power
|
||||
|
||||
'uploads:create': regular
|
||||
'uploads:use_downloader': power
|
||||
|
||||
## ONLY SET THESE IF DEPLOYING OUTSIDE OF DOCKER
|
||||
#debug: 0 # generate server logs?
|
||||
#show_sql: 0 # show sql in server logs?
|
||||
#data_url: /data/
|
||||
#data_dir: /var/www/data
|
||||
## usage: schema://user:password@host:port/database_name
|
||||
## example: postgres://szuru:dog@localhost:5432/szuru_test
|
||||
#database:
|
||||
Loading…
Add table
Add a link
Reference in a new issue