File size: 13,149 Bytes
192b2d2
 
afad319
192b2d2
afad319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192b2d2
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
 
 
 
192b2d2
 
 
 
 
 
 
 
 
 
 
afad319
192b2d2
afad319
 
 
 
 
 
 
192b2d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
192b2d2
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
 
 
 
192b2d2
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
 
 
 
192b2d2
 
 
afad319
192b2d2
 
 
 
afad319
192b2d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
 
 
 
192b2d2
 
 
afad319
192b2d2
 
 
 
 
 
 
 
 
 
 
 
 
 
afad319
192b2d2
 
 
 
 
afad319
192b2d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
 
 
 
192b2d2
 
afad319
192b2d2
afad319
 
 
 
 
192b2d2
 
afad319
 
 
 
 
 
192b2d2
 
 
 
afad319
192b2d2
 
 
 
 
 
 
afad319
192b2d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
 
 
 
192b2d2
 
 
 
 
 
afad319
192b2d2
afad319
 
 
 
 
 
 
192b2d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
 
 
 
 
 
 
 
192b2d2
 
afad319
192b2d2
 
 
 
 
 
 
 
 
 
afad319
192b2d2
 
 
 
 
 
 
 
 
afad319
 
 
 
 
192b2d2
 
 
 
 
 
 
afad319
192b2d2
 
 
 
 
 
afad319
192b2d2
 
afad319
 
 
 
192b2d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
afad319
 
 
 
192b2d2
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
#!/usr/bin/env python3
"""
# Test Script for Docker Deployment

This script provides comprehensive testing for the RAG system Docker deployment.

## Overview

The test script validates all components required for successful Docker deployment:
- Dockerfile syntax and structure
- Docker Compose configuration
- Docker build process
- Container runtime functionality
- File structure and dependencies

## Test Categories

1. **Dockerfile Tests**: Validate Dockerfile syntax and required components
2. **Docker Compose Tests**: Check docker-compose.yml configuration
3. **Build Tests**: Test Docker image building process
4. **Runtime Tests**: Validate container startup and health checks
5. **File Structure Tests**: Confirm all required files are present
6. **Requirements Tests**: Validate dependencies are properly specified

## Usage

Run the script to check Docker deployment readiness:
```bash
python test_docker.py
```

## Prerequisites

- Docker installed and running
- Docker Compose available
- Sufficient disk space for image building
- Network connectivity for base image downloads

## Expected Output

The script provides detailed feedback on each test:
- βœ… PASS: Component is ready for Docker deployment
- ❌ FAIL: Component needs attention before deployment
- ⚠️ WARNING: Optional component missing but not critical
"""

import os
import sys
import subprocess
from pathlib import Path


def test_dockerfile():
    """
    Test if Dockerfile exists and contains all required components

    This function validates:
    - Dockerfile exists in the project root
    - Contains essential Docker instructions
    - Proper syntax and structure
    - Required components for RAG system deployment

    Returns:
        bool: True if Dockerfile is valid, False otherwise
    """
    print("πŸ” Testing Dockerfile...")

    dockerfile_path = Path("Dockerfile")
    if not dockerfile_path.exists():
        print("❌ Dockerfile not found")
        return False

    try:
        with open(dockerfile_path, "r") as f:
            content = f.read()

        # List of essential Dockerfile components that must be present
        required_components = [
            "FROM python:",  # Base image specification
            "WORKDIR /app",  # Working directory setup
            "COPY requirements.txt",  # Requirements file copying
            "RUN pip install",  # Python package installation
            "COPY .",  # Application files copying
            "EXPOSE 8501",  # Port exposure for Streamlit
            'CMD ["streamlit"',  # Application startup command
        ]

        missing_components = []
        for component in required_components:
            if component in content:
                print(f"βœ… {component}")
            else:
                print(f"❌ {component} (missing)")
                missing_components.append(component)

        if missing_components:
            print(f"❌ Missing Dockerfile components: {missing_components}")
            return False

        return True

    except Exception as e:
        print(f"❌ Dockerfile test failed: {e}")
        return False


