From bdc0f58abde39bda60416bbe58d3a6f11bd8505d Mon Sep 17 00:00:00 2001 From: a2nr Date: Sat, 17 Jan 2026 13:16:58 +0700 Subject: [PATCH] update test suit and enhance render image --- static/style.css | 2 + templates/index.html | 22 +- templates/lesson.html | 31 +- test/Dockerfile | 22 -- test/locustfile.py | 548 ++++++++++++++++++++++++++++----- test/podman-compose.locust.yml | 14 - test/podman-compose.yml | 28 ++ test/requirements.txt | 1 - test/test_production.sh | 101 ------ 9 files changed, 551 insertions(+), 218 deletions(-) delete mode 100644 test/Dockerfile delete mode 100644 test/podman-compose.locust.yml create mode 100644 test/podman-compose.yml delete mode 100644 test/requirements.txt delete mode 100755 test/test_production.sh diff --git a/static/style.css b/static/style.css index 6ded92b..7ef4517 100644 --- a/static/style.css +++ b/static/style.css @@ -183,6 +183,8 @@ body { cursor: pointer; transition: transform 0.3s ease; object-fit: contain; /* Ensures the entire image is visible within its bounds */ + image-rendering: -webkit-optimize-contrast; /* Optimizes rendering for better performance */ + image-rendering: crisp-edges; /* Better rendering for scaled images */ } .lesson-content img.zoomable-img { diff --git a/templates/index.html b/templates/index.html index 2768926..e5d48ac 100644 --- a/templates/index.html +++ b/templates/index.html @@ -355,8 +355,8 @@ }); } - // Image zoom functionality for home page - document.addEventListener('DOMContentLoaded', function() { + // Dynamic image sizing and zoom functionality for home page + function setupHomeImageHandling() { // Select all images inside home content const homeImages = document.querySelectorAll('.home-content img'); @@ -366,6 +366,19 @@ img.classList.add('zoomable-img'); } + // Add load event to handle image sizing + if (!img.complete) { + img.addEventListener('load', function() { + // Image loaded, ensure it fits container + this.style.maxWidth = '100%'; + this.style.height = 'auto'; + }); + } else { + // Image already loaded + img.style.maxWidth = '100%'; + img.style.height = 'auto'; + } + img.addEventListener('click', function() { // Toggle enlarged class to zoom in/out this.classList.toggle('enlarged'); @@ -385,7 +398,10 @@ enlargedImg.classList.remove('enlarged'); } }); - }); + } + + // Call the function to set up image handling + setupHomeImageHandling(); }); diff --git a/templates/lesson.html b/templates/lesson.html index cc55166..f0b6653 100644 --- a/templates/lesson.html +++ b/templates/lesson.html @@ -997,21 +997,39 @@ }); } - // Image zoom functionality - document.addEventListener('DOMContentLoaded', function() { + // Dynamic image sizing and zoom functionality + function setupImageHandling() { // Select all images inside lesson content const lessonImages = document.querySelectorAll('.lesson-content img'); lessonImages.forEach(img => { // Add zoomable-img class to make them zoomable - img.classList.add('zoomable-img'); + if (!img.classList.contains('zoomable-img')) { + img.classList.add('zoomable-img'); + } + + // Add load event to handle image sizing + if (!img.complete) { + img.addEventListener('load', function() { + // Image loaded, ensure it fits container + this.style.maxWidth = '100%'; + this.style.height = 'auto'; + }); + } else { + // Image already loaded + img.style.maxWidth = '100%'; + img.style.height = 'auto'; + } img.addEventListener('click', function() { // Toggle enlarged class to zoom in/out this.classList.toggle('enlarged'); // Prevent default behavior to avoid any conflicts - event.preventDefault(); + if (event) { + event.preventDefault(); + event.stopPropagation(); + } }); }); @@ -1022,7 +1040,10 @@ enlargedImg.classList.remove('enlarged'); } }); - }); + } + + // Call the function to set up image handling + setupImageHandling(); }); diff --git a/test/Dockerfile b/test/Dockerfile deleted file mode 100644 index 9b0001c..0000000 --- a/test/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM python:3.11-slim - -# Install gcc compiler for C code compilation (needed for communication with LMS) -RUN apt-get update && \ - apt-get install -y gcc build-essential && \ - rm -rf /var/lib/apt/lists/* - -# Set working directory -WORKDIR /locust - -# Install dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# Copy locustfile -COPY locustfile.py . - -# Expose port for Locust web interface -EXPOSE 8089 - -# Command to run Locust -CMD ["locust", "-f", "locustfile.py"] \ No newline at end of file diff --git a/test/locustfile.py b/test/locustfile.py index cb52911..e614d56 100644 --- a/test/locustfile.py +++ b/test/locustfile.py @@ -1,105 +1,509 @@ -from locust import HttpUser, TaskSet, task, between +""" +Load testing script for C Programming Learning Management System using Locust +""" + +import os import random +import csv +import glob +from locust import HttpUser, TaskSet, task, between import json -class CProgrammingLMSUser(HttpUser): + +def get_number_of_students(): """ - Load testing for C Programming Learning Management System + Get the number of students from tokens_siswa.csv """ - wait_time = between(1, 3) - + tokens_file = '/mnt/locust/tokens_siswa.csv' + num_students = 1 # Default to 1 if file doesn't exist or is empty + + if os.path.exists(tokens_file): + with open(tokens_file, 'r', newline='', encoding='utf-8') as csvfile: + reader = csv.DictReader(csvfile, delimiter=';') + # Count the number of data rows (excluding header) + num_students = sum(1 for row in reader) + + return max(1, num_students) # Ensure at least 1 student + + +class LMSCUserBehavior(TaskSet): + """ + Define user behavior for the LMS-C application + """ + def on_start(self): """ - Called when a user starts + Initialize user session with a token from tokens_siswa.csv """ - # Get a list of available lessons to choose from - self.lessons = [] - response = self.client.get("/") - if response.status_code == 200: - # In a real implementation, we would parse the response to get lesson URLs - # For now, we'll use a predefined list of lesson paths - self.lessons = [ - "introduction_to_c.md", - "variables_and_data_types.md" - ] + # Read all student data from tokens_siswa.csv + tokens_file = '/mnt/locust/tokens_siswa.csv' + all_students = [] + + if os.path.exists(tokens_file): + with open(tokens_file, 'r', newline='', encoding='utf-8') as csvfile: + reader = csv.DictReader(csvfile, delimiter=';') + all_students = list(reader) # Convert to list to get all rows + + # Select a random student from the CSV + if all_students: + selected_student = random.choice(all_students) + self.student_token = selected_student.get('token', f"STUDENT_TOKEN_{random.randint(1000, 9999)}") + self.student_name = selected_student.get('nama_siswa', f"Student_{random.randint(1000, 9999)}") + else: + # Fallback if no students in CSV + self.student_token = f"STUDENT_TOKEN_{random.randint(1000, 9999)}" + self.student_name = f"Student_{random.randint(1000, 9999)}" + + # Register the student in the system + register_payload = { + "token": self.student_token, + "student_name": self.student_name + } + + # Try to register the student (this might fail if the token already exists, which is fine) + try: + self.client.post("/validate-token", json=register_payload) + except: + pass # If validation fails, we'll continue anyway + + # Read lesson files from content directory + content_dir = '/mnt/locust/content' # Path relative to where locust runs in container + self.lesson_files = [] + + if os.path.exists(content_dir): + lesson_paths = glob.glob(os.path.join(content_dir, "*.md")) + self.lesson_files = [os.path.basename(path) for path in lesson_paths if os.path.isfile(path)] + + # No fallback to example_content since it won't be on the server @task(3) - def view_home_page(self): + def view_homepage(self): """ - Task to view the home page + Task to view the homepage with lessons list """ self.client.get("/") - @task(5) + @task(2) def view_lesson(self): """ - Task to view a random lesson + Task to view a specific lesson """ - if self.lessons: - lesson = random.choice(self.lessons) - self.client.get(f"/lesson/{lesson}") + if self.lesson_files: + # Randomly select a lesson to view from available lessons + lesson = random.choice(self.lesson_files) + self.client.get(f"/lesson/{lesson}?token={self.student_token}") + else: + # Visit homepage instead of falling back to example content + self.client.get(f"/?token={self.student_token}") - @task(2) - def compile_c_code(self): + @task(1) + def login_student(self): """ - Task to compile C code using the API + Task to simulate student login """ - # Sample C code for testing - c_code = """ - #include - - int main() { - printf("Hello, World!\\n"); - return 0; + login_payload = { + "token": self.student_token } - """ - response = self.client.post( - "/compile", - data={"code": c_code}, - headers={"Content-Type": "application/x-www-form-urlencoded"} - ) + response = self.client.post("/login", json=login_payload) + + if response.status_code == 200: + data = response.json() + if data.get("success"): + print(f"Successfully logged in student: {data.get('student_name')}") + + @task(4) + def compile_code(self): + """ + Task to compile and run code (universal for any supported language) + """ + # Determine the programming language from environment or default to 'c' + programming_language = os.environ.get('DEFAULT_PROGRAMMING_LANGUAGE', 'c') + + # Get code from lesson content or use default samples + code = self.get_code_from_lessons(programming_language) + + if not code: + # Fallback to sample codes if no lesson content is available + code = self.get_default_sample_code(programming_language) + + # Prepare payload for compilation + compile_payload = { + "code": code, + "language": programming_language + } + + # Send POST request to compile endpoint + with self.client.post("/compile", + json=compile_payload, + catch_response=True) as response: + if response.status_code == 200: + result = response.json() + if result.get("success"): + response.success() + else: + response.failure(f"Compilation failed: {result.get('error', 'Unknown error')}") + else: + response.failure(f"HTTP {response.status_code}: Request failed") + + def get_code_from_lessons(self, language): + """ + Extract code from lesson content using markers + """ + if not self.lesson_files: + return None + + # Select a random lesson to extract code from + lesson_file = random.choice(self.lesson_files) + lesson_path = f'/mnt/locust/content/{lesson_file}' + + if not os.path.exists(lesson_path): + return None + + try: + with open(lesson_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Look for INITIAL_CODE and SOLUTION_CODE sections + initial_code = self.extract_code_section(content, '---INITIAL_CODE---', '---END_INITIAL_CODE---') + solution_code = self.extract_code_section(content, '---SOLUTION_CODE---', '---END_SOLUTION_CODE---') + + # Choose randomly between initial and solution code if both exist + available_codes = [code for code in [initial_code, solution_code] if code] + + if available_codes: + return random.choice(available_codes) + except Exception: + pass # If there's an error reading the file, return None + + return None + + def extract_code_section(self, content, start_marker, end_marker): + """ + Extract code between start and end markers + """ + start_idx = content.find(start_marker) + end_idx = content.find(end_marker) + + if start_idx != -1 and end_idx != -1 and end_idx > start_idx: + start_pos = start_idx + len(start_marker) + extracted_code = content[start_pos:end_idx].strip() + return extracted_code + + return None + + def get_default_sample_code(self, language): + """ + Get default sample code based on the programming language + """ + if language.lower() == 'c': + sample_codes = [ + '''#include + +int main() { + printf("Hello, World!\\n"); + return 0; +}''', + '''#include + +int main() { + int a = 5, b = 10; + int sum = a + b; + printf("Sum is: %d\\n", sum); + return 0; +}''', + '''#include + +int main() { + int i; + for(i = 0; i < 5; i++) { + printf("Count: %d\\n", i); + } + return 0; +}''' + ] + elif language.lower() == 'python': + sample_codes = [ + '''print("Hello, World!")''', + ''' +name = "Locust" +print(f"Hello, {name}!")''', + ''' +numbers = [1, 2, 3, 4, 5] +for num in numbers: + print(f"Number: {num}") +''' + ] + else: + # Default to C if language is not recognized + sample_codes = [ + '''#include + +int main() { + printf("Hello, World!\\n"); + return 0; +}''' + ] + + return random.choice(sample_codes) @task(1) def validate_token(self): """ - Task to validate a student token + Task to validate student token """ - # Use a random token for testing - token = f"token_{random.randint(1000, 9999)}" + validate_payload = { + "token": self.student_token + } - response = self.client.post( - "/validate-token", - json={"token": token} - ) - - @task(1) - def login_with_token(self): - """ - Task to login with a student token - """ - # Use a random token for testing - token = f"token_{random.randint(1000, 9999)}" - - response = self.client.post( - "/login", - json={"token": token} - ) + self.client.post("/validate-token", json=validate_payload) @task(1) def track_progress(self): """ - Task to track student progress + Task to track student progress for a lesson """ - # Use a random token and lesson for testing - token = f"token_{random.randint(1000, 9999)}" - lesson_name = random.choice(["introduction_to_c", "variables_and_data_types"]) - - response = self.client.post( - "/track-progress", - json={ - "token": token, - "lesson_name": lesson_name, - "status": "completed" - } - ) \ No newline at end of file + if self.lesson_files: + # Select a random lesson from available lessons, removing .md extension for lesson_name + lesson_file = random.choice(self.lesson_files) + lesson_name = lesson_file.replace('.md', '') + else: + # Don't track progress if no lessons are available + return + + progress_payload = { + "token": self.student_token, + "lesson_name": lesson_name, + "status": "completed" + } + + self.client.post("/track-progress", json=progress_payload) + + +class WebsiteUser(HttpUser): + """ + Main user class for the LMS-C load test + """ + tasks = [LMSCUserBehavior] + + # Wait time between tasks (1-3 seconds) + wait_time = between(1, 3) + + def on_start(self): + """ + Setup for each user when they start + """ + # Perform initial homepage visit + self.client.get("/") + + def on_stop(self): + """ + Cleanup when user stops + """ + # Logout the user + try: + self.client.post("/logout", json={}) + except: + pass # Ignore logout errors + + +# Additional task sets for more complex behaviors + +class LessonNavigationTaskSet(TaskSet): + """ + Task set focused on navigating through lessons + """ + + def on_start(self): + """ + Initialize lesson navigation with available lessons + """ + # Read lesson files from content directory + content_dir = '/mnt/locust/content' # Path relative to where locust runs in container + self.lesson_files = [] + + if os.path.exists(content_dir): + lesson_paths = glob.glob(os.path.join(content_dir, "*.md")) + self.lesson_files = [os.path.basename(path) for path in lesson_paths if os.path.isfile(path)] + + # No fallback to example_content since it won't be on the server + + @task(5) + def browse_lessons(self): + """ + Browse through different lessons + """ + if self.lesson_files: + for lesson in self.lesson_files: + self.client.get(f"/lesson/{lesson}") + self.wait() + else: + # No fallback since example_content won't be on the server + # Just visit the homepage if no lessons are found + self.client.get("/") + self.wait() + + @task(2) + def go_back_to_home(self): + """ + Navigate back to home page + """ + self.client.get("/") + + @task(1) + def stop_browsing(self): + """ + Stop browsing and potentially switch to another task set + """ + self.interrupt() + + +class CompilationFocusedTaskSet(TaskSet): + """ + Task set focused on code compilation activities + """ + + def on_start(self): + """ + Initialize with lesson files for code extraction + """ + # Read lesson files from content directory + content_dir = '/mnt/locust/content' # Path relative to where locust runs in container + self.lesson_files = [] + + if os.path.exists(content_dir): + lesson_paths = glob.glob(os.path.join(content_dir, "*.md")) + self.lesson_files = [os.path.basename(path) for path in lesson_paths if os.path.isfile(path)] + + # No fallback to example_content since it won't be on the server + + @task(10) + def compile_different_codes(self): + """ + Compile various programs based on the default language or from lesson content + """ + # Determine the programming language from environment or default to 'c' + programming_language = os.environ.get('DEFAULT_PROGRAMMING_LANGUAGE', 'c') + + # Get code from lesson content or use default samples + code = self.get_code_from_lessons(programming_language) + + if not code: + # Fallback to sample codes if no lesson content is available + code = self.get_default_sample_code(programming_language) + + response = self.client.post("/compile", json={ + "code": code, + "language": programming_language + }) + + if response.status_code == 200: + result = response.json() + if not result.get("success"): + print(f"Compilation error: {result.get('error')}") + + def get_code_from_lessons(self, language): + """ + Extract code from lesson content using markers + """ + if not self.lesson_files: + return None + + # Select a random lesson to extract code from + lesson_file = random.choice(self.lesson_files) + lesson_path = f'/mnt/locust/content/{lesson_file}' + + if not os.path.exists(lesson_path): + return None + + try: + with open(lesson_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Look for INITIAL_CODE and SOLUTION_CODE sections + initial_code = self.extract_code_section(content, '---INITIAL_CODE---', '---END_INITIAL_CODE---') + solution_code = self.extract_code_section(content, '---SOLUTION_CODE---', '---END_SOLUTION_CODE---') + + # Choose randomly between initial and solution code if both exist + available_codes = [code for code in [initial_code, solution_code] if code] + + if available_codes: + return random.choice(available_codes) + except Exception: + pass # If there's an error reading the file, return None + + return None + + def extract_code_section(self, content, start_marker, end_marker): + """ + Extract code between start and end markers + """ + start_idx = content.find(start_marker) + end_idx = content.find(end_marker) + + if start_idx != -1 and end_idx != -1 and end_idx > start_idx: + start_pos = start_idx + len(start_marker) + extracted_code = content[start_pos:end_idx].strip() + return extracted_code + + return None + + def get_default_sample_code(self, language): + """ + Get default sample code based on the programming language + """ + if language.lower() == 'c': + sample_codes = [ + '''#include +int main() { printf("Simple Hello World\\n"); return 0; }''', + '''#include +int main() { + int arr[] = {1, 2, 3, 4, 5}; + int i; + for(i = 0; i < 5; i++) { + printf("%d ", arr[i]); + } + printf("\\n"); + return 0; +}''', + '''#include +int factorial(int n) { + if(n <= 1) return 1; + return n * factorial(n-1); +} +int main() { + int num = 5; + printf("Factorial of %d is %d\\n", num, factorial(num)); + return 0; +}''' + ] + elif language.lower() == 'python': + sample_codes = [ + '''print("Hello, World!")''', + ''' +name = "Locust" +print(f"Hello, {name}!")''', + ''' +numbers = [1, 2, 3, 4, 5] +for num in numbers: + print(f"Number: {num}") +''' + ] + else: + # Default to C if language is not recognized + sample_codes = [ + '''#include +int main() { printf("Simple Hello World\\n"); return 0; }''' + ] + + return random.choice(sample_codes) + + +class AdvancedUser(HttpUser): + """ + Advanced user that performs more complex behaviors + """ + weight = 2 # Twice as likely to be chosen as WebsiteUser + tasks = {LessonNavigationTaskSet: 2, CompilationFocusedTaskSet: 3, LMSCUserBehavior: 4} + + wait_time = between(0.5, 2) \ No newline at end of file diff --git a/test/podman-compose.locust.yml b/test/podman-compose.locust.yml deleted file mode 100644 index b05b185..0000000 --- a/test/podman-compose.locust.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: '3.8' - -services: - locust: - build: . - ports: - - "8089:8089" - volumes: - - ./:/locust - command: ["locust", "-f", "locustfile.py", "--host", "http://:5000"] - -networks: - default: - driver: bridge diff --git a/test/podman-compose.yml b/test/podman-compose.yml new file mode 100644 index 0000000..d1d5250 --- /dev/null +++ b/test/podman-compose.yml @@ -0,0 +1,28 @@ +version: '3.8' + +services: + master: + container_name: locust_master + image: locustio/locust + ports: + - "8089:8089" + volumes: + - ../../:/mnt/locust + environment: + # URL target default (bisa diganti saat run) + - LOCUST_HOST=${TARGET_URL:-http://example.com} + # Number of students to simulate - defaults to 10 but should match number in tokens_siswa.csv + - LOCUST_NUM_STUDENTS=${LOCUST_NUM_STUDENTS:-10} + command: -f /mnt/locust/elemes/test/locustfile.py --master + + worker: + image: locustio/locust + volumes: + - ../../:/mnt/locust + environment: + - LOCUST_HOST=${TARGET_URL:-http://example.com} + # Number of students to simulate - defaults to 10 but should match number in tokens_siswa.csv + - LOCUST_NUM_STUDENTS=${LOCUST_NUM_STUDENTS:-10} + # Worker akan otomatis mendeteksi Master lewat nama service 'master' + command: -f /mnt/locust/elemes/test/locustfile.py --worker --master-host locust_master + diff --git a/test/requirements.txt b/test/requirements.txt deleted file mode 100644 index bedc024..0000000 --- a/test/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -locust==2.29.1 \ No newline at end of file diff --git a/test/test_production.sh b/test/test_production.sh deleted file mode 100755 index 8e45ad1..0000000 --- a/test/test_production.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash - -# Test script for LMS-C Production Environment - -echo "Starting LMS-C Production Environment Tests..." - -# Start the application -echo "Starting the application..." -cd .. -./start.sh -cd test - -# Wait for the application to start -echo "Waiting for application to start..." -sleep 10 - -# Test 1: Check if the home page loads and displays lessons -echo "Test 1: Checking home page and lesson display..." -HOME_PAGE_CHECK=$(curl -s http://localhost:5000/ | grep -i "Available Lessons" | wc -l) -if [ $HOME_PAGE_CHECK -gt 0 ]; then - echo "✓ Test 1 PASSED: Home page displays lessons correctly" -else - echo "✗ Test 1 FAILED: Home page does not display lessons" -fi - -# Test 2: Check if specific lesson pages load -echo "Test 2: Checking lesson page loading..." -LESSON_PAGE_CHECK=$(curl -s http://localhost:5000/lesson/welcome.md | grep -i "C Programming Learning System" | wc -l) -if [ $LESSON_PAGE_CHECK -gt 0 ]; then - echo "✓ Test 2 PASSED: Lesson pages load correctly" -else - echo "✗ Test 2 FAILED: Lesson pages do not load" -fi - -# Test 3: Test the code compilation API -echo "Test 3: Testing code compilation API..." -COMPILATION_TEST=$(curl -s -X POST http://localhost:5000/compile \ - -H "Content-Type: application/json" \ - -d '{"code":"#include \n\nint main() {\n printf(\"Hello, World!\\n\");\n return 0;\n}"}') - -# Check if the response contains success: true and the expected output -if echo "$COMPILATION_TEST" | grep -q '"success":true' && echo "$COMPILATION_TEST" | grep -q "Hello, World!"; then - echo "✓ Test 3 PASSED: Code compilation API works correctly" -else - echo "✗ Test 3 FAILED: Code compilation API not working" - echo " Response: $COMPILATION_TEST" -fi - -# Test 4: Test with a more complex C program -echo "Test 4: Testing complex code compilation..." -COMPLEX_CODE='{ - "code": "#include \nint main() {\n int i, sum = 0;\n for (i = 1; i <= 5; i++) {\n sum += i;\n }\n printf(\"Sum of 1 to 5 is: %d\\n\", sum);\n return 0;\n}" -}' - -COMPLEX_TEST=$(curl -s -X POST http://localhost:5000/compile \ - -H "Content-Type: application/json" \ - -d "$COMPLEX_CODE") - -if echo "$COMPLEX_TEST" | grep -q '"success":true' && echo "$COMPLEX_TEST" | grep -q "Sum of 1 to 5"; then - echo "✓ Test 4 PASSED: Complex code compilation works correctly" -else - echo "✗ Test 4 FAILED: Complex code compilation not working" - echo " Response: $COMPLEX_TEST" -fi - -# Test 5: Check if all expected lesson files are accessible -echo "Test 5: Checking lesson accessibility..." -LESSON_FILES=("welcome.md" "hello_world.md" "introduction_to_c.md" "arrays.md") -ALL_LESSONS_ACCESSIBLE=true - -for lesson in "${LESSON_FILES[@]}"; do - lesson_check=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:5000/lesson/$lesson") - if [ "$lesson_check" -ne 200 ]; then - echo "✗ Lesson $lesson returned HTTP $lesson_check" - ALL_LESSONS_ACCESSIBLE=false - fi -done - -if [ "$ALL_LESSONS_ACCESSIBLE" = true ]; then - echo "✓ Test 5 PASSED: All test lessons are accessible" -else - echo "✗ Test 5 FAILED: Some lessons are not accessible" -fi - -# Test 6: Check for errors in container logs -echo "Test 6: Checking container logs for errors..." -LOG_ERRORS=$(podman logs elemes_lms-c_1 2>&1 | grep -i error | wc -l) -if [ $LOG_ERRORS -eq 0 ]; then - echo "✓ Test 6 PASSED: No errors found in container logs" -else - echo "✗ Test 6 FAILED: Errors found in container logs" - podman logs elemes_lms-c_1 | grep -i error -fi - -# Stop the application -echo "Stopping the application..." -cd .. -./stop.sh -cd test - -echo "All tests completed!" \ No newline at end of file