|
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT License. |
| 3 | + |
| 4 | +#include <stdio.h> |
| 5 | +#include <stdlib.h> |
| 6 | +#include <string.h> |
| 7 | +#include <time.h> |
| 8 | +#include "../include/azurecosmos.h" |
| 9 | + |
| 10 | +#define TEST_PASS 0 |
| 11 | +#define TEST_FAIL 1 |
| 12 | + |
| 13 | +// Test counter |
| 14 | +static int tests_run = 0; |
| 15 | +static int tests_passed = 0; |
| 16 | + |
| 17 | +void report_test(const char *test_name, int passed) { |
| 18 | + tests_run++; |
| 19 | + if (passed) { |
| 20 | + tests_passed++; |
| 21 | + printf("✓ PASS: %s\n", test_name); |
| 22 | + } else { |
| 23 | + printf("✗ FAIL: %s\n", test_name); |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +// Test 1: Runtime context lifecycle |
| 28 | +int test_runtime_context_lifecycle() { |
| 29 | + printf("\n--- Test: runtime_context_lifecycle ---\n"); |
| 30 | + |
| 31 | + cosmos_runtime_context *runtime = cosmos_runtime_context_create(NULL); |
| 32 | + if (!runtime) { |
| 33 | + printf("Failed to create runtime context\n"); |
| 34 | + return TEST_FAIL; |
| 35 | + } |
| 36 | + printf("Created runtime context successfully\n"); |
| 37 | + |
| 38 | + // Free it |
| 39 | + cosmos_runtime_context_free(runtime); |
| 40 | + printf("Freed runtime context successfully\n"); |
| 41 | + |
| 42 | + return TEST_PASS; |
| 43 | +} |
| 44 | + |
| 45 | +// Test 2: Stack-allocated call context |
| 46 | +int test_call_context_stack_allocated() { |
| 47 | + printf("\n--- Test: call_context_stack_allocated ---\n"); |
| 48 | + |
| 49 | + cosmos_runtime_context *runtime = cosmos_runtime_context_create(NULL); |
| 50 | + if (!runtime) { |
| 51 | + printf("Failed to create runtime context\n"); |
| 52 | + return TEST_FAIL; |
| 53 | + } |
| 54 | + |
| 55 | + // Stack-allocated context |
| 56 | + cosmos_call_context ctx; |
| 57 | + ctx.runtime_context = runtime; |
| 58 | + ctx.include_error_details = false; |
| 59 | + |
| 60 | + printf("Created stack-allocated call context\n"); |
| 61 | + |
| 62 | + // Use it for a simple operation (get version) |
| 63 | + const char *version = cosmos_version(); |
| 64 | + if (!version) { |
| 65 | + printf("Failed to get version\n"); |
| 66 | + cosmos_runtime_context_free(runtime); |
| 67 | + return TEST_FAIL; |
| 68 | + } |
| 69 | + printf("Successfully used stack-allocated context (version: %s)\n", version); |
| 70 | + |
| 71 | + cosmos_runtime_context_free(runtime); |
| 72 | + return TEST_PASS; |
| 73 | +} |
| 74 | + |
| 75 | +// Test 3: Heap-allocated call context |
| 76 | +int test_call_context_heap_allocated() { |
| 77 | + printf("\n--- Test: call_context_heap_allocated ---\n"); |
| 78 | + |
| 79 | + cosmos_runtime_context *runtime = cosmos_runtime_context_create(NULL); |
| 80 | + if (!runtime) { |
| 81 | + printf("Failed to create runtime context\n"); |
| 82 | + return TEST_FAIL; |
| 83 | + } |
| 84 | + |
| 85 | + // Heap-allocated context |
| 86 | + cosmos_call_context *ctx = cosmos_call_context_create(runtime, false); |
| 87 | + if (!ctx) { |
| 88 | + printf("Failed to create heap-allocated call context\n"); |
| 89 | + cosmos_runtime_context_free(runtime); |
| 90 | + return TEST_FAIL; |
| 91 | + } |
| 92 | + printf("Created heap-allocated call context\n"); |
| 93 | + |
| 94 | + // Use it for a simple operation (get version) |
| 95 | + const char *version = cosmos_version(); |
| 96 | + if (!version) { |
| 97 | + printf("Failed to get version\n"); |
| 98 | + cosmos_call_context_free(ctx); |
| 99 | + cosmos_runtime_context_free(runtime); |
| 100 | + return TEST_FAIL; |
| 101 | + } |
| 102 | + printf("Successfully used heap-allocated context (version: %s)\n", version); |
| 103 | + |
| 104 | + cosmos_call_context_free(ctx); |
| 105 | + cosmos_runtime_context_free(runtime); |
| 106 | + return TEST_PASS; |
| 107 | +} |
| 108 | + |
| 109 | +// Test 4: Call context reuse |
| 110 | +int test_call_context_reuse() { |
| 111 | + printf("\n--- Test: call_context_reuse ---\n"); |
| 112 | + |
| 113 | + const char *endpoint = getenv("AZURE_COSMOS_ENDPOINT"); |
| 114 | + const char *key = getenv("AZURE_COSMOS_KEY"); |
| 115 | + |
| 116 | + if (!endpoint || !key) { |
| 117 | + printf("Skipping test - requires AZURE_COSMOS_ENDPOINT and AZURE_COSMOS_KEY\n"); |
| 118 | + return TEST_PASS; // Not a failure, just skipped |
| 119 | + } |
| 120 | + |
| 121 | + cosmos_runtime_context *runtime = cosmos_runtime_context_create(NULL); |
| 122 | + if (!runtime) { |
| 123 | + printf("Failed to create runtime context\n"); |
| 124 | + return TEST_FAIL; |
| 125 | + } |
| 126 | + |
| 127 | + cosmos_call_context ctx; |
| 128 | + ctx.runtime_context = runtime; |
| 129 | + ctx.include_error_details = true; |
| 130 | + |
| 131 | + cosmos_client *client = NULL; |
| 132 | + |
| 133 | + // First call - create client |
| 134 | + cosmos_error_code code = cosmos_client_create_with_key(&ctx, endpoint, key, &client); |
| 135 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 136 | + printf("First call failed with code: %d\n", code); |
| 137 | + cosmos_runtime_context_free(runtime); |
| 138 | + return TEST_FAIL; |
| 139 | + } |
| 140 | + printf("First call succeeded (client created)\n"); |
| 141 | + |
| 142 | + cosmos_database_client *database = NULL; |
| 143 | + |
| 144 | + // Reuse context for second call - try to get a database client |
| 145 | + code = cosmos_client_database_client(&ctx, client, "nonexistent-db", &database); |
| 146 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 147 | + printf("Second call failed with code: %d (expected, just testing reuse)\n", code); |
| 148 | + // This is okay - we're testing context reuse, not that the operation succeeds |
| 149 | + } else { |
| 150 | + printf("Second call succeeded (database client retrieved)\n"); |
| 151 | + cosmos_database_free(database); |
| 152 | + } |
| 153 | + |
| 154 | + // Reuse context for third call - try again |
| 155 | + database = NULL; |
| 156 | + code = cosmos_client_database_client(&ctx, client, "another-nonexistent-db", &database); |
| 157 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 158 | + printf("Third call failed with code: %d (expected, just testing reuse)\n", code); |
| 159 | + } else { |
| 160 | + printf("Third call succeeded (database client retrieved)\n"); |
| 161 | + cosmos_database_free(database); |
| 162 | + } |
| 163 | + |
| 164 | + printf("Successfully reused call context for multiple operations\n"); |
| 165 | + |
| 166 | + cosmos_client_free(client); |
| 167 | + cosmos_runtime_context_free(runtime); |
| 168 | + return TEST_PASS; |
| 169 | +} |
| 170 | + |
| 171 | +// Test 5: String memory management |
| 172 | +int test_string_memory_management() { |
| 173 | + printf("\n--- Test: string_memory_management ---\n"); |
| 174 | + |
| 175 | + const char *endpoint = getenv("AZURE_COSMOS_ENDPOINT"); |
| 176 | + const char *key = getenv("AZURE_COSMOS_KEY"); |
| 177 | + |
| 178 | + if (!endpoint || !key) { |
| 179 | + printf("Skipping test - requires AZURE_COSMOS_ENDPOINT and AZURE_COSMOS_KEY\n"); |
| 180 | + return TEST_PASS; |
| 181 | + } |
| 182 | + |
| 183 | + time_t current_time = time(NULL); |
| 184 | + char database_name[64]; |
| 185 | + snprintf(database_name, sizeof(database_name), "auto-test-db-str-mem-%ld", current_time); |
| 186 | + |
| 187 | + cosmos_runtime_context *runtime = cosmos_runtime_context_create(NULL); |
| 188 | + if (!runtime) { |
| 189 | + printf("Failed to create runtime context\n"); |
| 190 | + return TEST_FAIL; |
| 191 | + } |
| 192 | + |
| 193 | + cosmos_call_context ctx; |
| 194 | + ctx.runtime_context = runtime; |
| 195 | + ctx.include_error_details = true; |
| 196 | + |
| 197 | + cosmos_client *client = NULL; |
| 198 | + cosmos_database_client *database = NULL; |
| 199 | + cosmos_container_client *container = NULL; |
| 200 | + const char *read_json = NULL; |
| 201 | + int result = TEST_PASS; |
| 202 | + int database_created = 0; |
| 203 | + |
| 204 | + // Create client |
| 205 | + cosmos_error_code code = cosmos_client_create_with_key(&ctx, endpoint, key, &client); |
| 206 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 207 | + printf("Failed to create client\n"); |
| 208 | + result = TEST_FAIL; |
| 209 | + goto cleanup; |
| 210 | + } |
| 211 | + |
| 212 | + // Create database |
| 213 | + code = cosmos_client_create_database(&ctx, client, database_name, &database); |
| 214 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 215 | + printf("Failed to create database\n"); |
| 216 | + result = TEST_FAIL; |
| 217 | + goto cleanup; |
| 218 | + } |
| 219 | + database_created = 1; |
| 220 | + printf("Created database: %s\n", database_name); |
| 221 | + |
| 222 | + // Create container |
| 223 | + code = cosmos_database_create_container(&ctx, database, "test-container", "/pk", &container); |
| 224 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 225 | + printf("Failed to create container\n"); |
| 226 | + result = TEST_FAIL; |
| 227 | + goto cleanup; |
| 228 | + } |
| 229 | + printf("Created container\n"); |
| 230 | + |
| 231 | + // Create an item |
| 232 | + const char *json_data = "{\"id\":\"item1\",\"pk\":\"pk1\",\"value\":\"test\"}"; |
| 233 | + code = cosmos_container_upsert_item(&ctx, container, "pk1", json_data); |
| 234 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 235 | + printf("Failed to upsert item\n"); |
| 236 | + result = TEST_FAIL; |
| 237 | + goto cleanup; |
| 238 | + } |
| 239 | + printf("Upserted item\n"); |
| 240 | + |
| 241 | + // Read the item - this returns a string that must be freed |
| 242 | + code = cosmos_container_read_item(&ctx, container, "pk1", "item1", &read_json); |
| 243 | + if (code != COSMOS_ERROR_CODE_SUCCESS) { |
| 244 | + printf("Failed to read item\n"); |
| 245 | + result = TEST_FAIL; |
| 246 | + goto cleanup; |
| 247 | + } |
| 248 | + printf("Read item: %s\n", read_json); |
| 249 | + |
| 250 | + // Test freeing the string |
| 251 | + if (read_json) { |
| 252 | + cosmos_string_free(read_json); |
| 253 | + read_json = NULL; |
| 254 | + printf("Successfully freed JSON string\n"); |
| 255 | + } |
| 256 | + |
| 257 | + // Test freeing error details (trigger an error) |
| 258 | + code = cosmos_container_read_item(&ctx, container, "pk1", "nonexistent-item", &read_json); |
| 259 | + if (code == COSMOS_ERROR_CODE_NOT_FOUND) { |
| 260 | + printf("Got expected NOT_FOUND error\n"); |
| 261 | + if (ctx.error.detail) { |
| 262 | + printf("Error detail present: %s\n", ctx.error.detail); |
| 263 | + cosmos_string_free(ctx.error.detail); |
| 264 | + printf("Successfully freed error detail string\n"); |
| 265 | + } |
| 266 | + } |
| 267 | + |
| 268 | +cleanup: |
| 269 | + if (database && database_created) { |
| 270 | + cosmos_database_delete(&ctx, database); |
| 271 | + } |
| 272 | + |
| 273 | + if (container) { |
| 274 | + cosmos_container_free(container); |
| 275 | + } |
| 276 | + if (database) { |
| 277 | + cosmos_database_free(database); |
| 278 | + } |
| 279 | + if (client) { |
| 280 | + cosmos_client_free(client); |
| 281 | + } |
| 282 | + cosmos_runtime_context_free(runtime); |
| 283 | + |
| 284 | + return result; |
| 285 | +} |
| 286 | + |
| 287 | +int main() { |
| 288 | + printf("=== Test Suite 1: Context and Memory Management ===\n"); |
| 289 | + |
| 290 | + report_test("runtime_context_lifecycle", test_runtime_context_lifecycle() == TEST_PASS); |
| 291 | + report_test("call_context_stack_allocated", test_call_context_stack_allocated() == TEST_PASS); |
| 292 | + report_test("call_context_heap_allocated", test_call_context_heap_allocated() == TEST_PASS); |
| 293 | + report_test("call_context_reuse", test_call_context_reuse() == TEST_PASS); |
| 294 | + report_test("string_memory_management", test_string_memory_management() == TEST_PASS); |
| 295 | + |
| 296 | + printf("\n=== Test Summary ===\n"); |
| 297 | + printf("Tests run: %d\n", tests_run); |
| 298 | + printf("Tests passed: %d\n", tests_passed); |
| 299 | + printf("Tests failed: %d\n", tests_run - tests_passed); |
| 300 | + |
| 301 | + if (tests_passed == tests_run) { |
| 302 | + printf("\n✓ All tests passed!\n"); |
| 303 | + return 0; |
| 304 | + } else { |
| 305 | + printf("\n✗ Some tests failed\n"); |
| 306 | + return 1; |
| 307 | + } |
| 308 | +} |
0 commit comments