def test_dockerignore():
    """
    Test if .dockerignore exists (optional but recommended)

    This function checks for the presence of .dockerignore file,
    which helps optimize Docker builds by excluding unnecessary files.

    Returns:
        bool: True if .dockerignore exists or is optional, False if critical
    """
    print("\nπŸ” Testing .dockerignore...")

    dockerignore_path = Path(".dockerignore")
    if dockerignore_path.exists():
        print("βœ… .dockerignore exists")
        return True
    else:
        print("⚠️  .dockerignore not found (optional but recommended)")
        return True


def test_docker_compose():
    """
    Test if docker-compose.yml exists and is properly configured

    This function validates:
    - docker-compose.yml file exists
    - Contains proper service definitions
    - Port mappings are correct
    - Volume mounts are configured

    Returns:
        bool: True if docker-compose.yml is valid, False otherwise
    """
    print("\nπŸ” Testing docker-compose.yml...")

    compose_path = Path("docker-compose.yml")
    if compose_path.exists():
        print("βœ… docker-compose.yml exists")
        return True
    else:
        print("⚠️  docker-compose.yml not found (optional)")
        return True


def test_docker_build():
    """
    Test Docker build process locally

    This function:
    - Attempts to build the Docker image
    - Validates build process completes successfully
    - Checks for build errors and warnings
    - Ensures all dependencies are properly resolved

    Returns:
        bool: True if Docker build succeeds, False otherwise
    """
    print("\nπŸ” Testing Docker build...")

    try:
        # Test Docker build with timeout to prevent hanging
        result = subprocess.run(
            ["docker", "build", "-t", "rag-system-test", "."],
            capture_output=True,
            text=True,
            timeout=300,  # 5 minutes timeout for build
        )

        if result.returncode == 0:
            print("βœ… Docker build successful")
            return True
        else:
            print(f"❌ Docker build failed: {result.stderr}")
            return False

    except subprocess.TimeoutExpired:
        print("❌ Docker build timed out")
        return False
    except FileNotFoundError:
        print("⚠️  Docker not installed or not in PATH")
        return False
    except Exception as e:
        print(f"❌ Docker build test failed: {e}")
        return False


def test_docker_run():
    """
    Test Docker container runtime functionality

    This function:
    - Attempts to run the built Docker container
    - Validates container startup process
    - Checks if the application is accessible
    - Tests basic container functionality

    Returns:
        bool: True if Docker run succeeds, False otherwise
    """
    print("\nπŸ” Testing Docker run...")

    try:
        # Test Docker run with brief execution
        result = subprocess.run(
            [
                "docker",
                "run",
                "--rm",
                "-d",
                "-p",
                "8501:8501",
                "--name",
                "rag-test",
                "rag-system-test",
            ],
            capture_output=True,
            text=True,
            timeout=30,  # 30 seconds timeout for startup
        )

        if result.returncode == 0:
            print("βœ… Docker run successful")

            # Clean up the test container
            subprocess.run(["docker", "stop", "rag-test"], capture_output=True)
            return True
        else:
            print(f"❌ Docker run failed: {result.stderr}")
            return False

    except subprocess.TimeoutExpired:
        print("❌ Docker run timed out")
        return False
    except FileNotFoundError:
        print("⚠️  Docker not installed or not in PATH")
        return False
    except Exception as e:
        print(f"❌ Docker run test failed: {e}")
        return False


def test_file_structure():
    """
    Test if all required files exist for Docker deployment

    This function checks for essential files:
    - Main application files
    - Configuration files
    - Docker-related files
    - Documentation files

    Returns:
        bool: True if all required files exist, False otherwise
    """
    print("\nπŸ” Testing file structure...")

    # List of required files for Docker deployment
    required_files = [
        "app.py",  # Main Streamlit application
        "rag_system.py",  # Core RAG system
        "pdf_processor.py",  # PDF processing utilities
        "requirements.txt",  # Python dependencies
        "Dockerfile",  # Docker configuration
    ]

    # List of optional files (nice to have but not critical)
    optional_files = [
        ".dockerignore",  # Docker build optimization
        "docker-compose.yml",  # Multi-container setup
        "README.md",  # Project documentation
    ]

    missing_required = []
    missing_optional = []

    # Check required files
    for file in required_files:
        if os.path.exists(file):
            print(f"βœ… {file}")
        else:
            print(f"❌ {file} (missing)")
            missing_required.append(file)

    # Check optional files
    for file in optional_files:
        if os.path.exists(file):
            print(f"βœ… {file}")
        else:
            print(f"⚠️  {file} (optional)")
            missing_optional.append(file)

    if missing_required:
        print(f"❌ Missing required files: {missing_required}")
        return False

    return True


