mirror of
https://github.com/getnora-io/nora.git
synced 2026-04-12 17:20:33 +00:00
test: add comprehensive unit tests for storage and auth
- LocalStorage tests: put/get, list, stat, health_check, nested dirs - S3Storage tests with wiremock HTTP mocking - Auth/htpasswd tests: loading, validation, public paths - Token lifecycle tests: create, verify, expire, revoke Total: 75 tests passing
This commit is contained in:
@@ -129,3 +129,112 @@ impl StorageBackend for LocalStorage {
|
||||
"local"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_and_get() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
|
||||
storage.put("test/key", b"test data").await.unwrap();
|
||||
let data = storage.get("test/key").await.unwrap();
|
||||
assert_eq!(&*data, b"test data");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_not_found() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
|
||||
let result = storage.get("nonexistent").await;
|
||||
assert!(matches!(result, Err(StorageError::NotFound)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_with_prefix() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
|
||||
storage.put("docker/image/blob1", b"data1").await.unwrap();
|
||||
storage.put("docker/image/blob2", b"data2").await.unwrap();
|
||||
storage.put("maven/artifact", b"data3").await.unwrap();
|
||||
|
||||
let docker_keys = storage.list("docker/").await;
|
||||
assert_eq!(docker_keys.len(), 2);
|
||||
assert!(docker_keys.iter().all(|k| k.starts_with("docker/")));
|
||||
|
||||
let all_keys = storage.list("").await;
|
||||
assert_eq!(all_keys.len(), 3);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_stat() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
|
||||
storage.put("test", b"12345").await.unwrap();
|
||||
let meta = storage.stat("test").await.unwrap();
|
||||
assert_eq!(meta.size, 5);
|
||||
assert!(meta.modified > 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_stat_not_found() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
|
||||
let meta = storage.stat("nonexistent").await;
|
||||
assert!(meta.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_health_check() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
assert!(storage.health_check().await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_health_check_creates_directory() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let new_path = temp_dir.path().join("new_storage");
|
||||
let storage = LocalStorage::new(new_path.to_str().unwrap());
|
||||
|
||||
assert!(!new_path.exists());
|
||||
assert!(storage.health_check().await);
|
||||
assert!(new_path.exists());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_nested_directory_creation() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
|
||||
storage.put("a/b/c/d/e/file", b"deep").await.unwrap();
|
||||
let data = storage.get("a/b/c/d/e/file").await.unwrap();
|
||||
assert_eq!(&*data, b"deep");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_overwrite() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
|
||||
storage.put("key", b"original").await.unwrap();
|
||||
storage.put("key", b"updated").await.unwrap();
|
||||
|
||||
let data = storage.get("key").await.unwrap();
|
||||
assert_eq!(&*data, b"updated");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backend_name() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = LocalStorage::new(temp_dir.path().to_str().unwrap());
|
||||
assert_eq!(storage.backend_name(), "local");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,3 +127,184 @@ impl StorageBackend for S3Storage {
|
||||
"s3"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use wiremock::matchers::{method, path};
|
||||
use wiremock::{Mock, MockServer, ResponseTemplate};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_success() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("PUT"))
|
||||
.and(path("/test-bucket/test-key"))
|
||||
.respond_with(ResponseTemplate::new(200))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let result = storage.put("test-key", b"data").await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_failure() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("PUT"))
|
||||
.and(path("/test-bucket/test-key"))
|
||||
.respond_with(ResponseTemplate::new(500))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let result = storage.put("test-key", b"data").await;
|
||||
assert!(matches!(result, Err(StorageError::Network(_))));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_success() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/test-bucket/test-key"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_bytes(b"test data".to_vec()))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let data = storage.get("test-key").await.unwrap();
|
||||
assert_eq!(&*data, b"test data");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_not_found() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/test-bucket/missing"))
|
||||
.respond_with(ResponseTemplate::new(404))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let result = storage.get("missing").await;
|
||||
assert!(matches!(result, Err(StorageError::NotFound)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
let xml_response = r#"<?xml version="1.0"?>
|
||||
<ListBucketResult>
|
||||
<Key>docker/image1</Key>
|
||||
<Key>docker/image2</Key>
|
||||
<Key>maven/artifact</Key>
|
||||
</ListBucketResult>"#;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/test-bucket"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_string(xml_response))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let keys = storage.list("docker/").await;
|
||||
assert_eq!(keys.len(), 2);
|
||||
assert!(keys.iter().all(|k| k.starts_with("docker/")));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_stat_success() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("HEAD"))
|
||||
.and(path("/test-bucket/test-key"))
|
||||
.respond_with(
|
||||
ResponseTemplate::new(200)
|
||||
.insert_header("content-length", "1234")
|
||||
.insert_header("last-modified", "Sun, 06 Nov 1994 08:49:37 GMT"),
|
||||
)
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let meta = storage.stat("test-key").await.unwrap();
|
||||
assert_eq!(meta.size, 1234);
|
||||
assert!(meta.modified > 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_stat_not_found() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("HEAD"))
|
||||
.and(path("/test-bucket/missing"))
|
||||
.respond_with(ResponseTemplate::new(404))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let meta = storage.stat("missing").await;
|
||||
assert!(meta.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_health_check_healthy() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("HEAD"))
|
||||
.and(path("/test-bucket"))
|
||||
.respond_with(ResponseTemplate::new(200))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
assert!(storage.health_check().await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_health_check_bucket_not_found_is_ok() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("HEAD"))
|
||||
.and(path("/test-bucket"))
|
||||
.respond_with(ResponseTemplate::new(404))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
// 404 is OK for health check (bucket may be empty)
|
||||
assert!(storage.health_check().await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_health_check_server_error() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let storage = S3Storage::new(&mock_server.uri(), "test-bucket");
|
||||
|
||||
Mock::given(method("HEAD"))
|
||||
.and(path("/test-bucket"))
|
||||
.respond_with(ResponseTemplate::new(500))
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
assert!(!storage.health_check().await);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backend_name() {
|
||||
let storage = S3Storage::new("http://localhost:9000", "bucket");
|
||||
assert_eq!(storage.backend_name(), "s3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_s3_keys() {
|
||||
let xml = r#"<Key>docker/a</Key><Key>docker/b</Key><Key>maven/c</Key>"#;
|
||||
let keys = S3Storage::parse_s3_keys(xml, "docker/");
|
||||
assert_eq!(keys, vec!["docker/a", "docker/b"]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user