From bed4cc01214a19f81a40330890a34a704f3e607b Mon Sep 17 00:00:00 2001 From: Tom Hicks Date: Wed, 28 Jan 2026 09:47:34 -0800 Subject: [PATCH] feat(lib): initialize libnextcloud C++17 library scaffolding (#25) --- shared/CMakeLists.txt | 131 ++++++++++++ shared/README.md | 288 +++++++++++++++++++++++++++ shared/include/nextcloud/types.hpp | 176 ++++++++++++++++ shared/include/nextcloud/version.hpp | 57 ++++++ shared/libnextcloud.pc.in | 11 + shared/nextcloudConfig.cmake.in | 5 + shared/tests/CMakeLists.txt | 30 +++ shared/tests/smoke_test.cpp | 140 +++++++++++++ 8 files changed, 838 insertions(+) create mode 100644 shared/CMakeLists.txt create mode 100644 shared/README.md create mode 100644 shared/include/nextcloud/types.hpp create mode 100644 shared/include/nextcloud/version.hpp create mode 100644 shared/libnextcloud.pc.in create mode 100644 shared/nextcloudConfig.cmake.in create mode 100644 shared/tests/CMakeLists.txt create mode 100644 shared/tests/smoke_test.cpp diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt new file mode 100644 index 0000000..bb4a3ef --- /dev/null +++ b/shared/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.15) +project(libnextcloud VERSION 0.1.0 LANGUAGES CXX) + +# C++17 required +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Options +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +option(BUILD_TESTING "Build tests" ON) +option(BUILD_EXAMPLES "Build examples" OFF) + +# Library sources (currently empty, will be populated as we implement components) +set(NEXTCLOUD_SOURCES + # src/webdav_client.cpp # Will be added in future + # src/auth.cpp # Will be added in future + # src/upload_manager.cpp # Will be added in future + # src/folder_manager.cpp # Will be added in future + # src/config.cpp # Will be added in future +) + +# Create library target +# For now, it's header-only (version.hpp and types.hpp) +# We'll add sources when we implement components +add_library(nextcloud INTERFACE) + +# Include directories +target_include_directories(nextcloud + INTERFACE + $ + $ +) + +# Require C++17 for consumers +target_compile_features(nextcloud INTERFACE cxx_std_17) + +# Dependencies (will be uncommented as we add implementation) +# find_package(CURL REQUIRED) +# find_package(MbedTLS REQUIRED) +# find_package(tinyxml2 REQUIRED) + +# Link libraries (commented out until we have actual implementation) +# target_link_libraries(nextcloud +# PUBLIC +# CURL::libcurl +# MbedTLS::mbedtls +# tinyxml2::tinyxml2 +# ) + +# Compiler warnings +if(MSVC) + target_compile_options(nextcloud INTERFACE /W4) +else() + target_compile_options(nextcloud INTERFACE -Wall -Wextra -Wpedantic) +endif() + +# Testing +if(BUILD_TESTING) + enable_testing() + add_subdirectory(tests) +endif() + +# Examples +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +# Install rules +install(TARGETS nextcloud + EXPORT nextcloudTargets + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include +) + +install(DIRECTORY include/ + DESTINATION include + FILES_MATCHING PATTERN "*.hpp" +) + +# Generate and install CMake config files +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/nextcloudConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfig.cmake" + INSTALL_DESTINATION lib/cmake/nextcloud +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/nextcloudConfigVersion.cmake" + DESTINATION lib/cmake/nextcloud +) + +install(EXPORT nextcloudTargets + FILE nextcloudTargets.cmake + NAMESPACE nextcloud:: + DESTINATION lib/cmake/nextcloud +) + +# pkg-config file +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/libnextcloud.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/libnextcloud.pc" + @ONLY +) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnextcloud.pc" + DESTINATION lib/pkgconfig +) + +# Print configuration summary +message(STATUS "") +message(STATUS "libnextcloud Configuration Summary:") +message(STATUS " Version: ${PROJECT_VERSION}") +message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") +message(STATUS " C++ standard: C++${CMAKE_CXX_STANDARD}") +message(STATUS " Shared libraries: ${BUILD_SHARED_LIBS}") +message(STATUS " Build tests: ${BUILD_TESTING}") +message(STATUS " Build examples: ${BUILD_EXAMPLES}") +message(STATUS " Install prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS "") diff --git a/shared/README.md b/shared/README.md new file mode 100644 index 0000000..2f3045b --- /dev/null +++ b/shared/README.md @@ -0,0 +1,288 @@ +# libnextcloud + +Cross-platform C++17 library for Nextcloud connectivity on embedded devices and homebrew platforms. + +## Overview + +`libnextcloud` provides a clean, modern C++ API for interacting with Nextcloud servers. It's designed to work on resource-constrained devices like game console homebrew (Nintendo 3DS, Switch, Wii U) while also supporting desktop platforms. + +**Status**: 🚧 In Development - Scaffolding complete, components being implemented + +## Features (Planned) + +- ✅ **Version Management**: Semantic versioning with compile-time and runtime queries +- ✅ **Type System**: Comprehensive error codes and data structures +- 🚧 **WebDAV Client**: HTTP/HTTPS communication with Nextcloud WebDAV API +- 🚧 **Authentication**: Basic Auth (OAuth2 planned) +- 🚧 **File Upload**: Streaming uploads with chunking and progress tracking +- 🚧 **Folder Management**: List, create, navigate remote folders +- 🚧 **Favorites & Recent**: Persistent user preferences + +## Requirements + +### Build Tools +- **CMake** 3.15 or later +- **C++17 compiler**: GCC 7+, Clang 5+, MSVC 2017+ + +### Dependencies + +**Required** (when implementation complete): +- libcurl 7.68+ +- mbedTLS 2.16+ +- tinyxml2 9.0+ + +**Testing**: +- Google Test 1.11+ + +**Header-Only**: +- nlohmann/json 3.10+ (for configuration, future) + +### Platform Support + +| Platform | Status | Notes | +|----------|--------|-------| +| Linux | ✅ Development | Primary development platform | +| Nintendo 3DS | ✅ Planned | Via devkitARM in container | +| Nintendo Switch | 🔜 Planned | | +| Wii U | 🔜 Planned | | +| PlayStation Vita | 🔜 Planned | | +| Windows | 🔜 Planned | | +| macOS | 🔜 Planned | | + +## Quick Start + +### Building + +```bash +# Configure (modern CMake style) +cmake -S . -B build + +# Build +cmake --build build + +# Run tests +cmake --build build --target test +# or with CTest for detailed output: +# ctest --test-dir build --output-on-failure +``` + +### Using in Your Project + +#### CMake + +```cmake +find_package(nextcloud REQUIRED) + +add_executable(my_app main.cpp) +target_link_libraries(my_app nextcloud::nextcloud) +``` + +#### pkg-config + +```bash +g++ -std=c++17 main.cpp $(pkg-config --cflags --libs libnextcloud) +``` + +### Basic Usage + +```cpp +#include +#include +#include + +int main() { + // Get library version + std::cout << "libnextcloud v" << nextcloud::getVersionString() << std::endl; + + // Check version compatibility + if (nextcloud::isVersionAtLeast(0, 1, 0)) { + std::cout << "Version is compatible!" << std::endl; + } + + // Use error types + nextcloud::NextcloudError error = nextcloud::NextcloudError::Success; + std::cout << "Status: " << nextcloud::errorToString(error) << std::endl; + + return 0; +} +``` + +## Project Structure + +``` +shared/ +├── CMakeLists.txt # Build configuration +├── README.md # This file +├── libnextcloud.pc.in # pkg-config template +├── nextcloudConfig.cmake.in # CMake config template +├── include/ +│ └── nextcloud/ +│ ├── version.hpp # Version information +│ ├── types.hpp # Common types and errors +│ ├── webdav_client.hpp # (Coming soon) +│ ├── auth.hpp # (Coming soon) +│ ├── upload_manager.hpp # (Coming soon) +│ ├── folder_manager.hpp # (Coming soon) +│ └── config.hpp # (Coming soon) +├── src/ # Implementation files (future) +├── tests/ +│ ├── CMakeLists.txt +│ └── smoke_test.cpp # Basic tests +├── examples/ # Usage examples (future) +└── docs/ # Documentation (future) +``` + +## Building for Platforms + +### Linux (Development) + +```bash +mkdir build && cd build +cmake .. +make +make test +``` + +### Nintendo 3DS (Container) + +```bash +# From project root +podman run --rm -v .:/project:z tomusan/devkitarm-3ds:latest \ + bash -c "cd /project/shared && mkdir -p build && cd build && cmake .. && make" +``` + +## API Documentation + +### Version Information + +```cpp +#include + +// Constants +nextcloud::VERSION_MAJOR // 0 +nextcloud::VERSION_MINOR // 1 +nextcloud::VERSION_PATCH // 0 +nextcloud::VERSION_STRING // "0.1.0" + +// Functions +std::string getVersionString(); +void getVersion(int& major, int& minor, int& patch); +bool isVersionAtLeast(int major, int minor, int patch); +``` + +### Error Handling + +```cpp +#include + +// Error codes +enum class NextcloudError { + Success, + NetworkError, + AuthenticationFailed, + ServerError, + FileNotFound, + // ... more error codes +}; + +// Convert to string +const char* errorToString(NextcloudError error); + +// Result type +nextcloud::Result result; +if (result.isSuccess()) { + // Use result.value +} else { + std::cerr << result.errorMessage() << std::endl; +} +``` + +### Data Structures + +```cpp +// Upload progress +struct UploadProgress { + std::string filename; + uint64_t bytesUploaded; + uint64_t totalBytes; + float percentComplete; + float bytesPerSecond; + int secondsRemaining; +}; + +// File information +struct FileInfo { + std::string name; + std::string path; + bool isDirectory; + uint64_t size; + std::time_t modifiedTime; + std::string contentType; +}; + +// Folder information +struct FolderInfo { + std::string path; + std::string displayName; + std::time_t lastUsed; + bool isFavorite; +}; +``` + +## Testing + +```bash +# Run all tests +cd build +ctest --output-on-failure + +# Or use the custom target +make run_tests + +# Run specific test +./tests/smoke_test +``` + +## Development Status + +- ✅ **Phase 1: Scaffolding** - Complete + - Directory structure + - CMake build system + - Version and types headers + - Google Test integration + - Basic smoke tests + +- 🚧 **Phase 2: WebDAV Client** - Not started + - Issue #8 + +- 🚧 **Phase 3: Authentication** - Not started + - Issue #9 + +- 🚧 **Phase 4: Upload Manager** - Not started + - Issue #10 + +- 🚧 **Phase 5: Folder Management** - Not started + - Issue #11 + +- 🚧 **Phase 6: Favorites & Recent** - Not started + - Issue #12 + +- 🚧 **Phase 7: Testing & Docs** - Not started + - Issue #13 + +## Contributing + +See [CONTRIBUTING.md](../CONTRIBUTING.md) in the project root. + +## License + +[License TBD - to be determined] + +## Related Documentation + +- [Project Plan](../.plans/CreateLibNextcloud.md) +- [Issue #25](https://git.tomusan.com/maj/nextcloud-share/issues/25) - Initialize libnextcloud + +--- + +**Note**: This library is in active development. The API is subject to change until version 1.0.0. diff --git a/shared/include/nextcloud/types.hpp b/shared/include/nextcloud/types.hpp new file mode 100644 index 0000000..5a6740d --- /dev/null +++ b/shared/include/nextcloud/types.hpp @@ -0,0 +1,176 @@ +#ifndef NEXTCLOUD_TYPES_HPP +#define NEXTCLOUD_TYPES_HPP + +#include +#include +#include + +namespace nextcloud { + +/** + * @brief Error codes for Nextcloud operations + */ +enum class NextcloudError { + Success = 0, + + // Network errors + NetworkError, + ConnectionFailed, + Timeout, + SSLError, + + // Authentication errors + AuthenticationFailed, + InvalidCredentials, + Unauthorized, + + // Server errors + ServerError, + ServiceUnavailable, + InsufficientStorage, + + // File/path errors + FileNotFound, + PathNotFound, + InvalidPath, + FileAlreadyExists, + PathAlreadyExists, + + // Operation errors + OperationFailed, + OperationCancelled, + InvalidOperation, + + // Parsing/format errors + ParseError, + InvalidResponse, + UnsupportedFormat, + + // Configuration errors + InvalidConfiguration, + MissingConfiguration, + + // Unknown + Unknown +}; + +/** + * @brief Convert error code to human-readable string + * @param error The error code + * @return String description of the error + */ +inline const char* errorToString(NextcloudError error) { + switch (error) { + case NextcloudError::Success: return "Success"; + case NextcloudError::NetworkError: return "Network error"; + case NextcloudError::ConnectionFailed: return "Connection failed"; + case NextcloudError::Timeout: return "Operation timed out"; + case NextcloudError::SSLError: return "SSL/TLS error"; + case NextcloudError::AuthenticationFailed: return "Authentication failed"; + case NextcloudError::InvalidCredentials: return "Invalid credentials"; + case NextcloudError::Unauthorized: return "Unauthorized"; + case NextcloudError::ServerError: return "Server error"; + case NextcloudError::ServiceUnavailable: return "Service unavailable"; + case NextcloudError::InsufficientStorage: return "Insufficient storage"; + case NextcloudError::FileNotFound: return "File not found"; + case NextcloudError::PathNotFound: return "Path not found"; + case NextcloudError::InvalidPath: return "Invalid path"; + case NextcloudError::FileAlreadyExists: return "File already exists"; + case NextcloudError::PathAlreadyExists: return "Path already exists"; + case NextcloudError::OperationFailed: return "Operation failed"; + case NextcloudError::OperationCancelled: return "Operation cancelled"; + case NextcloudError::InvalidOperation: return "Invalid operation"; + case NextcloudError::ParseError: return "Parse error"; + case NextcloudError::InvalidResponse: return "Invalid response"; + case NextcloudError::UnsupportedFormat: return "Unsupported format"; + case NextcloudError::InvalidConfiguration: return "Invalid configuration"; + case NextcloudError::MissingConfiguration: return "Missing configuration"; + case NextcloudError::Unknown: return "Unknown error"; + default: return "Unrecognized error"; + } +} + +/** + * @brief Result type for operations that can fail + * @tparam T The success value type + */ +template +struct Result { + NextcloudError error; + T value; + + /** + * @brief Check if the operation was successful + * @return True if no error occurred + */ + bool isSuccess() const { return error == NextcloudError::Success; } + + /** + * @brief Check if an error occurred + * @return True if an error occurred + */ + bool isError() const { return error != NextcloudError::Success; } + + /** + * @brief Get error message + * @return Human-readable error description + */ + const char* errorMessage() const { return errorToString(error); } +}; + +/** + * @brief Upload progress information + */ +struct UploadProgress { + std::string filename; ///< Name of file being uploaded + uint64_t bytesUploaded; ///< Number of bytes uploaded so far + uint64_t totalBytes; ///< Total file size in bytes + float percentComplete; ///< Completion percentage (0.0 - 100.0) + float bytesPerSecond; ///< Current transfer speed + int secondsRemaining; ///< Estimated seconds remaining (-1 if unknown) +}; + +/** + * @brief Information about a file or directory + */ +struct FileInfo { + std::string name; ///< File or directory name + std::string path; ///< Full path on server + bool isDirectory; ///< True if this is a directory + uint64_t size; ///< File size in bytes (0 for directories) + std::time_t modifiedTime; ///< Last modification time (Unix timestamp) + std::string contentType; ///< MIME type (e.g., "image/png") + std::string etag; ///< ETag for change detection +}; + +/** + * @brief Information about a favorite or recent folder + */ +struct FolderInfo { + std::string path; ///< Full path on server + std::string displayName; ///< User-friendly name + std::time_t lastUsed; ///< Last access time (Unix timestamp) + bool isFavorite; ///< True if marked as favorite +}; + +/** + * @brief HTTP response information + */ +struct HttpResponse { + int statusCode; ///< HTTP status code (e.g., 200, 404) + std::string body; ///< Response body + std::string contentType; ///< Content-Type header value + NextcloudError error; ///< Error code if request failed + + /** + * @brief Check if response indicates success (2xx status code) + * @return True if status code is 200-299 + */ + bool isSuccess() const { + return statusCode >= 200 && statusCode < 300 && error == NextcloudError::Success; + } +}; + +} // namespace nextcloud + +#endif // NEXTCLOUD_TYPES_HPP diff --git a/shared/include/nextcloud/version.hpp b/shared/include/nextcloud/version.hpp new file mode 100644 index 0000000..11f7168 --- /dev/null +++ b/shared/include/nextcloud/version.hpp @@ -0,0 +1,57 @@ +#ifndef NEXTCLOUD_VERSION_HPP +#define NEXTCLOUD_VERSION_HPP + +#include + +namespace nextcloud { + +// Semantic versioning +constexpr int VERSION_MAJOR = 0; +constexpr int VERSION_MINOR = 1; +constexpr int VERSION_PATCH = 0; + +// Build metadata +constexpr const char* VERSION_STRING = "0.1.0"; +constexpr const char* BUILD_DATE = __DATE__; +constexpr const char* BUILD_TIME = __TIME__; + +/** + * @brief Get the library version as a string + * @return Version string in format "MAJOR.MINOR.PATCH" + */ +inline std::string getVersionString() { + return VERSION_STRING; +} + +/** + * @brief Get the library version components + * @param major Output parameter for major version + * @param minor Output parameter for minor version + * @param patch Output parameter for patch version + */ +inline void getVersion(int& major, int& minor, int& patch) { + major = VERSION_MAJOR; + minor = VERSION_MINOR; + patch = VERSION_PATCH; +} + +/** + * @brief Check if the library version is at least the specified version + * @param major Required major version + * @param minor Required minor version + * @param patch Required patch version + * @return True if current version >= required version + */ +inline bool isVersionAtLeast(int major, int minor, int patch) { + if (VERSION_MAJOR > major) return true; + if (VERSION_MAJOR < major) return false; + + if (VERSION_MINOR > minor) return true; + if (VERSION_MINOR < minor) return false; + + return VERSION_PATCH >= patch; +} + +} // namespace nextcloud + +#endif // NEXTCLOUD_VERSION_HPP diff --git a/shared/libnextcloud.pc.in b/shared/libnextcloud.pc.in new file mode 100644 index 0000000..bfe32f3 --- /dev/null +++ b/shared/libnextcloud.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libnextcloud +Description: Cross-platform C++ library for Nextcloud connectivity +Version: @PROJECT_VERSION@ +Requires: +Cflags: -I${includedir} +Libs: -L${libdir} -lnextcloud diff --git a/shared/nextcloudConfig.cmake.in b/shared/nextcloudConfig.cmake.in new file mode 100644 index 0000000..7fd79d4 --- /dev/null +++ b/shared/nextcloudConfig.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/nextcloudTargets.cmake") + +check_required_components(nextcloud) diff --git a/shared/tests/CMakeLists.txt b/shared/tests/CMakeLists.txt new file mode 100644 index 0000000..facac71 --- /dev/null +++ b/shared/tests/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.15) + +# Find Google Test +find_package(GTest REQUIRED) + +# Include Google Test helpers +include(GoogleTest) + +# Test executable +add_executable(smoke_test + smoke_test.cpp +) + +# Link against the library and GTest +target_link_libraries(smoke_test + PRIVATE + nextcloud + GTest::GTest + GTest::Main +) + +# Discover and register tests +gtest_discover_tests(smoke_test) + +# Add custom target to run tests +add_custom_target(run_tests + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + DEPENDS smoke_test + COMMENT "Running tests..." +) diff --git a/shared/tests/smoke_test.cpp b/shared/tests/smoke_test.cpp new file mode 100644 index 0000000..3c02e13 --- /dev/null +++ b/shared/tests/smoke_test.cpp @@ -0,0 +1,140 @@ +#include +#include +#include + +/** + * @file smoke_test.cpp + * @brief Basic smoke tests to verify library setup + * + * These tests ensure that the library headers compile correctly + * and basic functionality works as expected. + */ + +namespace { + +// Test version information +TEST(VersionTest, VersionConstants) { + EXPECT_EQ(nextcloud::VERSION_MAJOR, 0); + EXPECT_EQ(nextcloud::VERSION_MINOR, 1); + EXPECT_EQ(nextcloud::VERSION_PATCH, 0); + EXPECT_STREQ(nextcloud::VERSION_STRING, "0.1.0"); +} + +TEST(VersionTest, GetVersionString) { + std::string version = nextcloud::getVersionString(); + EXPECT_EQ(version, "0.1.0"); +} + +TEST(VersionTest, GetVersionComponents) { + int major, minor, patch; + nextcloud::getVersion(major, minor, patch); + EXPECT_EQ(major, 0); + EXPECT_EQ(minor, 1); + EXPECT_EQ(patch, 0); +} + +TEST(VersionTest, IsVersionAtLeast) { + // Current version is 0.1.0 + EXPECT_TRUE(nextcloud::isVersionAtLeast(0, 0, 0)); + EXPECT_TRUE(nextcloud::isVersionAtLeast(0, 1, 0)); + EXPECT_FALSE(nextcloud::isVersionAtLeast(0, 2, 0)); + EXPECT_FALSE(nextcloud::isVersionAtLeast(1, 0, 0)); +} + +// Test error types +TEST(TypesTest, ErrorToString) { + EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::Success), "Success"); + EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::NetworkError), "Network error"); + EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::AuthenticationFailed), "Authentication failed"); + EXPECT_STREQ(nextcloud::errorToString(nextcloud::NextcloudError::FileNotFound), "File not found"); +} + +TEST(TypesTest, ResultSuccess) { + nextcloud::Result result; + result.error = nextcloud::NextcloudError::Success; + result.value = 42; + + EXPECT_TRUE(result.isSuccess()); + EXPECT_FALSE(result.isError()); + EXPECT_EQ(result.value, 42); +} + +TEST(TypesTest, ResultError) { + nextcloud::Result result; + result.error = nextcloud::NextcloudError::NetworkError; + result.value = ""; + + EXPECT_FALSE(result.isSuccess()); + EXPECT_TRUE(result.isError()); + EXPECT_STREQ(result.errorMessage(), "Network error"); +} + +TEST(TypesTest, UploadProgressStruct) { + nextcloud::UploadProgress progress; + progress.filename = "test.txt"; + progress.bytesUploaded = 50; + progress.totalBytes = 100; + progress.percentComplete = 50.0f; + progress.bytesPerSecond = 1024.0f; + progress.secondsRemaining = 1; + + EXPECT_EQ(progress.filename, "test.txt"); + EXPECT_EQ(progress.bytesUploaded, 50); + EXPECT_EQ(progress.totalBytes, 100); + EXPECT_FLOAT_EQ(progress.percentComplete, 50.0f); +} + +TEST(TypesTest, FileInfoStruct) { + nextcloud::FileInfo file; + file.name = "document.pdf"; + file.path = "/Documents/document.pdf"; + file.isDirectory = false; + file.size = 1024; + file.modifiedTime = 1234567890; + file.contentType = "application/pdf"; + + EXPECT_EQ(file.name, "document.pdf"); + EXPECT_EQ(file.path, "/Documents/document.pdf"); + EXPECT_FALSE(file.isDirectory); + EXPECT_EQ(file.size, 1024); +} + +TEST(TypesTest, FolderInfoStruct) { + nextcloud::FolderInfo folder; + folder.path = "/Photos"; + folder.displayName = "My Photos"; + folder.lastUsed = 1234567890; + folder.isFavorite = true; + + EXPECT_EQ(folder.path, "/Photos"); + EXPECT_EQ(folder.displayName, "My Photos"); + EXPECT_TRUE(folder.isFavorite); +} + +TEST(TypesTest, HttpResponseSuccess) { + nextcloud::HttpResponse response; + response.statusCode = 200; + response.body = "OK"; + response.contentType = "text/plain"; + response.error = nextcloud::NextcloudError::Success; + + EXPECT_TRUE(response.isSuccess()); + EXPECT_EQ(response.statusCode, 200); +} + +TEST(TypesTest, HttpResponseError) { + nextcloud::HttpResponse response; + response.statusCode = 404; + response.body = "Not Found"; + response.error = nextcloud::NextcloudError::FileNotFound; + + EXPECT_FALSE(response.isSuccess()); + EXPECT_EQ(response.statusCode, 404); +} + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}