def test_requirements():
    """
    Test if requirements.txt contains all essential packages

    This function validates:
    - Essential packages are listed
    - Package versions are specified
    - No obvious missing dependencies
    - Compatibility with Docker environment

    Returns:
        bool: True if requirements are valid, False otherwise
    """
    print("\nπŸ” Testing requirements.txt...")

    try:
        with open("requirements.txt", "r") as f:
            requirements = f.read()

        # List of essential packages that must be present
        essential_packages = [
            "streamlit",  # Web framework
            "torch",  # Deep learning
            "transformers",  # Language models
            "sentence-transformers",  # Embeddings
            "faiss-cpu",  # Vector search
            "rank-bm25",  # Sparse retrieval
            "pypdf",  # PDF processing
        ]

        missing_packages = []
        for package in essential_packages:
            if package in requirements:
                print(f"βœ… {package}")
            else:
                print(f"❌ {package} (missing)")
                missing_packages.append(package)

        if missing_packages:
            print(f"❌ Missing packages: {missing_packages}")
            return False

        return True

    except Exception as e:
        print(f"❌ Requirements test failed: {e}")
        return False


def main():
    """
    Run all Docker deployment tests and provide comprehensive feedback

    This function:
    1. Executes all Docker-related test categories
    2. Tracks test results and provides detailed feedback
    3. Gives deployment recommendations
    4. Identifies potential issues before deployment

    The tests are designed to catch common Docker deployment issues early.
    """
    print("🐳 Docker Deployment Test\n")

    # Define all test functions with descriptive names
    tests = [
        ("File Structure", test_file_structure),
        ("Requirements", test_requirements),
        ("Dockerfile", test_dockerfile),
        (".dockerignore", test_dockerignore),
        ("docker-compose.yml", test_docker_compose),
        ("Docker Build", test_docker_build),
        ("Docker Run", test_docker_run),
    ]

    # Execute all tests and collect results
    results = []
    for test_name, test_func in tests:
        try:
            result = test_func()
            results.append((test_name, result))
        except Exception as e:
            print(f"❌ {test_name} test failed with exception: {e}")
            results.append((test_name, False))

    # =============================================================================
    # RESULTS SUMMARY
    # =============================================================================

    # Display comprehensive test results
    print("\n" + "=" * 50)
    print("πŸ“Š Test Results Summary")
    print("=" * 50)

    passed = 0
    total = len(results)

    # Show individual test results
    for test_name, result in results:
        status = "βœ… PASS" if result else "❌ FAIL"
        print(f"{test_name:20} {status}")
        if result:
            passed += 1

    # Display overall statistics
    print(f"\nOverall: {passed}/{total} tests passed")

    # =============================================================================
    # DEPLOYMENT RECOMMENDATIONS
    # =============================================================================

    if passed == total:
        print("πŸŽ‰ All tests passed! Ready for Hugging Face Docker deployment.")
        print("\nNext steps:")
        print("1. Create a new Hugging Face Space with Docker SDK")
        print("2. Upload all files from this directory")
        print("3. Wait for Docker build to complete")
        print("4. Test your RAG system!")
    else:
        print("⚠️  Some tests failed. Please fix the issues before deployment.")
        print("\nTroubleshooting:")
        print("1. Install Docker if not available")
        print("2. Check file permissions and paths")
        print("3. Verify Dockerfile syntax")
        print("4. Test Docker build locally: docker build -t rag-system .")


# =============================================================================
# SCRIPT ENTRY POINT
# =============================================================================

if __name__ == "__main__":
    main()