1
1
import pytest
2
2
import subprocess
3
3
import os
4
- import yaml
4
+ import time
5
5
6
- def test_docker_compose_ctfd_ipython_mount ():
7
- """Test that the docker-compose.yml contains the correct volume mount for ipython persistence"""
6
+ from utils import dojo_run
7
+
8
+
9
+ def is_dojo_environment_available ():
10
+ """Check if the dojo environment is available for testing"""
11
+ try :
12
+ # Try to run a simple dojo command to check if containers are running
13
+ result = subprocess .run (
14
+ ["docker" , "ps" , "--filter" , "name=ctfd" , "--format" , "{{.Names}}" ],
15
+ capture_output = True , text = True , timeout = 10
16
+ )
17
+ return "ctfd" in result .stdout
18
+ except (subprocess .TimeoutExpired , subprocess .CalledProcessError , FileNotFoundError ):
19
+ return False
20
+
21
+
22
+ def test_flask_ipython_history_persistence ():
23
+ """
24
+ Functional test: Check that you can run `dojo flask`, enter a command,
25
+ and have a resulting /data/ctfd-ipython/profile_default/history.sqlite file created.
8
26
9
- # Load and parse the docker-compose.yml file
10
- with open ( '/home/runner/work/dojo/dojo/docker-compose.yml' , 'r' ) as f :
11
- compose_config = yaml . safe_load ( f )
27
+ This tests the actual functionality of IPython history persistence rather than
28
+ just checking configuration files.
29
+ """
12
30
13
- # Check that the ctfd service exists
14
- assert 'ctfd' in compose_config ['services' ], "ctfd service should exist in docker-compose.yml"
31
+ # Check if the dojo environment is available
32
+ if not is_dojo_environment_available ():
33
+ pytest .skip ("Dojo environment (ctfd container) not running - skipping functional test" )
15
34
16
- # Check that the ctfd service has volumes
17
- ctfd_service = compose_config [ 'services' ][ ' ctfd' ]
18
- assert 'volumes' in ctfd_service , "ctfd service should have volumes "
35
+ # Set up paths for the test
36
+ history_dir = "/data/ ctfd-ipython/profile_default"
37
+ history_file = f" { history_dir } /history.sqlite "
19
38
20
- # Check that the ipython volume mount is present
21
- volumes = ctfd_service ['volumes' ]
22
- ipython_mount = "/data/ctfd-ipython:/root/.ipython"
23
- assert ipython_mount in volumes , f"ctfd service should have ipython volume mount: { ipython_mount } "
24
-
25
- def test_dojo_init_creates_ctfd_ipython_directory ():
26
- """Test that the dojo-init script creates the ctfd-ipython directory"""
39
+ # Clean up any existing history to start fresh
40
+ if os .path .exists (history_file ):
41
+ os .remove (history_file )
27
42
28
- # Read the dojo-init script
29
- with open ('/home/runner/work/dojo/dojo/dojo/dojo-init' , 'r' ) as f :
30
- init_script = f .read ()
43
+ # Ensure the directory exists (should be created by dojo-init)
44
+ os .makedirs (history_dir , exist_ok = True )
31
45
32
- # Check that the script creates the ctfd-ipython directory
33
- assert "mkdir -p /data/ctfd-ipython" in init_script , "dojo-init should create /data/ctfd-ipython directory"
34
-
35
- def test_flask_command_exists ():
36
- """Test that the dojo flask command exists and has correct implementation"""
46
+ # Test commands to run in the flask shell
47
+ # These are simple Python commands that should create IPython history
48
+ test_commands = [
49
+ "# Testing IPython history persistence" ,
50
+ "x = 42" ,
51
+ "print(f'The answer is {x}')" ,
52
+ "exit()"
53
+ ]
37
54
38
- # Read the dojo script
39
- with open ('/home/runner/work/dojo/dojo/dojo/dojo' , 'r' ) as f :
40
- dojo_script = f .read ()
55
+ command_input = "\n " .join (test_commands ) + "\n "
41
56
42
- # Check that the flask command exists
43
- assert '"flask")' in dojo_script , "dojo script should have flask command"
44
- assert 'docker exec $DOCKER_ARGS ctfd flask shell' in dojo_script , "flask command should execute flask shell in ctfd container"
57
+ try :
58
+ # Run the flask shell with commands that will create history
59
+ result = dojo_run (
60
+ "flask" ,
61
+ input = command_input ,
62
+ timeout = 60 ,
63
+ check = False # Don't fail if flask exits with non-zero (normal for interactive shells)
64
+ )
65
+
66
+ # Allow some time for IPython to write the history file
67
+ # IPython may write history on exit, so we need to wait
68
+ time .sleep (3 )
69
+
70
+ # Verify that the history file was created
71
+ assert os .path .exists (history_file ), (
72
+ f"IPython history file should be created at { history_file } after running flask commands. "
73
+ f"This indicates the volume mount /data/ctfd-ipython:/root/.ipython is working correctly."
74
+ )
75
+
76
+ # Verify the file has content (should not be empty for a real SQLite database)
77
+ file_size = os .path .getsize (history_file )
78
+ assert file_size > 0 , (
79
+ f"IPython history file should not be empty: { history_file } (size: { file_size } bytes). "
80
+ f"This suggests IPython successfully wrote command history to the persistent volume."
81
+ )
82
+
83
+ # Test persistence by running another command and checking the file grows
84
+ second_commands = [
85
+ "# Second session to test persistence" ,
86
+ "y = 24" ,
87
+ "print(f'Half the answer is {y}')" ,
88
+ "exit()"
89
+ ]
90
+
91
+ second_input = "\n " .join (second_commands ) + "\n "
92
+
93
+ dojo_run (
94
+ "flask" ,
95
+ input = second_input ,
96
+ timeout = 60 ,
97
+ check = False
98
+ )
99
+
100
+ time .sleep (3 )
101
+
102
+ # Verify that the history file was updated (size should have grown or stayed same)
103
+ final_size = os .path .getsize (history_file )
104
+ assert final_size >= file_size , (
105
+ f"IPython history file should maintain or grow in size after second session. "
106
+ f"Initial: { file_size } bytes, Final: { final_size } bytes. "
107
+ f"This confirms history persistence across flask shell sessions."
108
+ )
109
+
110
+ print (f"✓ IPython history persistence test passed" )
111
+ print (f" History file: { history_file } " )
112
+ print (f" Final size: { final_size } bytes" )
113
+ print (f" Volume mount /data/ctfd-ipython:/root/.ipython is working correctly" )
114
+
115
+ except subprocess .TimeoutExpired :
116
+ pytest .fail (
117
+ "Flask shell command timed out. This may indicate:\n "
118
+ "1. The ctfd container is not responding\n "
119
+ "2. IPython is hanging waiting for input\n "
120
+ "3. The flask shell setup has issues"
121
+ )
122
+ except subprocess .CalledProcessError as e :
123
+ pytest .fail (
124
+ f"Flask command failed with exit code { e .returncode } :\n "
125
+ f"stdout: { e .stdout } \n "
126
+ f"stderr: { e .stderr } \n "
127
+ f"This may indicate issues with the ctfd container or flask setup"
128
+ )
129
+ except Exception as e :
130
+ pytest .fail (f"Flask history persistence test failed: { e } " )
0 commit comments