package main import ( "bytes" "encoding/json" "fmt" "log" "net/http" "os" "strconv" "strings" "time" "linedb/pkg/linedb" ) // User представляет пользователя type User struct { ID int `json:"id"` Username string `json:"username"` Email string `json:"email"` IsActive bool `json:"isActive"` Role string `json:"role"` CreatedAt int64 `json:"createdAt"` LastLogin *int64 `json:"lastLogin,omitempty"` } // Product представляет продукт type Product struct { ID int `json:"id"` Name string `json:"name"` Price float64 `json:"price"` Category string `json:"category"` InStock bool `json:"inStock"` SellerID int `json:"sellerId"` CreatedAt int64 `json:"createdAt"` } // Order представляет заказ type Order struct { ID int `json:"id"` UserID int `json:"userId"` ProductID int `json:"productId"` Quantity int `json:"quantity"` TotalPrice float64 `json:"totalPrice"` Status string `json:"status"` CreatedAt int64 `json:"createdAt"` UpdatedAt int64 `json:"updatedAt"` } // OrderItem представляет элемент заказа type OrderItem struct { ID int `json:"id"` OrderID int `json:"orderId"` ProductID int `json:"productId"` Quantity int `json:"quantity"` Price float64 `json:"price"` } // APIResponse представляет ответ API type APIResponse struct { Success bool `json:"success"` Data interface{} `json:"data,omitempty"` Error string `json:"error,omitempty"` Message string `json:"message,omitempty"` } var Database *linedb.LineDb var server *http.Server // parseTestNumbers парсит номера тестов из аргументов командной строки func parseTestNumbers() []int { if len(os.Args) < 2 { return []int{} } // Получаем аргумент после имени программы arg := os.Args[1] // Проверяем, что это не флаг помощи if arg == "-h" || arg == "--help" || arg == "help" { printUsage() return []int{} } // Разбиваем по запятой parts := strings.Split(arg, ",") var numbers []int for _, part := range parts { part = strings.TrimSpace(part) if part == "" { continue } num, err := strconv.Atoi(part) if err != nil { fmt.Printf("Ошибка: '%s' не является числом\n", part) printUsage() return []int{} } // Проверяем диапазон if num < 1 || num > 4 { fmt.Printf("Ошибка: номер теста %d должен быть от 1 до 4\n", num) printUsage() return []int{} } numbers = append(numbers, num) } return numbers } // printUsage выводит справку по использованию func printUsage() { fmt.Println("\nИспользование:") fmt.Println(" go run main.go # Запустить все тесты") fmt.Println(" go run main.go 1 # Запустить только тест 1") fmt.Println(" go run main.go 1,3 # Запустить тесты 1 и 3") fmt.Println(" go run main.go 2,3,4 # Запустить тесты 2, 3 и 4") fmt.Println(" go run main.go help # Показать эту справку") fmt.Println("\nДоступные тесты:") fmt.Println(" 1 - Базовые CRUD операции") fmt.Println(" 2 - Сложные запросы и фильтрация") fmt.Println(" 3 - Работа с несколькими коллекциями") fmt.Println(" 4 - Тест производительности") fmt.Println(" 5 - HTTP API сервер") fmt.Println(" 6 - API endpoints") os.Exit(0) } // runAllTests запускает все тесты func runAllTests() { testBasicCRUDOperations() testComplexQueries() testMultipleCollections() testPerformance() testHTTPServer() testAPIEndpoints() } // runSelectedTests запускает только выбранные тесты func runSelectedTests(numbers []int) { for _, num := range numbers { switch num { case 1: fmt.Println("1. Базовые CRUD операции") testBasicCRUDOperations() case 2: fmt.Println("2. Сложные запросы и фильтрация") testComplexQueries() case 3: fmt.Println("3. Работа с несколькими коллекциями") testMultipleCollections() case 4: fmt.Println("4. Тест производительности") testPerformance() case 5: fmt.Println("5. HTTP API сервер") testHTTPServer() case 6: fmt.Println("6. API endpoints") testAPIEndpoints() } } } func testBasicCRUDOperations() { // Создаем пользователя user := map[string]any{ "id": 1, "username": "testuser", "email": "test@example.com", "isActive": true, "role": "user", "createdAt": time.Now().Unix(), } // Вставка if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil { log.Printf("Failed to insert user: %v", err) return } fmt.Printf("\tПользователь создан: %s\n", user["username"]) // Чтение result, err := Database.Read("users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to read users: %v", err) return } fmt.Printf("\tПользователей в базе: %d\n", len(result)) // Обновление updateData := map[string]any{"role": "admin"} updated, err := Database.Update(updateData, "users", map[string]any{"id": 1}, linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to update user: %v", err) return } fmt.Printf(" Обновлено пользователей: %d\n", len(updated)) // Удаление deleted, err := Database.Delete(map[string]any{"id": 1}, "users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to delete user: %v", err) return } fmt.Printf(" Удалено пользователей: %d\n", len(deleted)) // Проверяем, что пользователь удален result, err = Database.Read("users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to read users after delete: %v", err) return } fmt.Printf(" Пользователей после удаления: %d\n", len(result)) } func testComplexQueries() { // Создаем несколько пользователей users := []any{ map[string]any{ "id": 1, "username": "john_doe", "email": "john@example.com", "isActive": true, "role": "user", "createdAt": time.Now().Unix(), }, map[string]any{ "id": 2, "username": "jane_smith", "email": "jane@example.com", "isActive": true, "role": "admin", "createdAt": time.Now().Unix(), }, map[string]any{ "id": 3, "username": "bob_wilson", "email": "bob@example.com", "isActive": false, "role": "user", "createdAt": time.Now().Unix(), }, } if err := Database.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil { log.Printf("Failed to insert users: %v", err) return } // Фильтрация по роли admins, err := Database.ReadByFilter(map[string]any{"role": "admin"}, "users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to filter admins: %v", err) return } fmt.Printf(" Администраторов: %d\n", len(admins)) // Фильтрация по активности activeUsers, err := Database.ReadByFilter(map[string]any{"isActive": true}, "users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to filter active users: %v", err) return } fmt.Printf(" Активных пользователей: %d\n", len(activeUsers)) // Строковая фильтрация emailFilter, err := Database.ReadByFilter("email == 'john@example.com'", "users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to filter by email: %v", err) return } fmt.Printf(" Пользователей с email john@example.com: %d\n", len(emailFilter)) // Частичное совпадение partialMatch, err := Database.ReadByFilter( map[string]any{"username": "john"}, "users", linedb.LineDbAdapterOptions{StrictCompare: false}, ) if err != nil { log.Printf("Failed to partial match: %v", err) return } fmt.Printf(" Пользователей с именем содержащим 'john': %d\n", len(partialMatch)) } func testMultipleCollections() { // Создаем пользователя user := map[string]any{ "id": 1, "username": "customer1", "email": "customer1@example.com", "isActive": true, "role": "customer", "createdAt": time.Now().Unix(), } // Создаем продукты products := []any{ map[string]any{ "id": 1, "name": "Laptop", "price": 999.99, "category": "Electronics", "inStock": true, "sellerId": 1, "createdAt": time.Now().Unix(), }, map[string]any{ "id": 2, "name": "Mouse", "price": 29.99, "category": "Electronics", "inStock": true, "sellerId": 1, "createdAt": time.Now().Unix(), }, } // Создаем заказ order := map[string]any{ "id": 1, "userId": 1, "productId": 1, "quantity": 2, "totalPrice": 1999.98, "status": "pending", "createdAt": time.Now().Unix(), "updatedAt": time.Now().Unix(), } // Создаем элементы заказа orderItems := []any{ map[string]any{ "id": 1, "orderId": 1, "productId": 1, "quantity": 2, "price": 999.99, }, } // Вставляем данные во все коллекции if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil { log.Printf("Failed to insert user: %v", err) return } if err := Database.Insert(products, "products", linedb.LineDbAdapterOptions{}); err != nil { log.Printf("Failed to insert products: %v", err) return } if err := Database.Insert(order, "orders", linedb.LineDbAdapterOptions{}); err != nil { log.Printf("Failed to insert order: %v", err) return } if err := Database.Insert(orderItems, "orderItems", linedb.LineDbAdapterOptions{}); err != nil { log.Printf("Failed to insert order items: %v", err) return } // Читаем данные из всех коллекций users, err := Database.Read("users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to read users: %v", err) return } productsData, err := Database.Read("products", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to read products: %v", err) return } orders, err := Database.Read("orders", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to read orders: %v", err) return } orderItemsData, err := Database.Read("orderItems", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to read order items: %v", err) return } fmt.Printf(" Пользователей: %d\n", len(users)) fmt.Printf(" Продуктов: %d\n", len(productsData)) fmt.Printf(" Заказов: %d\n", len(orders)) fmt.Printf(" Элементов заказов: %d\n", len(orderItemsData)) // Сложный запрос: найти все заказы пользователя userOrders, err := Database.ReadByFilter(map[string]any{"userId": 1}, "orders", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to get user orders: %v", err) return } fmt.Printf(" Заказов пользователя 1: %d\n", len(userOrders)) } func testPerformance() { // Тест производительности массовой вставки recordCount := 500 fmt.Printf(" Создание %d записей для теста производительности...\n", recordCount) // Создаем пользователей users := make([]any, recordCount) for i := 1; i <= recordCount; i++ { users[i-1] = map[string]any{ "id": i, "username": fmt.Sprintf("user%d", i), "email": fmt.Sprintf("user%d@example.com", i), "isActive": i%2 == 0, // чередуем активных и неактивных "role": "user", "createdAt": time.Now().Unix(), } } // Измеряем время вставки startTime := time.Now() if err := Database.Insert(users, "users", linedb.LineDbAdapterOptions{}); err != nil { log.Printf("Failed to insert users: %v", err) return } endTime := time.Now() duration := endTime.Sub(startTime) fmt.Printf(" Вставлено пользователей: %d\n", recordCount) fmt.Printf(" Время выполнения: %v\n", duration) fmt.Printf(" Скорость: %.2f записей/сек\n", float64(recordCount)/duration.Seconds()) // Тест производительности чтения startTime = time.Now() result, err := Database.Read("users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to read users: %v", err) return } endTime = time.Now() readDuration := endTime.Sub(startTime) fmt.Printf(" Прочитано пользователей: %d\n", len(result)) fmt.Printf(" Время чтения: %v\n", readDuration) fmt.Printf(" Скорость чтения: %.2f записей/сек\n", float64(len(result))/readDuration.Seconds()) // Тест производительности фильтрации startTime = time.Now() activeUsers, err := Database.ReadByFilter(map[string]any{"isActive": true}, "users", linedb.LineDbAdapterOptions{}) if err != nil { log.Printf("Failed to filter active users: %v", err) return } endTime = time.Now() filterDuration := endTime.Sub(startTime) fmt.Printf(" Активных пользователей: %d\n", len(activeUsers)) fmt.Printf(" Время фильтрации: %v\n", filterDuration) } func testHTTPServer() { // Настраиваем HTTP сервер mux := http.NewServeMux() // Обработчики API mux.HandleFunc("/api/users", handleUsers) mux.HandleFunc("/api/products", handleProducts) mux.HandleFunc("/api/orders", handleOrders) server = &http.Server{ Addr: ":3001", Handler: mux, } fmt.Printf(" Запуск HTTP сервера на порту 3001...\n") fmt.Printf(" Доступные эндпоинты:\n") fmt.Printf(" - GET /api/users - получить всех пользователей\n") fmt.Printf(" - POST /api/users - создать пользователя\n") fmt.Printf(" - GET /api/products - получить все продукты\n") fmt.Printf(" - POST /api/products - создать продукт\n") fmt.Printf(" - GET /api/orders - получить все заказы\n") fmt.Printf(" - POST /api/orders - создать заказ\n") // Запускаем сервер в горутине go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Printf("HTTP server error: %v", err) } }() // Даем серверу время на запуск time.Sleep(1 * time.Second) // Тестируем API fmt.Printf(" Тестирование API...\n") testAPIEndpoints() // Останавливаем сервер if err := server.Close(); err != nil { log.Printf("Failed to close server: %v", err) } } func handleUsers(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch r.Method { case "GET": users, err := Database.Read("users", linedb.LineDbAdapterOptions{}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Data: users}) case "POST": var user map[string]any if err := json.NewDecoder(r.Body).Decode(&user); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if err := Database.Insert(user, "users", linedb.LineDbAdapterOptions{}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "User created successfully"}) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } func handleProducts(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch r.Method { case "GET": products, err := Database.Read("products", linedb.LineDbAdapterOptions{}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Data: products}) case "POST": var product map[string]any if err := json.NewDecoder(r.Body).Decode(&product); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if err := Database.Insert(product, "products", linedb.LineDbAdapterOptions{}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "Product created successfully"}) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } func handleOrders(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") switch r.Method { case "GET": orders, err := Database.Read("orders", linedb.LineDbAdapterOptions{}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Data: orders}) case "POST": var order map[string]any if err := json.NewDecoder(r.Body).Decode(&order); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } if err := Database.Insert(order, "orders", linedb.LineDbAdapterOptions{}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } json.NewEncoder(w).Encode(APIResponse{Success: true, Message: "Order created successfully"}) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } func testAPIEndpoints() { // Тестируем GET /api/users resp, err := http.Get("http://localhost:3001/api/users") if err != nil { log.Printf("Failed to test GET /api/users: %v", err) return } defer resp.Body.Close() if resp.StatusCode == http.StatusOK { fmt.Printf(" ✓ GET /api/users работает\n") } else { fmt.Printf(" ✗ GET /api/users вернул статус %d\n", resp.StatusCode) } // Тестируем POST /api/users userData := map[string]any{ "id": 999, "username": "apitest", "email": "apitest@example.com", "isActive": true, "role": "user", "createdAt": time.Now().Unix(), } jsonData, _ := json.Marshal(userData) resp, err = http.Post("http://localhost:3001/api/users", "application/json", bytes.NewBuffer(jsonData)) if err != nil { log.Printf("Failed to test POST /api/users: %v", err) return } defer resp.Body.Close() if resp.StatusCode == http.StatusOK { fmt.Printf(" ✓ POST /api/users работает\n") } else { fmt.Printf(" ✗ POST /api/users вернул статус %d\n", resp.StatusCode) } } func main() { // Очищаем тестовую папку os.RemoveAll("./data/test-linedb-integration") // Создаем опции инициализации initOptions := &linedb.LineDbInitOptions{ CacheSize: 1000, CacheTTL: 10 * time.Second, DBFolder: "./data/test-linedb-integration", Collections: []linedb.JSONLFileOptions{ { CollectionName: "users", AllocSize: 256, IndexedFields: []string{"id", "username", "email"}, }, { CollectionName: "products", AllocSize: 256, IndexedFields: []string{"id", "name", "category"}, }, { CollectionName: "orders", AllocSize: 256, IndexedFields: []string{"id", "userId", "status"}, }, { CollectionName: "orderItems", AllocSize: 256, IndexedFields: []string{"id", "orderId", "productId"}, }, }, Partitions: []linedb.PartitionCollection{ { CollectionName: "orders", PartIDFnStr: "userId", }, }, } // Создаем базу данных Database = linedb.NewLineDb(nil) // Инициализируем базу данных if err := Database.Init(true, initOptions); err != nil { log.Fatalf("Failed to init database: %v", err) } defer Database.Close() fmt.Println("=== LineDb Integration Tests Demo ===") numbers := parseTestNumbers() if len(numbers) > 0 { runSelectedTests(numbers) } else { runAllTests() } // // Тест 1: Базовые CRUD операции // fmt.Println("1. Базовые CRUD операции") // testBasicCRUDOperations() // // Тест 2: Сложные запросы и фильтрация // fmt.Println("\n2. Сложные запросы и фильтрация") // testComplexQueries() // // Тест 3: Работа с несколькими коллекциями // fmt.Println("\n3. Работа с несколькими коллекциями") // testMultipleCollections() // // Тест 4: Производительность // fmt.Println("\n4. Тест производительности") // testPerformance() // // Тест 5: HTTP API сервер // fmt.Println("\n5. HTTP API сервер") // testHTTPServer() fmt.Println("\n=== Все тесты завершены ===") }